1 单机、单节点、单实例通用问题
- 单点故障
- 容量有限
- 压力(来自连接数、cpu)
2 解决方案:AKF拆分原则
- 可扩展艺术一书中提供的设计微服务时的原则之一,是将大规模系统拆分成多个小规模系统时,所应该遵守的原则。遵守该原则可以解决大规模系统的容量、性能、模块数量增长带来的系统复杂度等问题。防止系统设计的不合理导致需要将系统进行反复地重构
2.1 基于X轴的拆分
- 全量,镜像复制,用于解决单点故障问题
- 会引发数据一致性的问题,也就是几台节点数据可能不一致
- 一致性问题解决方案
- 强一致性
- 客户端写入A后,A不返回结果,而是继续向B和C写入,同时A阻塞,等B、C都写完,再由B、C将成功的消息返回给A,A再返回客户端。ABC一台机器写入失败就全部失败
- 成本太高,基本无法达到,因为如果B写入太慢,会导致整个交易变慢
- 会破坏可用性,因为如果数据没写进去,整个服务就不能再正常使用了
- 基于X轴拆分,本来就是为了解决可用性,结果强一致性还影响了可用性
- 弱一致性:可能导致数据丢失
- 最终一致性:最终的数据是一致的,过程中有可能取到不一致的数据
- 在A和B、C间加一个消息中间件,例如kafka,A和kafka之间同步阻塞,kafka和BC间异步
- 要求kafka必须可靠(集群模式)、响应速度够快
- 最终B、C中数据一定可以和A一致,叫做最终一致性
- 但如果对于主从集群,当client访问到了B和C,还是可能取到与A不一致数据,比如B、C还在同步kafka中数据时被访问
- 集群的模式
- 主从:主负责读写,从只负责读。即读写分离
- 主备:备机不提供服务
- 高可用性的解决方案:指当主机宕机后,如何能够快速的由备机接管,目的是替代人,之前的keepalived就是这种技术
- 可以使用一个程序监控主和从
- 但如果是一个程序,就又涉及到了单点故障问题,即一旦这个监控程序故障,当主机宕机,仍然无法切换到备机
- 因此这个程序也应是一个集群
- 如果这个监控程序也是有主有从,就又会产生单点故障,因此该程序是使用投票的方式来运行,即监控程序的集群中,必须有n/2+1个监控程序认为主机宕机,才算是真正宕机
- 因为如果不够n/2+1,会导致可能n/2个监控认为主机宕机,剩下n/2认为主机正常,造成脑裂,也就是网络分区。即不同网络下系统状态不一致
- 一般集群中的监控程序节点数是奇数台,因为一个集群中有5台还是6台,效果是一样的,都只能最多允许两个节点故障,而台数越多,故障的机器数越多,且购买成本越高
- 监控程序之间是需要知道对方状态的,这样监控程序才能判断出谁和自己状态相同,从而判断当达到一定数量监控程序和自己监控结果相同,进而决定下一步对主如何处理
- 此时单点故障问题可以解决,读的压力也被分散,但写的压力还没有被分散,也无法解决容量问题
2.2 基于Y轴的拆分
- 按不同的业务功能,进行拆分,不同的业务跑不同的redis。在mysql中称为分库
2.3 基于Z轴的拆分
- 按数据进行拆分,例如1-100000跑第一套redis,100000-200000放在第二套redis
3 Redis的主从复制
- 是redis采用的基于X轴拆分的具体方案
- redis默认使用的是异步复制,也就是弱一致性,这是为了保证redis快的特点。而且是主从集群,主可以提供读写,从只能提供读
- 主机称为master,从机称为replica,意思是复制品
- 实验
#1. 建立test目录,复制配置文件进来,配置文件中关闭后台运行、日志、aof、rdb文件路径
cp /etc/redis/6379.conf /root/test/6379.conf
#2. 建立三个配置文件,6379、6380、6381
#3. 删除持久化目录中内容,防止内容被自动加载
cd /var/lib/redis/6379
rm -rf *
#4. 关闭之前启动的redis实例,按新配置文件启动三个redis实例
service redis_6379 stop
redis-server /root/test/6379.conf
redis-server /root/test/6380.conf
redis-server /root/test/6381.conf
#5. 客户端连接6380和6381,并执行命令让他们成为6379的replica
redis-cli -p 6380
#redis5.0之前使用slaveof命令
replicaof 127.0.0.1 6379
#6. 此时观察6379中日志,发现它触发bgsave,产生rdb文件,然后将这个rdb文件传送到6380的rdb路径下。而6380中,会先执行flushall清空自身数据,然后加载6379传输过来的的rdb文件
#7. 此时客户端连接6380,能读到之前客户端向6379写入的数据,尝试写入6380会报错,默认为主从模式,从不允许写
#8. 可以使用如下命令在启动时,就作为从进行启动,同时需要设定其master是谁。如果6380宕机,使用如下命令重启6379就不会再生成rdb文件,而是使用原来6380宕机时最后保留的rdb文件进行恢复,并在这基础上追加增量数据
redis-server /root/test/6380.conf --replicaof 127.0.0.1 6379
#9. 但如果用aof的模式启动,此时6379又会产生rdb文件。这是因为使用aof模式启动后,6380不再从rdb文件恢复,而是从aof文件恢复,而redis现在版本中,aof文件中并没有其追随master的id号,因此虽然6380之前产生了aof文件,但6380并不知道现在追随的6379,是不是之前产生aof文件的那个master,因此不能直接在当前留存aof文件基础上去追加增量数据。而rdb文件中有master的id号,这样6380就知道自己路径下的rdb文件是6379某个时点的数据,因此只要在此基础上进行追加即可
redis-server /root/test/6380.conf --replicaof 127.0.0.1 6379 --appendonly yes
#10. 当master宕机,replica仍然可以查询,但无法写入,可以手动将6380切换为master,再让其他replica追随6380,这样6380就会产生rdb文件并传送给它的replica。注意master上会记录replica都有谁
#6380客户端执行
REPLICAOF no one
#6381客户端执行
REPLICAOF 127.0.0.1 6380
- 配置文件
################################# REPLICATION #################################
# 配置追随的master的ip和port
# replicaof <masterip> <masterport>
# 如果master设置了密码保护,需要配置密码
# masterauth <master-password>
# master将rdb文件发送给replica时,需要将rdb文件传输到replica的rdb目录中,而rdb传输需要一定时间,该参数表示这段时间内,replica是否还提供对外服务,no表示必须同步完,变成最新状态的数据才能提供查询
replica-serve-stale-data yes
# replica是否启用只读模式,也就是无法写入
replica-read-only yes
# 连接master时,master产生的rdb文件,是否不必放到master的磁盘上,而直接通过socket传送给replica,no表示必须先落磁盘
repl-diskless-sync no
# master中会维护一个队列,存放近期的操作,当replica宕机后,恢复时,会发送给master一个偏移量offset,master判断如果这个偏移量并没有超过自己维护的队列的范围,就不会重新生成rdb文件,replica直接使用本地rdb恢复,然后master只将宕机到恢复这段期间,队列中对应的数据发送给replica进行增量的恢复。该参数就用于控制这个队列的大小
# repl-backlog-size 1mb
# 至少保证有3个健康的replica,master才允许继续进行写操作,该值默认为0,表示无论replica状态如何不影响主机写入,实际上该参数的目的是尽量让一致性向强一致性靠拢
# 延迟小于10的replica才被认为是健康的
# min-replicas-to-write 3
# min-replicas-max-lag 10
4 Redis的高可用
- redis通过哨兵Sentinel来实现高可用,即Sentinel可以可以替代人工,进行监控、提醒、故障转移
- 使用Sentinel的两种启动方式
- redis-sentinel
- redis-server /path/to/sentinel.conf --sentinel
- 哨兵模式下,使用客户端API连接redis时,应该连接哨兵集群,而不是直连master
- 实验
#1. 参照redis主目录下的sentinel.conf,在/root/test下建立3个哨兵的配置文件,26379.conf、26380.conf、26381.conf
#哨兵占用的端口号
port 26379
#指定哨兵监控的集群中master为127.0.0.1 6379,且投票的权重值为2,即至少有2个哨兵认为主机宕机才会进行切换。因为一套哨兵可以监控多套主从复制集群,因此此处可以设置监听多个master
sentinel monitor mymaster 127.0.0.1 6379 2
#2. 启动三个redis实例,6379为主,6380和6381为从
redis-server ./6379.conf
redis-server ./6380.conf --replicaof 127.0.0.1 6379
redis-server ./6381.conf --replicaof 127.0.0.1 6379
#3. 启动26379这个哨兵
#1. 在之前配置的$REDIS_HOME/bin目录中,虽然有redis-sentinel命令,该命令和redis源文件中的src下的redis-sentinel命令不同,前者是一个指向redis-server命令的链接
#2. 也就是说redis将redis-sentinel的功能集成在了redis-server中,只需要使用--sentinel参数就可以告诉redis-server命令,当前不是要启动一个redis实例,而是要启动一个哨兵实例
#3. 发现该哨兵马上发现了master的两个replica进程,哨兵称其为slave(奴隶)。这是因为master知道有哪些replica,哨兵监控master,因此也能知道有哪些replica
#4. 然后哨兵在master上发布并订阅名为"__sentinel__:hello"的频道,这样当其他哨兵启动后,也会发布订阅该频道,这样一个哨兵就能知道其他哨兵的地址和端口了
#5. 我们可以通过psubscribe *监听redis上所有发布订阅的消息,发现26379一直在__sentinel__:hello频道发布自身的id、ip、port
redis-server ./26379.conf --sentinel
#4. 接下来启动26380这个哨兵,查看26379控制台会发现26379已经发现了26380的加入,并获取到了26380的id、ip、port等信息。这就是上面描述的"监控程序之间是需要知道对方状态的"
#5. 当master宕机,几个哨兵会反复确定master状态,直到确定master确实已经宕机,几个哨兵投票推选出6381替代原有的master,然后在6381设置REPLICAOF no one,在6380和6379上设置REPLICAOF 127.0.0.1 6381
#6. 同时哨兵的配置文件中监控的master的ip和port也会变为6381的ip和端口
5 Redis的分区
- 可以让Redis管理更大的内存
- 提升Redis计算能力、扩充Redis的网络带宽
5.1 数据分区的方式
5.1.1 根据业务逻辑拆分
- 客户端根据不同的业务场景,将数据存入不同redis中,读取时也根据不同业务场景,去访问不同的redis
- 相当于基于Y轴的拆分
5.1.2 根据数据进行拆分
- 相当于基于Z轴的拆分
5.1.2.1 modula
- 计算key的hash值,然后用hash值模上redis节点数,从而决定数据应该放到哪台redis实例中
- 会影响分布式下的扩展性。例如原本2个节点扩展为6个节点因,如果原来数据10对2取模,因此放在第一台节点上,但现在客户端在获取10时,需要对6取模,判断该数据会放在第4台节点上,但去第4台节点上获取时发现获取不到
- 哈希算法本质上就是是一种映射算法,即无论给定哪个字符串都能经过映射算法得到一个等宽的其他值。hash、crc、fnv、md5都是映射算法
5.1.2.2 random
- 随机将数据写入服务端,但会造成读的时候不知道从哪读,只适合特定场景
5.1.2.3 kemata
- 一致性哈希算法
- 先规划一个哈希环,环中每个点对应0到2^32范围内的一个整数
- 将服务器节点的ip经过哈希计算,然后用其哈希值对2^32取模,得到的结果对应圆上的一点,称为物理点
- 当数据传入,对key也进行相同的哈希计算,然后用其哈希值对2^32取模,得到的结果也对应圆上一个点,在圆上找到比该值大的最小的物理点,将该key放到这个物理点对应的服务器节点上存放
- 此时如果新增一个服务器节点node3,不影响查询key的哈希值在node3到node2之间的数据,但会影响查询key的哈希值在node01到node03之间的所有数据,因为按新的规则,查询他们时,会到node03中查询,但其实他们在node02上
- 优点
- 相比modula,不会造成数据全局洗牌,确实可以分担其他节点压力
- 缺点
- 新增节点会造成一部分数据不能命中
- 缓存无法命中,就会造成击穿,一部分数据压到mysql中
- 解决方案
- 每次取大于key的哈希值的最小的两个物理点,但增加节点变多后,仍然会造成击穿
- 因此这种方案更倾向作为缓存而不是作为数据库用
- 可以对各个节点设置lru、lfu等清理策略,保证失效数据被及时清理
- 同时一旦发现缓存击穿,及时将该数据放入到新节点中进行缓存
- 虚拟节点
- 只用node01、node02、node03的ip去进行哈希计算,只能得到三个物理点,很容易造成数据倾斜,比如数据都恰好存放在node01中
- 因此可以为node01和node02都加上几个虚拟节点,比如1-10,然后根据ip和虚拟节点值一起进行哈希计算,这样每台服务器就都能得到10个物理点,从而解决数据倾斜问题
5.2 分区的实现
- 客户端分区:客户端掌握modula、random、kemata等算法,可以决定数据会被存储到哪个redis节点的算法
- 代理分区:客户端将请求发送给代理服,然后代理决定去哪个节点写数据或者读数据。twemproxy、codis、predixy都属于代理服务器
- 查询路由:客户端随机地请求任意一个redis实例,然后由Redis将请求转发给正确的Redis节点。Redis Cluster实现了一种混合形式的查询路由,但并不是直接将请求从一个redis节点转发到另一个redis节点,而是在客户端的帮助下直接redirected到正确的redis节点
5.3 Redis的分区实现
5.3.1 支持一致性哈希的客户端
- 客户端掌握一致性哈希算法,决定将数据发送给具体哪台服务器
- 缺点
- 一台客户端假如会启用n个线程,而每个线程如果使用1个redis连接,那么每台客户端就会对每台redis服务器产生n个连接,如果该redis服务器上连接着m台客户端,就会产生m*n个连接
- 而连接本身是需要成本的,因为无论长连接还是短连接,都需要经历3次握手、4次分手,大量的连接会导致redis服务器性能降低
- 所有客户端中都需要持有一致性哈希算法,对客户端要求较高
5.3.2 Twemproxy
- 可以在客户端与redis服务端之间加一个代理服务器,并将modula、kemata、random等逻辑放在代理服务器上
- 由于代理服务器只负责接收客户端请求而不进行处理,因此它可以承受更大量的连接
- 由于代理服务器不存放数据、无状态,因此代理服务器数量是可以随意扩展的
- 同时由于代理服务器的存在,客户端不必再支持modula、kemata、random等算法
- 当一台代理服务器无法承受住所有客户端连接时,可以增加代理服务器数量,并使用lvs和keepalive技术,为代理服务器进一步分担压力。无论企业后面技术多么复杂,对于客户端都是透明的
- lvs是4层的,不持有连接,所以不会由于连接而造成压力
- lvs是单机的有单点故障隐患,因此需要为其提供一台备用机,并使用keepalived技术,保证其高可用
5.3.3 Redis 集群(cluster)
- 对于modula或kemata这两种模式,一旦增加节点,缓存的迁移非常费劲,正常来说,以modula为例,需要将缓存中所有数据拿出来,按新服务器个数取模,重新将所有数据分配到不同的服务器上,计算量非常大
- 因此redis设计了槽位的概念,redis上有16384个哈希槽,最开始就将数据的哈希值按16383进行取模,得到0-16383之间的值,分别映射到两台服务器上,例如node01负责0-5460槽位上的数据,node02负责5461-10922槽位上的数据,node03负责10923-16383,这样,当新增节点,可以将槽位进行迁移,将他们上的槽位各取一部分分配给新节点
- Redis集群:客户端随机地请求任意一个redis实例,该实例判断客户端执行的命令涉及的数据是否在自己持有的槽位中,如果有直接从自身取,如果没有,从自己记录的所有槽位与实例的映射关系中,找到这个槽位在哪个实例中,然后将客户端连接重定向到该实例上,并执行命令
5.4 分区引发的问题
- 无法实现聚合、事务等功能,聚合操作需要redis访问所有实例,影响速度,而redis本质是快,因此不支持。而多个命令可能会发送到不同机器上,因此无法实现事务
- redis的集群提供了补偿方案,可以指定哈希标签(hash tag),例如{},redis只会对{}中内容进行哈希计算,这样客户端可以将想要实现聚合或事务的key,设置为类似{ooxx}k1、{ooxx}k2的形式,这样他们就一定会放在同一台实例中,也就可以支持聚合和事务了
6 实际操作
6.1 twemproxy
- 安装
#1. 在soft中克隆twitter/twemproxy项目
git clone https://github.com/twitter/twemproxy.git
#2. 如果执行失败
yum update nss
#3. 安装automake和libtool工具,下面用到的configure命令需要autoconf、automake、libtool这三个安装包,而autoconf在centos中自带但版本过低
yum install automake libtool
#4. 查看当前仓库中autoconf工具支持的最高版本
yum search autoconf
#5. 发现系统自带仓库的版本不够用,需要引入阿里的epel仓库。进入https://developer.aliyun.com/mirror/网页,点开epel标签,复制epel(RHEL 6)下记录的RHEL6如何获取该仓库的命令并执行
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-6.repo
#6. 执行后系统中/etc/yum.repos.d目录下多了一个指向阿里仓库的文件epel.repo。之前使用的CentOS-Base.repo指向的是red hat社区自己的仓库
#7. 清理缓存的仓库,这样yum就能找到新仓库
yum clean all
#8. 再次查找autoconf最新版本,发现叫做autoconf268
yum search autoconf
#9. 下载autoconf268
yum install autoconf268
#10. 进入twemproxy主目录执行autoreconf268命令生成configure命令
autoreconf268 -fvi
#11. 使用生成的configure命令进行配置
./configure --enable-debug=full
#12. 在twemproxy主目录执行make命令,可以在src目录下生成nutcracker程序
make
#13. 让twemproxy作为系统服务启动,将脚本拷贝到/etc/init.d中
cp /root/twemproxy/twemproxy/scripts/nutcracker.init /etc/init.d/twemproxy
#14. 为/etc/init.d/twemproxy赋予执行权限
chmod +x twemproxy
- 配置文件
#1. 查看twemproxy脚本,查看里面需要/etc/nutcracker/nutcracker.yml文件,同时需要用到nutcracker命令
#1. 将/root/twemproxy/twemproxy/conf中所有配置文件拷贝到/etc/nutcracker目录中
#2. 在path中引入nutcracker,由于path中包括/usr/bin,所以可以直接将nutcracker拷贝到/usr/bin中即可,此时输入nut,然后按tab就能补全该命令了,说明确实放入了path中
#2. 进一步修改/etc/nutcracker/nutcracker.yml配置文件
#a. 默认有alpha和beta等多组配置,这是因为twemproxy可以为多套redis主从提供代理服务,这里我们配置一组就可以了,因此只保留alpha
alpha:
listen: 127.0.0.1:22121
hash: fnv1a_64
distribution: ketama
auto_eject_hosts: true
redis: true
server_retry_timeout: 2000
server_failure_limit: 1
servers:
#b. 1表示权重,权重越大,数据被放入的概率越大
- 127.0.0.1:6379:1
- 127.0.0.1:6380:1
- 实验
#1. 手动执行redis-server启动服务时,会使用当前目录作为数据的持久化目录,也就是存放rdb文件的目录,因此此处建立/test/data/6379和6380两个文件夹,在其内分别手工启动两个redis服务
redis-server --port 6379
#2. 应该检查所有启动的redis进程,都杀掉,防止对实验有影响
redis-cli -p 6380 shutdowm
service redis_6379 stop
#3. 启动twemproxy,service中的这个名称需要和/etc/init.d中的一致
service twemproxy start
#4. 此时客户端就可以连twemproxy这个代理,而不用直接连redis实例
redis-cli -p 22121
#5. 聚合、watch、multi等命令执行报错
keys *
#6. 写入数据后,使用客户端分别直连6379和6380,发现只有6380中有新的key,因为twemproxy根据上面设定的算法将key放入到了6380,set 1 1234123时候,会放入6379
redis-cli -p 6379
6.2 predixy
- predixy需要c++11编译器,centos6没有这个编译器,很难补全,因此直接从github下载编译好的程序
- 进入https://github.com/joyieldInc/predixy,点击releases,复制predixy-1.0.5-bin-amd64-linux.tar.gz的连接地址
- 安装
mkdir predixy
cd predixy
wget https://github.com/joyieldInc/predixy/releases/download/1.0.5/predixy-1.0.5-bin-amd64-linux.tar.gz
tar xf predixy-1.0.5-bin-amd64-linux.tar.gz
- 配置文件
#1. conf路径下的predixy.conf
#a. 打开
Bind 127.0.0.1:7617
#b. 引入sentinel.conf这个配置文件,注意这个配置文件不是redis中的那个,而是predixy中的
# Include cluster.conf
#c. 配置文件中支持引入哨兵的配置文件,通过哨兵predixy就能知道谁是master谁是replica
Include sentinel.conf
# Include try.conf
#2. sentinel.conf
#1.
SentinelServerPool {
Databases 16
Hash crc16
#设置HashTag为{}
HashTag "{}"
Distribution modula
MasterReadPriority 60
StaticSlaveReadPriority 50
DynamicSlaveReadPriority 50
RefreshInterval 1
ServerTimeout 1
ServerFailureLimit 10
ServerRetryTimeout 1
KeepAlive 120
Sentinels {
#配置哨兵的地址
+ 127.0.0.1:26379
+ 127.0.0.1:26380
+ 127.0.0.1:26381
}
#一组哨兵可以监控多套主从复制结构,也就是会监控多个master,因此这多个master分为多个组,predixy会根据不同算法将数据分配到这两套主从复制结构中
#该名称必须为redis的哨兵配置文件中定义的master的别名
Group ooxx {
}
Group xxoo {
}
}
#3. vi操作补充
#a. ':'表示进入末行模式,'.'表示当前光标所在位置,'$'表示最后一行,','表示从哪到哪,y为复制,回车后就复制从当前光标位置到最后一行的所有数据
:.,$y
#b. 's'表示替换,此处表示将光标位置到文件最后的所有内容中的#替换为空
:.,$s/#//
#c. 删除当前光标后面2行内容
2dd
#d. 删除当前光标下面所有行
dG
- 实验
#1. 修改redis哨兵配置文件/root/test下的26379.conf、26380.conf、26381.conf。哨兵会监控两套主从结构,主机分别为36379和46379,哨兵为26379-26381
port 26379
sentinel monitor ooxx 127.0.0.1 36379 2
sentinel monitor xxoo 127.0.0.1 46379 2
#2. 先启动三台哨兵
redis-server 26379.conf --sentinel
redis-server 26380.conf --sentinel
redis-server 26381.conf --sentinel
#3. 启两套主从复制集群,在test目录中,建立四个目录36379、36380、46379、46380
redis-server --port 36379
redis-server --port 36380 --replicaof 127.0.0.1 36379
#4. 进入predixy的bin目录中,启动perdixy
./predixy ../conf/predixy.conf
#5. 连接predixy
redis-cli -p 7617
#6. 单独连两台master,发现k1和k2被放入了不同的master中
#7. 发现{oo}k1、{oo}k2,放入了同一个master中
set {oo}k1 kjfkjd
set {oo}k2 kjfkjd
#8. keys *、watch {oo}k1等命令还是无法执行
#9. 删除predixy配置文件中ooxx组,那么即使哨兵监控了多套主从复制结构,数据也无法写入,重启predixy后,predixy会判断当前只连接了一套主从结构,就又开启了聚合、事务等操作,因此又能使用watch k1了,开启事务也没问题
6.3 redis cluster
- Redis4.0以前需要使用ruby启动reids集群,新版本使用redis自己的命令即可
- 使用/root/redis-5.0.5/utils/create-cluster/create-cluster命令完成集群操作
#1. README中有集群的详细操作方法
#2. 查看create-cluster脚本,其中NODES=6表示会启动6个redis实例,REPLICAS=1表示一个master有一个replica,也就是6个redis实例会构成6/(1+REPLICAS)=3套主从结构
#3. 启动集群,通过打印可以看出6个redis实例被启动,端口号从30001到30006
./create-cluster start
#4. 将6个redis实例配置成3套主从结构,30001-30003被设置为master,并为每套主从结构分配槽位(slots),根据提示输入yes
./create-cluster create
#5. 随意连接到一个实例上
redis-cli -p 30001
#6. 命令执行失败,提示需要连接到30003上才能完成key的创建,此时不会自动路由到30003
set k1 123456
#7. 使用-c连接到任意一个实例上
redis-cli -c -p 30001
#8. 命令执行成功,会显示先自动路由到了12706槽位对应的30003机器上,然后才执行了set k1 123456命令,并且执行成功。此时客户端自动连到了30003上,再输命令已经是在30003上输入了
set k1 123456
#9. 命令执行成功,会先路由到k1所在的30003,然后执行watch k1命令,multi开启事务也没问题,此时set k2 123456,又被路由到了30001上。此时执行exec会报错,因为30001上没开启过multi
watch k1
multi
set k2 123456
exec
#10. 可以利用hash tag保证都存放在同一个节点上,这样就不会发生自动路由,执行exec也就不会报错。其他客户端如果在这中间连入不会导致当前连接被自动路由,所以只要客户端对事务中所有key都是用hash tag,那么就不会由于自动路由导致事务失败
set {oo}k1 123456
watch {oo}k1
multi
set {oo}k2 123456
get {oo}k3
exec
#11. 结束集群
./create-cluster stop
#12. 该命令所在文件夹下会产生一堆配置文件、日志、持久化文件等内容,可以使用如下命令全量删除
./create-cluster clean
- 使用redis-cli --cluster进行集群操作
#1. 查看集群操作帮助文档
redis-cli --cluster help
#2. 该命令其实是将已启动的几个redis实例组成一个集群,且如果某redis实例是为了被组成集群而启动,在配置文件中需要开启cluster-enabled yes,否则无法组成集群。此处为了方便启动6个redis实例,还是调用./create-cluster start启动redis实例,然后调用下面命令将他们分配槽位,设定主从关系,组成cluster。之前./create-cluster create只能将自身服务器上的几个redis实例组成cluster,无法将不同ip不同端口下的实例组成cluster
redis-cli --cluster create 127.0.0.1:30001 127.0.0.1:30002 127.0.0.1:30003 127.0.0.1:30004 127.0.0.1:30005 127.0.0.1:30006 --cluster-replicas 1
#3. 启动客户端连接这个集群,连接的实例不是master也能连接成功
redis-cli -c -p 30001
#4. 移动数据,也是随机连接到cluster中哪台实例都可以,也可以连接到replica
#a. 提示你想移动多少槽位,从1-16384,此处输入2000
#b. 提示想移动到的节点的ID,节点的ID在create命令和当前reshard命令的输出中都有记录,此处必须输入master的ID
#c. 输入移出节点的ID号,回车后可以再选一个节点,如果不需要了输入done
#d. 按提示输入yes
#e. 具体移动哪号槽位是无法控制的
redis-cli --cluster reshard 127.0.0.1 30001
#5. 查看集群的相关信息,命令后面的端口可以是master的也可以是replica的,此时发现每个master上的slots数量不再相同
redis-cli --cluster info 127.0.0.1:30001
#6. 查看每个master具体拥有哪些槽位
redis-cli --cluster check 127.0.0.1:30001