VDL:唯品会强一致、高可用、高性能分布式日志存储介绍(产品篇)

“You can’t fully understand databases, NoSQL stores, key value stores,
replication, paxos, hadoop, version control, or almost any software
system without understanding logs。”

应有尽有剖析Redis Cluster原理和动用


–《The Log: What every software engineer should know about real-time
data’s unifying abstraction》

1.Redis Cluster总览

VDL是VIP Distributed
Log的缩写,是唯品会自研的基于Raft协议的新一代分布式Log存储系统。这里的Log不是指glog或者log4j等日志库记录的应用程序日志,可以省略地把Log了然成广义的Data,和Database中的Data本质上是同等的,无非是VDL存储的Data数据Schema-less的,业务和用户可以灵活自解析,而Database中的Data日常和向来或直接和Schema相关。

1.1 设计规范和初衷

合法文档Cluster
Spec
中,作者详细介绍了Redis集群为何要统筹成现在的样子。最基本的靶子有五个:

  1. 性能:这是Redis赖以生活的看家本领,扩大集群效益后当然不可以对性能爆发太大影响,所以Redis采用了P2P而非Proxy情势、异步复制、客户端重定向等设计,而牺牲了部分的一致性、使用性。
  2. 水平扩展:集群的最重点力量自然是扩展,文档中称可以线性扩张到1000结点。
  3. 可用性:在Cluster推出以前,可用性要靠Sentinel保证。有了集群之后也自动具有了Sentinel的督查和机关Failover能力。

 

1.2 架构变化与CAP理论

Redis
Cluster集群功用推出已经有一段时间了。在单机版的Redis中,每个Master之间是绝非其他通信的,所以我们一般在Jedis客户端或者Codis这样的代办中做Pre-sharding。遵照CAP理论来说,单机版的Redis属于担保CP(Consistency
&
Partition-Tolerancy)而牺牲A(Availability)
,也就说Redis可以保证拥有用户看到同一的多少(一致性,因为Redis不自动冗余数据)和网络通信出题目时,暂时隔离开的子系统能延续运行(分区容忍性,因为Master之间尚无直接涉及,不需要通信),可是不保证某些结点故障时,所有请求都能被响应(可用性,某个Master结点挂了的话,那么它下边分片的数额就不能访问了)。

有了Cluster功能后,Redis从一个单独的NoSQL内存数据库改为了分布式NoSQL数据库,CAP模型也从CP变成了AP。也就是说,通过自动分片和冗余数据,Redis具有了着实的分布式能力,某个结点挂了的话,因为数量在另外结点上有备份,所以任何结点顶上来就足以继承提供服务,保证了Availability。然则,也正因为这一点,Redis无法保证曾经的强一致性了。这也是CAP理论要求的,三者只好取其二。

关于CAP理论的易懂讲解,请参考我的译文《可能是CAP理论的最好解释
。简单分析了Redis在架构上的扭转后,大家就一块儿来体会一下Redis
Cluster效率吧!


VDL介绍体系分两篇著作对VDL举行介绍,包括:

2.Redis集群初探

Redis的安装很简短,此前曾经介绍过,就不详细说了。关于Redis
Cluster的基础知识在此以前也有过收拾,请参考《Redis集群效用预览》。假设急需通盘的摸底,这自然要看法定文档Cluster
Tutorial
,只看这个就够了!

– 产品篇: 介绍VDL暴发的背景、当前出品形象和特色、后续演进思路等;
– 实现篇和质量管控篇: 实现重点分析VDL的技能实现细节与高性能手段,和我们大快朵颐我们备受的搦战和踩过的坑。质量管控则介绍VDL如何确保产品质量,首要不外乎分布式系统怎样测试,咋样举办非凡和错误注入,以及分布式系统中各样节点间的多寡一致性咋样表明等。

2.1 集群配置

要想打开Redis
Cluster形式,有几项配置是必须的。此外为了方便使用和继承的测试,我还附加做了一些安排:

  • 绑定地址:bind
    192.168.XXX.XXX。不能够绑定到127.0.0.1或localhost,否则辅导客户端重定向时会报”Connection
    refused”的不当。
  • 开启Cluster:cluster-enabled yes
  • 集群配置文件:cluster-config-file
    nodes-7000.conf。这一个布局文件不是要我们去配的,而是Redis运行时保留配置的文本,所以我们也不得以修改那个文件。
  • 集群超时时间:cluster-node-timeout
    15000。结点超时多长时间则以为它宕机了。
  • 槽是否全覆盖:cluster-require-full-coverage
    no。默认是yes,若果有结点宕机导致16384个槽没全被遮住,整个集群就整个悬停服务,所以毫无疑问要改为no
  • 后台运行:daemonize yes
  • 出口日志:logfile “./redis.log”
  • 监听端口:port 7000

布置好后,遵照大家的集群规模,拷贝出来几份同样的配备文件,唯一不同的就是监听端口,能够依次改为7001、7002…
因为Redis
Cluster假使数据冗余是1的话,至少要3个Master和3个Slave,所以我们拷贝出6个实例的布局文件。为了避免相互影响,为6个实例的布置文件建立独立的公文夹。

[root@8gVm redis-3.0.4]# pwd
/root/Software/redis-3.0.4
[root@8gVm redis-3.0.4]# tree -I "*log|nodes*" cfg-cluster/
cfg-cluster/
├── 7000
│   └── redis.conf.7000
├── 7001
│   └── redis.conf.7001
├── 7002
│   └── redis.conf.7002
├── 7003
│   └── redis.conf.7003
├── 7004
│   └── redis.conf.7004
└── 7005
    └── redis.conf.7005

6 directories, 6 files
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

 

2.2 redis-trib管理器

Redis作者应该是个Ruby爱好者,Ruby客户端就是她开发的。本次集群的管住效果尚未放置到Redis代码中,于是作者又随手写了个名叫redis-trib的治本脚本。redis-trib依赖Ruby和RubyGems,以及redis扩大。可以先用which命令查看是否已安装ruby和rubygems,用gem
list –local查看本地是否已设置redis扩张。

最便捷的法门就是用apt或yum包管理器安装RubyGems后实施gem install
redis。假设网络或环境受限的话,可以手动安装RubyGems和redis扩展(外国链接或者无法下载,可以从CSDN下载):

[root@8gVm Software]# wget https://github.com/rubygems/rubygems/releases/download/v2.2.3/rubygems-2.2.3.tgz
[root@8gVm Software]# tar xzvf rubygems-2.2.3.tgz 
[root@8gVm Software]# cd rubygems-2.2.3
[root@8gVm rubygems-2.2.3]# ruby setup.rb --no-rdoc --no-ri

[root@8gVm Software]# wget https://rubygems.org/downloads/redis-3.2.1.gem
[root@8gVm Software]# gem install redis-3.2.1.gem --local --no-rdoc --no-ri
Successfully installed redis-3.2.1
1 gem installed
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

本篇是产品篇,会因此分布式系统的本色,讲解VDL的发出背景和产品一定。通过这篇小说,希望有更五人了解VDL,也冀望为作业系列带来便利并发出价值。

2.3 集群建立

率先,启动咱们安排好的6个Redis实例。

[root@8gVm redis-3.0.4]# for ((i=0; i<6; ++i))
> do
> cd cfg-cluster/700$i && ../../src/redis-server redis.conf.700$i && cd -
> done
  • 1
  • 2
  • 3
  • 4

  • 1
  • 2
  • 3
  • 4

此时6个实例还未曾形成集群,现在用redis-trb.rb管理脚本建立起集群。可以观看,redis-trib默认用前3个实例作为Master,后3个作为Slave。因为Redis基于Master-Slave做数据备份,而非像卡Sandra(Cassandra)(Cassandra)或Hazelcast一样不区分结点角色,自动复制并分配Slot的职务到各样结点

[root@8gVm redis-3.0.4]# src/redis-trib.rb create --replicas 1 192.168.1.100:7000 192.168.1.100:7001 192.168.1.100:7002 192.168.1.100:7003 192.168.1.100:7004 192.168.1.100:7005
>>> Creating cluster
Connecting to node 192.168.1.100:7000: OK
Connecting to node 192.168.1.100:7001: OK
Connecting to node 192.168.1.100:7002: OK
Connecting to node 192.168.1.100:7003: OK
Connecting to node 192.168.1.100:7004: OK
Connecting to node 192.168.1.100:7005: OK
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
192.168.1.100:7000
192.168.1.100:7001
192.168.1.100:7002
Adding replica 192.168.1.100:7003 to 192.168.1.100:7000
Adding replica 192.168.1.100:7004 to 192.168.1.100:7001
Adding replica 192.168.1.100:7005 to 192.168.1.100:7002
    ...
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join....
>>> Performing Cluster Check (using node 192.168.1.100:7000)
    ...
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

迄今,集群就已经创立成功了!“贴心”的Redis还在utils/create-cluster下提供了一个create-cluster脚本,可以创建出一个集群,类似大家位置建立起的3主3从的集群。

 VDL的制品定位

2.4 简单测试

大家连接受集群中的任意一个结点,启动redis-cli时要加-c选项,存取多少个Key-Value感受一下Redis久违的集群效益。

[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7000
192.168.1.100:7000> set foo bar
-> Redirected to slot [12182] located at 192.168.1.100:7002
OK
192.168.1.100:7002> set hello world
-> Redirected to slot [866] located at 192.168.1.100:7000
OK
192.168.1.100:7000> get foo
-> Redirected to slot [12182] located at 192.168.1.100:7002
"bar"
192.168.1.100:7002> get hello
-> Redirected to slot [866] located at 192.168.1.100:7000
"world"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

仔细观望可知专注到,redis-cli依据指示,不断在7000和7002结点以前重定向跳转。假诺启动时不加-c选项的话,就能见到以错误格局展现出的MOVED重定向信息。

[root@8gVm redis-3.0.4]# src/redis-cli -h 192.168.1.100 -p 7000
192.168.1.100:7000> get foo
(error) MOVED 12182 192.168.1.100:7002
  • 1
  • 2
  • 3

  • 1
  • 2
  • 3

图片 1

2.5 集群重启

现阶段redis-trib的效率还相比较弱,需要重启集群的话先手动kill掉各样进程,然后再次起动就能够了。这也有点太…
网上有人重启后会际遇问题,我还相比幸运,这种“土鳖”的方法重启试了两次还没察觉问题。

[root@8gVm redis-3.0.4]# ps -ef | grep redis | awk '{print $2}' | xargs kill
  • 1

  • 1

在论述VDL的制品定位以前,咱们先商讨一个题材:用户对一个Storage
System的诉求是如何?以及Client的诉求被科学地领略并满意了吧?如下图所示,大多数用户对一个存储系统的诉求,可以简单地概括为两点:

3.高级效用尝鲜

就是“高级效用”,其实在另外分布式系统中曾经都有落实了,只不过在Redis世界里是相比新鲜的。本有的首要考查弹指间Redis
Cluster中的数据迁移(Resharding)和故障转移职能。

 

3.1 数据迁移

本小节大家感受一下Redis集群的Resharding效率!

-存储系统遵照用户写入的顺序存储数据;

3.1.1 创制测试数据

先是保存foo1~10共10个Key-Value作为测试数据。

[root@8gVm redis-3.0.4]# for ((i=0; i<10; ++i))
> do
> src/redis-cli -c -h 192.168.1.100 -p 7000 set foo$i bar
> done

[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7000
192.168.1.100:7000> keys *
1) "foo6"
2) "foo7"
3) "foo3"
4) "foo2"
192.168.1.100:7000> get foo4
-> Redirected to slot [9426] located at 192.168.1.100:7001
"bar"
192.168.1.100:7001> keys *
1) "foo4"
2) "foo8"
192.168.1.100:7001> get foo5
-> Redirected to slot [13555] located at 192.168.1.100:7002
"bar"
192.168.1.100:7002> keys *
1) "foo5"
2) "foo1"
3) "foo10"
4) "foo9"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

-用户总是能从存储系统查询到新型的写入(结果)。

3.1.2 启动新结点

参照以前的法子新拷贝出两份redis.conf配置文件redis.conf.7010和7011,与往日结点的安排文件做一下区分。启动新的六个Redis实例之后,通过redis-trib.rb脚本添加新的Master和Slave到集群中。

[root@8gVm redis-3.0.4]# cd cfg-cluster/7010 && ../../src/redis-server redis.conf.7010 && cd -
[root@8gVm redis-3.0.4]# cd cfg-cluster/7011 && ../../src/redis-server redis.conf.7011 && cd -
  • 1
  • 2

  • 1
  • 2

图片 2

3.1.3 添加到集群

使用redis-trib.rb add-node个别将多个新结点添加到集群中,一个用作Master,一个当做其Slave。

[root@8gVm redis-3.0.4]# src/redis-trib.rb add-node 192.168.1.100:7010 192.168.1.100:7000
>>> Adding node 192.168.1.100:7010 to cluster 192.168.1.100:7000
Connecting to node 192.168.1.100:7000: OK
Connecting to node 192.168.1.100:7001: OK
Connecting to node 192.168.1.100:7002: OK
Connecting to node 192.168.1.100:7005: OK
Connecting to node 192.168.1.100:7003: OK
Connecting to node 192.168.1.100:7004: OK
>>> Performing Cluster Check (using node 192.168.1.100:7000)
    ...
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
Connecting to node 192.168.1.100:7010: OK
>>> Send CLUSTER MEET to node 192.168.1.100:7010 to make it join the cluster.
[OK] New node added correctly.

[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7000 cluster nodes
0d1f9c979684e0bffc8230c7bb6c7c0d37d8a5a9 192.168.1.100:7010 master - 0 1442452249525 0 connected
    ...

[root@8gVm redis-3.0.4]# src/redis-trib.rb add-node --slave --master-id 0d1f9c979684e0bffc8230c7bb6c7c0d37d8a5a9 192.168.1.100:7011 192.168.1.100:7000
>>> Adding node 192.168.1.100:7011 to cluster 192.168.1.100:7000
Connecting to node 192.168.1.100:7000: OK
Connecting to node 192.168.1.100:7010: OK
Connecting to node 192.168.1.100:7001: OK
Connecting to node 192.168.1.100:7002: OK
Connecting to node 192.168.1.100:7005: OK
Connecting to node 192.168.1.100:7003: OK
Connecting to node 192.168.1.100:7004: OK
>>> Performing Cluster Check (using node 192.168.1.100:7000)
    ...
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
Connecting to node 192.168.1.100:7011: OK
>>> Send CLUSTER MEET to node 192.168.1.100:7011 to make it join the cluster.
Waiting for the cluster to join.
>>> Configure node as replica of 192.168.1.100:7010.
[OK] New node added correctly.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

在最初的单机系统或者依照IOE的系统,这种诉求看起来无可厚非,而且也正如便于被满足,例如一个单机的MySQL数据库,由于其本人装有RDBMS系统的ACID属性,配置得当的话,那样的诉求大多数景观下都是可以满意的。

3.1.4 Resharding

通过redis-trib.rb reshard可以交互式地迁移Slot。上边的例证将5000个Slot从7000~7002迁移到7010上。也得以经过./redis-trib.rb reshard <host>:<port> --from <node-id> --to <node-id> --slots --yes在程序中活动完成搬迁。

[root@8gVm redis-3.0.4]# src/redis-trib.rb reshard 192.168.1.100:7000
Connecting to node 192.168.1.100:7000: OK
Connecting to node 192.168.1.100:7010: OK
Connecting to node 192.168.1.100:7001: OK
Connecting to node 192.168.1.100:7002: OK
Connecting to node 192.168.1.100:7005: OK
Connecting to node 192.168.1.100:7011: OK
Connecting to node 192.168.1.100:7003: OK
Connecting to node 192.168.1.100:7004: OK
>>> Performing Cluster Check (using node 192.168.1.100:7000)
M: b2036adda128b2eeffa36c3a2056444d23b548a8 192.168.1.100:7000
   slots:0-5460 (4128 slots) master
   1 additional replica(s)
M: 0d1f9c979684e0bffc8230c7bb6c7c0d37d8a5a9 192.168.1.100:7010
   slots:0 (4000 slots) master
   1 additional replica(s)
   ...
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 5000
What is the receiving node ID? 0d1f9c979684e0bffc8230c7bb6c7c0d37d8a5a9
Please enter all the source node IDs.
  Type 'all' to use all the nodes as source nodes for the hash slots.
  Type 'done' once you entered all the source nodes IDs.
Source node #1:all

[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7000 cluster nodes
0d1f9c979684e0bffc8230c7bb6c7c0d37d8a5a9 192.168.1.100:7010 master - 0 1442455872019 7 connected 0-1332 5461-6794 10923-12255
b2036adda128b2eeffa36c3a2056444d23b548a8 192.168.1.100:7000 myself,master - 0 0 1 connected 1333-5460
b5ab302f5c2395e3c8194c354a85d02f89bace62 192.168.1.100:7001 master - 0 1442455875022 2 connected 6795-10922
0c565e207ce3118470fd5ed3c806eb78f1fdfc01 192.168.1.100:7002 master - 0 1442455874521 3 connected 12256-16383
    ...
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

搬迁完成后,查看往日封存的foo1~10的分布情状,能够看出有的Key已经搬迁到了新的结点7010上。

[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7000 keys "*"
1) "foo3"
2) "foo7"
[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7001 keys "*"
1) "foo4"
2) "foo8"
3) "foo0"
[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7002 keys "*"
1) "foo1"
2) "foo9"
3) "foo5"
[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7010 keys "*"
1) "foo6"
2) "foo2"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

 

3.2 故障转移

在高可用性方面,Redis可到头来能够”Auto”一把了!Redis
Cluster重用了Sentinel的代码逻辑,不需要独自启动一个Sentinel集群,Redis
Cluster本身就能活动举办Master选举和Failover切换

下面大家有意识kill掉7010结点,之后可以见见结点状态成为了fail,而Slave
7011被公推为新的Master。

[root@8gVm redis-3.0.4]# kill 43637

[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7000 cluster nodes
0d1f9c979684e0bffc8230c7bb6c7c0d37d8a5a9 192.168.1.100:7010 master,fail - 1442456829380 1442456825674 7 disconnected
b2036adda128b2eeffa36c3a2056444d23b548a8 192.168.1.100:7000 myself,master - 0 0 1 connected 1333-5460
b5ab302f5c2395e3c8194c354a85d02f89bace62 192.168.1.100:7001 master - 0 1442456848722 2 connected 6795-10922
0c565e207ce3118470fd5ed3c806eb78f1fdfc01 192.168.1.100:7002 master - 0 1442456846717 3 connected 12256-16383
5a3c67248b1df554fbf2c93112ba429f31b1d3d1 192.168.1.100:7005 slave 0c565e207ce3118470fd5ed3c806eb78f1fdfc01 0 1442456847720 6 connected
99bff22b97119cf158d225c2b450732a1c0d3c44 192.168.1.100:7011 master - 0 1442456849725 8 connected 0-1332 5461-6794 10923-12255
cd305d509c34842a8047e19239b64df94c13cb96 192.168.1.100:7003 slave b2036adda128b2eeffa36c3a2056444d23b548a8 0 1442456848220 4 connected
64b544cdd75c1ce395fb9d0af024b7f2b77213a3 192.168.1.100:7004 slave b5ab302f5c2395e3c8194c354a85d02f89bace62 0 1442456845715 5 connected
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

品尝查询从前封存在7010上的Key,可以看到7011顶替上来继续提供劳务,整个集群没有遭逢震慑。

[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7000 get foo6
"bar"
[root@8gVm redis-3.0.4]# 
[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7000 get foo2
"bar"
  • 1
  • 2
  • 3
  • 4
  • 5

  • 1
  • 2
  • 3
  • 4
  • 5

再回到大家集团如今的现状(也是多数互联网商家的现状),Scale
Up已经黔驴技穷缓解事情扩大的要求,不知不觉中,我们的系统现已Scale
Out到一个精神上的分布式系统,存储系统也是这样。

4.里面原理分析

前方大家曾经学习过,用Redis提供的redis-trib或create-cluster脚本能几步仍然一步就确立起一个Redis集群。这一片段我们为了深刻学习,所以要临时丢掉那些有利的工具,完全手动建立三遍下边的3主3从集群。

图片 3

4.1 集群发现:MEET

最开端时,每个Redis实例自己是一个集群,我们透过cluster meet让各种结点相互“握手”。这也是Redis
Cluster近年来的一个不足之处:紧缺结点的电动发现意义

[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7000 cluster nodes
33c0bd93d7c7403ef0239ff01eb79bfa15d2a32c :7000 myself,master - 0 0 0 connected

[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7000 cluster meet 192.168.1.100 7001
OK
    ...
[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7000 cluster meet 192.168.1.100 7005
OK

[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7000 cluster nodes
7b953ec26bbdbf67179e5d37e3cf91626774e96f 192.168.1.100:7003 master - 0 1442466369259 4 connected
5d9f14cec1f731b6477c1e1055cecd6eff3812d4 192.168.1.100:7005 master - 0 1442466368659 4 connected
33c0bd93d7c7403ef0239ff01eb79bfa15d2a32c 192.168.1.100:7000 myself,master - 0 0 1 connected
63162ed000db9d5309e622ec319a1dcb29a3304e 192.168.1.100:7001 master - 0 1442466371262 3 connected
45baa2cb45435398ba5d559cdb574cfae4083893 192.168.1.100:7002 master - 0 1442466372264 2 connected
cdd5b3a244761023f653e08cb14721f70c399b82 192.168.1.100:7004 master - 0 1442466370261 0 connecte
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

诸如此类的话,对于同一的用户诉求,现在的蕴藏系统需要满意的约束规范发生了很大的变动:

4.2 角色设置:REPLICATE

结点全体“握手”成功后,就可以用cluster replicate命令为结点指定角色了,默认每个结点都是Master。

[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7003 cluster replicate 33c0bd93d7c7403ef0239ff01eb79bfa15d2a32c
OK
[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7004 cluster replicate 63162ed000db9d5309e622ec319a1dcb29a3304e
OK
[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7005 cluster replicate 45baa2cb45435398ba5d559cdb574cfae4083893
OK

[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7000 cluster nodes
7b953ec26bbdbf67179e5d37e3cf91626774e96f 192.168.1.100:7003 slave 33c0bd93d7c7403ef0239ff01eb79bfa15d2a32c 0 1442466812984 4 connected
5d9f14cec1f731b6477c1e1055cecd6eff3812d4 192.168.1.100:7005 slave 45baa2cb45435398ba5d559cdb574cfae4083893 0 1442466813986 5 connected
33c0bd93d7c7403ef0239ff01eb79bfa15d2a32c 192.168.1.100:7000 myself,master - 0 0 1 connected
63162ed000db9d5309e622ec319a1dcb29a3304e 192.168.1.100:7001 master - 0 1442466814987 3 connected
45baa2cb45435398ba5d559cdb574cfae4083893 192.168.1.100:7002 master - 0 1442466811982 2 connected
cdd5b3a244761023f653e08cb14721f70c399b82 192.168.1.100:7004 slave 63162ed000db9d5309e622ec319a1dcb29a3304e 0 1442466812483 3 connected
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

 

4.3 槽指派:ADDSLOTS

安装好主从涉嫌之后,就足以用cluster addslots指令指派16384个槽的职位了。有点恶心的是,ADDSLOTS命令需要在参数中一个个指明槽的ID,而不可以指定范围。这里用Bash
3.0的特性简化了,不然就得用Bash的大循环来形成了:

[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7000 cluster addslots {0..5000}
OK
[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7001 cluster addslots {5001..10000}
OK
[root@8gVm redis-3.0.4]# src/redis-cli -c -h 192.168.1.100 -p 7001 cluster addslots {10001..16383}
OK

[root@8gVm redis-3.0.4]# src/redis-trib.rb check 192.168.1.100:7000
Connecting to node 192.168.1.100:7000: OK
  ...
>>> Performing Cluster Check (using node 192.168.1.100:7000)
  ...
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

这样大家就经过手动执行命令拿到了与从前一样的集群。

-要求一律次写入,要在多少个节点上不变地、按照用户发起的一一写入;

4.4 数据迁移:MIGRATE

诚然起首Resharding此前,redis-trib会先在源结点和目标结点上推行cluster setslot <slot> importingcluster setslot <slot> migrating一声令下,将要迁移的槽分别标记为迁出中和导入中的状态。然后,执行cluster getkeysinslot得到Slot中的所有Key。最终就足以对每个Key执行migrate一声令下进行搬迁了。槽迁移完成后,执行cluster setslot指令公告所有集群槽的差使已经暴发变化。

有关迁移过程中的数据访问,客户端访问源结点时,假若Key还在源结点上就直接操作。假设已经不在源结点了,就向客户端再次回到一个ASK错误,将客户端重定向到目标结点

-要求四个节点组成的蕴藏集群,总是可以查询重临最新的写入结果。

4.5 内部数据结构

Redis
Cluster效用涉及五个着力的数据结构clusterState、clusterNode、clusterLink都在cluster.h中定义。这五个数据结构中最根本的性能就是:clusterState.slots、clusterState.slots_to_keys和clusterNode.slots了,它们保存了二种炫耀关系

  • clusterState:集群状态 
    • nodes:所有结点
    • migrating_slots_to:迁出中的槽
    • importing_slots_from:导入中的槽
    • slots_to_keys:槽中富含的装有Key,用于迁移Slot时收获其涵盖的Key
    • slots:Slot所属的结点,用于拍卖请求时判断Key所在Slot是否和谐承担
  • clusterNode:结点音信 
    • slots:结点负责的富有Slot,用于发送Gossip音信通知其他结点自己肩负的Slot。通过位图情势保存节省空间,16384/8正假若2048字节,所以槽总数16384不是随意定的
  • clusterLink:与其它结点通信的连续

// 集群状态,每个节点都保存着一个这样的状态,记录了它们眼中的集群的样子。
// 另外,虽然这个结构主要用于记录集群的属性,但是为了节约资源,
// 有些与节点有关的属性,比如 slots_to_keys 、 failover_auth_count 
// 也被放到了这个结构里面。
typedef struct clusterState {
    ...
    // 指向当前节点的指针
    clusterNode *myself;  /* This node */

    // 集群当前的状态:是在线还是下线
    int state;            /* REDIS_CLUSTER_OK, REDIS_CLUSTER_FAIL, ... */

    // 集群节点名单(包括 myself 节点)
    // 字典的键为节点的名字,字典的值为 clusterNode 结构
    dict *nodes;          /* Hash table of name -> clusterNode structures */

    // 记录要从当前节点迁移到目标节点的槽,以及迁移的目标节点
    // migrating_slots_to[i] = NULL 表示槽 i 未被迁移
    // migrating_slots_to[i] = clusterNode_A 表示槽 i 要从本节点迁移至节点 A
    clusterNode *migrating_slots_to[REDIS_CLUSTER_SLOTS];

    // 记录要从源节点迁移到本节点的槽,以及进行迁移的源节点
    // importing_slots_from[i] = NULL 表示槽 i 未进行导入
    // importing_slots_from[i] = clusterNode_A 表示正从节点 A 中导入槽 i
    clusterNode *importing_slots_from[REDIS_CLUSTER_SLOTS];

    // 负责处理各个槽的节点
    // 例如 slots[i] = clusterNode_A 表示槽 i 由节点 A 处理
    clusterNode *slots[REDIS_CLUSTER_SLOTS];

    // 跳跃表,表中以槽作为分值,键作为成员,对槽进行有序排序
    // 当需要对某些槽进行区间(range)操作时,这个跳跃表可以提供方便
    // 具体操作定义在 db.c 里面
    zskiplist *slots_to_keys;
    ...
} clusterState;

// 节点状态
struct clusterNode {
    ...
    // 节点标识
    // 使用各种不同的标识值记录节点的角色(比如主节点或者从节点),
    // 以及节点目前所处的状态(比如在线或者下线)。
    int flags;      /* REDIS_NODE_... */

    // 由这个节点负责处理的槽
    // 一共有 REDIS_CLUSTER_SLOTS / 8 个字节长
    // 每个字节的每个位记录了一个槽的保存状态
    // 位的值为 1 表示槽正由本节点处理,值为 0 则表示槽并非本节点处理
    // 比如 slots[0] 的第一个位保存了槽 0 的保存情况
    // slots[0] 的第二个位保存了槽 1 的保存情况,以此类推
    unsigned char slots[REDIS_CLUSTER_SLOTS/8]; /* slots handled by this node */

    // 指针数组,指向各个从节点
    struct clusterNode **slaves; /* pointers to slave nodes */

    // 如果这是一个从节点,那么指向主节点
    struct clusterNode *slaveof; /* pointer to the master node */
    ...
};

/* clusterLink encapsulates everything needed to talk with a remote node. */
// clusterLink 包含了与其他节点进行通讯所需的全部信息
typedef struct clusterLink {
    ...
    // TCP 套接字描述符
    int fd;                     /* TCP socket file descriptor */

    // 与这个连接相关联的节点,如果没有的话就为 NULL
    struct clusterNode *node;   /* Node related to this link if any, or NULL */
    ...
} clusterLink;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

 

4.6 处理流程全梳理

在单机形式下,Redis对请求的处理很粗略。Key存在的话,就执行请求中的操作;Key不设有的话,就告知客户端Key不存在。但是在集群模式下,因为关乎到请求重定向和Slot迁移,所以对请求的处理变得很复杂,流程如下:

  1. 自我批评Key所在Slot是否属于当前Node? 
    2.1 计算crc16(key) % 16384得到Slot 
    2.2 查询clusterState.slots负责Slot的结点指针 
    2.3 与myself指针相比较
  2. 若不属于,则响应MOVED错误重定向客户端
  3. 若属于且Key存在,则平素操作,重返结果给客户端
  4. 若Key不设有,检查该Slot是否迁出中?(clusterState.migrating_slots_to)
  5. 若Slot迁出中,重返ASK错误重定向客户端到搬迁的目标服务器上
  6. 若Slot未迁出,检查Slot是否导入中?(clusterState.importing_slots_from)
  7. 若Slot导入中且有ASKING标记,则直接操作
  8. 要不响应MOVED错误重定向客户端

这多个约束的原形就是Linearizability
Consistency(https://en.wikipedia.org/wiki/Linearizability)。通俗地说,用户总是站在甲方的姿态,他不关心存储系统的后端如何实现,他要求一个分布式集群的行为,还是要满足单机系统的提供给他的承诺。

5.用到案例收集

 

5.1 有道:Redis Cluster使用经验

详情请参见原文,关键内容摘录如下:

在一个分布式系统中,因为大家处于异步通讯的条件中,所以要满意这六个条件实在是特别艰辛的。万变不离其宗的就是分布式系统中的一致性,多少个节点要就“五回呼吁的全局写入顺序编号达成一致”和“哪个节点上富有最新的写入结果”等关键问题达成一致。仅仅是一致性达成这或多或少,从Paxos、Viewstamp
Replication、ZAB到Raft等,核心都是解决这几个问题,从理论到工程执行更是经历了一个经久不衰的过程。其实难题还不仅如此,尽管大家就某几遍写入的大局序号达成了同一,从而保证了有着的用户写入请求是一个大局有序的队列,但某一个写入请求在不同的节点上实施,也不必然会发生同样的结果:比如两遍写入中凭借地点hostname、本地timestamp等等。所以大家这边探讨的前提是Log是Deterministic的,对于non-deterministic的Log,一般都是由此内部一个Node执行处理,将以此non-deterministic转化成一个Deterministic的Phyciological
Log。

5.1.1 三个毛病

“redis
cluster的宏图在这块有点奇葩,跟集群相关的操作需要一个外表的ruby脚本来帮忙(当然或许是为了让主程序的代码充分简洁?),然后相当剧本还只扶助填实例的ip不匡助host,还不告知您不扶助让你用host之后各个莫名其妙。”

“第一个毛病就是严俊信赖客户端driver的成熟度。假若把redis
cluster设计成类似Cassandra,请求集群中其它一个节点都足以负担转发呼吁,client会好写一些。”

“第二个毛病完全是设计问题了,就是一个redis进程既承担读写多少又肩负集群交互,即使设计者已经竭尽简化了代码和逻辑,但仍然让redis从一个内存NoSQL变成了一个分布式NoSQL。分布式系统很容易有坑,一旦有坑必须升级redis。”

 

5.1.2 去主旨化 vs. Proxy

“关于redis
cluster的设计,Gossip/P2P的去大旨化架构本身不是题材,但尽管有了主导节点,能做的政工就多了,比如sharding不均匀是很容易自行rebalance的,而无主题的只好靠外界来搞。然后redis
cluster又是slot的花样而非C*式的一致性哈希,新节点分slot又不自行,看重外界(ruby脚本)来分配显得不便于更不漂亮和谐。而且因为是master-slave的系统而非W+R>N的这种,master挂掉之后不久发现是相比紧要的,gossip对于节点挂掉的觉察终究没有基本节点/zookeeper方便快捷。”

“基于proxy做转账表示屏蔽了下层存储,完全可以依照前缀/tag/冷热程度,来把有些仍旧大多数数目放在磁盘从而节省成本又保证一致性,这都是有主题节点所带来的便宜。”

扭动看,如果一个仓储系统不可能提供给用户那样的许诺,这用户程序的逻辑势必分外复杂而且脆弱。用户可能面对的题材,比例:已经查询到某个数据的V3版本,当用户程序和存储系统里面网络中断又重连后,只可以读取到V2版本(V3版本所在的节点宕机,切换来一个多少没有一块的节点下边)。出色长的一段时间内,MySQL数据库主从间的异步复制就可能存在这一个题目,只是只要用户程序没有同时Crash切换,我们一般可以在用户程序本地缓存一些数码操作的流水,曰镪这种情状举行补给论理。

5.2 奇虎360:Redis Cluster浅析和Bada对比

详情请参见原文,关键内容摘录如下:

 

5.2.1 负载均衡问题

“redis
cluster的主备是以节点为单位,而bada则是以partition为单位,那样,同样是3个节点,1024个partition的情形下,redis
cluster的主节点负责整个1024个partition的劳务,而五个从节点则只承担异步备份,导致集群负载不均,再看bada,将1024个partition的主均分到3个节点中,每个节点各有主备,主对外提供劳动,这样均分了拜访压力,有效的施用了资源。”

末尾,回到主旨即VDL的制品定位,VDL的对象是将这么些复杂的题材尽可能地贯彻在VDL分布式存储集群的中间。给用户程序尽量简洁明了的语义承诺:线性一致性,这样用户总是可以像往常单机服务器时代一样自由(实际上,为了在不同的气象给用户更多拔取,VDL可以提供其余一个一致性模型:严谨依照约束规范1,如果用户可以容忍读取到非最新写入的结果,放松约束规范2,也就是常见讲的“时序一致性”)。可想而知,VDL的成品稳定是具备如下条件的通用分布式存储系统:

5.2.2 一致性的管教

redis
cluster与bada一样,最后一致性
,读写都只请求主节点,当一条写请求在对应的主节点写成功后,会霎时回去给客户端成功,然后主节点通过异步的法子将新的数目同步到相应的从节点,这样的办法收缩了客户端三个节点写成功等待的日子,然而在少数情形下会导致写丢失:

1)当主节点接受一条写请求,写入并回到给客户端成功后不幸宕掉,此时刚刚的写还未共同给其对应的从节点,而从节点在发现主节点挂掉并再一次选主后,新的主节点则永久丢失了前面老的主节点向用户确认的写

2)当网络发生割裂,将集群分裂成少数派与大多数派,这样在客户端不知情的情景下,会将写继续写入到个别派中的某些主节点中,而当割裂超越一定时长后,集群感知到非常,此时个别派中的所有主节点会截至响应所有的写请求,多数派的其相应的从节点则会倡导选举成为新的主节点,就算过了一会后割裂恢复生机,老的主节点发现有更新的主存在,自动成为其从节点,而新的主节点中则会永远丢失掉网络割裂至集群感知异常举办切主这一个等级老主节点肯定的具有写

相持于redis cluster的永久丢失,bada通过binlog
merge有效的解决了这一题目
。所有partition的主节点在响应客户端的写请求时,都会在地面记录binlog,binlog实质就是包含时间戳的KV对。当老主以从节点的地点重新出席集群时,会触发binlog
merge操作,新主会相比并且统一二者的binlog,这样就足以将事先丢失掉得写再补回来。”

 

5.2.3 请求重定向问题

“bada服务端节点在收取本不该由自己担当的Partition请求后,不会向客户端重临重定向新闻,而是通过代理的措施,直接在集群内部向科学节点转发客户端的伏乞,并将结果同meta信息再倒车回客户端。”

“再看multi key操作,redis cluster为了追求高性能,辅助multi
key的前提是独具的key必须在同一个节点中
,
可是如此的拍卖需要提交用户,对需要举办multi
key操作的装有key,在写入前人为的增长hash tags。当redis
cluster举行resharding的时候,也就是将某些slot从一个节点迁移到另一个节点时,此时的multi
key操作可能会破产,因为在搬迁的slot中的key此时设有于三个节点。

bada怎么办吧?用户若是对multi key操作性能很在乎时,可以采用与redis
cluster同样的法门,给这么些key加上hash
tags来让它们落在同一个节点,倘使得以承受性能的有点损耗而解放用户的拍卖逻辑,则能够像single
key操作一样,恳请任一bada节点,它会代理所有的key请求并将结果重临给用户。并且在multi
key操作在任什么日期候都得以,即便在举行partition的迁移
,bada也会提前举办切主,保证服务的健康提供。”

强一致,提供线性一致性和时序一致性;

5.3 芒果电视:Redis服务解决方案

详情请参见原文,关键内容摘录如下:

芒果电视在Redis Cluster基础上举行支付,首要扩充了五个零部件:

  • 督察管理:以Python为机要支出框架的Web应用程序Redis-ctl
  • 伸手代理:以C++11为付出语言的轻量数据代理程序cerberus。其功效和长处为: 
    • 集群代理程序的活动请求分发/重试机制使得应用不必修改自身代码或更新Redis库
    • 代办节点为拥有Redis节点加上统一管理和景观监测, 能够查看历史数据,
      或在暴发其他问题之后很快响应修复
    • 代理过程的无状态性使之可在故障后急速回复,
      不影响后端集群数据完整性

这两个零件都已开源到GitHub上,大家可以关注一下!


高吞吐,在毫无意外牺牲RT的情事下,有效保证系统全部的吞吐量;

6.Pros & Cons总结

关于Redis
Cluster带来的各种优势就背着了,在此处最重假设“鸡蛋里挑骨头”,总计一下当下集群效应的欠缺之处和可能的“坑”。

低延时,抛开实际部署拓扑中节点间物理距离引起的RTT开销,通过技术手段最大限度地降落单请求的端到端延时;

6.1 无中央化架构

持久化,Ack给用户此前,一定已经在大多数派节点上落盘,制止用户境遇“回档”;

6.1.1 Gossip消息

Gossip音讯的网络支出和时延是控制Redis
Cluster可以线性增加的要素之一。关于那一个题目,在《redis
cluster百万QPS的挑战》
一文中装有提及。

可控性,这多少个系统从设计到实现到代码细节,我们要统统HOLD住,不可能像拿一个大型开源软件随便用用,遭受严重问题就抓瞎。

6.1.2 结点粒度备份

另外,Redis
Cluster也许是为了简化设计使用了Master-Slave复制的数据备份方案,并不曾选取如Cassandra或IMDG等对等分布式系统中常见的Slot粒度(或叫Partition/Bucket等)的活动冗余和派遣。

这种设计尽管制止比较复杂的分布式技术,但也拉动了有些题目:

  • Slave完全闲置:就算是读请求也不会被重定向到Slave结点上,Slave属于“冷备”
  • 写压力无法分摊:Slave闲置导致的另一个题目就是写压力也都在Master上

 

6.2 客户端的挑衅

鉴于Redis Cluster的筹划,客户端要担负起一部分权利:

  • Cluster协议协理:不管Dummy如故Smart形式,都要具备解析Cluster协议的力量
  • 网络开发:Dummy客户端不断重定向的网络支付
  • 老是维护:Smart客户端对连日到集群中每个结点Socket的维护
  • 缓存路由表:Smart客户端Slot路由表的缓存和立异
  • 内存消耗:Smart客户端上述维护的音讯都是有内存消耗的
  • MultiOp有限帮忙:对于MultiOp,由客户端通过KeyTag保证所有Key都在同一Slot。而尽管如此,迁移时也会促成MultiOp战败。同理,对Pipeline和Transaction的支撑也受限于必须操作同一Slot内的Key。

介绍完VDL的产品稳定,先从我们权衡的角度来看望VDL和主流开开源产品间的固定差异,为何不遵照开源产品做二次开发?其实那多少个题目也相比简单,对于一个大型开源项目,例如MySQL/Kafka/Zookeeper/Cassandra(Cassandra)等,从深入利益来看,其实要统统精通它们和初步开发一个同类产品,投入产出比一对一。

6.3 Redis实现问题

即使属于无中央化架构一类的分布式系统,但不同产品的细节实现和代码质地依旧有许多差异的,就比如Redis
Cluster有些地方的计划性看起来就有一部分“奇葩”和简陋:

  • 不可能自动发现:无Auto
    Discovery效能。集群建顿时以及运行中新增结点时,都要因而手动执行MEET命令或redis-trib.rb脚本添加到集群中
  • 无法自动Resharding:不仅不自动,连Resharding算法都未曾,要和谐总计从怎么样结点上迁移多少Slot,然后仍旧得经过redis-trib.rb操作
  • 严重依赖外部redis-trib:如上所述,像集群健康意况检查、结点参预、Resharding等等功效全都抽离到一个Ruby脚本中了。还不了解下边提到的缺失效能将来是要继续加到这么些本子里依然相会并到集群结点中?redis-trib也许要变为Codis中Dashboard的角色
  • 无监督管理UI:尽管以后加了UI,像迁移进度这种音讯在无中央化设计中很难得到
  • 只保证最后一致性:写Master成功后及时赶回,如需强一致性,自行通过WAIT命令实现。但对此“脑裂”问题,方今Redis没提供网络苏醒后的Merge效用,“脑裂”期间的换代可能有失

VDL和Kafka的交流和界别

6.4 性能损耗

是因为事先手头尚无空闲的物理机资源,所以只在虚拟机上做了简约的单机测试,在独立的一台压力机使用YCSB测试框架向虚拟机发生读写负载。虚拟机的配置为8核AMDXeon CPU
X5650@2.67GHz,16GB内存,分别搭建了4结点的单机版Redis和集群版Redis,测试一下Redis
Cluster的性能损耗。由于不是近来做的测试,所以Jedis用的2.6.2版本。注:当然Redis
Cluster可以因此多机部署得到水平增添带来的特性提升,这里只是由于环境有限所以做的简易单机测试。

是因为YCSB本身仅匡助Redis单机版,所以需要我们温馨扩张扩大插件,具体方法请参考《YCSB性能测试工具使用》。通过YCSB爆发2000w随机数据,Value大约100Byte左右。然后通过YCSB测试Read-Mostly(90%
Read)和Read-Write-Mixed(50% Read)二种状态:

  • 数量加载:吞吐量上有约18%的大跌。
  • Read-Mostly:吞吐量上有约3.5%~7.9%的下降。
  • Read-Write-Mixed:吞吐量上有约3.3%~5.5%下降。
  • 内存占用:Jedis客户端多占用380MB内存。

图片 4Kafka是一个百般成熟的信息系统,除了有着传统信息系统的Message
Queue和Message
Sub/Pub能力之外,还有着一些其他相比较完美的特色,例如:数据分区、灵活可控的副本策略等等。Kafka典型气象(https://kafka.apache.org/uses)包含:

6.5 最终的下结论

从当下看来,相比较Sentinel或Codis等方案,Redis
Cluster的优势还真是简单,个人认为最大的优点有多少个:

  1. 合法提供的Slot实现而不用像Codis这样去改源码了;
  2. 不用额外的Sentinel集群或接近的代码实现了。

同其他分布式系统,如卡Sandra(Cassandra)(Cassandra),或内存型的IMDG如Hazelcast和GridGain,除了性能方面外,从功能上Redis
Cluster简直被爆端庄无完肤…
看看我事先总结过的GridGain介绍《开源IMDG之GridGain》

  • 结点自动发现和Rebalance
  • 分区粒度的备份
  • 故障时分区角色自动调整
  • 结果聚合(不会重定向客户端)
  • “脑裂”复苏后的Merge(Hazelcast匡助多种集合策略)
  • 多Primary分区写操作(见Replicated格局)

这一个都是Redis
Cluster没有或者要手动完成的。当然这也相差为奇,因为这与Redis的计划性初衷有关
,毕竟作者都早已说了,最主旨的规划目标就是性质、水平伸缩和可用性。

从Redis
Cluster的环境搭建使用到高级成效和中间原理分析,再到应用案例收集和优缺点的剖析罗列,讲了如此多,关于Redis集群到底咋样,相信我们依据自己亲身和花色的具体情形一定有了和谐的下结论。不管是评估测试也好,二次开发也好,依然直接上线使用能够,相信随着官方的穿梭迭代改进和豪门的力量,Redis
Cluster一定会日渐周详成熟的!

 

-Traditional Message Broker
传统的Message Queue 和Message Sub/Pub功能

-Stream Processing – Staged Pipeline
和其余产品类似,比如Storm,就是足以让Message可以在几个Stage间流转,每一个Stage的拍卖逻辑可能不一样,然则Stage之间的输入/输出接口是联合的

-Commit Log
作为Commit
Log来选用,那或多或少和VDL的目标场景之一是一模一样的。但是为何我们并没有选用Kafka来作为Commit
Log场景的选型呢?首要原因有三个,第一是LinkedIn集团温馨的开发espresso(https://www.percona.com/live/data-performance-conference-2016/sessions/espresso-linkedins-distributed-document-store-top-mysql)数据库,就采用Kafka作为Commit
Log来举行espresso数据库主从之间的复制。在汤姆 Quiggle二〇一八年演讲《espresso
database replication with
kafka》中多少来看,平均复制延迟为小于90ms,我们觉得这么些延迟太大了,不可能知足大家对数据库复制高性能、低延时的要求。第二,Kafka的数量复制协议,即便总体上参照了微软PacificA随想作为理论基础,然而相比较PacificA小说说的相同,这是一个复制框架、一个原型系统,Kafka具体的贯彻其实差异如故很大。更着重的是,从二零一二年Kafka复制协议的V1版本开首,直到二零一七年KIP-101(https://cwiki.apache.org/confluence/display/KAFKA/KIP-101+-+Alter+Replication+Protocol+to+use+Leader+Epoch+rather+than+High+Watermark+for+Truncation,最新Kafka
0.11.*本子),一直留存着相比严重的多寡丢失的恐怕。即便那些复制协议变得愈加像Raft协议,不过一向缺乏严刻的反驳推导声明。第三,Kafka要用做一个保险的Commit
Log,需要的布局较为复杂,同时在这么些严刻的布置下,性能较差。具体能够参见:Jiangjie
(Becket) Qin 的演说《Data Loss and Data Duplication in Kafka》。

-Others
Metric和利用调用链、Log聚合等。

 

换一个角度来看,除了Apache
Kafka官方的第一名气象介绍,我们看看一个音信系统,其实提供给用户的联结抽象能够了解成这样:

图片 5

也就是说,信息系统是储存系统的一个虚无封装,而VDL其实定位就是音讯系统的一个储存引擎。新闻系统于用户而言,一个通用抽象模型是:

图片 6

所以,音信系统是Log存储之上的一个更高层级的架空,对于事情体系,可以拔取使用新闻系统、也可以挑选直接利用Log存储系统。紧要借助工作特色,两者是相互补充的,共同构成公司数量处理的技术栈。

VDL和etcd/Zookepper/Consul的联系\区别

图片 7etcd的永恒是一个分布式的强一致K/V存储系统,所以实际上etcd可以算是在VDL之上的一个更细分的仓储形态。VDL的本色类似于etcd中的Write
Ahead Log。etcd其实也是一种基于Replicated State
Machine(前面我们会讲,这也是VDL使用情状之一)方法的分布式系统,不同在于VDL保证的分界是用户提交请求的大局有序,且持久化到集群中的各类副本上,之后State
Machine咋样replay这个Log可以依照具体情状而定。比如K/V系统,假若不考虑多少个Key之间的事情涉嫌关系,其实不比Key对应的写入请求可以并行replay到State
Machine(Parallel
RSM方向也有无数新成果,但落地的难度仍然很大)。把Log一致性和State
Machine的贯彻完全解耦开来,为实现State
Machine的出现重放等,打下了稳步的功底,而且一个一致性的Log
Stream,可以对应三个State Machine实现。

 

etcd提议的施用情况包含(https://coreos.com/etcd/docs/latest/learning/why.html):

-Metadata存储,当然也得以储存一些配备音信;

-Distributed Coordination,这么些和Zookeeper和Consul的常用场景类似。

 

其它,etcd/Zookeeper/consul可以看作全局上,就是一个独门的一致性协议实例。而VDL一个LogStream就是一个Raft
Group,我们透过在Raft
Group级此外调度和分配,达到更好的资源利用和负载均衡效果。

VDL的利用场景

图片 8 我们考虑中,VDL的出众使用情形如下: 

-RSM(Replicated State Machine)

图片 9

-Database replication
现阶段,其实主流的数据库复制都是依据Log
Ship的点子,无论一个分布式系统接纳哪个种类多少复制方式,其实基本的都是要保管Log在同一全局序号包含的内容相同,同时存在六个副本。Log可以是影响主库状态变化的Log(原始请求被主库处理后的出口),也可以是直接的用户写入请求,只是后者平日需要有手腕保证那个Log是deterministic,也就是不会因为replay这么些Log的节点不同,相同输入的Log发生了不同的结果输出。

图片 10

 

-Storage Engine of Other Distributed System
正如前方提到的如出一辙,即使大家要开销一个分布式强一致的K/V存储系统,那么使用LevelDB或者RocksDB作为本地的状态机就足以了。虽然我们兑现一个分布式强一致的Cache存储系统,那么我们可以动用Redis或者Memcached作为地点的状态机。当然,实际落实的长河中,State
Machine的Snapshot怎么样促成,也有些一定的复杂度,但是不管如何这么些抽象分层已经很好第把这一个复杂度局限在了一个有些。

比如Apache Pulsar(http://pulsar.apache.org/),其实就是采用Apache
Bookkeeper作为其后端存储系统,Pulsar负责对音信语义举办抽象和元数据的管住等。VDL在这上边的应用,基本思想就是一种Pluggable
Store
Engine的思考。那在成千上万重型仓储系统中,已经是一个基本的架构形态。 

-Unified Shared Log Abstraction

对集团来说,能否采纳好数据对自己的高速运转十分紧要,而使用多少就关系到“数据的移动”和“数据的计量”。VDL的一个初衷就是去解决“数据移动”的题材,高效且容错地将Schema-less的多寡共享在此外工作系统面前。同时,统一的共享Log服务,已经正在被许多重型互联网集团采取和倚重。比如,非死不可的LogDevice,腾讯的PaxosStore,Twitter的Distributed
Log等,都呈现出不同档次地对联合Schema-less
Log存储的尊重。Google平昔对强一致高可用的仓储系统分外重视,GoogleResearch有一篇作品《Ubiq- A Scalable and Fault-tolerant Log Processing
Infrastructure》,大致讲到了不怎么看似的笔触。

 

除此以外,无论基于传统新闻系统的业务架构体系,依旧近年来提得相比较多的Streaming
Platform(https://www.streaml.io/)体系,都非常依赖统一的Log存储系统。streamlio依赖Apache
Bookkeeper作为分布式Log存储系统。

 VDL的衍生产品

图片 11

对唯品会来说,基于VDL衍生的率先个产品是Binlog
Server,这么些造型综合了RSM和Unified Log Abstraction二种情景。

 

图片 12

下一周将发表VDL的兑现及质量管控介绍,敬请关注。

 

推荐阅读

 

 

 

图片 13

【唯实践】Memcached使用这么些事

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注