redis复制、哨兵、集群详细介绍 我就是我 2022-10-22 07:58 178阅读 0赞 **目录** 1、redis性能瓶颈在哪里? 2、redis为什么需要高可用? 3、主从复制 3.1、主从复制原理 3.1.1、复制初始化 3.1.2、复制同步阶段 3.1.3、增量复制 3.1.4、无硬盘复制 3.2、异步复制导致主从库数据不一致怎么解决? 3.3、主从复制崩溃恢复 4、哨兵 5、集群 5.1、插槽 5.2、集群的主从复制模型 5.3、集群一致性 5.4、搭建集群 6、redis模板配置文件 7、安装Ruby -------------------- # 1、redis性能瓶颈在哪里? # 首先说说redis为什么这么快。主要有以下几点原因: * 纯内存操作; * 单线程操作,避免频繁上下文切换; * 采用IO多路复用模型; * 纯ANSI C语言编写; 由于redis是纯内存操作并且采用了IO多路复用模型,因此redis的性能瓶颈不应该是CPU,内存和网络延迟更应该是redis的性能瓶颈。 # 2、redis为什么需要高可用? # 需要高可用的原因主要为: * 单点故障:因此需要将数据生成多个副本分布在不同的机器上; * 内存容易成为瓶颈:因此需要对数据进行分片; redis有三种高可用方案,分别是: * 主从复制 * 哨兵 * 集群 # 3、主从复制 # 主库:可以读写,将数据同步给从库 从库:只能读,如果修改从库数据会报错(将slave-read-only设置为no就不会报错) 一个主库可以有多个从库,但一个从库只能有一个主库。从库也可以作为主库。 主从复制可以同来实现读写分离,比如在电商系统中,读的压力大于写,那么可以配置一主多从的redis高可用架构,主库用来写,多个从库用来读。 由于主从复制不能有多个主库,因此当写压力过大时一主多从不再满足需求,此时需要使用redis集群。 如何配置主从复制?两种方法如下: * 在从库的redis.conf中配置slaveof host port; * 在从库执行slaveof host port命令; info section命令以一种易于理解和阅读的格式,返回关于Redis服务器的各种信息和统计数值。比如查看复制相关信息,可以使用info replication。 在一个主库上执行info replication命令打印出来的信息如下所示。 127.0.0.1:6379> INFO replication # Replication role:master connected_slaves:1 slave0:ip=127.0.0.1,port=6380,state=online,offset=14,lag=0 master_replid:0dca9fd8627630d1058cd3ae740c39edc03f07c5 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:14 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:14 也可以使用role命令查看节点角色。 如果当前库已经是某个主库的从库,那么执行slaveofhost port 将使当前库停止对旧主库的同步,丢弃旧数据集,转而开始接收新主库的同步。 另外,对一个从库执行命令slave no one将使得这个从库关闭复制功能,并从从库转变回主库,原来同步所得的数据集不会被丢弃。 利用slave no one不会丢弃同步所得数据集这个特性,可以在主库失败的时候,将从库作为新的主库,从而实现无间断运行。 ## 3.1、主从复制原理 ## ### 3.1.1、复制初始化 ### 从库启动后,向主库发生SYNC命令 主库接收到命令并在后台保存RDB快照(异步),并将保存快照期间接收到的客户端命令缓存起来,然后一起发给从库 从库会将收到的内容写入磁盘临时文件,最后用临时文件替换旧的RDB文件(在写临时文件期间,从库不会阻塞客户端请求,并且用同步前的数据响应客户端,可以在redis.conf中配置slave-serve-stale-data为no来让从库在同步完成前对除了info和slave之外的所有命令响应错误) ### 3.1.2、复制同步阶段 ### 主库每当收到写命令就将命令同步给从库(同步的内容就是redis通信协议的内容) ### 3.1.3、增量复制 ### 主从库断开连接后,从库再次连接主库时,不必再进行一次复制初始化,而是直接增量复制(redis2.8版本开始支持)。 原理: 1. 在复制同步阶段,主库每接收到一个命令,都会将该命令放到积压队列中,并记录好偏移量,从库同步数据时,也会记录好偏移量; 2. 增量复制使用psync命令代替sync命令,且psync命令会带两个参数,psync 主库id 断开前最新的命令偏移量; 3. 主库收到psync命令后,会判断从库传过来的主库id与自身id是否相等;如果相等,则再判断从库传过来的偏移量是否在积压队列中; 4. 如果上述两个条件有一个不满足,则不满增量复制,而进行一次复制初始化; ### 3.1.4、无硬盘复制 ### 复制初始化时,由于主库会进行快照生成RDB文件,因此会影响性能。redis2.8版本支持无硬盘复制(redis.conf中将repl-diskless-sync设置为yes),主库直接通过网络将数据发生给从库 ## 3.2、异步复制导致主从库数据不一致怎么解决? ## redis主从复制不保证强一致性,而是保证最终一致性。 另外,可以在主库的redis.conf中可以做如下配置。 # 允许最小连接的从库数 min-slaves-to-write 3 # 允许从库最长失去连接的时间 min-slaves-max-lag 10 以上两个配置只要有一个不满足,则主库不可写。 ## 3.3、主从复制崩溃恢复 ## 在主从复制中,主库一般是禁用持久化的,因为从库已经做数据备份了,禁用持久化可以提高性能。 从库崩溃,直接重启从库即可 主库崩溃,不能直接重启主库,因为主库禁用持久化,如果重启主库则直接丢失数据,然后同步给从库,从库也相应地丢失数据。 为了避免数据丢失,当主库崩溃时,需要做以下两步操作: 在从库执行slave no one命令将从库升级为主库 重启崩溃的主库,然后使用slave命令将其设置为新主库的从库 无论主库崩溃还是从库崩溃,都需要手动操作,手动操作不仅麻烦还容易出错,因此redis提供哨兵这一机制来支持自动操作。 # 4、哨兵 # 哨兵的功能: * 监控主从库是否正常运行; * 主库故障时自动将主库转为从库; 可以部署多个哨兵,哨兵之间也会互相监控。 哨兵甚至可以监控多个主从系统,通过在sentinel.conf中配置多行sentinel monitor配置来实现。 哨兵是一个可执行文件,在redis的src目录下面,名称为redis-sentinel。 在sentinel.conf中,做如下配置。 sentinel monitor mymaster 127.0.0.1 6379 1 mymaster是主库名。 127.0.0.1:6379是主库的ip和端口号,sentinel.conf中只需要配置要监控的主库即可,而从库sentinel会根据主库自动发现。 1表示至少需要1个哨兵节点同意。 然后执行以下命令启动sentinel进程 src/redis-sentinel sentinel.conf 下面演示一下哨兵是如何自动化监控主从复制的。 为了简单起见,现有一主一从,主库为127.0.0.1::6379,从库为127.0.0.1::6380。 1. 启动一个哨兵进程,观察启动日志; 2. 然后我们刻意停掉主库,观察哨兵的日志变化; 3. 最后,手动重启旧主库,观察哨兵的日志变化(哨兵只会帮助我们选举新的主库,然后将旧主库变为新主库的从库,并不会为我们重启旧主库); 以上三步哨兵的日志如下图所示。 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hsXzE4MDM_size_16_color_FFFFFF_t_70][] # 5、集群 # 虽然引入了哨兵,但主从复制中每个节点都存储全量数据,因此最大能存储多少数据受限于内存最小的那个节点,形成木桶效应,因此需要对redis做数据分片。 客户端分片: * 旧版redis使用使用客户端分片,即由客户端决定每个键交由那个数据库节点存储; * 弊端:如果集群增删节点,则需要手动迁移数据,且为了保证迁移过程中的数据一致性需要将集群暂时下线; redis3.0开始支持集群。 集群特点: * 拥有和单机实例同样的性能; * 在网络分区后提供一定的可访问性以及对主库故障恢复的支持; * redis集群并不支持处理多个key的命令(如mget),这是因为在不同的节点间移动数据会达不到像单机redis那样的性能,在高负载的情况下可能会导致不可预料的错误; * 只能使用0号数据库,如果用select切换则报错; * 支持数据分片和主从复制; ## 5.1、插槽 ## redis cluster没有使用一致性hash,而是有一个哈希槽的概念,默认有16384个插槽。 redis将每个键键名的有效部分使用CRC16算法计算出散列值,然后对16384取余。 有效部分: * 如果键名中包含\{至少一个字符\},则有效部分为大括号里面的内容,比如\{user001\}:username和\{user001\}:password这两个键的有效部分都是user001,那么它们会被分配到一个结点上,可以使用涉及多键的命令(比如mget)去处理它们; * 如果键名中不包含\{至少一个字符\},则整个键名都是有效部分; * 如果有多个大括号则算法会在匹配第一个大括号就停止,然后进行判断。比如键a\{b\}c\{d\}的有效部分为b,而a\{\}c\{d\}的有效部分为整个键名; 集群中的每个主节点负责分配一部分插槽,cluster slots命令用于查看插槽分配情况,而从节点只负责备份主节点的插槽。 增删节点怎么办? * 比如现在有A、B、C三个主节点,假如想增加D节点,那么只要将A、B、C节点中的部分槽迁移到D即可,如果想删除B节点,则只需要将B节点的槽迁移到A、C节点即可。 增删节点或者是修改某个节点槽的数量都不会导致集群不可用。 ## 5.2、集群的主从复制模型 ## redis集群是支持主从复制的,目的是为了提高可用性。 比如当前集群有三个主节点A、B、C和三个从节点A1、B1、C1,则有以下情况: 1. 如果从节点B1挂掉了,则不影响,整个集群可用; 2. 如果主节点B挂掉了,集群会选举B1为新的主节点,整个集群仍然是可用的,待B重启后会变为B1的从节点; 3. 但如果B和B1都挂掉了,那集群就不可用了,比如执行set命令时,会报错:(error) CLUSTERDOWN The cluster is down; 主从节点只要不是都挂,则不需要开启持久化,如果都挂,那必须要开启持久化了。 如果你的redis cluster只用于缓存,那么为了最大程度提升性能,不需要开启持久化。 如果你的redis cluster用来做数据库,那么就要看情况了,如果主从同时挂掉的几率不大且你对数据丢失的容忍度还行的话,就不需要开启持久化,否则需要。 ## 5.3、集群一致性 ## redis集群不保证强一致性,导致不一致的可能原因有两个: * 主从节点间是异步复制的; * 某个主节点发生了网络分区,比如集群有A、B、C三个主节点以及对应的从节点A1、B1、C1,此时主节点B与集群形成网络分区,在网络分区期间某个客户端往B节点写数据,如果网络分区很短暂,那数据就不会丢失;但如果在网络分区期间集群重新选举了B1节点为主节点,那么数据就会丢失了; 形成网络分区的节点的超时时间是可以通过cluster-node-timeout这个配置项配置的,如果没有超时,则客户端可以正常向分区节点写数据,如果超时则不能写入。 ## 5.4、搭建集群 ## 集群至少需要3个节点才能正常运行,下面演示一下在单个centos系统上配置一个3主3从的redis集群。 集群重要配置如下。 cluster-enable yes cluster-node-timeout 5000 cluster-config-file nodes.conf 集群文件夹结构如下图所示。 ![20210411191136111.png][] 6381节点配置文件内容如下,其它节点类似(关于模板配置文件见本文第6节-redis模板配置文件)。 include ../run/redis-cluster.conf port 6381 pidfile /usr/local/redis-cluster/run/redis-6381.pid logfile /usr/local/redis-cluster/run/redis-6381.log dir /usr/local/redis-cluster/6381/ cluster-config-file /usr/local/redis-cluster/run/nodes-6381.conf 启动各个节点,用redis-cli连接任意节点执行info cluster命令可以查看集群状态,cluster\_enable为1表示可用。 但此时还不能写数据,比如在任意节点执行set age 10命令时,会报错:(error) CLUSTERDOWN Hash slot not served。 这是因为此时每个节点还是独立状态,下面需要将各节点联系在一起,即初始化集群。 如果是旧版本的redis,可以使用src/redis-trib(ruby语言编写,安装Ruby见本文第7节-安装Ruby)来初始化集群,初始化集群的命令如下。 src/redis-trib.rb --replicas 1 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384 127.0.0.1:6385 127.0.0.1:6386 replicas 1表示每个主节点的副本个数为1。 如果执行上述命令提示WARNING: redis-trib.rb is not longer available!,则表示当前版本的redis已经不支持redis-trib.rb,具体的提示信息如下图所示。 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hsXzE4MDM_size_16_color_FFFFFF_t_70 1][] 根据该提示,我们知道了应该使用redis-cli来代替redis-trib.rb。 首先在redis-cli中执行cluster help命令,查看与集群cluster相关的命令有哪些,如下所示。 ADDSLOTS <slot> [slot ...] -- 为当前节点分配槽位 BUMPEPOCH -- Advance the cluster config epoch. COUNT-failure-reports <node-id> -- 根据<node-id>返回失败报告的数量 COUNTKEYSINSLOT <slot> - 根据槽位返回key数量 DELSLOTS <slot> [slot ...] -- 在当前节点删除指定槽位 FAILOVER [force|takeover] -- 将当前复制节点提升为主节点 FORGET <node-id> -- 从集群中移除节点 GETKEYSINSLOT <slot> <count> -- 返回当前节点存储在插槽中的键名 FLUSHSLOTS -- 删除当前节点的槽位信息 INFO - 返回关于集群的信息. KEYSLOT <key> -- 根据key返回hash槽 MEET <ip> <port> [bus-port] -- 将节点连接到工作集群 MYID -- 返回当前节点id NODES -- 返回节点看到的集群配置。输出格式:<id> <ip:port> <flags> <master> <pings> <pongs> <epoch> <link> <slot> ... <slot> REPLICATE <node-id> -- 配置当前节点为<node-id>的复制节点 RESET [hard|soft] -- 重置当前节点 (默认: soft). SET-config-epoch <epoch> - Set config epoch of current node. SETSLOT <slot> (importing|migrating|stable|node <node-id>) -- 设置槽位状态. REPLICAS <node-id> -- 返回指定主节点的副本节点 SLOTS -- 返回关于槽范围映射的信息。每个系列都是由:start, end, master and replicas IP addresses, ports and ids 初始化集群步骤: 1、使用cluster meet命令将各个节点联系在一起,如下图所示。 ![2021041221053639.png][] 再执行cluster nodes命令,可以看到六个节点联系在一起了,如下图所示。但此时这六个节点都是主库,并且还没有分配插槽。 ![2021041310580922.png][] 2、使用cluster addslots命令为所有主库分配插槽。 由于cluster addslots命令不支持批量添加插槽,所以我写了一个shell脚本利用for循环批量分配插槽,shell脚本如下。 for ((i=$3; i<=$4; i ++)) do 6381/src/redis-cli -h $1 -p $2 cluster addslots $i > /dev/null done 现在让6381、6382、6383为主库,我们将16384个插槽分配给这三个主库,具体分配情况如下: 6381占0~5460号插槽,共5461个 6382占5461~10920号插槽,共5462个 6383占10921~16383号插槽,共5461个 执行如下命令即可分配插槽: ./cluster-addslots.sh 127.0.0.1 6381 0 5460 ./cluster-addslots.sh 127.0.0.1 6382 5461 10920 ./cluster-addslots.sh 127.0.0.1 6383 10921 16383 最新使用cluster slots命令查找插槽分配情况。 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hsXzE4MDM_size_16_color_FFFFFF_t_70 2][] 3、使用cluster replicate命令设置从库 执行如下命令,使得: * 6384为6381的从库; * 6383为6382的从库; * 6384为6383的从库; 6381/src/redis-cli -h 127.0.0.1 -p 6384 cluster replicate 6381的节点id 6381/src/redis-cli -h 127.0.0.1 -p 6385 cluster replicate 6382的节点id 6381/src/redis-cli -h 127.0.0.1 -p 6386 cluster replicate 6383的节点id 最后使用cluster nodes命令查看集群节点信息,即可看到有三个主库和三个从库,如下图所示。 ![2021041310560459.png][] 集群搭建好后,我们使用redis-cli往集群写一些数据试试,但如果不幸运的话写第一个数据就会报错,如下图所示。 ![20210413105932961.png][] 这是因为redis-cli连接的是6381这个节点,但user:1这个key会被分配到6382节点,因此写入错误。 解决办法是,redis-cli支持以集群模式连接到某一个节点,并且支持自动重定向,如下图所示。 ![20210413110255696.png][] # 6、redis模板配置文件 # redis-5.0.5版本默认配置文件(去除注释)如下。 ################################## INCLUDES ################################### ################################## MODULES ##################################### ################################## NETWORK ##################################### bind 127.0.0.1 protected-mode yes port 6379 tcp-backlog 511 timeout 0 tcp-keepalive 300 ################################# GENERAL ##################################### daemonize no supervised no pidfile /var/run/redis_6379.pid loglevel notice logfile "" databases 16 always-show-logo yes ################################ SNAPSHOTTING ################################ save 900 1 save 300 10 save 60 10000 stop-writes-on-bgsave-error yes rdbcompression yes rdbchecksum yes dbfilename dump.rdb dir ./ ################################# REPLICATION ################################# replica-serve-stale-data yes replica-read-only yes repl-diskless-sync no repl-diskless-sync-delay 5 repl-disable-tcp-nodelay no replica-priority 100 ################################## SECURITY ################################### ################################### CLIENTS #################################### ############################## MEMORY MANAGEMENT ################################ ############################# LAZY FREEING #################################### lazyfree-lazy-eviction no lazyfree-lazy-expire no lazyfree-lazy-server-del no replica-lazy-flush no ############################## APPEND ONLY MODE ############################### appendonly no appendfilename "appendonly.aof" appendfsync everysec no-appendfsync-on-rewrite no auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb aof-load-truncated yes aof-use-rdb-preamble yes ################################ LUA SCRIPTING ############################### lua-time-limit 5000 ################################ REDIS CLUSTER ############################### ########################## CLUSTER DOCKER/NAT support ######################## ################################## SLOW LOG ################################### slowlog-log-slower-than 10000 slowlog-max-len 128 ################################ LATENCY MONITOR ############################## latency-monitor-threshold 0 ############################# EVENT NOTIFICATION ############################## notify-keyspace-events "" ############################### ADVANCED CONFIG ############################### hash-max-ziplist-entries 512 hash-max-ziplist-value 64 list-max-ziplist-size -2 list-compress-depth 0 set-max-intset-entries 512 zset-max-ziplist-entries 128 zset-max-ziplist-value 64 hll-sparse-max-bytes 3000 stream-node-max-bytes 4096 stream-node-max-entries 100 activerehashing yes client-output-buffer-limit normal 0 0 0 client-output-buffer-limit replica 256mb 64mb 60 client-output-buffer-limit pubsub 32mb 8mb 60 hz 10 dynamic-hz yes aof-rewrite-incremental-fsync yes rdb-save-incremental-fsync yes ########################### ACTIVE DEFRAGMENTATION ####################### 将默认的配置文件做如下修改(如何修改具体看需求),使其变为所有节点通用的模板配置文件: * 注释bind * protected-mode改为no * 注释port * daemonize改为yes * 注释pidfile * 注释logfile * 注释三个save * 注释dir 如果是集群模式,则还需要: * 新增cluster-enabled yes; 然后再针对单个节点用include指令导入公共模板(注意使用相对路径,否则启动会报错),然后再做一些本节点的特殊配置,如下所示: include ../run/redis_useful.conf port 6379 pidfile /var/run/redis_6379.pid logfile /var/log/redis_6379.log dir /usr/local/redis-5.0.5/ # 7、安装Ruby # 1. wget https://cache.ruby-lang.org/pub/ruby/3.0/ruby-3.0.1.tar.gz 2. tar -xvzf ruby-3.0.1.tar.gz 3. ./configure 4. make 5. make install [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hsXzE4MDM_size_16_color_FFFFFF_t_70]: /images/20221022/af0a79bdf4d94539a8315b2fda3abbeb.png [20210411191136111.png]: /images/20221022/339f1737c38f4e02ae7d56aa8bed0362.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hsXzE4MDM_size_16_color_FFFFFF_t_70 1]: /images/20221022/b196962dfe5d4dc29a8b049c43fa297b.png [2021041221053639.png]: /images/20221022/9bfa02c69eb7451cb1cfedd4a90f4d62.png [2021041310580922.png]: /images/20221022/c9b67bff9e8b4fff86a13ec339ad4db8.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3hsXzE4MDM_size_16_color_FFFFFF_t_70 2]: /images/20221022/480e57342c334a138390e009cfd7f001.png [2021041310560459.png]: /images/20221022/d717acb9a89844bc91e643fad21b2e93.png [20210413105932961.png]: /images/20221022/6bf5f4e385a24aa8be8ea96569584114.png [20210413110255696.png]: /images/20221022/71994c0170d74792a405d2ad762e8d57.png
还没有评论,来说两句吧...