云数据库 GeminiDB
云数据库 GeminiDB
- 最新动态
- 功能总览
- 服务公告
- 产品介绍
-
GeminiDB Redis接口
- 产品介绍
- 计费说明
- 快速入门
-
用户指南
- 权限管理
- 购买GeminiDB Redis实例
- 实例连接及管理
-
数据迁移
- Redis数据迁移方案概览
- 使用DRS服务将GeminiDB Redis迁移到Redis(推荐)
- 阿里云数据库Redis/Tair到GeminiDB Redis的迁移
- 腾讯云Redis到GeminiDB Redis的迁移
- 使用DRS服务将自建Redis或者Redis集群迁移到GeminiDB Redis(推荐)
- 通过Redis-Shake迁移工具将自建Redis迁移到GeminiDB Redis
- 使用Redis-Shake工具将RDB文件/AOF文件 导入到GeminiDB Redis
- 使用数据导入功能将RDB文件恢复到GeminiDB Redis(推荐)
- Kvrocks到GeminiDB Redis的迁移
- Pika到GeminiDB Redis的迁移
- SSDB到GeminiDB Redis的迁移
- LevelDB到GeminiDB Redis的迁移
- RocksDB到GeminiDB Redis的迁移
- AWS ElasticCache for Redis数据库到GeminiDB Redis的迁移
- 迁移后Redis数据一致性校验
- 实例管理
- 变更实例
- 数据备份
- 数据恢复
- 诊断分析
- 账号与安全
- 参数管理
- 日志与审计
- 查看监控指标与配置告警
- GeminiDB Redis标签管理
- GeminiDB Redis用户资源配额
- 通过GeminiDB Redis实现MySQL内存加速
- 开发参考
- 最佳实践
- 性能白皮书
-
常见问题
- 高频常见问题
-
产品咨询
- GeminiDB Redis和开源Redis、其他开源Redis云服务有什么区别?
- 和开源Redis相比,GeminiDB Redis性能如何?
- GeminiDB Redis兼容Redis哪些版本,兼容哪些命令,客户端连接是否需要修改
- 自建Redis是否可以搬迁至GeminiDB Redis,需要注意什么
- 什么是GeminiDB Redis实例可用性
- GeminiDB Redis实例总容量是总内存吗,内存和容量之间是什么联系
- 购买GeminiDB Redis实例时,如何选择合适的节点规格和节点数量?
- 购买x GB的GeminiDB Redis的实例,优选主备还是集群?
- GeminiDB Redis持久化机制是怎样的,会丢数据吗
- GeminiDB Redis的内存淘汰策略是什么
- GeminiDB Redis是否支持布隆过滤器等modules
- 计费相关
-
数据库使用
- scan指定match参数,数据中确实存在匹配的key,为什么返回的是空
- 业务侧原本做了数据分片,切换到GeminiDB Redis后如何处理这部分逻辑
- GeminiDB Redis接口是否支持keys命令的模糊查询
- GeminiDB Redis是否支持多DB
- 对于scan类的操作,GeminiDB Redis接口与开源Redis 5.0的返回值顺序为什么有差异
- 针对某些不合法命令,GeminiDB Redis接口与开源Redis 5.0的报错信息为什么有差异
- 如何处理报错:CROSSSLOT Keys in request don't hash to the same slot
- GeminiDB Redis单次事务推荐包含的命令条数
- GeminiDB Redis集群版实例中,哪些命令需要使用hashtag
- 如何处理报错“ERR unknown command sentinel"
- 对于阻塞命令,GeminiDB Redis接口(主备实例)与开源Redis的返回值为什么可能有差异
- GeminiDB Redis存储扩容需要多久,对业务有影响吗?
- GeminiDB Redis多个节点同时扩容需要多长时间,对业务影响如何?
- GeminiDB Redis规格变更包含的在线变更和离线变更有什么区别,通常需要多长时间,对业务有哪些影响?
- GeminiDB Redis版本补丁升级包含的在线升级和离线升级有什么区别,通常需要多长时间,对业务有哪些影响?
- GeminiDB Redis备份文件是否可以下载到本地,是否支持线下恢复数据
- GeminiDB Redis数据备份工作机制是怎样的,对业务有哪些影响?
- 购买GeminiDB Redis 1U*2节点特惠型实例后,业务访问量比较少,但CPU占用率比较高,是什么原因?
- GeminiDB Redis监控面板上key数量下降又恢复至正常数量是什么原因?
- GeminiDB Redis节点CPU偶发冲高,可能是哪些原因
- GeminiDB Redis如何从5.0版本升级到6.2版本
- GeminiDB Redis什么时候进入只读
-
数据库连接
- 如何接入GeminiDB Redis
- 如何使用GeminiDB Redis提供的多个节点IP地址
- GeminiDB Redis提供的ELB的实现方式是怎样的
- 如何创建和连接弹性云服务器
- GeminiDB Redis实例购买成功后是否支持更换VPC
- 绑定了弹性公网IP但是连接不上数据库
- 内网如何访问GeminiDB Redis
- GeminiDB Redis自带的负载均衡地址是否能绑定公网IP?如何通过公网连接GeminiDB Redis实例?
- 设置了安全组,还需要设置负载均衡内网访问控制吗?
- 如何处理客户端连接池报错“Could not get a resource from the pool”
- 常见客户端报错及解决方法
- 备份与恢复
- 区域和可用区
-
数据迁移
- DRS上找不到GeminiDB Redis链路
- 报错ERR the worker queue is full, and the request cannot be excecuted
- 报错ERR the request queue of io thread is full, and the request cannot be excecuted
- 报错 read error, please check source redis log or network
- 报错 slaveping_thread.cc-ThreadMain-90: error: Ping master error
- 同步状态正向迁移速度太慢
- 同步状态正向迁移速度太快,报错:ERR server reply timeout, some responses may lose, but requests have been executed
- 4.0、5.0以及6.2版本的自建Redis能迁移至GeminiDB Redis吗?
- 自建Redis主备、集群实例如何迁移到GeminiDB Redis?
- 为什么阿里云Redis、腾讯云Redis等云服务不能使用DRS进行数据迁移?
- 自建主备Redis,迁移到GeminiDB Redis集群,需要考虑哪些因素?
- 迁移完成后数据量变少了,100GB的数据迁移到GeminiDB Redis只有20-30GB,数据是不是没迁移完?
- 内存加速
- 资源冻结/释放/删除/退订
- GeminiDB Influx接口
-
GeminiDB Cassandra接口
- 产品介绍
- 计费说明
- 快速入门
-
用户指南
- 权限管理
- 购买GeminiDB Cassandra实例
- 实例连接及管理
- 数据迁移
- 实例生命周期管理
- 变更实例
- 同城容灾
- 异地双活
- 数据备份
- 数据恢复
- 参数管理
- 日志与审计
- 查看监控指标与配置告警
- 企业项目
- GeminiDB Cassandra标签管理
- GeminiDB Cassandra用户资源配额
- 最佳实践
- 性能白皮书
- 常见问题
- GeminiDB (兼容DynamoDB API)实例
- HBase协议兼容版实例
- GeminiDB Mongo接口
- 技术白皮书
-
API参考
- 使用前必读
- API概览
- 如何调用API
- 快速入门
-
API v3(推荐)
- 查询API版本
- 接口版本和规格
-
实例管理
- 创建实例
- 删除实例
- 查询实例列表和详情
- 扩容实例存储容量
- 扩容实例的节点数量
- 缩容实例的节点数量
- 获取节点会话列表
- 查询实例节点会话统计信息
- 关闭实例节点会话
- 查询实例可变更规格
- 变更实例规格
- 修改实例的管理员密码
- 修改实例名称
- 变更实例安全组
- 数据库补丁升级
- 批量数据库补丁升级
- 创建冷数据存储
- 扩容冷数据存储
- 绑定/解绑弹性公网IP
- 切换实例SSL开关
- 重启实例
- 设置磁盘自动扩容策略
- 修改数据库端口
- 判断弱密码
- 修改副本集跨网段访问配置
- 删除扩容失败的节点
- 查询创建实例或扩容节点时需要的IP数量
- 查询磁盘自动扩容策略
- 变更实例存储容量
- 查询高危命令
- 修改高危命令
- 查询Redis实例的热key
- 设置Redis禁用命令
- 查询Redis禁用命令
- 删除Redis禁用命令
- 设置实例可维护时间段
- Redis主备切换
- 支持节点的开关机
- 查询GeminiDB Redis实例的大key
- 获取GeminiDB Redis的免密配置
- 支持修改GeminiDB Redis的免密配置
- 查询内存加速映射列表和详情
- 创建内存加速规则
- 解除内存加速映射
- 创建内存加速映射
- 修改内存加速规则
- 查询内存加速规则列表和详情
- 删除内存加速规则
- 开启/关闭实例数据导出
- 开启/关闭秒级监控
- 查询秒级监控配置
- 连接管理
- 备份与恢复
- 参数模板管理
- 管理数据库和账号
- 标签管理
- 日志管理
- 配额管理
- 容灾管理
- 任务管理
- 企业项目管理
- 实例负载均衡管理
- API v3(即将下线)
- 权限策略和授权项
- 附录
- SDK参考
- 场景代码示例
- 视频帮助
- 文档下载
- 通用参考
链接复制成功!
大Bitmap初始化
Bitmap,即位图类型,开源Redis直接使⽤STRING类型表达,因此可能会产⽣超⼤的STRING数据,进⽽在某些场景下出现⼤KEY的性能问题。GeminiDB Redis的Bitmap类型采⽤的是特殊编码的格式,内部采⽤分片算法,可以规避产⽣⼀个超⼤的STRING数据,并且可以⽀持更⾼效的随机位数的插入和删除操作。
但是,实际应⽤场景中,我们可能会从其它地⽅获取⼀个超⼤的Bitmap数据,⽽这些数据通常会⽤STRING类型来表达。 对于⼀个超⼤的Bitmap数据,例如 64 MB,如果直接使⽤ SET 命令插入GeminiDB Redis,会执⾏较⻓时间,并且对其它正常访问产⽣⼲扰,造成时延抖动。 因此我们提供了⼀套平滑的插入⽅案,其原理是,对于超⼤的初始数据,我们先将其拆分为较⼩的字串(例如1MB),然后⾸次插入仍然采⽤SET命令,然后通过⼀个GETBIT的只读命令将其转化为Bitmap类型,后续的字串,通过APPEND命令进⾏插入即可。
注意事项
- 该功能需升级到特定版本,您可以在管理控制台右上角,选择“工单 > 新建工单”提工单联系客服咨询实例版本是否支持该功能。
- 由于APPEND命令对顺序有要求,因此整个流程要避免出现APPEND乱序(并发APPEND的场景)。
- 可以使⽤PIPELINE模式加速,PIPELINE本⾝也是保证执⾏顺序的,因此不会有乱序的问题。
- 拆分的粒度可以根据实际情况选择,拆的越细,产生的时延毛刺就越小,但是初始化时间就越长,通常建议256KB-1MB左右的值。
代码参考
- C++
#include <string> #include <vector> #include "hiredis/hiredis.h" constexpr std::size_t kBitmapSubSize = 1024 * 1024; // 1 MB void SmoothInitBitmap(std::string bitmap) { // Split bitmap std::vector<std::string> sub_bitmaps; std::size_t pos = 0; while (pos < bitmap.size()) { sub_bitmaps.emplace_back(bitmap.substr(pos, kBitmapSubSize)); pos += kBitmapSubSize; } std::string key = "BITMAP_KEY"; // Connect to redis redisContext* redis = redisConnect("127.0.0.1", 6666); redisReply* reply = nullptr; // First part use 'SET' command reply = (redisReply*)redisCommand(redis, "SET %b %b", key.data(), key.size(), sub_bitmaps[0].data(), sub_bitmaps[0].size()); freeReplyObject(reply); // Use 'GETBIT' to transform to bitmap format reply = (redisReply*)redisCommand(redis, "GETBIT %b 0", key.data(), key.size()); freeReplyObject(reply); // Use 'APPEND' for remaining bitmap data for (auto i = 1u; i < sub_bitmaps.size(); ++i) { reply = (redisReply*)redisCommand(redis, "APPEND %b %b", key.data(), key.size(), sub_bitmaps[i].data(), sub_bitmaps[i].size()); freeReplyObject(reply); } } int main() { std::string bitmap ="123457890abcdef123457890abcdef123457890abcdef123457890abcdef123457890abcdef123456"; SmoothInitBitmap(bitmap); }
- JAVA(Jedis)
package nosql.cloud.huawei.jedis; import redis.clients.jedis.Jedis; import java.nio.ByteBuffer; import java.util.BitSet; public class BitMapOperation { private Jedis jedis; public BitMapOperation(Jedis jedis) { this.jedis = jedis; } /** * SetBit operation especially for big bitmap * * @param key key * @param value value * @param groupLength groupLength (Unit: byte) */ public void setBitGrouped(byte[] key, BitSet value, int groupLength) { if (value.isEmpty()) { jedis.set(key, new byte[0]); return; } byte[] byteArray = disposeBitMap(value); // round count int round = byteArray.length % groupLength == 0 ? byteArray.length / groupLength : byteArray.length / groupLength + 1; // last round length int lastPacketLength = byteArray.length % groupLength == 0 ? groupLength : byteArray.length % groupLength; if (round == 1) { // if only one round byte[] lastPacketByte = new byte[lastPacketLength]; System.arraycopy(byteArray, 0, lastPacketByte, 0, lastPacketLength); // set and getBit setAndGetBit(key, lastPacketByte); return; } byte[] packetByte = new byte[groupLength]; byte[] lastPacketByte = new byte[lastPacketLength]; for (int i = 0; i < round; i++) { if (i == 0) { // first set System.arraycopy(byteArray, i * groupLength, packetByte, 0, groupLength); // set and getBit setAndGetBit(key, packetByte); } else if (i != round - 1) { // regular append System.arraycopy(byteArray, i * groupLength, packetByte, 0, groupLength); jedis.append(key, packetByte); } else { // last append System.arraycopy(byteArray, i * groupLength, lastPacketByte, 0, lastPacketLength); jedis.append(key, lastPacketByte); } } } private byte[] disposeBitMap(BitSet bitSet) { // get words and count the number of word(Long) long[] words = bitSet.toLongArray(); int n = words.length; if (n == 0) return new byte[0]; for (int i = 0; i < n; i++) { // reverse words[i] = reverseLong(words[i]); } return longToBytes(words); } public static byte[] longToBytes(long[] longArray) { ByteBuffer buffer = ByteBuffer.allocate(longArray.length * 8); for (long value : longArray) { buffer.putLong(value); } return buffer.array(); } public void setAndGetBit(byte[] key, byte[] value) { jedis.set(key, value); jedis.getbit(key, 0); } public static long reverseLong(long n) { n = (n >>> 32) | (n << 32); n = ((n & 0xFFFF0000FFFF0000L) >>> 16) | ((n & 0x0000FFFF0000FFFFL) << 16); n = ((n & 0xFF00FF00FF00FF00L) >>> 8) | ((n & 0x00FF00FF00FF00FFL) << 8); n = ((n & 0xF0F0F0F0F0F0F0F0L) >>> 4) | ((n & 0x0F0F0F0F0F0F0F0FL) << 4); n = ((n & 0xCCCCCCCCCCCCCCCCL) >>> 2) | ((n & 0x3333333333333333L) << 2); n = ((n & 0xAAAAAAAAAAAAAAAAL) >>> 1) | ((n & 0x5555555555555555L) << 1); return n; } }
- Python
import redis import random import string from bitmap import BitMap # pip install bitmap # 参数 max_bytes = 1024 * 1024 * 64 # 构造一个64MB的bitmap max_bits = max_bytes * 8 # 一个byte可以存储8个bit,对应大概5亿多元素 # 这个方案不需要python内置的bitmap类型 # index_list 存储了所有要设置为1的下标 index_list = [] for i in range(1000000): index_list.append(random.randint(0, max_bits - 1)) # 使用bytearray构造位图 byte_array = bytearray(max_bytes) for i in index_list: index = i // 8 offset = i % 8 byte_array[index] |= (1 << (7 - offset)) # 转化成bytes类型,用于后续的redis操作 bitmap_str = bytes(byte_array) # 连接到redis r = redis.Redis(host='127.0.0.1', port=6379) r.execute_command("auth a") key = "BITMAP_KEY" # 分割参数 bitmap_pos = 0 bitmap_sub_size = 256 * 1024 # 调整分片大小 step = bitmap_sub_size - 1 # 处理第一部分 first_part = bitmap_str[bitmap_pos : bitmap_pos + step] r.execute_command("SET", key, first_part) r.execute_command("GETBIT", key, 0) # 使用getbit进行bitmap编码优化 # 处理剩余的部分 bitmap_pos += step while bitmap_pos < len(bitmap_str) : rest_part = bitmap_str[bitmap_pos : bitmap_pos + step] r.execute_command("APPEND", key, rest_part) bitmap_pos += step # 下面是测试验证的代码,注意会比较耗时,因为需要执行100w次getbit进行验证 # 注意,最后一个bitcount命令是O(n)命令,会产生百毫秒的毛刺,请勿随意在生产环境使用 # 构造一个python内置的bitmap类型进行数据验证(可选) bm = BitMap(max_bits) for i in index_list: bm.set(i) print('BitMap.count(): ' + str(bm.count())) # 调用redis命令校验是否设置正确 success = True for i in index_list: if r.execute_command("GETBIT", key, i) != 1: print('GETBIT check error, pos is' + str(i)) success = False if success: print('GETBIT check success') print("Bitcount: " + str(r.execute_command("BITCOUNT", key)))
父主题: 开发参考