Dynomite研究(Netflix数据库同步工具)

1、概述

Dynomite是Netflix实现的一个Redis数据库同步复制方案。其思路来自于亚马逊的Dynamo的白皮书 Dynamo whitepaper。目前支持Redis和Memcached,Dynomite的最终目标是能够在存储引擎上实现高效的,不复杂的高可用性和跨数据中心复制,即使存储引擎本身不提供这种功能。

Dynomite有数据中心、机架、节点的三层概念,数据中心可以有多个机架,机架可以有多个节点。每个机架数据是完整的,机架上的不同节点各有一部分数据,即是数据分片。大体原理可以看下这个文章关于Dynomite的部分原理解释。

2、安装配置

2.1、编译

可以通过以下步骤安装所需工具,使用wget下载特定版本代码并编译,目前最新稳定版l'l是v0.6

yum install -y git autoconf automake libtool openssl-devel net-tools
wget https://github.com/Netflix/dynomite/archive/v0.6.zip
unzip v0.6.zip 
cd dynomite-0.6
autoreconf -fvi
./configure --enable-debug=false
make
src/dynomite -h

在src目录下的 dynomite 就是编译结果

接着建立工作目录,并移动执行程序到工作目录下

mkdir -p /apps/dynomite/bin
mkdir -p /apps/dynomite/conf
cp src/dynomite /apps/dynomite/bin/

2.2、部署

为了方便测试,我在两个云服务器A、B分别部署两个Dynomite节点,A1、A2、B1、B2共四个节点构成集群。每个Dynomite代理一个Redis节点,在Dynomite所在机器本机上启动对应Redis即可。

也就是一个云服务器模拟一个数据中心,同一个云服务上的两个Dynomite节点模拟同一个机架上的两个节点。这样就可以模拟一个双数据中心集群,一个数据中心有一个机架,同机架的两个节点按数据分片规则保存部分数据。

首先每个服务器上起两个redis实例,分别用6378、6379端口,并打开远程连接。打开远程主要是为了方便后面进行非本机压测性能对比,实际使用时如果Dynomite和Redis在同服务器可以不开。

6378配置文件redis6378_simple.conf

bind 改为对应ip
protected-mode no
port 6378

6379配置redis6379_simple.conf

bind 改为对应ip
protected-mode no
port 6379

然后使用指令启动两个Redis实例

redis-server /etc/redis6378_simple.conf&
redis-server /etc/redis6379_simple.conf&

接着生成一个公钥私钥对,用下面的指令在 conf目录生成两个秘钥文件 dynomite.pem 和 dynomite.pem.pub,这两个文件后面要写到配置文件中。

ssh-keygen -t rsa -f conf/dynomite.pem

接着配置Dynomite节点,其主要配置项意义如下

rack             机架号
dyn_listen        dynomite集群同步数据的监听地址和端口
dyn_seeds         集群中其他节点的信息
data_store        存储类型  0 代表redis 1 代表memcached
listen            客户端访问数据的地址和端口
servers           本Dynomite节点对接的redis的ip,端口,权重。这个字段未来可能会支持列表的,即支持一个Dynomite节点对接多个Redis节点
tokens            可以认为是集群中的唯一编码,最好是按照一定规则区分编码,避免重复
secure_server_option 通讯加密方案,可选值'none'、'rack'、'datacenter'、'all',none表示无加密,rack表示不同机架间通讯加密,datacenter表示不同数据中心间通讯加密,all表示全部通讯加密。
recon_key_file     公钥文件
recon_iv_file      私钥文件
max_msgs           缓存的最大消息数,默认200000,可以配置大点

将下面实例的Dynomite节点的配置文件保存到目录/apps/dynomite/conf中。

A1对应的dynomite配置

dyn_o_mite:
  datacenter: dc1
  rack: rack1
  dyn_listen: 192.168.1.1:8101
  dyn_seeds:
  - 192.168.1.1:8201:rack1:dc1:112
  - 192.168.1.2:8101:rack1:dc2:211
  - 192.168.1.2:8201:rack1:dc2:212
  data_store: 0
  listen: 192.168.1.1:8102
  dyn_seed_provider: simple_provider
  servers:
  - 192.168.1.1:6378:1
  tokens: 111
  secure_server_option: none
  stats_listen: 127.0.0.1:30001
  recon_key_file: conf/dynomite.pem.pub
  recon_iv_file: conf/dynomite.pem
  max_msgs: 500000

A2对应的dynomite配置

dyn_o_mite:
  datacenter: dc1
  rack: rack1
  dyn_listen: 192.168.1.1:8201
  dyn_seeds:
  - 192.168.1.1:8101:rack1:dc1:111
  - 192.168.1.2:8101:rack1:dc2:211
  - 192.168.1.2:8201:rack1:dc2:212
  data_store: 0
  listen: 192.168.1.1:8202
  dyn_seed_provider: simple_provider
  servers:
  - 192.168.1.1:6379:1
  tokens: 112
  secure_server_option: none
  stats_listen: 127.0.0.1:30002
  recon_key_file: conf/dynomite.pem.pub
  recon_iv_file: conf/dynomite.pem  
  max_msgs: 500000  

B1对应的dynomite配置

dyn_o_mite:
  datacenter: dc2
  rack: rack1
  dyn_listen: 192.168.1.2:8101
  dyn_seeds:
  - 192.168.1.2:8201:rack1:dc2:212
  - 192.168.1.1:8101:rack1:dc1:111
  - 192.168.1.1:8201:rack1:dc1:112
  data_store: 0
  listen: 192.168.1.2:8102
  dyn_seed_provider: simple_provider
  servers:
  - 192.168.1.2:6378:1
  tokens: 211
  secure_server_option: none
  stats_listen: 127.0.0.1:30001
  recon_key_file: conf/dynomite.pem.pub
  recon_iv_file: conf/dynomite.pem  
  max_msgs: 500000  

B2对应的dynomite配置

dyn_o_mite:
  datacenter: dc2
  rack: rack1
  dyn_listen: 192.168.1.2:8201
  dyn_seeds:
  - 192.168.1.2:8101:rack1:dc2:211
  - 192.168.1.1:8101:rack1:dc1:111
  - 192.168.1.1:8201:rack1:dc1:112
  data_store: 0
  listen: 192.168.1.2:8202
  dyn_seed_provider: simple_provider
  servers:
  - 192.168.1.2:6379:1
  tokens: 212
  secure_server_option: none
  stats_listen: 127.0.0.1:30002 
  recon_key_file: conf/dynomite.pem.pub
  recon_iv_file: conf/dynomite.pem  
  max_msgs: 500000

然后使用使用以下指令分别启动dynomit节点,-d 表示后台运行

./bin/dynomite -c conf/node1.yml -d --output=node1.log
./bin/dynomite -c conf/node2.yml -d --output=node2.log

接着使用redis-cli连接到dynomite的任一个节点进行测试,会发现任意dynomite节点修改数据后另外一个dynomite实例都可以查到。注意keys * 、flushall、del key1 key2 等批量指令不能正常运行。

redis-cli -h 更改为你的ip -p port

接着直接连接各个redis节点检查数据,会发现同一个数据中心的一个机架包含了完整数据,而同一个机架中的节点的数据不重复。

redis-cli -h 更改为你的ip -p port

4、性能分析

4.1、Dynomite代理造成的性能损失

每个Dynomite节点都会作为一个Redis实例的代理,直接使用压测工具 redis-benchmark 测试连接到Dynomite以及直连Redis的性能表现,便可以大体了解代理造成的性能损耗。

我们选取B1、B2两个Dynomite以及其代理的Redis来进行压测分析。由于实际使用时发现数据并不是随机或者平均落到B1、B2对应的Redis节点,而是所有数据都落到了B1代理的Redis中,数据分片的规则需要再分析。所以对Dynomite压测时,数据在本身代理的Redis中比起不在的情况,可能性能有区别。

所以我们分别在本机对B1、B2以及B1代理的Redis进行压测,之后再在服务器A对B1、B2进行压测,将得到的五个压测数据进行对比。

压测的指令参数如下

redis-benchmark -h ip -p 端口 -r 1000000 -t get,set,lpush,lpop -n 500000 -c 300 -d 1000 -q

压测结果如下

压测目标 本机压测 同区服务器压测
B1代理的Redis SET 99265 GET 94607 SET 50080 GET 47943
B1 SET 69070 GET 82331 SET 48642 GET 47888
B2 SET 69851 GET 79833 SET 50241 GET 48971

可以看出本机访问Dynomite比起直接访问Reids有接近三成的性能损耗;不同Dynomite节点之间性能差别基本可以忽略;同区云服务访问Dynomite代理的性能和直接访问Redis相比几乎没差别,应该是因为Dynomite对Redis的连接使用了 pipelinie 功能。

由于实际使用时,服务和Redis一般不在一个服务器上,所以Dynomite的性能表现比较理想。

5、对Redis指令支持度

支持度较高,除了以下情况外未发现其他不支持的指令

  • keys * 、flushall、del key1 key2 等批量执行指令实际上只能处理到Dynomite直接连接的Redis节点的数据。这很好理解,因为批量指令要到多个实例去执行并合并结果,执行时间会较长,而且如果执行结果只有部分正常,合并后的执行结果将会相当复杂。其中keys * 指令会获取到Dynomite链接的Redis实例的key列表
  • 订阅发布指令不支持,估计也是因为集群下比较难处理
  • 不支持 rename 指令

6、优缺点及其应用于生产环境的风险评估

优点

  • 支持多主集群
  • 配置使用相对较为简单直观
  • 比起直连Redis性能折损相当少,可以忽略
  • 对Redis的支持度相当高,完全足够平时开发使用

缺点

  • 集群功能的辅助功能不够完整,缺少不停机动态扩容功能
  • 缺少内置的数据同步功能,新增节点
  • 缺少内置的数据同步功能,Dynomite或Redis节点故障停机重启后不会自动从其他节点同步数据
  • 高可用功能有一定缺陷,Dynomite节点对应的Redis挂掉之后,访问这个节点时,如果key是属于这个Redis的会直接报错,不会到其他数据中心拿数据
  • 文档比较少特别是中文文档,不够详细,比如各类配置的可选项、各配置的关联互动、异常处理说明、第三方配合使用工具说明很少,
  • 社区不活跃
  • 更新有点慢,4-6个月更新一次代码

缺点1可以通过Dynomite节点代理Redis哨兵模式或者Redis-cluster集群解决,但会带来一定性能折损;2可以通过第三方数据同步工具做处理如Redis-migrate-tool、Redis-shake;3无法解决;4可以引入进程进程存活监控,出问题后立刻重启Redis,但期间丢失的数据也即是缺点3无法解决;其他无法解决。

其他风险,分别用了两个分支的最新版本,rel_0.6_prod_safe(2019.9.25更新)和0.6(2019.11.21更新)

  • rel_0.6_prod_safe分支 快速压测几百万个1k大小的数据包后,Dynomite服务挂掉的情况。
  • rel_0.6_prod_safe以及0.6分支出现max_msgs超过上限的错误日志,之后停止压测很长时间在压测仍然会出现,出现这种错误是会出现非常高的内存占用,在redis只占用了不到3-5g内存情况下,Dynomite占用了8-12G内存。并且这个内存不会下降,推测是未处理的消息堆积导致。
  • 0.6分支配置打开enable_gossip时启动服务后出现了使用redis-cli连接长时间无响应卡住的情况

对于数据库集群方案,以下几点非常重要

  • 1、零侵入:业务系统不需要做任何改造就能接入
  • 2、高吞吐量:基于现有业务峰值TPS乘以10,得出TPS要达到1万
  • 3、低延时:我司的多活业务不会出现跨机房读取数据的情况,所以定的目标延时低于1s。实际情况延时在50ms左右
  • 4、高堆积能力:基于跨机房网络的不确定性,当网络闪断时能够保证指令不丢失
  • 5、高可用性:当网络故障或者Redis宕机恢复时,同步任务能自动恢复
  • 6、可配置性:业务系统可以自由定制需要同步哪些Key

Dynomite在第1、2、3 方面做得比较好,第4支持但是有一定缺陷,第5不够完善,6不支持。

总的来说Dynomite作为集群方案是功能不够完善,和Redis Cluster相比多了多主功能,但是缺失动态扩容、自动同步数据等功能;高可用方面也有一定缺陷。社区活跃度和文档都比较欠缺,更新较慢。生产环境使用风险较大。如果实在要用建议搭配Redis Cluster使用以解决动态扩容、新增节点和故障节点自动同步数据等问题,并且应该将其当做缓存集群,避免当做持久化数据库,特别是用户数据等核心数据。

参考资料

  • Dynomite的Github项目
  • Redis高可用第三方开源集群方案介绍
  • 较详细的操作指南
  • 关于Dynomite的部分原理解释

你可能感兴趣的:(Dynomite研究(Netflix数据库同步工具))