Redis On SSD方案
基础组件
现状
Redislabs的Redis On Flash(https://redislabs.com/lp/redis-enterprise-flash/),
360的pika(https://github.com/Qihoo360/pika)
美图的kvrocks(https://github.com/bitleak/kvrocks),
技术选型
其中Redis On Flash是商业化的产品,无开源代码,pika市面上使用的公司比较多,但缺点也很明显:
- 面向客户端的协议是Redis的二进制协议,而面向复制的却是基于google的protobuf格式,语义层面有割裂感。
- 复制是基于Rsync的多进程模式,这种复制模式比较重,出现问题也不好定位。
- 代码风格比较乱,此外网络类库也用的是360自家的pink,二次开发比较困难。
kvrocks和 Pika 有什么不一样?
- API 设计上更加兼容 Redis 原始语义,全部数据类型在同一个 DB, 不允许同一个 key 在不同类型中重复出现。
- 支持 Namespace 对不同业务数据做隔离
- 主从同步不适用 Rsync, 同步设计简单且问题定位也简单
- 支持对慢请求 profiling,问题定位更加简单
- 代码更加简洁 (主观看法)
最终方案
kvrocks
kvrocks
安装部署
Supported platforms
- centos 6/7
- ubuntu
- macosx
requirements
- g++ (required by c++11, version >= 4.8)
- autoconf automake libtool
Build
NOTE: You shoud install the snappy first:
# Centos/Redhat
sudo yum install -y snappy snappy-devel autoconf automake libtool
# Ubuntu
sudo apt-get install libsnappy-dev autoconf automake libtool
# MACOSX
brew install snappy
$ git clone --recursive https://github.com/bitleak/kvrocks.git
$ cd kvrocks
$ make -j4
run
$ ./src/kvrocks -c kvrocks.conf
支持命令
String Commands
Command | Supported OR Not | Desc |
---|---|---|
get | √ | |
getrange | √ | |
getset | √ | |
incr | √ | |
incrby | √ | |
incrbyfloat | √ | |
mget | √ | |
mset | √ | |
msetnx | √ | |
psetex | √ | only supports second |
set | √ | |
setex | √ | |
setnx | √ | |
setrange | √ | |
strlen | √ |
Hash Commands
Command | Supported OR Not | Desc |
---|---|---|
hdel | √ | |
hexists | √ | |
hget | √ | |
hgetall | √ | |
hincrby | √ | |
hincrbyfloat | √ | |
hkeys | √ | |
hlen | √ | |
hmget | √ | |
hmset | √ | |
hset | √ | |
hsetnx | √ | |
hstrlen | √ | |
hvals | √ | |
hscan | √ |
List Commands
Command | Supported OR Not | Desc |
---|---|---|
blpop | √ | |
brpop | √ | |
brpoplpush | X | |
lindex | √ | Caution: linsert is O(N) operation, don’t use it when list was extreme long |
linsert | √ | |
llen | √ | |
lpop | √ | |
lpush | √ | |
lpushx | √ | |
lrange | √ | |
lrem | √ | Caution: lrem is O(N) operation, don’t use it when list was extreme long |
lset | √ | |
ltrim | √ | Caution: ltrim is O(N) operation, don’t use it when list was extreme long |
rpop | √ | |
rpoplpush | √ | |
rpush | √ | |
rpushx | √ |
Set Commands
Command | Supported OR Not | Desc |
---|---|---|
sadd | √ | |
scard | √ | |
sdiff | √ | |
sdiffstore | √ | |
sinter | √ | |
sinterstore | √ | |
sismember | √ | |
smembers | √ | |
smove | √ | |
spop | √ | pop the member with key oreder |
srandmember | √ | always first N members if not changed |
srem | √ | |
sunion | √ | |
sunionstore | √ | |
sscan | √ |
ZSet Commands
Command | Supported OR Not | Desc |
---|---|---|
bzpopmin | X | |
bzpopmax | X | |
zadd | √ | |
zcard | √ | |
zcount | √ | |
zincrby | √ | |
zinterstore | √ | |
zlexcount | √ | |
zpopmin | √ | |
zpopmax | √ | |
zrange | √ | |
zrangebylex | √ | |
zrangebyscore | √ | |
zrank | √ | |
zrem | √ | |
zremrangebylex | √ | |
zremrangebyrank | √ | |
zremrangebyscore | √ | |
zrevrange | √ | |
zrevrangebylex | X | |
zrevrangebyscore | √ | |
zscan | √ | |
zscore | √ | |
zmscore | √ | multi zscore |
zunionscore | √ |
Key Commands
Command | Supported OR Not | Desc |
---|---|---|
del | √ | |
dump | X | |
exists | √ | |
expire | √ | |
expireat | √ | |
keys | √ | |
persist | √ | |
pexpire | √ | precision is seconds |
pexpireat | √ | precision is seconds |
pttl | √ | |
ttl | √ | |
type | √ | |
scan | √ | |
rename | X | |
randomkey | √ |
Bit Commands
Command | Supported OR Not | Desc |
---|---|---|
getbit | √ | |
setbit | √ | |
bitcount | √ | |
bitpos | √ | |
bitfield | X | |
bitop | X |
NOTE : String and Bitmap is different type in kvrocks, so you can’t do bit with string, vice versa.
Pub/Sub Commands
Command | Supported OR Not | Desc |
---|---|---|
psubscribe | √ | |
publish | √ | |
pubsub | √ | |
punsubscribe | √ | |
subscribe | √ | |
unsubscribe | √ |
Sortedint Commands
Command | Supported OR Not | Desc |
---|---|---|
sicard | √ | like scard |
siadd | √ | like sadd, but member is int |
sirem | √ | like srem, but member is int |
sirange | √ | sirange key offset count cursor since_id |
sirevrange | √ | sirevrange key offset count cursor max_id |
siexists | √ | siexists key member1 (member2 …) |
Administrator Commands
Command | Supported OR Not | Desc |
---|---|---|
monitor | √ | |
info | √ | |
config | √ | |
dbsize | √ | |
namespace | √ | |
flushdb | √ | |
flushall | √ |
NOTE : The db size was updated async after execute dbsize scan
command
GEO Commands
Command | Supported OR Not | Desc |
---|---|---|
geoadd | √ | |
geodist | √ | |
geohash | √ | |
geopos | √ | |
georadius | √ | |
georadiusbymember | √ |
Hyperloglog Commands
Not Supported
多租户
https://github.com/bitleak/kvrocks/blob/master/docs/multi-tenant.CN.md
名称空间用于隔离用户之间的数据。与通过requirepass访问所有redis数据库不同,我们为每个命名空间使用一个令牌。clients通过令牌来访问redis,而requirepass已升级为管理员令牌,只有管理员令牌才允许访问命名空间命令,以及某些命令,例如config,slaveof,bgave等…
# add token
127.0.0.1:6666> namespace add ns1 mytoken
OK
# update token
127.0.0.1:6666> namespace set ns1 new_token
OK
# list namespace
127.0.0.1:6666> namespace get *
1) "ns1"
2) "new_token"
3) "__namespace"
4) "foobared"
# delete namespace
127.0.0.1:6666> namespace del ns1
OK
kvrocks 允许通过一个实例在线增加或者减少 token, 每个 token 之间的数据是隔离的。 实现上也比较简单,在分配 token 的时候关联一个业务标识(Namespace), 在业务写入数据的时候会自动在 key 前面增加这个标识,读取数据 key 的时候自动去掉标识。
假设我们配置了一个 namespace N1 对应 token T1, 当业务使用这个 token 执行 SET a 1
, 那么在存储里面实际上会变成类似 T1|a => 1
,读取数据 key 的时候会自动去掉 T1。
对应Java代码实现:
public static void testRedis() {
//连接本地的 Redis 服务
Jedis jedis = new Jedis("192.168.8.95", 6379);
// 如果 Redis 服务设置来密码,需要下面这行,没有就不需要
jedis.auth("T1");
System.out.println("连接成功");
//查看服务是否运行
System.out.println("服务正在运行: " + jedis.ping());
jedis.set("a", "1");
System.out.println(jedis.get("a"));
}
参数定义
参数名 | 默认值 | 描述 | 需要调整 |
---|---|---|---|
bind | 0.0.0.0 | 默认情况下,kvrocks监听来自所有网络接口的连接在服务器上可用。可以只听一个或多个接口使用“bind”配置指令,后面跟着一个or更多IP地址。 | 是 |
port | 6666 | 接受指定端口上的连接 | 是 |
timeout | 0 | 在客户端空闲N秒后关闭连接 | |
workers | 8 | 工作线程的数量的增加或减少会影响性能.一般CPU核数-2 | 是 |
repl-workers | 1 | 复制工作线程的数量,增加或减少它将影响复制性能 | 否 |
daemonize | no | 默认情况下,kvrocks不作为守护进程运行。如果你需要的话,用yes。注意kvrocks将在/var/run/kvrocks.中写入一个pid文件当监控pid | 是 |
maxclients | 10000 | 设置同时连接的客户端的最大数量。默认情况下这个限制被设置为10000个客户端,但是如果配置超过操作系统进程文件限制,此配置无效。一旦达到限制,服务器将关闭所有发送的新连接,错误“达到的最大客户数量” | 是 |
requirepass | foobared | redis-cli操作管理员密码 | 是 |
masterauth | foobared | master-slave之间身份验证。如果配置了requirepass, 必须配置此参数=requirepass | 是 |
db-name | change.me.db | 主缓冲复制将检查数据库名称是否匹配。如果没有,slave应该拒绝从主服务器同步数据库。不要使用默认值,设置db-name来标识集群 | 是 |
dir | /tmp/kvrocks | 工作目录。DB将写入到这个目录中,注意,这里必须指定目录,而不是文件名。 | 是 |
pidfile | /var/run/kvrocks.pid | 当以daemonized运行时,kvrocks在${CONFIG_DIR}/kvrocks中写入一个pid文件。您可以在这里指定自定义pid文件位置。 | 是 |
slave-read-only | yes | 从节点只读 | 否 |
slave-priority | 100 | 用来主从选举参数, 较低优先级可升级为master。如果有三个优先级为10,100,25的slave,哨兵就会选择优先级为10的那一个,那是最低的 | 否 |
tcp-backlog | 511 | backlog是tcp一个连接队列,tcp三次握手后先添加到队列中再处理 | 是 |
repl-bind | 0.0.0.0 | 绑定从节点 | 否 |
slave-serve-stale-data | yes | 当从服务器失去与主服务器的连接时,或者当复制时仍在进行中,slave可以以两种不同的方式行动: 1) 如果slave-serve- stal- data设置为‘yes’(默认),那么slave将会仍然回复客户端请求,可能使用过期的数据,或者如果是第一次同步,数据集可能是空的。 2)如果slave-serve- stal- data设置为“no”,slave将回答为所有类型的命令都出现了“与正在进行中的主同步”的错误 |
否 |
max-replication-mb | 0 | 复制应该使用的最大允许速率(MB/s)。如果速率超过最大复制mb,复制将会变慢。默认值:0(即没有限制) | 否 |
max-io-mb | 500 | 允许的刷新和压缩的最大聚合写入速率(MB/s)。如果速率超过最大io-mb, io将会变慢。0没有限制,默认值:500 | 否 |
max-db-size | 0 | RocksDB应该使用的最大允许空间(GB)。如果SST文件的总大小超过max_allowed_space,写入RocksDB将会失败。 请参见:https://github.com/facebook/rocksdb/wiki/Managing-Disk-Space-Utilization 默认值:0(即没有限制) |
否 |
max-backup-to-keep | 1 | 要保留的最大备份,服务器cron将每分钟运行一次,以检查当前的数量备份,如果超过要保留的最大备份num,则清除旧备份。如果为0,则不保留备份。 | 否 |
max-backup-keep-hours | 168 | 保存备份的最长时间。如果max-backup-keep-hours为0,则不会清除任何备份。 默认值:1周 |
否 |
codis-enabled | no | 使kvrocks支持codis协议,如果db在第一次打开时启用了codis模式,此选项在重启后不能被禁用,反之亦然 | codis集群使用 |
slowlog-log-slower-than | 100000 | 慢查时间 | 是 |
slowlog-max-len | 128 | 慢查存储长度 | 是 |
compact-cron | 0 3 * * * | 每天凌晨3点,压缩DB,执行bgsave保存DB数据 | 是 |
compaction-checker-range | 0-7 | 压缩检查器将处于活动状态的时间范围。意味压缩检测会再每天0~7点时间段 | 否 |
rocksdb.metadata_block_cache_size | 2048MB | ||
rocksdb.subkey_block_cache_size | 2048MB | ||
rocksdb.max_open_files | 4096 | ||
rocksdb.write_buffer_size | 64MB | ||
rocksdb.target_file_size_base | 256MB | ||
rocksdb.max_write_buffer_number | 4 | ||
rocksdb.max_background_flushes | 4 | ||
rocksdb.max_sub_compactions | 2 | ||
rocksdb.wal_ttl_seconds | 3600 | ||
rocksdb.wal_size_limit_mb | 100GB | ||
rocksdb.block_size | 4KB | ||
rocksdb.cache_index_and_filter_blocks | no | ||
rocksdb.compression | snappy | ||
rocksdb.compaction_readahead_size | 2MB | ||
rocksdb.delayed_write_rate | 0 | ||
rocksdb.enable_pipelined_write | no | ||
rocksdb.level0_slowdown_writes_trigger | 20 | ||
rocksdb.stats_dump_period_sec | 0 | ||
rocksdb.disable_auto_compactions | no |
主从同步
原理
Redis的主从复制模式可以将主节点的数据改变同步给从节点, 这样从节点就可以起到两个作用: 第一, 作为主节点的一个备份, 一旦主节点出了
故障不可达的情况, 从节点可以作为后备“顶”上来, 并且保证数据尽量不丢失(主从复制是最终一致性) 。 第二, 从节点可以扩展主节点的读能力, 一
旦主节点不能支撑住大并发量的读操作, 从节点可以在一定程度上帮助主节点分担读压力
架构图
一主一从
一主多从(星形结构)
一主多从(树状主从结构)
!
主从命令
kvrocks2个进程,分别占用6379、6679 2个端口。
设置主从
主从切换操作:
缺点
- 一旦主节点出现故障, 需要手动将一个从节点晋升为主节点, 同时需要修改应用方的主节点地址, 还需要命令其他从节点去复制新的主节点, 整
个过程都需要人工干预 - 主节点的写能力受到单机的限制
- 主节点的存储能力受到单机的限制
哨兵
原理
- 监控: Sentinel节点会定期检测Redis数据节点、 其余Sentinel节点是否可达
- 通知: Sentinel节点会将故障转移的结果通知给应用方
- 主节点故障转移: 实现从节点晋升为主节点并维护后续正确的主从关系
- 配置提供者: 在Redis Sentinel结构中, 客户端在初始化的时候连接的是Sentinel节点集合, 从中获取主节点信息
架构图
安装部署
- 下载redis安装包,拷贝sentinel到/usr/local/bin下
-
修改配置
port 26379 # 端口 daemonize yes # 后台运行 pidfile "/root/sentinel-01/redis-sentinel.pid" # 进程ID dir "/root/sentinel-01" # 工作目录 sentinel monitor mymaster 192.168.8.95 6379 2 # Sentinel节点要监控的是一个名字叫做<master-name>, ip地址和端口为<ip><port>的主节点。 <quorum>代表要判定主节点最终不可达所需要的票数。<quorum>参数用于故障发现和判定, 例如将quorum配置为2, 代表至少有2个Sentinel节点认为主节点不可达, 那么这个不可达的判定才是客观的。对于<quorum>设置的越小, 那么达到下线的条件越宽松, 反之越严格。 一般建议将其设置为Sentinel节点的一半加1。
-
启动哨兵
redis-sentinel /root/sentinel-01/sentinel.conf
常用命令
sentinel masters
展示所有被监控的主节点状态以及相关的统计信息
sentinel master
展示指定
sentinel slaves
展示指定
sentinel sentinels
展示指定
sentinel failover
对指定
sentinel ckquorum
检测当前可达的Sentinel节点总数是否达到
sentinel flushconfig
将Sentinel节点的配置强制刷到磁盘上, 这个命令Sentinel节点自身用得比较多, 对于开发和运维人员只有当外部原因(例如磁盘损坏) 造成配置文件损坏或者丢失时, 这个命令是很有用的
sentinel remove
取消当前Sentinel节点对于指定
注:这个命令仅仅对当前Sentinel节点有效
sentinel monitor
这个命令和配置文件中的含义是完全一样的, 只不过是通过命令的形式来完成Sentinel节点对主节点的监控
Redis Sentinel客户端的基本步骤
- 遍历Sentinel节点集合获取一个可用的Sentinel节点, 后面会介绍Sentinel节点之间可以共享数据, 所以从任意一个Sentinel节点获取主节点信息都是可以的
- 通过sentinel get-master-addr-by-name master-name这个API来获取对应主节点的相关信息
-
验证当前获取的“主节点”是真正的主节点, 这样做的目的是为了防止故障转移期间主节点的变化
-
保持和Sentinel节点集合的“联系”, 时刻获取关于主节点的相关“信息”
相关代码调用
public static void sentinel() {
Set<String> sentinels = new HashSet<>();
sentinels.add("192.168.8.95:26379");
sentinels.add("192.168.8.95:26380");
sentinels.add("192.168.8.95:26381");
//创建JedisSentinelPool
JedisSentinelPool sentinelPool = new JedisSentinelPool("mymaster", sentinels);
int count=0;
//测试死循环的情况下主服务器出现宕机的情况下sentinel发生故障转移的测试
while(true){
count ++;
Jedis jedis = null;
try{
jedis = sentinelPool.getResource();
int index = new Random().nextInt();
//对redis保存key和value
String key = "k-" + index;
String value = "v-" + index;
jedis.set(key,value);
if (count % 100 == 0) {
//打印日志信息
System.out.println(String.format("key:%s,value:%s", key, jedis.get(key)));
}
//睡眠时间
TimeUnit.MILLISECONDS.sleep(10);
}catch (Exception e){
e.printStackTrace();
}finally {
if (jedis!=null){
jedis.close();
}
}
}
}
缺点
- 主节点的写能力受到单机的限制
- 主节点的存储能力受到单机的限制
扩缩容(主从/哨兵模式)
- 新购集群,集群同步slave2节点数据
- 判断slave2节点数据offset与新建集群offset基本一致
- 切换slave2为master, 移除原集群master和slave-1
- 从新集群选Master节点,移除slave-2
Codis
由于zk和codis的组建开销,该方案比较适合做大集群多租户方案
如何使用
- 客户端直接通过域名+端口连接和单机redis连接没有区别
- 也可以通过zk获取可用proxy进行连接 - 备选
- codis也提供java版客户端jodis - 备选
部署方案
Codis 分片原理
- Codis会把所有的key分成1024个槽, 这1024个槽对应着的就是Redis的集群(Group),这个在Codis中是会在内存中维护着这1024个槽与Redis实例的映射关系
- 槽是可以配置,可以设置成 2048 或者是4096个。Redis的节点数量偏多的话,可以设置槽多一些
- 分片算法,先是把key进行CRC32 后,得到一个32位的数字,然后再hash%1024后得到一个余数,这个值就是这个key对应着的槽
Codis 3.x 组件简介
- Codis Server:基于 redis-3.2.8 分支开发。增加了额外的数据结构,以支持 slot 有关的操作以及数据迁移指令
- Codis Proxy:客户端连接的 Redis 代理服务, 实现了 Redis 协议。 除部分命令不支持以外(不支持的命令列表),表现的和原生的 Redis 没有区别(就像 Twemproxy)。
- 对于同一个业务集群而言,可以同时部署多个 codis-proxy 实例;
- 不同 codis-proxy 之间由 codis-dashboard 保证状态同步。
- Codis Dashboard:集群管理工具,支持 codis-proxy、codis-server 的添加、删除,以及据迁移等操作。在集群状态发生改变时,codis-dashboard 维护集群下所有 codis-proxy 的状态的一致性。
- 对于同一个业务集群而言,同一个时刻 codis-dashboard 只能有 0个或者1个;
- 所有对集群的修改都必须通过 codis-dashboard 完成。
- Codis Admin:集群管理的命令行工具。
- 可用于控制 codis-proxy、codis-dashboard 状态以及访问外部存储。
- Codis FE:集群管理界面。
- 多个集群实例共享可以共享同一个前端展示页面;
- 通过配置文件管理后端 codis-dashboard 列表,配置文件可自动更新。
- Storage:为集群状态提供外部存储。
- 提供 Namespace 概念,不同集群的会按照不同 product name 进行组织;
- 目前仅提供了 Zookeeper、Etcd、Fs 三种实现,但是提供了抽象的 interface 可自行扩展。
Codis 主从切换问题
- 对下层的 redis 实例来说,当一个 group 的 master 挂掉的时候,应该让管理员清楚,并手动的操作,因为这涉及到了数据一致性等问题(redis的主从同步是最终一致性的)
- 因此 codis 不会自动的将某个 slave 升级成 master。
- 关于外部 codis-ha 工具(具体可以参考之前的章节),这是一个通过 codis-dashboard 开放的 RESTful API 实现自动切换主从的工具。
- 该工具会在检测到 master 挂掉的时候主动应用主从切换策略,提升单个 slave 成为新的 master。
- 需要注意,codis 将其中一个 slave 升级为 master 时,该组内其他 slave 实例是不会自动改变状态的,这些 slave 仍将试图从旧的 master 上同步数据,因而会导致组内新的 master 和其他 slave 之间的数据不一致。因此当出现主从切换时,需要管理员手动创建新的 sync action 来完成新 master 与 slave 之间的数据同步(codis-ha 不提供自动操作的工具,因为这样太不安全了)。
Codis-dashboard配置
coordinator_name = "zookeeper"
coordinator_addr = "127.0.0.1:2181"
coordinator_auth = ""
# Set Codis Product Name/Auth.
product_name = "codis-ouyang"
product_auth = "teddy"
# Set bind address for admin(rpc), tcp only.
admin_addr = "0.0.0.0:18080"
# Set arguments for data migration (only accept 'sync' & 'semi-async').
migration_method = "semi-async"
migration_parallel_slots = 100
migration_async_maxbulks = 200
migration_async_maxbytes = "32mb"
migration_async_numkeys = 500
migration_timeout = "30s"
# Set configs for redis sentinel.
sentinel_client_timeout = "1s"
# 将这个主服务器判断为失效至少需要 1 个 Sentinel 同意
sentinel_quorum = 1
# 选项指定了在执行故障转移时, 最多可以有多少个从服务器同时对新的主服务器进行同步
sentinel_parallel_syncs = 1
# 指定了 Sentinel 认为服务器已经断线所需的毫秒数(判定为主观下线SDOWN)。
sentinel_down_after = "2s"
sentinel_failover_timeout = "5m"
codis-dashboard启动
nohup /root/codis3.2.2/codis-dashboard --ncpu=4 --config=dashboard.toml --log=./dashboard.log --log-level=DEBUG &
Codis- Proxy配置
# Set Codis Product Name/Auth.
product_name = "codis-ouyang"
product_auth = "teddy"
# Set auth for client session
# 1. product_auth is used for auth validation among codis-dashboard,
# codis-proxy and codis-server.
# 2. session_auth is different from product_auth, it requires clients
# to issue AUTH <PASSWORD> before processing any other commands.
session_auth = "codis-client-auth-teddy"
# Set bind address for admin(rpc), tcp only.
admin_addr = "0.0.0.0:11080"
# Set bind address for proxy, proto_type can be "tcp", "tcp4", "tcp6", "unix" or "unixpacket".
proto_type = "tcp4"
proxy_addr = "0.0.0.0:19000"
# Set jodis address & session timeout
# 1. jodis_name is short for jodis_coordinator_name, only accept "zookeeper" & "etcd".
# 2. jodis_addr is short for jodis_coordinator_addr
# 3. jodis_auth is short for jodis_coordinator_auth, for zookeeper/etcd, "user:password" is accepted.
# 4. proxy will be registered as node:
# if jodis_compatible = true (not suggested):
# /zk/codis/db_{PRODUCT_NAME}/proxy-{HASHID} (compatible with Codis2.0)
# or else
# /jodis/{PRODUCT_NAME}/proxy-{HASHID}
jodis_name = "zookeeper"
jodis_addr = "127.0.0.1:2181"
jodis_auth = ""
jodis_timeout = "20s"
jodis_compatible = false
# Set datacenter of proxy.
proxy_datacenter = ""
# Set max number of alive sessions.
proxy_max_clients = 1000
# Set max offheap memory size. (0 to disable)
proxy_max_offheap_size = "1024mb"
# Set heap placeholder to reduce GC frequency.
proxy_heap_placeholder = "256mb"
# Proxy will ping backend redis (and clear 'MASTERDOWN' state) in a predefined interval. (0 to disable)
backend_ping_period = "5s"
# Set backend recv buffer size & timeout.
backend_recv_bufsize = "128kb"
backend_recv_timeout = "30s"
# Set backend send buffer & timeout.
backend_send_bufsize = "128kb"
backend_send_timeout = "30s"
# Set backend pipeline buffer size.
backend_max_pipeline = 20480
# Set backend never read replica groups, default is false
backend_primary_only = false
# Set backend parallel connections per server
backend_primary_parallel = 1
backend_replica_parallel = 1
# Set backend tcp keepalive period. (0 to disable)
backend_keepalive_period = "75s"
# Set number of databases of backend.
backend_number_databases = 16
# If there is no request from client for a long time, the connection will be closed. (0 to disable)
# Set session recv buffer size & timeout.
session_recv_bufsize = "128kb"
session_recv_timeout = "30m"
# Set session send buffer size & timeout.
session_send_bufsize = "64kb"
session_send_timeout = "30s"
# Make sure this is higher than the max number of requests for each pipeline request, or your client may be blocked.
# Set session pipeline buffer size.
session_max_pipeline = 10000
# Set session tcp keepalive period. (0 to disable)
session_keepalive_period = "75s"
# Set session to be sensitive to failures. Default is false, instead of closing socket, proxy will send an error response to client.
session_break_on_failure = false
# Set metrics server (such as http://localhost:28000), proxy will report json formatted metrics to specified server in a predefined period.
metrics_report_server = ""
metrics_report_period = "1s"
# Set influxdb server (such as http://localhost:8086), proxy will report metrics to influxdb.
metrics_report_influxdb_server = ""
metrics_report_influxdb_period = "1s"
metrics_report_influxdb_username = ""
metrics_report_influxdb_password = ""
metrics_report_influxdb_database = ""
# Set statsd server (such as localhost:8125), proxy will report metrics to statsd.
metrics_report_statsd_server = ""
metrics_report_statsd_period = "1s"
metrics_report_statsd_prefix = ""
Codis- Proxy启动
# 启动
nohup /root/codis3.2.2/codis-proxy --ncpu=4 --config=proxy.toml --log=proxy.log --log-level=WARN &
# 加入集群
./codis-admin --dashboard=127.0.0.1:18080 --create-proxy --addr=192.168.8.96:11080
容灾
- 多个实例部署codis-proxy保证服务器可用
- codis-proxy由SLB提供负载均衡,提供健康检测
- kvrocks一主一从,redis-sentinal保证主从切换服务可用
扩容
新实例部署完kvrocks后通过codis-admin执行如下命令进行扩容, 也可以通过Codis-FE管理界面操作
# 创建组
./codis-admin --dashboard=127.0.0.1:18080 --create-group --gid=4 ## gid 新组id, 递增
# 将kvrocks加入组
./codis-admin --dashboard=127.0.0.1:18080 --group-add --gid=4 --addr=192.168.8.95:6379 # master
./codis-admin --dashboard=127.0.0.1:18080 --group-add --gid=4 --addr=192.168.8.96:6379 # slave
# slot 自动负载均衡
./codis-admin --dashboard=127.0.0.1:18080 --rebalance --confirm
# sentinel 监控同步新的group
./codis-admin --dashboard=127.0.0.1:18080 --sentinel-resync
多租户
kvrocks本身支持多租户但由于codis不支持多租户所以需要进行二次开发支持, 根据不同的requirepass区分不同用户
优点
-
支持 slot 同步迁移、异步迁移和并发迁移,对 key 大小无任何限制
-
基于 redis-sentinel 实现主备自动切换
-
提供了更加友好的 dashboard 和 fe 界面
-
配置简单、扩容方便,可以实现自动扩容迁移分桶数据
缺点
- 不再支持更新,最近一次更新在2018.7.29
- 不支持多租户,无法配合使用kvrocks的多租户功能
- 集群配置中心使用 zk 来实现,意味着在部署上增加了 zk 运维的代价
- 增加了 Proxy 作为中转层,所有在网络开销上要比单个 Redis 大
问题
- 当业务出现故障出现大量生产消耗连接数可能会对其他业务有影响
- Codis需要二次开发,包括支持多租户,Proxy健康检测等功能
成本核算
初步建议配置:
Codis、ZK组件规格:2 vCPU 2 GiB,密集计算型 ic5,Centos 8.2 64,高效云盘100GB ¥143.5每月
KVRocks实例规格:2 vCPU 2 GiB,密集计算型 ic5,Centos 8.2 64,ESSD云盘100GB,PL1 IOPS五万,¥189每月
- 1台实例部署: codis-dashboard、codis-fe、codis-admin
- 3台实例部署:codis-proxy + redis-sentinel + zookeeper
- 6台实例部署:3个kvrocks集群(一主一从)
- 负载均衡SLB: 标准2, 支持连接数: 100000,新建连接数 (CPS): 10000,每秒查询数 (QPS): 10000, ¥285.00每月
总计: 1426
差不多价格的Redis规格 ¥1,610 :
- Redis 5.0架构类型:集群版分片数:4分片节点类型:双副本实例规格:24G集群版(4节点)
- 40000 最大连接数:40000 每分片带宽:96MByte 最大内网带宽:384MByte
数据迁移
-
迁移redis数据到kvrocks
kvrocks官方文档介绍方案,但通过测试数据迁移不成功
redis-shake是阿里云Redis&MongoDB团队开源的用于redis数据同步的工具。
支持源redis到目的redis数据同步
监控
kvrocks监控
redis_exporter方案
https://github.com/oliver006/redis_exporter
codis-proxy监控
性能压测
基本压测
测试环境
8vCPU, 8G内存 SSD磁盘 ECS 2台;
搭建主从环境
操作系统: Centos 7.6 64位
redis版本:5.0.8
kvrocks版本: 1.1.27
测试工具
redis-benchmark
测试所在机器:从节点
测试结果
-
1024个客户端,10 万随机 key 连续 执行10 万次命令
redis-benchmark -h 192.168.24.43 -p 6379 -a kvs -c 1024 -r 100000 -n 100000 -q
执行结果:
减少客户端个数 512,10 万随机 key 连续 执行10 万次命令。CPU同样出现占用高的问题
单独测试命令:
[root@ba-test-kvrocks-02 ~]# redis-benchmark -h 192.168.24.43 -p 6379 -a kvs -c 512 -t set,get -r 100000 -n 1000000 -q
SET: 85222.43 requests per second
GET: 86752.84 requests per second
[root@ba-test-kvrocks-02 ~]# redis-benchmark -h 192.168.24.43 -p 6379 -a kvs -c 512 -t lpush,rpush -r 100000 -n 1000000 -q
LPUSH: 30162.27 requests per second
RPUSH: 33840.95 requests per second
[root@ba-test-kvrocks-02 ~]# redis-benchmark -h 192.168.24.43 -p 6379 -a kvs -c 512 -t lpop,rpop -r 100000 -n 1000000 -q
LPOP: 23703.98 requests per second
RPOP: 25206.06 requests per second
执行push、pop等命令CPU都有相应提升,证明:kvrocks在push、pop命令上性能存在问题
-
kvrocks不同数据大小(bit)下GET、SET响应时间
[root@ba-test-kvrocks-02 ~]# redis-benchmark -h 192.168.24.43 -p 6379 -a kvs -c 512 -d 128 -t get,set -r 100000 -n 1000000 -q SET: 84040.67 requests per second GET: 81973.93 requests per second [root@ba-test-kvrocks-02 ~]# redis-benchmark -h 192.168.24.43 -p 6379 -a kvs -c 512 -d 512 -t get,set -r 100000 -n 1000000 -q SET: 78179.97 requests per second GET: 77845.24 requests per second [root@ba-test-kvrocks-02 ~]# redis-benchmark -h 192.168.24.43 -p 6379 -a kvs -c 512 -d 1024 -t get,set -r 100000 -n 1000000 -q SET: 58271.66 requests per second GET: 76946.75 requests per second [root@ba-test-kvrocks-02 ~]# redis-benchmark -h 192.168.24.43 -p 6379 -a kvs -c 4096 -d 1024 -t get,set -r 100000 -n 1000000 -q SET: 49490.25 requests per second GET: 52391.68 requests per secon