关于redis集群:Whats-new-in-Pika-v350

时隔两年,Pika 社区正式公布经由社区 50 多人参加开发并在 360 生产环境验证可用的 v3.5.0 版本,新版本在晋升性能的同时,也反对了 Codis 集群部署,BlobDB KV 拆散,减少 Exporter 等新个性。 咱们将具体介绍该版本引入的重要新个性。 1 去除 Rsync在 v3.5.0 版本之前,Pika 应用 Rsync 工具进行引擎中存量数据的同步,Pika 过程启动时创立 Rsync 子过程。这种同步形式在理论应用中呈现了一些问题,包含Pika 过程 crash 后从新拉起无奈失常同步以及同步过程中 Rsync 过程无端退出等。在往年公布的 v3.5.0 版本中,咱们在全量同步计划方面进行了重要的改良,摒弃了以往应用的 Rsync,实现了全新的数据同步计划,反对了断点续传,动静调节传输限速等个性,以确保同步过程更加稳固、可控。这些改良不仅加强了同步的可靠性,还为用户提供了更好的应用体验。 去除 Rsync 过程,应用自研全量同步形式实现断点续传,传输限速性能Pika 主从同步时,进行 master run\_id 的测验2 兼容更多 Redis 命令在 v3.5.0 版本中,咱们迈出了更大的一步,晋升了对 Redis 命令的兼容性,对 Redis 命令提供了更宽泛的反对。这个版本的改良使得 Pika 在与 Redis 生态系统的集成中体现更加杰出,为用户提供了更丰盛的性能和更广大的可能性。咱们对命令反对的扩大,为用户提供了更多的灵活性,以满足不同场景下的需要。 反对 UNLINK 命令反对 INFO COMMANDSTATS 命令反对 HELLO、SETNAME 命令反对 BLPOP、BRPOP 命令新增 Pika 原创 DISKRECOVERY 命令3 RocksDB 版本升级和分级压缩在 v3.5.0 版本中,咱们进行了一项重要的降级,将 RocksDB 引擎降级至 v8.1.1 版本,并实现了分级压缩性能的整合。这一降级不仅是技术的飞跃,也是咱们对系统性能和优化的继续关注的体现。通过这项降级,咱们为 Pika 减少了更高级别的数据管理能力,同时也让零碎更好地适应不同的压缩需要,为用户的数据存储和检索提供了更大的灵活性和效率。 ...

August 24, 2023 · 2 min · jiezi

关于redis集群:独家深度解析redis集群的架构问题附脑洞

本文首发自[慕课网](imooc.com) ,想理解更多IT干货内容,程序员圈内热闻,欢送关注"慕课网"及“慕课网公众号”!   作者:一凡|慕课网讲师 Redis 是一种开源(BSD 许可)、数据结构存储在内存中的零碎,用作数据库、缓存和音讯队列。Redis 提供了诸如字符串、散列、列表、汇合、带范畴查问的排序汇合、位图、超级日志、天文空间索引和流等数据结构。Redis 内置复制、Lua 脚本、LRU 驱赶、事务和不同级别的磁盘长久化,并通过 Redis Sentinel 和 Redis Cluster 主动分区提供高可用性。 1 集群的劣势上面是redis集群的几个显著劣势。 1.1 伸缩性,数据规模一直增大的时候,容易扩容 单实例模式:只能垂直扩大,增大机器内存的容量; 集群模式:反对垂直扩大,也反对程度扩大,有更好的灵活性,也能够反对更大的容量; 1. 2 高可用,服务故障的状况,影响范畴小 单实例模式:故障转移前100%不可用(slave转换为master之前); 集群模式:故障转移前局部不可用(集群规模越大,故障影响越小);  1. 3 高性能,查问和写入的性能 单实例模式:查问能够扩散在多个slave,写入却只有一个master; 集群模式:查问有多个master和多个slave,写入也有多个master; 2 数据分片,一致性hash实现redis集群的外围点,是针对数据的分片,这里的一致性hash算法就十分要害。 2.1 一般的hash 算法node=hash(key)%number 数量变动和node程序变动,导致node抉择的差异性微小,造成微小的缓存生效。 2.2 一致性hash hash(node) 造成虚构节点环,hash(key)落在虚构节点环,找到对应的node。 因为hash(node)的稳定性,与node程序无关。node变更只影响一小部分数据。 2.3 redis cluster的hash slot算法 关系: cluster > node > slot > key Redis Cluster在设计中没有应用一致性哈希(Consistency Hashing),而是应用数据分片引入哈希槽(hash slot)来实现。 一个 Redis Cluster蕴含16384(0~16383)个哈希槽,存储在Redis Cluster中的所有键都会被映射到这些slot中。 集群中的每个键都属于这16384个哈希槽中的一个,集群应用公式slot=CRC16(key)/16384来计算key属于哪个槽,其中CRC16(key)语句用于计算key的CRC16 校验和。 依照槽来进行分片,通过为每个节点指派不同数量的槽,能够管制不同节点负责的数据量和申请数。 3 集群元数据的一致性3.1 比照:集中式存储元数据 ...

May 29, 2023 · 2 min · jiezi

关于redis集群:Redis集群介绍及测试思路

作者:京东批发 李磊 Redis集群介绍Redis集群个别有四种形式,别离为:主从复制、哨兵模式、Cluster以及各大厂的集群计划。在3.0版本之前只反对单实例模式,3.0之后反对了集群形式。在3.0之前各大厂为了解决单实例Redis的存储瓶颈问题各自推出了本人的集群计划,其核心思想就是数据分片,次要有客户端分片、代理分片、服务端分片。这里咱们只介绍前三种形式:主从、哨兵、Cluster。 1、主从复制Redis单节点的数据是存储在一台服务器上的,如果服务器呈现故障,会导致数据不可用,而且读写都是在同一台服务器上,申请量大时会呈现I/O瓶颈。为了防止单点故障和读写不拆散,Redis提供了复制性能来实现Master中的数据向Slave数据库的同步。Master能够有多个Slave节点,Slave节点也能够有Slave节点,从节点是级联构造,如下图所示: 主从复制工作原理个别状况下为了让数据读写拆散,Master节点用来执行写操作,Slave节点提供读操作,Master执行写操作时将变动的数据同步到Slave,其工作原理如下图所示: Redis主从复制基本原理有三种:全量复制、基于长连贯的命令流传、增量复制。 首先介绍一下全量复制,当主从服务器刚建设连贯的时候,会依照三个阶段实现数据的第一次同步。假如当初有实例1(192.168.1.1)和实例2(192.168.1.2),当咱们在实例2上执行“replicaof 192.168.1.1 6379”命令后,实例2就变成了实例1的从库,并开始从实例1上复制数据,有如下三个阶段: 第一个阶段,是主从库之间建设连贯、协商同步的过程,为全量复制做筹备。具体来说,从库给主库发送psync命令,示意要进行数据同步,主库依据这个命令的参数来启动复制。psync命令蕴含了主库的runID和复制进度offset两个参数。 •runID:是每个Redis实例启动时主动生成的一个随机ID,用来惟一标记这个实例。当从库和主库第一次复制时,因为不晓得主库的runID,所以将runID设置为“?”。 •offset:设置为-1,示意第一次复制。 主库收到psync命令后,会用FULLRESYNC响应命令带上两个参数:主库runID和主库目前的复制进度offset,返回给从库,从库收到响应后会记录下这两个参数。FULLRESYNC响应示意第一次复制采纳的全量复制,也就是说,主库会把以后所有的数据都复制给从库。 第二个阶段,主库将所有数据同步给从库,从库收到数据后,首先清空现有数据,而后在本地实现数据加载。这个过程依赖于内存快照生成的RDB文件。具体来说,主库执行bgsave命令,生成RDB文件,接着将文件发给从库。 第三个阶段,主库会把第二阶段执行过程中新接管到的写命令,再发送给从库。具体来说,当主库实现RDB文件发送后,就会把此时replication buffer中的批改和新增操作发给从库,从库再从新执行这些操作。这样一来,主从库就实现同步了。 以上是全量复制的根本流程,一旦主从库实现了全量复制,它们之间就会始终保护一个网络连接,主库会通过这个连贯将后续陆续收到的命令操作再同步给从库,这个过程也称为基于长连贯的命令流传,能够防止频繁建设连贯的开销。 长连贯是基于网络的,那么它就存在网络断开的危险,在Redis2.8之前,如果主从库在命令流传时呈现了网络闪断,那么从库会和主库从新进行一次全量复制,开销十分大。在Redis2.8开始,网络闪断之后,主从库会采纳增量复制的形式持续同步,就只会把主从网络断连期间主库收到的命令同步给从库。 增量复制外围在于repl\_backlog\_buffer这个缓冲区。当主从库断连后,主库会把断连期间收到的写操作命令写入replication buffer,同时也会写入repl\_backlog\_buffer这个缓冲区。repl\_backlog\_buffer是一个环形缓冲区,主库记录本人写到的地位,从库也记录本人读到的地位。主从连贯复原之后,从库首先给主库发送psync命令,并把本人以后的slave\_repl\_offset发给主库,主库会判断本人的master\_repl\_offset和slave\_repl\_offset之间的差距,一般来说master\_repl\_offset会大于slave\_repl\_offset。此时,主库只用把master\_repl\_offset和slave\_repl\_offset之间的命令操作同步给从库就行。 2、哨兵模式sentinel,中文名哨兵。Redis的sentinel零碎用于治理多个Redis实例,该零碎次要执行以下四个工作:1.监控(Monitoring):Sentinel会一直的查看主服务器和从服务器是否失常运作。 2.主动故障转移(Automatic failover):当主节点不能失常工作时,哨兵会开始主动故障转移操作,它会将生效主节点的其中一个从节点降级为新的主节点,并让其余从节点改为复制新的主节点。 3.告诉(Notification):哨兵能够将故障转移的后果发送给客户端。 4.配置提供者(Configuration provider):客户端在初始化时,通过连贯哨兵来取得以后Redis服务的主节点地址。 其中,监控和主动故障转移性能,使得哨兵能够及时发现主节点故障并实现转移;而配置提供者和告诉性能,则须要在与客户端的交互中能力体现。 哨兵用于实现Redis集群的高可用性,自身也是分布式的,作为一个哨兵集群去运行。Sentinel的过程之间应用谰言协定(gossip protocols)来接管对于主服务器是否下线的信息, 并应用投票协定(agreement protocols)来决定是否执行主动故障迁徙, 以及抉择哪个从服务器作为新的主服务器。上面别离介绍一下监控和主动故障转移的基本原理: Sentinel集群监控原理 1.每个 Sentinel 以每秒一次的频率向它所知的主从服务器以及其它 Sentinel 实例发送一个 PING 命令。 2.如果一个实例间隔最初一次无效回复 PING 命令的工夫超过指定的值, 那么这个实例会被 Sentinel 标记为主观下线。 3.正在监督这个主服务器的所有 Sentinel 要以每秒一次的频率确认主服务器确实进入了主观下线状态。 4.有足够数量的 Sentinel 在指定的工夫范畴内批准这一判断, 那么这个主服务器被标记为主观下线。 5.每个 Sentinel 会以每 10 秒一次的频率向它已知的所有主从服务器发送 INFO 命令。当一个主服务器被 Sentinel 标记为主观下线时, Sentinel 向下线主服务器的所有从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次。 ...

April 7, 2023 · 3 min · jiezi

关于redis集群:Redis集群和哨兵搭建说明亲测成功

Redis集群和哨兵搭建阐明_亲测胜利1.运行环境阐明1.1 服务器零碎支流Linux零碎 1.2 Redis版本Redis-5.0.3 redis从3.0开始反对集群 2. Redis装置1 新建一个软件保留目录如:/data/software/ cd /data/software/wget http://download.redis.io/releases/redis-5.0.3.tar.gztar -zxvf redis-5.0.3.tar.gz2 新建一个利用目录如:/data/apps mv redis-5.0.3 /data/appscd /data/apps/redis-5.0.3make3.集群搭建3.1 机器筹备筹备三台机器,别离装置redis 192.168.1.51 主-从192.168.1.52 主-从192.168.1.53 主-从 3.1.1 在51机器上执行cd /data/apps/redis-5.0.3mkdir 7000mkdir 7001cp redis.conf 7000/redis.conf批改配置vim 7000/redis.conf#bind 127.0.0.1 #能够近程拜访protected-mode no #保护模式敞开daemonize yes #后盾启动port 7000pidfile /var/run/redis_7000.pidlogfile "/data/apps/redis-5.0.3/7000/7000.log"dbfilename dump.rdbdir /data/apps/redis-5.0.3/7000/cluster-enabled yes #开启集群cluster-config-file nodes_7000.conf #集群的配置文件cluster-node-timeout 5000 #申请超时 设置5秒够了appendonly yes #aof日志开启 有须要就开启,它会每次写操作都记录一条日志appendfilename "appendonly7000.aof"设置集群明码 在配置文件外面减少明码选项,肯定要加上masterauth,不然Redirected的时候会失败masterauth abcd_123requirepass abcd_123cp 7000/redis.conf 7001/#批改7001目录下redis.conf的对应配置,7000改为70013.1.2 在52机器上执行 与下面51机器配置相似, 把对应配置改成7002和7003。 mkdir 7002mkdir 70037002/redis.conf7003/redis.conf3.1.3 在53机器上执行 与下面51机器配置相似, 把对应配置改成7004和7005。 mkdir 7004mkdir 70057004/redis.conf7005/redis.conf3.1.4 别离启动6台Rediscd /data/apps/redis-5.0.3/src/redis-server 7000/redis.conf src/redis-server 7001/redis.conf src/redis-server 7002/redis.conf src/redis-server 7003/redis.conf src/redis-server 7004/redis.confsrc/redis-server 7005/redis.conf ps -ef | grep redis 查看启动状况3.1.5 在某一台上执行退出集群三主三从./redis-cli --cluster create 192.168.1.51:7000 192.168.1.52:7002 192.168.1.53:7004 192.168.1.51:7001 192.168.1.52:7003 192.168.1.53:7005 --cluster-replicas 1 -a abcd_123留神: 输出yes确认之前, 先看好哪几台是主,哪几台是从 (如果只有3台机器,确定是穿插主从后输出yes创立)如果是外网拜访:须要凋谢端口7000 17000 ...等所对应端口 ...

December 28, 2022 · 2 min · jiezi

关于redis集群:Redis集群部署的三种模式

一、Redis简介Redis 是一款齐全开源收费、恪守BSD协定的高性能(NOSQL)的key-value数据库。它应用ANSI C语言编写,反对网络、可基于内存亦可长久化的日志型、Key-Value数据库,并提供多种语言的API。 Redis的应用场景有如下一些: 读写效率要求高,须要将数据进行缓存的。此时,能够把一些须要频繁拜访的数据,而且在短时间之内不会发生变化的,放入Redis中进行操作,从而进步用户的申请速度和升高网站的负载,升高数据库的读写次数。须要实时计算的场景。须要实时变动和展现的性能,就能够把相干数据放在Redis中进行操作,能够大大提高拜访效率。音讯队列场景。比方在应答实时聊天零碎时,就能够应用Redis,能够大大提高利用的可用性。正是因为Redis有这么多的益处,所以不论是大中型我的项目,都会用到Redis。而咱们明天要讲的就是Redis的三种集群部署模式:主从模式,Sentinel(哨兵)模式,Cluster模式。 Rdis最开始应用主从模式做集群,若master宕机须要手动配置slave转为master;起初为了高可用提出来哨兵模式,该模式下有一个哨兵监督master和slave,若master宕机可主动将slave转为master,但它也有一个问题,就是不能动静裁减;所以在3.x提出cluster集群模式。 二、主从模式2.1 主从模式简介主从模式是三种模式中最简略的,在主从复制中,数据库分为两类:主数据库(master)和从数据库(slave)。其中,主从复制有如下特点: 主数据库能够进行读写操作,当读写操作导致数据变动时会主动将数据同步给从数据库;从数据库个别是只读的,并且接管主数据库同步过去的数据;一个master能够领有多个slave,然而一个slave只能对应一个master;slave挂了不影响其余slave的读和master的读和写,重新启动后会将数据从master同步过去;master挂了当前,不影响slave的读,但redis不再提供写服务,master重启后redis将从新对外提供写服务;master挂了当前,不会在slave节点中从新选一个master;上面是主从模式的工作示意图。 工作机制: 当slave启动后,被动向master发送SYNC命令。master接管到SYNC命令后在后盾保留快照(RDB长久化)和缓存保留快照这段时间的命令,而后将保留的快照文件和缓存的命令发送给slave。slave接管到快照文件和命令后加载快照文件和缓存的执行命令。复制初始化后,master每次接管到的写命令都会同步发送给slave,保障主从数据一致性。2.2 环境阐明为了不便演示主从模式,咱们须要筹备至多3台机器(虚拟机)。 2.3 下载解压Redis安装包接下来,就是下载Redis的安装包,下载地址:http://download.redis.io/rele...。 cd /opt/softwarewget http://download.redis.io/releases/redis-7.0.3.tar.gz# 解压tar -xf redis-7.0.3.tar.gzcd redis-7.0.3# 设置环境变量echo "export REDIS_HOME=/opt/software/redis-7.0.3">> /etc/profilesource /etc/profile2.4 编译装置接下来是,编译装置所有的节点。 cd $REDIS_HOMEyum -y install gcc gcc++make && make install# 默认装置目录 /usr/local/bin2.5 配置服务装置节点之后,为了不便前期启动和保护服务, 须要对装置的节点进行服务的配置。 cat << EOF > /usr/lib/systemd/system/redis.service[Unit]Description=Redis persistent key-value databaseAfter=network.targetAfter=network-online.targetWants=network-online.target[Service]ExecStart=/usr/local/bin/redis-server /usr/local/redis/redis.conf --supervised systemdExecStop=/usr/libexec/redis-shutdownType=forkingUser=redisGroup=redisRuntimeDirectory=redisRuntimeDirectoryMode=0755LimitNOFILE=65536PrivateTmp=true[Install]WantedBy=multi-user.targetEOF上面是配置的相干形容和阐明: Description: # 形容服务After: # 形容服务类别[Service] # 服务运行参数的设置Type=forking # 是后盾运行的模式ExecStart # 为服务的具体运行命令ExecReload # 为重启命令ExecStop # 为进行命令LimitNOFILE=65536 # 关上文件数和过程数有限度,默认限度为1024,如果不设置,或者设置为LimitNOFILE=unlimited(不辨认),则失去了1024PrivateTmp=True # 示意给服务调配独立的长期空间【留神】[Service]的启动、重启、进行命令全副要求应用绝对路径 [Install] #运行级别下服务装置的相干设置,可设置为多用户,即零碎运行级别为3 ...

November 19, 2022 · 5 min · jiezi

关于redis集群:面试官Redis集群有哪些方式Leader选举又是什么原理呢

哈喽!大家好,我是小奇,一位不靠谱的程序员小奇打算以轻松风趣的对话形式来分享一些技术,如果你感觉通过小奇的文章学到了货色,那就给小奇一个赞吧文章继续更新,能够微信搜寻【小奇JAVA面试】第一工夫浏览,回复【材料】更有我为大家筹备的福利哟!一、前言作为一名Java程序员,Redis底层的一些原理是咱们不用学会就能够搬砖工作的一种技能点,然而小奇为什么还要讲一下呢?难道就是为了节约大家1分钟的宝贵时间,一个人1分钟,50万人就是1年,5000万人就是100年,赚了,小奇以一己之力胜利搞挂一个人(血赚)。 当然不是,并且小奇的文章也没有那么多人看,最多也就节约个肾吧。 学习Redis底层原理是因为面试官要问啊!,所以咱们就要学,什么?不实用的你不学?那街坊小奇可要使劲学啦,到时候面试官只有小奇不要你。 至于你问为什么面试官要问Redis底层原理呢,这个。。。我把这次机会留给你,下次你面试的时候面试官问:“讲一下Redis底层原理”。你:“面试官你好,请问为什么你要问Redis底层原理呢,你给我台电脑,我五分钟给你搭建好图书管理系统他不香吗,咱们键盘上见真章”。这时面试官就会通知你答案,你就能够把答案打在评论区,让小奇以及泛滥小伙伴一起晓得一下到底为什么要问? 二、面试在一个晴朗的周日,我来到了一个生疏的园区(别问为什么是周日,问就是997,不过为了填饱肚子的打工人,只能明知山有虎、偏差虎山行),坐在生疏的会议室,期待HR小姐姐去叫面试官,此时我的情绪和各位小伙伴一样五味杂陈,放心面试官问的会不会很难?问到我的常识盲区我该怎么办?一会自我介绍的时候要不要吹一下我和小奇的关系? 一位英俊潇洒,眼神犀利的面试官走了进来,看到他那犀利、好像能看穿所有的眼神 ,我在想要不然一会就不要20k了,要8k得了,这个面试官一看就不好糊弄啊,然而我想起来我来之前刚看了小奇的趣学编程系列,我曾经齐全学会了小奇的精华,我登时就来了底气,决定一会要30k,不给就学小奇赖着不走(哈哈) 面试官:小奇是吧,带简历了吗? 我:没带,当初彩印两块一张,我简历五张,每次面试都要花费十块,我敌人说了还没工作就先让你掏钱的工作不要去。 面试官:。。。那你靠什么来驯服我,让我录用你 我:气质? (此时面试官并没有叫保安,而是从门后拿出了恭候我多时的棍子,我霎时怂了) 我只好从我的双肩包中拿出了我上午从其余公司面试官手中要回的简历,上午的情景是这样的。上午的面试官:明天的面试就到这吧,回去等告诉吧!我:面试官你好,如果贵公司不打算录取我的话,能不能把我的纸质简历还给我,我下午还有一家面试。上午的面试官:我说你的简历怎么皱皱巴巴,原来你始终在循环利用啊!这个症状呈现多久了?我:半拉月了。。。(当我把皱皱巴巴的简历交给面试官后,这场面试才得以持续进行。。。)三、Redis哨兵集群面试官:我看你简历上写的精通Redis?(哼,面试官轻蔑的一笑) (看着面试官轻蔑的笑容,我忍不住拿出了我的Redis书籍推给了他) 我:这本书我滚瓜烂熟,你轻易发问,答不上来算我输,答上来你就要为你的轻蔑向我赔罪。 (我的笑容逐步自信。。。) (此时面试官看着书若有所思,我狐疑他必定在想他对这本书的理解水平吧) 面试官:好吧,那你先说一下Redis有哪些集群形式呢 我:Redis次要有两种持集群形式,哨兵集群和Cluster(高可用)集群。 面试官:能够说一说两者的区别和如何配置应用吗 我:上次面试我曾经简略说了一下哨兵模式,当初再来简略看一下 这里咱们能够看到客户端只能连贯一个哨兵集群,也就是说客户端在写入数据的时候只能通过哨兵集群通知的地址来进行写入 如果主节点挂了,那么哨兵选举一个从节点成为主节点,在这期间客户端来拜访是被阻塞的,因为主节点正在被选举,还不晓得谁是主节点呢,怎么插入数据。 那么为了解决这个问题,咱们能够应用高可用集群模式Cluster模式,也能够称之为多个主从节点(主从节点上一章讲过)模式集群 四、Redis高可用集群Cluster模式面试官:能说一下高可用集群模式具体是怎么一个流程吗,为什么能够解决选举期待问题? 我:(能不说吗。。。) 通过图中能够看出咱们客户端通过拜访入口能够拜访多个主节点,如果其中一个主节点挂了,那么其余的主节点还能够失常工作不受影响。 面试官:这么多主节点,我哪晓得我set一个数据应该放入到哪个主从构造外面呢? 我:(你随机放一个不就行了吗。。。不对,随机放一个取的时候就不晓得去哪个主从构造里去取了,总不能每一个主节点外面都去查找一遍吧) 我:他会依据要set进去的key进行一个hash计算,计算完后就晓得要往哪个主节点外面寄存值了,获得时候也依据key进行一个hash计算,就晓得去哪个主节点里拿取了。 面试官:那这个时候来一个age计算hash是150要插入哪个主节点 我:必定是第二个主节点啊,redis集群采纳分片模式,将所有数据分片放入多个主节点中,不便与程度扩大。 面试官:如果主节点挂了,哨兵模式下哨兵集群会选举出一个主节点,那你这种高可用模式,如果一个主节点挂了,怎么选举呢? 我:(应该是依照大小个吧。。。) 面试官:你特么找打是吧,数据那特么有大小个。。。 五、Leader选举原理我:这个时候就须要其余主节点来反对选举了,我来用一张图形容一下。 我:咱们能够把这三个主从节点看做是三个国家,如果一个国家的喽罗挂掉了,那么他底下其中一股权势就会找其余的国家喽罗来反对本人成为喽罗。 我:当然其余国家的喽罗不会看你国家这两股权势哪个好哪个坏,而是哪个先找我我就反对你。 我:而且图中有三个国家,反对本人的票数必须超过总国家的一半以上,也就是起码要有两票反对,所以redis集群节点起码要三个主从构造,不然的话两个没方法选举,票数最多一票不够两个国家的半数以上。 面试官:还别说你小子应用这个图讲的是绘声绘色 我:你这不废话吗,趣学编程不趣学那还学个屌。。。 面试官:那塔利班和正规军他们两个怎么确定谁先发出请求呢,必定是谁先收回音讯谁赢呗? 我:他们两个小权势并不是喽罗挂了就立马发出请求反对音讯,而是有肯定的延迟时间,这个延迟时间依据一个公式计算,公式中蕴含随机数,和依据SLAVE_RANK的大小来计算,SLAVE_RANK的大小示意从节点从主节点同步的数据总量的Rank。Rank越小代表一复制的数据越新,能够做主节点。 提早公式:DELAY = 500ms +random(0 ~ 500ms) + SLAVE_RANK * 1000ms 面试官:如果当初网络抖动,从节点一瞬间连贯不上主节点了,他就开始发送音讯,网络好了当前不就有两个主节(俗称脑裂,主节点相当于一个主从构造的大脑,只能有一个)点了吗? 我:那咱们能够配置连贯不上的工夫让他长一点,通过cluster-node-timeout来配置,如果配置5秒钟,那么5秒钟之内从节点连贯不上主节点是不会发动申请反对的信息的。 面试官:万一5秒不够呢,万一我网络抖动的厉害呢? 我:你是食堂阿姨吗,手抖的这么厉害。。。 我:你说的这种状况属于一瞬间有两个主节点都在写入新的数据,那么等网络复原当前只会将一个节点作为主节点,其余节点变为从节点过去同步,那么就会失落一部分数据。 我:这里咱们会发现age 20数据失落了,那这个怎么办呢,咱们能够通过一个配置min‐replicas‐to‐write [数量],这个配置是写的数据起码同步的从节点数量。 如果我这里配置的是1,那么就是我写入一个值起码有一个从节点同步到了这个值才算写入胜利,不然就是写入失败。 ...

March 18, 2022 · 1 min · jiezi

关于redis集群:Gossip算法及其在Redis集群里的运用

Gossip的一些特点 在一个有界网络中,每个节点都随机地与其它节点通信,通过一段无序的通信后,最终所有节点的状态都会达成统一,即便是有节点宕机后重启或有新节点退出,但一段时间后这些节点的状态也会与其它节点达成统一,从这一点来说,Gossip人造具备分布式容错的长处。依据下面的形容,咱们晓得 Gossip是一个最终一致性算法,它无奈保障在某个时刻所有节点状态统一,但能够保障最终统一。另外Gossip不要求节点晓得所有其它节点,因而又具备去中心化的特点,节点之间齐全对等,不须要任何的核心节点。但Gossip的毛病也很显著,冗余通信会对网络带宽与CPU资源造成很大的负载,从有的材料上看到说当集群规模超过百节点级别后,Gossip 协定的效率将会显著降落,通信老本越来越高。 Gossip节点的三种通信形式 两个节点(A,B)之间存在三种通信形式: push:A节点将数据(key,value,version)及对应的版本号推送给B节点,B节点更新A中比本人新的数据pull:A仅将数据(key,version)推送给B,B将本地比A新的数据(key,value,version)推送给A,A更新本地push/pull:与pull相似,只是多了一步,A再将本地比B新的数据推送给B,B更新本地如果将两个节点数据同步一次定义为一个周期,则在一个周期内,push需通信1次,pull需2次,push/pull则需3次。但从成果上来讲,push/pull最好,实践上一个周期能够使两个节点完全一致。 Gossip在Redis Cluster中的作用 在分布式系统中,须要提供保护节点元数据信息的机制,所谓元数据是指节点负责哪些数据、主从属性、是否故障等状态信息。常见的元数据保护形式个别分集中式与无核心式。Redis Cluster采纳Gossip协定实现无核心式。具体来讲,Redis Cluster中应用Gossip次要有两大作用: 去中心化,以实现分布式和弹性扩大失败检测,以实现高可用节点通信根底 Redis Cluster中的每个实例监听两个TCP端口:6379用于服务客户端查问;16379用于集群外部通信。集群中节点通信形式如下: 每个节点在固定周期内通过特定规定抉择几个节点发送Ping音讯接管到Ping音讯的节点用Pong音讯作为回应集群中每个节点通过肯定规定筛选要通信的节点,每个节点可能晓得全副节点,也可能仅晓得局部节点,只有这些节点彼此能够失常通信,最终它们会达到统一的状态。当节点故障、新节点退出、主从关系变动,槽信息变更等事件产生时,通过一直的Ping/Pong音讯通信,通过一段时间后所有的节点都会晓得集群全副节点的最新状态,从而达到集群状态同步的目标。 Gossip在Redis Cluster里的音讯品种 Meet音讯:用于告诉新节点退出。音讯发送者告诉接收者退出到以后集群,Meet音讯通信失常实现后,接管节点会退出到集群中并进行周期性的Ping,Pong音讯替换Ping音讯:集群内替换最频繁的音讯,集群内每个节点每秒向多个其它节点发送Ping音讯,用于检测节点是否在线和替换彼此状态信息。Ping音讯发送封装了本身节点和局部其它节点的状态数据Pong音讯:当接管到Ping,Meet音讯时,作为响应音讯回复给发送方确认音讯失常通信。Pong音讯外部封装了本身状态数据。节点也能够向集群内播送本身的Pong音讯来告诉整个集群对本身状态进行更新Fail音讯:当节点判集群内另一节点下线时,会向集群内播送一个Fail音讯,其余节点接管到Fail音讯之后会将对应节点更新为下线状态因为集群外部须要频繁地进行节点信息替换,而Ping/Pong音讯携带以后节点和局部其它节点的状态数据,会减轻带宽和计算的累赘。Redis集群内节点通信采纳固定频率(定时工作每秒执行10次),因而,节点每次抉择须要通信的节点列表十分重要。通信节点抉择过多尽管能够做到信息及时替换但老本过高。节点抉择过少则会升高集群内所有节点彼此信息替换的频率,从而影响故障断定、新节点发现等需要的速度。因而Redis集群的Gossip协定须要兼顾信息替换实时性和老本开销。 最初,redis集群的故障检测,故障转移与master选举能够看之前的文章:redis学习之集群 参考的文章:Gossip算法 Gossip算法分布式一致性协定 Gossip 和 Redis 集群原理解析

March 16, 2022 · 1 min · jiezi

关于redis集群:技术实践第三期|HashTag在Redis集群环境下的使用

作者:友盟+技术专家 鹏程 一、背景数据源列表增加缓存反对,types字段可传多值,如app, mini, web等,会构建如下缓存key, application_list:123456:appapplication_list:123456:miniapplication_list:123456:webapplication_list:123456:app,miniapplication_list:123456:app,webapplication_list:123456:mini,webapplication_list:123456:app,mini,web...当创立利用,更新利用或删除利用的时候,须要批量删除旧版本缓存。 二、思路1.依照前缀 application_list:123456,查问所有相干的key2.遍历keys,执行删除 /** * 移除缓存 * * @param prefix prefix */public static void deleteByPrefix(String prefix) { long start = System.currentTimeMillis(); Set<String> keys; try { keys = jedisCluster.keys(CacheKeyUtils.buildCacheKey(prefix, "*")); LOGGER.info("cache keys {} with prefix {}", keys, prefix); if (keys != null && !keys.isEmpty()) { jedisCluster.del(keys.toArray(new String[keys.size()])); } } catch (Exception e) { LOGGER.error("cache deleteByPrefix error, prefix = {}", prefix, e); throw new BusinessException(CoreErrorEnum.CACHE_DELETE_ERROR, prefix); } long end = System.currentTimeMillis(); LOGGER.info("cache deleteByPrefix success, prefix = {}, cost {} ms", prefix, (end - start));}三、问题依照这个写完,执行报错,"JedisCluster only supports KEYS commands with patterns containing hash-tags ( curly-brackets enclosed strings )" ...

December 28, 2021 · 1 min · jiezi

关于redis集群:介绍一款监测Redis性能-可视化管理和监控的工具

Redis是一个功能强大、性能高效的开源数据结构服务器,Redis最典型的利用是NoSQL。 自己在工作中我的项目也是宽泛应用了Redis,工作中也遇到了Redis的数据可视化不便、Redis的数据查看保护艰难、Redis状态监控运维不易等问题。 置信大家在工作中也会遇到我说的这些问题,因为我也是一线的开发,也深切的感触到了没有棘手Redis治理监控工具带来的不便,通过一段时间的寻找应用,一款轻量高性能的Redis治理及监控工具WGCLOUD横空出世了。 上面我就具体给大家介绍一下WGCLOUD的装置及各项性能。 下载和装置,在官网有具体阐明:http://www.wgstar.tcom 我来说下WGCLOUD能够监测Redis哪些指标 1.能够监测Redis的过程内存使用率%2.能够监测Redis的过程cpu使用率%3.能够监测Redis的吞吐量4.能够监测Redis的端口是否失常通信5.能够监测Reids的日志文件6.能够在Redis过程下线、端口不通、日志呈现谬误时候发送告警告诉 [root@centos04 bin]# ./redis-server 26765:C 22 Nov 2018 20:46:31.066 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo26765:C 22 Nov 2018 20:46:31.066 # Redis version=5.0.2, bits=64, commit=00000000, modified=0, pid=26765, just started26765:C 22 Nov 2018 20:46:31.066 # Warning: no config file specified, using the default config. In order to specify a config file use ./redis-server /path/to/redis.conf26765:M 22 Nov 2018 20:46:31.067 * Increased maximum number of open files to 10032 (it was originally set to 1024). _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 5.0.2 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 | `-._ `._ / _.-' | PID: 26765 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 26765:M 22 Nov 2018 20:46:31.104 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.26765:M 22 Nov 2018 20:46:31.104 # Server initialized26765:M 22 Nov 2018 20:46:31.104 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.26765:M 22 Nov 2018 20:46:31.105 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.26765:M 22 Nov 2018 20:46:31.105 * Ready to accept connections

November 16, 2021 · 2 min · jiezi

关于redis集群:Redis-Cluster-集群解决方案

Redis Cluster 集群解决方案多个 Redis 实例协同进行采纳 slot (槽)宰割数据,是 CRC16 与 16384 取模后扩散主从构造和选举算法,保障每个节点的可靠性客户端能够连贯任意一个 node 进行操作 所有的 redis 节点彼此互联(PING-PONG 机制),外部应用二进制协定优化传输速度和带宽。节点的 fail 是通过集群中超过半数的节点检测生效时才失效。客户端与 redis 节点直连,不须要两头 proxy 层,客户端不须要连贯集群所有节点,连贯集群中任何一个可用节点即可。redis-cluster 把所有的物理节点映射到 [0-16383] slot 上,cluster 负责保护 node <-> slot <-> value Redis Cluster 注意事项不齐全反对批量操作:mset、mget事务不能跨节点反对不反对多实例key 是最小粒度起码 6 个能力保障组成残缺高可用的集群连贯的时候只须要连贯 1 台服务器即可。如果 1 个主从连贯宕机的话,那么集群就宕机了。Redis Cluster 配置步骤(倡议应用官网安装包的形式装置 redis,不要应用 apt-get install 或者 yum 间接装置) 别离装置 6 台 服务器,三个主节点,三个从节点我这里采纳的是虚拟机,相应的 ip 地址别离为: 192.168.174.128 (28 号服务器)192.168.174.129 (29 号服务器)192.168.174.130 (30 号服务器)192.168.174.131 (31 号服务器)192.168.174.132 (32 号服务器)192.168.174.133 (33 号服务器)配置 redis.conf 配置文件 (在所有的服务器上操作)vim /etc/redis/redis.conf ...

July 24, 2021 · 2 min · jiezi

关于redis集群:Redis哨兵模式搭建

Redis哨兵模式是建设在主从模式的根底上的,解决的是主实例单机故障问题,通过哨兵零碎能够主动故障转移,切换到新的主实例上。咱们先来搭建一个由三个Redis实例组成的主从集群。之前咱们讲过应用utils/install-server.sh来装置Redis服务。咱们应用默认门路将Redis装置到/usr/local/bin目录下。 搭建主从模式单机centos7环境下启动三个Redis服务,端口别离为 6379、6380、6381,将6379实例作为主实例。在/opt目录下创立redis目录寄存配置文件,日志、数据文件等 cd /optmkdir redis批改6379.conf文件,次要批改如下 # 端口port 6379# 数据文件目录dir /var/lib/redis/6379# 日志文件logfile /var/log/redis_6379.log启动6379 Redis cd /usr/local/bin./redis-server /opt/redis/6379.conf同样的6380和6381的依照6379配置,变更端口号即可。但作为Redis从服务要指定复制的redis主实例,减少如下配置并启动 # 指定Redis复制节点replicaof 127.0.0.1 6379这样一主两从的Redis主从模式集群搭建实现了。 哨兵零碎搭建哨兵零碎用来监控主从集群各个实例的衰弱状况。哨兵零碎也是一个集群零碎,避免哨兵单点故障。筹备哨兵配置,次要配置如下(剩下的应用默认配置) # 指定哨兵的端口号(默认26379)port 26379# 指定监控的主从集群的mater实例127.0.0.1 6379,# 监控的集群名字mymaster(能够随便定义名字,保障惟一即可)# 最初的2示意投票权重,个别为哨兵实例总数/2 + 1sentinel monitor mymaster 172.0.0.1 6379 2启动哨兵启动哨兵应用的启动程序和Redis服务的启动程序一样,指定不同配置文件和sentinel模式就好 ./redis-server /opt/redis/26379.conf --sentinel 启动26379哨兵实例胜利后,哨兵会生成一个本人的实例Id,并会通过监控的master找到slave的地址,将他们保留到26379.conf配置文件中。 port 26379sentinel monitor mymaster 172.18.0.111 6379 2# Generated by CONFIG REWRITEprotected-mode nouser default on nopass ~* &* +@alldir "/usr/local/bin"sentinel myid 8479791aeec61fdef62ff78556e55b07e7e80c0dsentinel config-epoch mymaster 0sentinel leader-epoch mymaster 0sentinel current-epoch 0sentinel known-replica mymaster 172.18.0.111 6381sentinel known-replica mymaster 172.18.0.111 6380剩下的两个哨兵实例别离应用26380和26381做为端口,将对应的配置文件的port批改即可。 ...

July 8, 2021 · 2 min · jiezi

关于redis集群:Redis5基于docker搭建集群

首先是装置docker以及docker-compose这个步骤很简略就略过了而后是下载redis的镜像docker pull redis:5.0这边是基于redis5.0版本去装置的,所以指定了版本号。如果不加版本号,默认是下载最新版本的redis 这里留神辨别下。配置redis 在本地创立指定的redis个数,我是建设6个redis实例每个redis配置都是雷同的 列一份redis次要配置项 redis.confbind 0.0.0.0cluster-enabled yescluster-config-file "/redis/conf/nodes.conf"cluster-node-timeout 5000protected-mode noport 6379daemonize nodir "/redis/data"logfile "/redis/log/redis.log"创立配置文件docker-compose.yaml version: "3.7" # 确定docker-composer文件的版本services: # 代表就是一组服务 - 简略来说一组容器 redis_200: # 这个示意服务的名称,课自定义; 留神不是容器名称image: redis:5.0 # 指定容器的镜像文件networks: ## 引入内部事后定义的网段 redis_5_sm: ipv4_address: 172.24.141.200 #设置ip地址container_name: redis_5_cluster_200 # 这是容器的名称ports: # 配置容器与宿主机的端口 - "6320:6379" # php java python 语言连贯 - "16320:16379" # 对节点 6379 + 10000 = 端口 对节点进行通信volumes: # 配置数据挂载 - /data/redis/cluster/200:/rediscommand: /usr/local/bin/redis-server /redis/conf/redis.conf redis_201: # 这个示意服务的名称,课自定义; 留神不是容器名称image: redis:5.0 # 指定容器的镜像文件networks: ## 引入内部事后定义的网段 redis_5_sm: ipv4_address: 172.24.141.201 #设置ip地址container_name: redis_5_cluster_201 # 这是容器的名称ports: # 配置容器与宿主机的端口 - "6321:6379" - "16321:16379"volumes: # 配置数据挂载 - /data/redis/cluster/201:/rediscommand: /usr/local/bin/redis-server /redis/conf/redis.conf redis_202: # 这个示意服务的名称,课自定义; 留神不是容器名称image: redis:5.0 # 指定容器的镜像文件networks: ## 引入内部事后定义的网段 redis_5_sm: ipv4_address: 172.24.141.202 #设置ip地址container_name: redis_5_cluster_202 # 这是容器的名称ports: # 配置容器与宿主机的端口 - "6322:6379" - "16322:16379"volumes: # 配置数据挂载 - /data/redis/cluster/202:/rediscommand: /usr/local/bin/redis-server /redis/conf/redis.conf redis_203: # 这个示意服务的名称,课自定义; 留神不是容器名称image: redis:5.0 # 指定容器的镜像文件networks: ## 引入内部事后定义的网段 redis_5_sm: ipv4_address: 172.24.141.203 #设置ip地址container_name: redis_5_cluster_203 # 这是容器的名称ports: # 配置容器与宿主机的端口 - "6323:6379" - "16323:16379"volumes: # 配置数据挂载 - /data/redis/cluster/203:/rediscommand: /usr/local/bin/redis-server /redis/conf/redis.conf redis_204: # 这个示意服务的名称,课自定义; 留神不是容器名称image: redis:5.0 # 指定容器的镜像文件networks: ## 引入内部事后定义的网段 redis_5_sm: ipv4_address: 172.24.141.204 #设置ip地址container_name: redis_5_cluster_204 # 这是容器的名称ports: # 配置容器与宿主机的端口 - "6324:6379" - "16324:16379"volumes: # 配置数据挂载 - /data/redis/cluster/204:/rediscommand: /usr/local/bin/redis-server /redis/conf/redis.conf redis_205: # 这个示意服务的名称,课自定义; 留神不是容器名称image: redis:5.0 # 指定容器的镜像文件networks: ## 引入内部事后定义的网段 redis_5_sm: ipv4_address: 172.24.141.205 #设置ip地址container_name: redis_5_cluster_205 # 这是容器的名称ports: # 配置容器与宿主机的端口 - "6325:6379" - "16325:16379"volumes: # 配置数据挂载 - /data/redis/cluster/205:/rediscommand: /usr/local/bin/redis-server /redis/conf/redis.conf# 网段设置networks: #援用内部事后定义好的网段 redis_5_sm:driver: bridgeipam: #定义网段 config: - subnet: "172.24.141.0/24"启动redis ...

June 12, 2021 · 2 min · jiezi

关于redis集群:Redis主从复制

单机版的redis存在三个次要问题 单机故障容量受限单机负载瓶颈,波及socket连接数、CPU资源、IO压力等针对这些问题redis提供了集群的形式来进行解决。在redis中有三种集群模式:主从模式、哨兵模式和Cluster集群模式。这篇文章咱们来讲一下主从模式的实现原理。 架构简介简略来说是已主多从,master做为redis的主实例,反对数据的读写,他会定时的将数据同步给slave从库。这样咱们能够将大部分的读操作转移到slave从库上,在读取方面解决了单点故障问题,同时slave的可伸缩行能够升高单机负载,进步性能。主从模式下,咱们能够敞开master的长久化性能,由slave来负责长久化,这样能够升高master的IO,大大晋升性能。但须要留神,当master呈现故障重启时,因为master没有可供复原的aof或rdb文件,因而一旦重启数据会全副革除。而slave会同步master数据将本地的长久化文件也做删除。这种状况下,要敞开master的主动重启性能,避免出现相似问题。但这样依然是很危险的,倡议同时开启master和slave的长久化性能。 同步原理在redis的主从模式中,数据同步是一个低提早、高性能的异步处理过程。全量同步slave在首次胜利连贯到master之后会触发一次全量的同步操作,具体过程如下 slave连贯到master胜利之后,发送PSYNC命令,申请master进行数据同步master收到同步命令fork一个子过程,并将以后的内存数据生成RDB文件,写入磁盘;同时master会接着接管写操作并保留到长期缓冲区master将RDB文件全量发送给slaveslave接管并写入磁盘,而后从rdb文件加载到内存master将缓冲区中的所有命令发送给slave对于master端同步过程是由子过程来解决的,因而是非阻塞的,同步期间redis依然可接管客户端的申请。在数据安全性上,为了保障同步的安全性,当连贯的slave数量小于m,且延迟时间小于ns时,master可用失常接管写解决,否则告诉客户端谬误。 min-slaves-to-write 3min-slaves-max-lag 10全量同步时master会进行RDB写盘而后再发送到slave,这种形式交disk-back形式。它的长处是当有多个slave申请同步时,只须要生成一次RDB文件就能够服务多个slave,但对于磁盘IO较差的状况会比拟蹩脚。Redis另外提供了一种形式diskless,同步时master将内存中的数据生成RDB数据后间接写入到socket字节流传给slave,而不会进行磁盘IO。很显著这种形式缩小了磁盘的IO操作,须要留神的是处理过程中,其余slave的同步申请无奈被解决,只能排队期待。在该模式下咱们个别会设置一个工夫用于master的期待,当master接管到一个同步申请时,会先进行m秒的期待,看是否有其余slave也有同步需要,对多个slave进行并行同步。 repl-diskless-sync yesrepl-diskless-sync-delay 5增量同步在晚期的版本中,slave故障重启、断后重连master,都会导致全量的同步。高版本中对这里做了一些优化,会先尝试进行增量同步,如果不行再做一次全量同步。具体做法是master应用一个replicationid来标记惟一的本人,同时定义一个offset偏移量示意同步解决的数据偏移量。当slave连贯到master进行同步时,slave发送它记录的master的replicationid和本人解决的偏移量到master,这样master只须要查问增量数据传输给slave。增量数据是存储在一个缓冲区中的,该缓冲区一次性调配肯定的空间大小(可配),写操作命令会保留在缓冲区内。slave的增量同步会从该缓冲区查找数据,如果缓冲区设置的较大,则能反对slave断连的工夫会越长。当master不存在slave且超过指定工夫时,该缓冲区也会被主动开释 repl-backlog-size 1mbrepl-backlog-ttl 3600主从模式配置slave配置指定master的地址,开启主从模式 replicaof <masterip> <masterport>当master须要明码校验时,从库配置明码 masterauth <master-password>无硬盘同步模式,设置yes开启,当开启时,须要配置执行同步时要期待多少秒再开始同步 repl-diskless-sync norepl-diskless-sync-delay 5复制缓冲区大小 repl-backlog-size 1mbslave不存在后多少毫秒缓冲区被开释 repl-backlog-ttl 3600同步时master可承受写操作的slave个数和同步延迟时间 min-replicas-to-write 3min-replicas-max-lag 10

May 24, 2021 · 1 min · jiezi

关于redis集群:Redis-高可用篇Cluster-集群能支持的数据量有多大

码哥出品,必属精品。关注公众号「码哥字节」并加码哥微信(MageByte1024),窥探硬核文章背地的男人的另一面。 本文将对集群的节点、槽指派、命令执行、从新分片、转向、故障转移、音讯等各个方面进行深刻拆解。 目标在于把握什么是 Cluster ?Cluster 分片原理,客户端定位数据原理、故障切换,选主,什么场景应用 Cluster,如何部署集群 …... [toc] 为什么须要 Cluster65 哥:码哥,自从用上了你说的哨兵集群实现故障主动转移后,我终于能够开心的跟女朋友么么哒也不怕 Redis 宕机深夜宕机了。 可是最近遇到一个糟心的问题,Redis 须要保留 800 万个键值对,占用 20 GB 的内存。 我就应用了一台 32G 的内存主机部署,然而 Redis 响应有时候十分慢,应用 INFO 命令查看 latest_fork_usec 指标(最近一次 fork 耗时),发现特地高。 次要是 Redis RDB 长久化机制导致的,Redis 会 Fork 子过程实现 RDB 长久化操作,fork 执行的耗时与 Redis 数据量成正相干。 而 Fork 执行的时候会阻塞主线程,因为数据量过大导致阻塞主线程过长,所以呈现了 Redis 响应慢的表象。 65 哥:随着业务规模的拓展,数据量越来越大。主从架构降级单个实例硬件难以拓展,且保留大数据量会导致响应慢问题,有什么方法能够解决么?保留大量数据,除了应用大内存主机的形式,咱们还能够应用切片集群。俗话说「众人拾材火焰高」,一台机器无奈保留所有数据,那就多台分担。 应用 Redis Cluster 集群,次要解决了大数据量存储导致的各种慢问题,同时也便于横向拓展。 两种计划对应着 Redis 数据增多的两种拓展计划:垂直扩大(scale up)、程度扩大(scale out)。 垂直拓展:降级单个 Redis 的硬件配置,比方减少内存容量、磁盘容量、应用更弱小的 CPU。程度拓展:横向减少 Redis 实例个数,每个节点负责一部分数据。比方须要一个内存 24 GB 磁盘 150 GB 的服务器资源,有以下两种计划: ...

May 13, 2021 · 4 min · jiezi

关于redis集群:Redis集群

1.为什么要搭建集群通常,为了进步网站响应速度,总是把热点数据保留在内存中而不是间接从后端数据库中读取。Redis 是一个很好的 Cache 工具。大型网站利用,热点数据量往往微小,几十 G 上百 G是很失常的事儿。因为内存大小的限度,应用一台 Redis 实例显然无奈满足需要,这时就须要应用多台Redis 作为缓存数据库。然而如何保证数据存储的一致性呢,这时就须要搭建 redis 集群. 采纳正当的机制,保障用户的失常的拜访需要. 采纳 redis 集群,能够保证数据扩散存储,同时保证数据存储的一致性.并且在外部实现高可用的机制.实现了服务故障的主动迁徙. 2.集群搭建2.1 集群搭建打算主从划分:主机:3台从机:3台端口:7000-7005 2.2 筹备集群文件夹1.筹备集群文件夹 mkdir cluster2.在 cluster 文件夹中别离创立 7000-7005 文件夹 cd clustermkdir 7000 7001 7002 7003 7004 7005 2.3 编辑配置文件配置好的redis.confhttps://segmentfault.com/a/11... 阐明:将 redis 根目录中的 redis.conf 文件复制到 cluster/7000/ 并以原名保留 cp redis.conf cluster/7000/ 2.4 编辑配置文件cd 7000ls

February 4, 2021 · 1 min · jiezi

关于redis集群:redisconf

Redis configuration file example.Note that in order to read the configuration file, Redis must bestarted with the file path as first argument:./redis-server /path/to/redis.confNote on units: when memory size is needed, it is possible to specifyit in the usual form of 1k 5GB 4M and so forth:1k => 1000 bytes1kb => 1024 bytes1m => 1000000 bytes1mb => 1024*1024 bytes1g => 1000000000 bytes1gb => 102410241024 bytesunits are case insensitive so 1GB 1Gb 1gB are all the same.INCLUDESInclude one or more other config files here. This is useful if youhave a standard template that goes to all Redis servers but also needto customize a few per-server settings. Include files can includeother files, so use this wisely.Notice option "include" won't be rewritten by command "CONFIG REWRITE"from admin or Redis Sentinel. Since Redis always uses the last processedline as value of a configuration directive, you'd better put includesat the beginning of this file to avoid overwriting config change at runtime.If instead you are interested in using includes to override configurationoptions, it is better to use include as the last line.include /path/to/local.confinclude /path/to/other.confMODULESLoad modules at startup. If the server is not able to load modulesit will abort. It is possible to use multiple loadmodule directives.loadmodule /path/to/my_module.soloadmodule /path/to/other_module.soNETWORKBy default, if no "bind" configuration directive is specified, Redis listensfor connections from all the network interfaces available on the server.It is possible to listen to just one or multiple selected interfaces usingthe "bind" configuration directive, followed by one or more IP addresses.Examples:bind 192.168.1.100 10.0.0.1bind 127.0.0.1 ::1~ WARNING ~ If the computer running Redis is directly exposed to theinternet, binding to all the interfaces is dangerous and will expose theinstance to everybody on the internet. So by default we uncomment thefollowing bind directive, that will force Redis to listen only intothe IPv4 loopback interface address (this means Redis will be able toaccept connections only from clients running into the same computer itis running).IF YOU ARE SURE YOU WANT YOUR INSTANCE TO LISTEN TO ALL THE INTERFACESJUST COMMENT THE FOLLOWING LINE.~~~~~~~~~~~~~~~~bind 127.0.0.1Protected mode is a layer of security protection, in order to avoid thatRedis instances left open on the internet are accessed and exploited.When protected mode is on and if:1) The server is not binding explicitly to a set of addresses using the"bind" directive.2) No password is configured.The server only accepts connections from clients connecting from theIPv4 and IPv6 loopback addresses 127.0.0.1 and ::1, and from Unix domainsockets.By default protected mode is enabled. You should disable it only ifyou are sure you want clients from other hosts to connect to Rediseven if no authentication is configured, nor a specific set of interfacesare explicitly listed using the "bind" directive.protected-mode no ...

February 4, 2021 · 41 min · jiezi

关于redis集群:Redis-实战-01-Redis-数据结构简介

一些数据库和缓存服务器的个性和性能 P4名称类型数据存储选项查问类型附加性能Redis应用内存存储(in-memory)的非关系数据库字符串、列表、哈希表、汇合、有序汇合每种数据类型都有本人的专属命令,另外还有批量操作(bulk operation)和不齐全(partial)的事务反对公布与订阅,主从复制(master/slave replication)memcached应用内存存储的键值缓存键值之间的映射增删改查以及其余几个命令为晋升性能而设的多线程服务器MySQL关系数据库每个数据库能够蕴含多个表,每个表能够蕴含多个行;能够解决多个表的试图(view);反对空间(spatial)和第三方扩大增删改查、函数、存储过程反对ACID性质(须要应用InnoDB),主从复制和主主复制(master/master replication)PostgreSQL关系数据库每个数据库能够蕴含多个表,每个表能够蕴含多个行;能够解决多个表的试图;反对空间和第三方扩大;反对可定制类型增删改查、内置函数、自定义的存储过程反对ACID性质,主从复制,由第三方反对的多主复制(multi-master replication)MongoDB应用硬盘存储(on-disk)的非关系文档存储每个数据库能够蕴含多个表,每个表能够蕴含多个无schema (schema-less) 的 BSON 文档(Binary JSON,相似json的一种二进制格局)增删改查反对 map-reduce 操作,主从复制,分片,空间索引(spatial index)长久化形式 P4RDB(redis database):以快照的模式将数据长久化到磁盘AOF(append only file):以日志的模式记录每个操作,将Redis执行过的所有指令全副记录下来(读操作不记录),只许追加文件但不能够批改文件,Redis启动时会读取AOF配置文件重构数据Redis 的 5 种构造 P6构造类型构造存储的值构造的读写能力STRING能够是字符串、整数或者浮点数对整个字符串或者字符串的其中一部分执行操作;对整数和浮点数执行自增(increment)或者自减(decrement)LIST一个链表,链表上的每个节点都蕴含了一个字符串从链表的两端推入或者弹出元素;依据偏移量对链表进行修剪(trim);读取单个或者多个元素;依据值查找或者移除元素HASH蕴含键值对的无序哈希表增加、获取、移除单个键值对;获取所有键值对SET蕴含字符串的无序收集器(unordered collection),并且被蕴含的每个字符串都是举世无双、各不相同的增加、获取、移除单个元素;查看一个元素是否存在于汇合中;计算交加、并集、差集;从汇合外面随机获取元素ZSET(有序汇合)字符串成员(member)与浮点数分值(score)之间的有序映射,元素的排列程序由分值的大小决定增加、获取、移除单个元素;依据分值范畴(range)或者成员来获取元素STRING 根底命令 P8STRING 存储键值对,值既能够是字符串,又能够是数值(数值反对自增和自减) package mainimport ( "fmt" "github.com/gomodule/redigo/redis" "time")func main() { // 获取连贯 conn, err := redis.Dial("tcp", "127.0.0.1:6379") if err != nil { // 连贯失败,则打印错误信息,并退出 fmt.Println(err) return } // 如果连贯胜利,则提早执行 敞开 defer conn.Close() executeStringCommand(conn)}// 执行 STRING 的相干命令func executeStringCommand(conn redis.Conn) { fmt.Println("--------- executeStringCommand start ---------") // 设置 键为 hello 的值为 world,永恒无效 result, err := conn.Do("SET", "hello", "world") handleResult(result, err) // 获取 键为 hello 的剩余时间(TTL -> 秒,PTTL -> 毫秒) result, err = redis.Int(conn.Do("TTL", "hello")) handleResult(result, err) // 获取 键为 hello 的值 result, err = redis.String(conn.Do("GET", "hello")) handleResult(result, err) // 设置 键为 hello 的值为 world,无效工夫为 1000ms(EX -> 秒,PX -> 毫秒;不可同时应用) result, err = conn.Do("SET", "hello", "world", "EX", "1") handleResult(result, err) // 获取 键为 hello 的剩余时间(TTL -> 秒,PTTL -> 毫秒) result, err = redis.Int(conn.Do("PTTL", "hello")) handleResult(result, err) time.Sleep(time.Second * 2) // 获取 键为 hello 的剩余时间(TTL -> 秒,PTTL -> 毫秒) result, err = redis.Int(conn.Do("PTTL", "hello")) handleResult(result, err) // 设置 键为 hello 的值为 world,永恒无效(NX -> 键不存在时,才进行设置操作;XX -> 键曾经存在时,才进行设置操作) result, err = conn.Do("SET", "hello", "world!", "XX") handleResult(result, err) // 设置 键为 hello 的值为 world,永恒无效(NX -> 键不存在时,才进行设置操作;XX -> 键曾经存在时,才进行设置操作) result, err = conn.Do("SET", "hello", "world!", "NX") handleResult(result, err) // 删除 键为 hello 的值(能够用于所有类型) result, err = conn.Do("DEL", "hello") handleResult(result, err) // 获取 键为 hello 的值 result, err = redis.String(conn.Do("GET", "hello")) handleResult(result, err) fmt.Println("--------- executeStringCommand end ---------")}// 解决操作 redis 的后果func handleResult(result interface{}, err error) { if err != nil { fmt.Println("ERROR: ", err) return } fmt.Println(result)}LIST 根底命令 P9// 执行 LIST 的相干命令func executeListCommand(conn redis.Conn) { fmt.Println("--------- executeListCommand start ---------") // 在 list 列表的 右端 插入值 result, err := conn.Do("RPUSH", "list", "item-1", "item-2") handleResult(result, err) // 在 list 列表的 左端 插入值 result, err = conn.Do("LPUSH", "list", "item-3", "item-4") handleResult(result, err) // 在 list 列表中 以左端为头获取 [1, 3] 范畴内的所有值(完结索引为 -1 示意能够取出到列表尾的所有元素;没有 RRANGE 命令) result, err = redis.Strings(conn.Do("LRANGE", "list", "1", "-1")) handleResult(result, err) // 在 list 列表中 以左端为头获取 下标为 0(没有 RINDEX 命令) result, err = redis.String(conn.Do("LINDEX", "list", "0")) handleResult(result, err) // 在 list 列表的 右端 弹出并一个值 result, err = redis.String(conn.Do("RPOP", "list")) handleResult(result, err) // 在 list 列表的 左端 弹出并一个值 result, err = redis.String(conn.Do("LPOP", "list")) handleResult(result, err) // 删除 键为 list 的值(能够用于所有类型) result, err = conn.Do("DEL", "list") handleResult(result, err) // 在 list 列表中 以左端为头获取所有值 result, err = redis.Strings(conn.Do("LRANGE", "list", "0", "-1")) handleResult(result, err) fmt.Println("--------- executeListCommand end ---------")}SET 根底命令 P10// 执行 SET 的相干命令func executeSetCommand(conn redis.Conn) { fmt.Println("--------- executeSetCommand start ---------") // 在 set 汇合中 增加值 result, err := conn.Do("SADD", "set", "item-1", "item-2", "item-2", "item-3", "item-4") handleResult(result, err) // 从 set 汇合中 删除值 result, err = conn.Do("SREM", "set", "item-2", "item-3") handleResult(result, err) // 判断一个值是否在 set 汇合中 result, err = redis.Bool(conn.Do("SISMEMBER", "set", "item-2")) handleResult(result, err) // 获取 set 汇合中 的所有值(不举荐应用,大 key 执行慢) result, err = redis.Strings(conn.Do("SMEMBERS", "set")) handleResult(result, err) // 删除 键为 set 的值(能够用于所有类型) result, err = conn.Do("DEL", "set") handleResult(result, err) fmt.Println("--------- executeSetCommand end ---------")}HASH 根底命令 P11HASH 能够存储多个键值对之间的映射,和 STRING 一样,存储的值既能够是字符串,又能够是数值(数值反对自增和自减) ...

January 20, 2021 · 4 min · jiezi

关于redis集群:最新Redis图文教程中内附学习视频

Java操作Redis创立我的项目创立我的项目 增加依赖<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.xxxx</groupId> <artifactId>redisdemo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>redisdemo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <!-- spring data redis 组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <!-- 1.x 的版本默认采纳的连接池技术是 Jedis, 2.0 以上版本默认连接池是 Lettuce, 如果采纳 Jedis,须要排除 Lettuce 的依赖。 --> <exclusions> <exclusion> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> </exclusion> </exclusions> </dependency> <!-- jedis 依赖 --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <!-- web 组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- test 组件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies></project>配置文件spring: redis: # Redis服务器地址 host: 192.168.10.100 # Redis服务器端口 port: 6379 # Redis服务器明码 password: root # 抉择哪个库,默认0库 database: 0 # 连贯超时工夫 timeout: 10000ms jedis: pool: # 最大连接数,默认8 max-active: 1024 # 最大连贯阻塞等待时间,单位毫秒,默认-1ms max-wait: 10000ms # 最大闲暇连贯,默认8 max-idle: 200 # 最小闲暇连贯,默认0 min-idle: 5Java怎么连贯Redis?/** * 连贯Redis */@Testpublic void initConn01() { // 创立jedis对象,连贯redis服务 Jedis jedis = new Jedis("192.168.10.100", 6379); // 设置认证明码 jedis.auth("root"); // 指定数据库 默认是0 jedis.select(1); // 应用ping命令,测试连贯是否胜利 String result = jedis.ping(); System.out.println(result);// 返回PONG // 增加一条数据 jedis.set("username", "zhangsan"); // 获取一条数据 String username = jedis.get("username"); System.out.println(username); // 开释资源 if (jedis != null) jedis.close();}通过Redis连接池获取连贯对象并操作服务器/** * 通过Redis连接池获取连贯对象 */@Testpublic void initConn02() { // 初始化redis客户端连接池 JedisPool jedisPool = new JedisPool(new JedisPoolConfig(), "192.168.10.100", 6379, 10000, "root"); // 从连接池获取连贯 Jedis jedis = jedisPool.getResource(); // 指定数据库 默认是0 jedis.select(2); // 应用ping命令,测试连贯是否胜利 String result = jedis.ping(); System.out.println(result);// 返回PONG // 增加一条数据 jedis.set("username", "zhangsan"); // 获取一条数据 String username = jedis.get("username"); System.out.println(username); // 开释资源 if (jedis != null) jedis.close();}封装JedisUtil对外提供连贯对象获取办法@Configurationpublic class RedisConfig { //服务器地址 @Value("${spring.redis.host}") private String host; //端口 @Value("${spring.redis.port}") private int port; //明码 @Value("${spring.redis.password}") private String password; //超时工夫 @Value("${spring.redis.timeout}") private String timeout; //最大连接数 @Value("${spring.redis.jedis.pool.max-active}") private int maxTotal; //最大连贯阻塞等待时间 @Value("${spring.redis.jedis.pool.max-wait}") private String maxWaitMillis; //最大闲暇连贯 @Value("${spring.redis.jedis.pool.max-idle}") private int maxIdle; //最小闲暇连贯 @Value("${spring.redis.jedis.pool.min-idle}") private int minIdle; @Bean public JedisPool redisPoolFactory(){ JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); //留神值的转变 jedisPoolConfig.setMaxWaitMillis(Long.parseLong(maxWaitMillis.substring(0,maxWaitMillis.length()-2))); //留神属性名 jedisPoolConfig.setMaxTotal(maxTotal); jedisPoolConfig.setMaxIdle(maxIdle); jedisPoolConfig.setMinIdle(minIdle); JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, Integer.parseInt(timeout.substring(0, timeout.length() - 2)), password); return jedisPool; }}Java操作Redis五种数据类型连贯与开释@Autowiredprivate JedisPool jedisPool;private Jedis jedis = null;//初始化jedis对象实例@Beforepublic void initConn(){ jedis = jedisPool.getResource();}//开释资源@Afterpublic void closeConn(){ if (jedis!=null){ jedis.close(); }}操作String// 1.操作String@Testpublic void testString() { // 增加一条数据 jedis.set("username", "zhangsan"); jedis.set("age", "18"); // 增加多条数据 参数奇数为key 参数偶数为value jedis.mset("address", "bj", "sex", "1"); // 获取一条数据 String username = jedis.get("username"); System.out.println(username); // 获取多条数据 List<String> list = jedis.mget("username", "age", "address", "sex"); for (String str : list) { System.out.println(str); } // 删除 //jedis.del("username");}操作hash// 2.操作Hash@Testpublic void testHash() { /* * 增加一条数据 * 参数一:redis的key * 参数二:hash的key * 参数三:hash的value */ jedis.hset("userInfo", "name", "lisi"); // 增加多条数据 Map<String, String> map = new HashMap<>(); map.put("age", "20"); map.put("sex", "1"); jedis.hmset("userInfo", map); // 获取一条数据 String name = jedis.hget("userInfo", "name"); System.out.println(name); // 获取多条数据 List<String> list = jedis.hmget("userInfo", "age", "sex"); for (String str : list) { System.out.println(str); } // 获取Hash类型所有的数据 Map<String, String> userMap = jedis.hgetAll("userInfo"); for (Entry<String, String> userInfo : userMap.entrySet()) { System.out.println(userInfo.getKey() + "--" + userInfo.getValue()); } // 删除 用于删除hash类型数据 //jedis.hdel("userInfo", "name");}操作list // 3.操作list @Test public void testList() { // 左增加(上)// jedis.lpush("students", "Wang Wu", "Li Si"); // 右增加(下)// jedis.rpush("students", "Zhao Liu"); // 获取 start起始下标 end完结下标 蕴含关系 List<String> students = jedis.lrange("students", 0, 2); for (String stu : students) { System.out.println(stu); } // 获取总条数 Long total = jedis.llen("students"); System.out.println("总条数:" + total); // 删除单条 删除列表中第一次呈现的Li Si// jedis.lrem("students", 1, "Li Si"); // 删除多条// jedis.del("students"); }操作set// 4.操作set-无序@Testpublic void testSet() { // 增加数据 jedis.sadd("letters", "aaa", "bbb", "ccc", "ddd", "eee"); // 获取数据 Set<String> letters = jedis.smembers("letters"); for (String letter: letters) { System.out.println(letter); } //获取总条数 Long total = jedis.scard("letters"); System.out.println(total); // 删除 //jedis.srem("letters", "aaa", "bbb");}操作sorted set// 5.操作sorted set-有序@Testpublic void testSortedSet() { Map<String, Double> scoreMembers = new HashMap<>(); scoreMembers.put("zhangsan", 7D); scoreMembers.put("lisi", 3D); scoreMembers.put("wangwu", 5D); scoreMembers.put("zhaoliu", 6D); scoreMembers.put("tianqi", 2D); // 增加数据 jedis.zadd("score", scoreMembers); // 获取数据 Set<String> scores = jedis.zrange("score", 0, 4); for (String score: scores) { System.out.println(score); } // 获取总条数 Long total = jedis.zcard("score"); System.out.println("总条数:" + total); // 删除 //jedis.zrem("score", "zhangsan", "lisi");}Redis中以层级关系、目录模式存储数据// Redis中以层级关系、目录模式存储数据@Testpublic void testdir(){ jedis.set("user:01", "user_zhangsan"); System.out.println(jedis.get("user:01"));}设置key的生效工夫Redis 有四个不同的命令能够用于设置键的生存工夫(键能够存在多久)或过期工夫(键什么时候会被删除) : ...

December 24, 2020 · 6 min · jiezi

关于redis集群:基于Dokcer搭建Redis集群主从集群冲就完了

本文基于 Docker + Redis 5.0.5 版本,通过 cluster 形式创立一个 6 个 redis 实例的主从集群,当然文章会指出相应的参数阐明,这样即使是创立 9 个实例的集群形式也是一样的。 1、拉取 Redis 镜像基于 Redis:5.0.5 版本,执行如下指令: docker pull redis:5.0.5 2、创立 6 个 Redis 容器创立 6 个Redis 容器: redis-node1:6379redis-node2:6380redis-node3:6381redis-node4:6382redis-node5:6383redis-node6:6384执行命令如下: docker create --name redis-node1 --net host -v /data/redis-data/node1:/data redis:5.0.5 --cluster-enabled yes --cluster-config-file nodes-node-1.conf --port 6379docker create --name redis-node2 --net host -v /data/redis-data/node2:/data redis:5.0.5 --cluster-enabled yes --cluster-config-file nodes-node-2.conf --port 6380docker create --name redis-node3 --net host -v /data/redis-data/node3:/data redis:5.0.5 --cluster-enabled yes --cluster-config-file nodes-node-3.conf --port 6381docker create --name redis-node4 --net host -v /data/redis-data/node4:/data redis:5.0.5 --cluster-enabled yes --cluster-config-file nodes-node-4.conf --port 6382docker create --name redis-node5 --net host -v /data/redis-data/node5:/data redis:5.0.5 --cluster-enabled yes --cluster-config-file nodes-node-5.conf --port 6383docker create --name redis-node6 --net host -v /data/redis-data/node6:/data redis:5.0.5 --cluster-enabled yes --cluster-config-file nodes-node-6.conf --port 6384 局部参数解释: --cluster-enabled:是否启动集群,选值:yes 、no--cluster-config-file 配置文件.conf :指定节点信息,主动生成--cluster-node-timeout 毫秒值: 配置节点连贯超时工夫--appendonly:是否开启长久化,选值:yes、no执行命令截图: 3、启动 Redis 容器执行命令如下: docker start redis-node1 redis-node2 redis-node3 redis-node4 redis-node5 redis-node6 启动截图如下: 4、组建 Redis 集群进入任意一个 Redis 实例: # 这里以 redis-node1 实例为例docker exec -it redis-node1 /bin/bash 执行组件集群的命令: # 组建集群,10.211.55.4为以后物理机的ip地址redis-cli --cluster create 10.211.55.4:6379 10.211.55.4:6380 10.211.55.4:6381 10.211.55.4:6382 10.211.55.4:6383 10.211.55.4:6384 --cluster-replicas 1 执行命令截图如下: 创立胜利后,通过 redis-cli 查看一下集群节点信息: root@CentOS7:/data# redis-cli127.0.0.1:6379> cluster nodes 执行命令截图如下: 4、对于Redis集群搭建咱们再回到创立集群的命令上: redis-cli --cluster create 10.211.55.4:6379~6384 --cluster-replicas 1 大家着重看这个参数 --cluster-replicas 1,参数前面的数字示意的是主从比例,比方这里的 1 示意的是主从比例是 1:1,什么概念呢? ...

December 11, 2020 · 1 min · jiezi

关于redis集群:Redis学习-复制以及三种部署模式

Redis学习 - 复制以及三种部署模式什么是复制单机的redis通常状况是无奈满足我的项目需要的,个别都倡议应用集群部署的形式进行数据的多机备份和部署,这样既能够保障数据安全,同时在redis宕机的时候,复制也能够对于数据进行疾速的修复。 <!-- more --> 采取的形式单机部署(疏忽)主从链一主多从哨兵模式集群模式复制的前提须要保障redis.conf外面的配置是正确的,比方:dir ./dbfilename "dump.rdb"须要保障指定的门路对于redis来说是可写的,意味着如果当前目录没有写权限同样会失败从服务器连贯主服务器的几种形式在从服务器的配置文件外面配置连贯那个主服务器:连贯的具体配置如下: 在5.0版本中应用了replicaof代替了slaveof(github.com/antirez/red…),slaveof还能够持续应用,不过倡议应用replicaof上面是集体的配置 # replicaof <masterip> <masterport> replicaof 127.0.0.1 16379正告:此大节只阐明了这一个配置的更改,进行主从配置的时候还有其余几个参数须要更改,这里只作为局部内容参考在启动的适宜,在redis从服务器的redis-cli当中敲击如下的命令:127.0.0.1:16380> slaveof 127.0.0.1 16379OK Already connected to specified master这样就能够在从服务器动静的指定要连贯哪个主服务器了,然而这种配置是以后运行时无效,下次再次进入的时候,会依据配置文件进行配置或者依照默认的规定以后实例就是master3. 在从服务器执行slaveof no one,以后实例脱离管制主动成为masterredis 复制启动的过程==(重点)==主服务器操作从服务器操作1. (期待命令)1. 连贯(从新连贯)主服务器,发送sync命令2. 开始执行bgsave,应用缓冲区记录bgsave之后执行所有写命令2. 依据配置选项是应用现有的数据(存在)解决客户端申请,还是向申请的客户端返回错误信息3. bgsave执行结束,向从服务器发送快照文件,同时异步执行缓冲区记录的写命令3. 抛弃所有的旧数据,载入主服务器的快照文件4. 快照文件发送结束,开始向着从服务器发送存储在缓冲区的写命令4. 实现对于快照的解释操作,复原日常的申请操作5. 缓冲区写命令发送实现,同时当初每执行一个写命令就像从服务器发送雷同写命令5. 执行主服务器发来的所有存储在缓冲区的写命令,并且从当初开始承受主服务器的每一个命令倡议:因为bgsave须要开启进行子线程的创立写入缓冲区的创立,所以最好在零碎中预留30% - 45% 内存用于redis的bgsave操作特地留神:当从服务器连贯主服务器的那一刻,执行到第三步会清空以后redis外面的所有数据。 配置形式和命令形式的区别:redis.conf 配置slaveof 的形式:不会马上进行主服务器同步,而是先载入以后本地存在的rdb或者aof到redis中进行数据恢复,而后才开始同步复制 命令slaveof形式:会立刻连贯主服务器进行同步操作 对于redis的主主复制:如果咱们尝试让两台服务器相互slaveof 那么会呈现下面状况呢? 从下面的复制过程能够看到,当一个服务器slaveof另一个服务器,产生的后果只会是两边互相笼罩,也就是从服务器会去同步主服务器的数据,如果此时依照主主的配置,两边相互同步对方的数据,这样产生的数据可能会不统一,或者数据罗唆就是不残缺的。不仅如此,这种操作还会大量占用资源区让两台服务器相互晓得对方 当一台服务器连贯另一台服务器的时候会产生什么?当有新服务器连贯的时候主服务器操作步骤3还没有执行所有从服务器都会收到雷同的快照文件和雷同缓冲区写命令步骤3正在执行或者曾经执行实现了之前同步的五个操作之后,会跟新服务器从新执行一次新的五个步骤系统故障解决复制和长久化尽管曾经根本能够保证系统的数据安全,然而总有意外的状况,比方忽然断电断网,零碎磁盘故障,服务器宕机等一系列状况,那么会呈现各种莫名微妙的问题,上面针对这些状况阐明一下解决形式: 验证快照文件以及aof文件在redis的bin目录上面,存在如下的两个sh -rwxr-xr-x 1 root root 9722168 Nov 15 20:53 redis-check-aof-rwxr-xr-x 1 root root 9722168 Nov 15 20:53 redis-check-rdb他们的命令作用和内容如下: ...

November 29, 2020 · 7 min · jiezi

关于redis集群:centos7部署redis329-cluster部署三主三从

centos7部署redis_3.2.9 cluster部署(三主三从1,虚拟机环境应用的Linux环境曾经版本: Centos 7 64位零碎 主机ip : 192.168.56.180 192.168.56.181 192.168.56.182 每台服务器是1主1从,试验3台服务器课成为3主3从。 Redis装置的我的项目目录,日志,配置文件等都寄存在/ root / svr /目录下。 2,下载相干的安装包以及解压首先在192.168.56.180机器操作: cd /data/work/wget http://download.redis.io/releases/redis-3.2.9.tar.gztar -zxvf redis-3.2.9.tar.gz3,装置在/data/work/redis-3.2.9/目录下执行: make && make install PREFIX = / data / work / redis-3.2.94,配置信息创立性能配置,日志日志,数据所在的文件夹: cd /data/work/redis-3.2.9/`mkdir cluster-conf`mkdir -pv /data/work/redis-3.2.9/logs 创立可用端口文件夹:cd cluster-confmkdir 7777mkdir 8888 配置复制文件到/数据/工作/redis-3.2.9/cluster-conf/7777目录下:cp /data/work/redis-3.2.9/redis.conf  /data/work/redis-3.2.9/cluster-conf/7777批改7777目录下redis.conf(主)配置文件:bind 192.168.56.181 127.0.0.1protected-mode yesmasterauth "xxxxx"requirepass "xxxxx"port 8888tcp-backlog 511timeout 0tcp-keepalive 300daemonize yessupervised nopidfile /data/work/redis-3.2.9/cluster-conf/8888/redis_8888.pidloglevel noticelogfile "/data/work/redis-3.2.9/logs/redis_8888.log"databases 16save 900 1save 300 10save 60 10000stop-writes-on-bgsave-error yesrdbcompression yesrdbchecksum yesdbfilename dump.rdbdir ./slave-serve-stale-data yesslave-read-only yesrepl-diskless-sync norepl-diskless-sync-delay 5repl-disable-tcp-nodelay noslave-priority 100maxclients 100000appendonly noappendfilename "appendonly.aof"appendfsync everysecno-appendfsync-on-rewrite noauto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mbaof-load-truncated yeslua-time-limit 5000cluster-enabled yescluster-config-file /data/work/redis-3.2.9/cluster-conf/8888/nodes-8888.confslowlog-log-slower-than 10000slowlog-max-len 128latency-monitor-threshold 0notify-keyspace-events ""hash-max-ziplist-entries 512hash-max-ziplist-value 64list-max-ziplist-size -2list-compress-depth 0set-max-intset-entries 512zset-max-ziplist-entries 128zset-max-ziplist-value 64hll-sparse-max-bytes 3000activerehashing yesclient-output-buffer-limit normal 0 0 0client-output-buffer-limit slave 256mb 64mb 60client-output-buffer-limit pubsub 32mb 8mb 60hz 10aof-rewrite-incremental-fsync yes群集配置文件/data/work/redis-3.2.9/cluster-conf/8888/redis.conf (从)bind 192.168.56.181 127.0.0.1protected-mode yesmasterauth "xxxxx"requirepass "xxxxx"port 8888tcp-backlog 511timeout 0tcp-keepalive 300daemonize yessupervised nopidfile /data/work/redis-3.2.9/cluster-conf/8888/redis_8888.pidloglevel noticelogfile "/data/work/redis-3.2.9/logs/redis_8888.log"databases 16save 900 1save 300 10save 60 10000stop-writes-on-bgsave-error yesrdbcompression yesrdbchecksum yesdbfilename dump.rdbdir ./slave-serve-stale-data yesslave-read-only yesrepl-diskless-sync norepl-diskless-sync-delay 5repl-disable-tcp-nodelay noslave-priority 100maxclients 100000appendonly yesappendfilename "appendonly.aof"appendfsync everysecno-appendfsync-on-rewrite noauto-aof-rewrite-percentage 100auto-aof-rewrite-min-size 64mbaof-load-truncated yeslua-time-limit 5000cluster-enabled yescluster-config-file /data/work/redis-3.2.9/cluster-conf/8888/nodes-8888.confslowlog-log-slower-than 10000slowlog-max-len 128latency-monitor-threshold 0notify-keyspace-events ""hash-max-ziplist-entries 512hash-max-ziplist-value 64list-max-ziplist-size -2list-compress-depth 0set-max-intset-entries 512zset-max-ziplist-entries 128zset-max-ziplist-value 64hll-sparse-max-bytes 3000activerehashing yesclient-output-buffer-limit normal 0 0 0client-output-buffer-limit slave 256mb 64mb 60client-output-buffer-limit pubsub 32mb 8mb 60hz 10aof-rewrite-incremental-fsync yes5,应用scp复制装置和配置好的redis复制到其余服务器上曾经在192.168.56.180机器配置好,复制到181和182机器 ...

November 23, 2020 · 2 min · jiezi

关于redis集群:redis-安装

1.新建redis文件夹mkdir /home/redis2.进入redis目录cd /home/redis3.下载 redis-5.0.8 wget http://download.redis.io/rele...4.解压redistar zxvf redis-5.0.8.tar.gz5.进入 redis-5.0.8cd /home/reids/redis-5.0.8/src6.编译(编译前确定根据装置了gcc,如果没有装置则yum install gcc)make7.配置redis8.设置redis为开机启动 新建开机启动脚本 vi /etc/init.d/redis 输出开机脚本: #chkconfig: 2345 10 90# description: Start and Stop redis# Simple Redis init.d script conceived to work on Linux systems# as it does use of the /proc filesystem.REDISPORT=6379EXEC=/home/redis/redis-5.0.8/src/redis-serverCLIEXEC=/home/redis/redis-5.0.8/src/redis-cliPIDFILE=/var/lib/redis/redis_${REDISPORT}.pid#CONF="/etc/redis/${REDISPORT}.conf"CONF="/home/redis/redis-5.0.8/redis.conf"case "$1" in start) if [ -f $PIDFILE ] then echo "$PIDFILE exists, process is already running or crashed" else echo "Starting Redis server..." $EXEC $CONF & fi ;; stop) if [ ! -f $PIDFILE ] then echo "$PIDFILE does not exist, process is not running" else PID=$(cat $PIDFILE) echo "Stopping ..." $CLIEXEC -p $REDISPORT shutdown while [ -x /proc/${PID} ] do echo "Waiting for Redis to shutdown ..." sleep 1 done echo "Redis stopped" fi ;; restart) "$0" stop sleep 3 "$0" start ;; *) echo "Please use start or stop or restart as first argument" ;;esac9.增加redis服务chkconfig --add redis10.设置为开机启动 chkconfig redis on 11.启动服务 nohup service redis start ...

November 10, 2020 · 1 min · jiezi

关于redis集群:第三阶段-Day15-Redis集群搭建

实现Redis集群============= 1.1 为什么须要搭建集群redis分片特点:1.能够实现Redis内存数据的扩容.2.redis分片自身没有高可用成果的.如果宕机将间接影响用户的应用. redis哨兵特点:1.Redis哨兵能够实现Redis节点的高可用.然而哨兵自身没有实现高可用机制.(最好不要引入第三方)2.Redis哨兵有主从的构造 实现了内存数据的备份. 然而没有实现内存扩容的成果. 降级:须要Redis内容扩容同时须要Redis高可用性所以应该应用Redis集群. 1.2 对于Redis集群搭建问题阐明敞开所有的redis服务器sh stop.sh删除多余的文件3.重启redis服务器 执行挂载命令`redis-cli --cluster create --cluster-replicas 1 192.168.126.129:7000 192.168.126.129:7001 192.168.126.129:7002 192.168.126.129:7003 192.168.126.129:7004 192.168.126.129:7005` 1.3 Redis入门案例`@Test public void testCluster(){ Set<HostAndPort> nodes = new HashSet<>(); nodes.add(new HostAndPort("192.168.126.129", 7000)); nodes.add(new HostAndPort("192.168.126.129", 7001)); nodes.add(new HostAndPort("192.168.126.129", 7002)); nodes.add(new HostAndPort("192.168.126.129", 7003)); nodes.add(new HostAndPort("192.168.126.129", 7004)); nodes.add(new HostAndPort("192.168.126.129", 7005)); JedisCluster jedisCluster = new JedisCluster(nodes); jedisCluster.set("cluster", "集群的测试!!!!"); System.out.println(jedisCluster.get("cluster")); }` 1.4 对于选举机制-脑裂景象阐明: 当集群进行选举时,如果间断3次都呈现了平票的后果的则可能呈现脑裂的景象.问题: 呈现脑裂景象的概率是多少??? 1/8数学建模:抛银币间断3次呈现平票的概念是多少? 1/8=12.5%第一次: 正正 正反 反正 反反 1/2第二次: 正正 正反 反正 反反 1/2第三次: 正正 正反 反正 反反 1/2预防: 减少主节点的数量能够无效的升高脑裂景象的产生. ...

November 9, 2020 · 2 min · jiezi

关于redis集群:缓存服务器Redis-08-Redis集群算法及持久化策略及内存优化

redis集群数据存储原理hash槽算法/分区算法:RedisCluster采纳此分区,所有的键依据哈希函数(CRC16[key]%16384)映射到0-16383槽内,共16384个槽位,每个节点保护局部槽及槽所映射的键值数据.依据主节点的个数,平衡划分区间.算法:哈希函数: Hash()=CRC16[key]%16384 当向redis集群中插入数据时,首先将key进行计算.之后将计算结果匹配到具体的某一个槽的区间内,之后再将数据set到治理该槽的节点中. 题目剖析redis中最多存储16384个key----错例:CRC16(KEY1)%16384 = 2000/CRC16(KEY2)%16384 = 2000示意key1和key2都归节点1进行治理. 至于节点到底是否可能存储 由内存决定. Redis集群中最多有16384台主机,Redis集群中起码有3台主机. Redis长久化策略长久化需要阐明Redis数据都保留在内存中,如果内存断电则导致数据的失落.为了保障用户的内存数据不失落,须要开启长久化机制.长久化----定期将内存中的数据保留到磁盘中. 长久化分类阐明:Redis中的长久化形式次要有2种.形式1: RDB模式 dump.rdb 默认的长久化形式形式2: AOF模式 appendonly.aof 默认敞开的须要手动的开启 RDB模式1.阐明:RDB模式是Redis中默认的长久化策略. 2.特点:1).RDB模式能够实现定期的长久化,然而可能导致数据失落.(相似冷备份)2).RDB模式作的是内存数据的快照, 并且后拍摄的快照会笼罩之前的快照.所以长久化文件较小.复原数据的速度较快. 工作的效率较高. 3.命令:用户能够通过命令要求redis进行长久化操作.1).save 是同步操作 要求redis立刻执行长久化操作. 用户可能会陷入阻塞状态.2).bgsave 是异步操作, 开启独自的线程执行长久化操作. 长久化时不会影响用户的应用. 不能保障立刻马上执行. 4.长久化策略阐明:LG: save 900 1 redis在900秒内执行一次set操作时则长久化一次.用户操作越频繁则长久化的周期越短. 5.长久化目录: 能够执行长久化文件生成的地位.dir ./ 6.长久化文件名称的设定.dbfilename dump.rdb AOF模式1.特点:1).AOF模式默认的条件下是敞开状态,须要手动的开启.2).AOF模式记录的是用户的执行的状态.所以长久化文件占用空间绝对较大.复原数据的速度较慢.所以效率较低.3).能够保证用户的数据尽可能不失落. 2.配置:1).开启AOF配置appendonly yes2).AOF模式的长久化策略 appendfsync always 如果用户执行的了一次set操作则长久化一次appendfsync everysec aof每秒长久化一次appendfsync no 不被动长久化.对于RDB/AOF模式特点1.如果用户能够容许大量的数据失落能够选用RDB模式(快).2.如果用户不容许数据失落则选用AOF模式.3.理论开发过程中个别2种形式都会配置. 个别主机开启RDB模式,从机开启AOF模式. 理论状况如果在线上执行了FLUSHALL命令怎么办?---应该将AOF模式中flushAll删除,之后重启redis即可. Redis内存优化Redis能够当做内存应用,然而如果始终往里存储不删除数据,则必然导致内存溢出. LRU算法1.LRU是Least Recently Used的缩写,即最近起码应用,是一种罕用的页面置换算法,抉择最近最久未应用的页面予以淘汰。该算法赋予每个页面一个拜访字段,用来记录一个页面自上次被拜访以来所经验的工夫 t,当须淘汰一个页面时,抉择现有页面中其 t 值最大的,即最近起码应用的页面予以淘汰。2.维度:工夫T3.LRU算法是当下实现内存清理的最优算法. LFU算法1.LFU(least frequently used (LFU) page-replacement algorithm)。即最不常常应用页置换算法,要求在页置换时置换援用计数最小的页,因为常常应用的页应该有一个较大的援用次数。然而有些页在开始时应用次数很多,但当前就不再应用,这类页将会长工夫留在内存中,因而能够将援用计数寄存器定时右移一位,造成指数衰减的均匀应用次数。2.维度: 援用次数 ...

September 14, 2020 · 1 min · jiezi

关于redis集群:缓存服务器Redis-07-Redis集群

redis集群劣势采纳redis集群,能够保证数据扩散存储(主机-主机),同时保证数据存储的一致性(主机-从机),并且在外部实现高可用的机制,实现了服务故障的主动迁徙. 搭建打算主从--3台主/3台从(1主1从) 端口--7000/7001/7002/7003/7004/7005 集群搭建1.筹备集群文件夹:筹备集群文件夹Mkdir cluster在cluster文件夹中别离创立7000-7005文件夹 2.复制配置文件:将redis根目录中的redis.conf文件复制到cluster/7000/ 并以原名保留cp redis.conf cluster/7000/ 3.编辑配置文件:1).正文本地绑定IP地址(69行)#bind 127.0.0.12).敞开保护模式(88行)protected-mode no3).批改端口号(92行)port 70004).启动后盾启动(136行)daemonize yes5).批改pid文件(158行)pidfile /usr/local/src/redis/cluster/7000/redis.pid6).批改长久化文件(263行)dir /usr/local/src/redis/cluster/70007).设定内存优化策略(597行)maxmemory-policy volatile-lru8).敞开AOF模式(699行)appendonly no9).开启集群配置(838行)cluster-enabled yes10).开启集群配置文件(846行)cluster-config-file nodes.conf11).批改集群超时工夫(852行)cluster-node-timeout 15000 4.复制批改后的配置文件:阐明:将7000文件夹下的redis.conf文件别离复制到7001-7005中 [root@localhost cluster]# cp 7000/redis.conf 7001/[root@localhost cluster]# cp 7000/redis.conf 7002/[root@localhost cluster]# cp 7000/redis.conf 7003/[root@localhost cluster]# cp 7000/redis.conf 7004/[root@localhost cluster]# cp 7000/redis.conf 7005/5.批量批改配置文件:阐明:别离将7001-7005文件中的7000改为对应的端口号的名称,批改时留神方向键的应用在vim编辑器内底行模式,通过:%s/7000/7001/g命令批改g--示意批量批改,将文件内7000都替换为7001/7002... 6.通过脚本编辑启动/敞开指令:1).创立启动脚本 vim start.sh #!/bin/shredis-server 7000/redis.conf &redis-server 7001/redis.conf &redis-server 7002/redis.conf &redis-server 7003/redis.conf &redis-server 7004/redis.conf &redis-server 7005/redis.conf &2).编辑敞开的脚本 vim shutdown.sh #!/bin/shredis-cli -p 7000 shutdown &redis-cli -p 7001 shutdown &redis-cli -p 7002 shutdown &redis-cli -p 7003 shutdown &redis-cli -p 7004 shutdown &redis-cli -p 7005 shutdown &3).启动redis节点 sh start.sh4).查看redis节点启动是否失常 ps -ef | grep redis ...

September 12, 2020 · 1 min · jiezi

关于redis集群:Redis事件处理机制详解

全是干货的技术号:本文已收录在github,欢送 star/fork:https://github.com/Wasabi1234...Redis 服务器的事件次要解决两方面: 解决文件事件:在多个客户端中实现多路复用,承受它们发来的命令申请,并将命令的执行后果返回给客户端工夫事件:实现服务器惯例操作1 文件事件Redis server通过在多个客户端间多路复用, 实现了高效的命令申请解决: 多个客户端通过socket连贯到 Redis server, 但只有在socket可无阻塞读/写时, server才会和这些客户端交互。 Redis 将这类因为对socket进行多路复用而产生的事件称为文件事件, 文件事件可分类如下: 1.1 读事件读事件标记着客户端命令申请的发送状态。 当一个新的client连贯到服务器时, server会给该client绑定读事件, 直到client断开连接后, 该读事件才会被移除。 读事件在整个网络连接的生命期内, 都会在期待和就绪两种状态之间切换: 当client只是连贯到server,但并未向server发送命令时,该客户端的读事件就处于期待状态当client给server发送命令申请,并且申请已达到时(相应的套接字能够无阻塞地执行读操作),该client的读事件处于就绪状态。示例如图展现三个已连贯到server、但并未发命令的client 此时客户端的状态: Client读事件状态命令发送状态A期待未发送B期待未发送C期待未发送起初,A向服务器发送命令申请, 并且命令申请已达到时, A的读事件状态变为就绪:此时客户端的状态: Client读事件状态命令发送状态A就绪已发送且已达到B期待未发送C期待未发送当事件处理器被执行时,就绪的文件事件会被辨认到,相应的命令申请就会被发送到命令执行器,并对命令进行求值。 1.2 写事件写事件标记着client对命令后果的接管状态。 和client从头至尾都关联着读事件不同, server只会在有命令后果要传回给client时, 才会为client关联写事件, 并且在命令后果传送结束之后, client和写事件的关联就会被移除。 一个写事件会在两种状态之间切换: 当server有命令后果需返回给client,但client还未能执行无阻塞写,那么写事件处期待状态当server有命令后果需返回给client,且client可无阻塞写,那么写事件处就绪状态当client向server发命令申请, 且申请被承受并执行后, server就需将保留在缓存内的命令执行后果返回给client, 这时server就会为client关联写事件。 示例server正等待client A 变得可写, 从而将命令后果返回给A:此时客户端的状态: Client读事件状态写事件状态A期待期待B期待无C期待无当A的socket可无阻塞写时, 写事件就绪, server将保留在缓存内的命令执行后果返回给client:此时client状态: Client读事件状态写事件状态A期待已就绪B期待无C期待无当命令执行后果被传回client后, client和写事件的关联会被解除(只剩读事件),返回命令执行后果的动作执行结束,回到最后: 1.3 同时关联读/写事件咱们说过,读事件只有在client断开和server的连贯时,才会被移除。即当client关联写事件时,实际上它在同时关联读/写事件。 因为在同一次文件事件处理器的调用中, 单个客户端只能执行其中一种事件(要么读,要么写,不能又读又写), 当呈现读事件和写事件同时就绪时,事件处理器优先解决读事件。 即当server有命令后果要返回client, 而client又有新命令申请进入时, server先解决新命令申请。 2 工夫事件工夫事件记录着那些要在指定工夫点运行的事件,多个工夫事件以无序链表构造保留在服务器状态中。 无序链表并不影响工夫事件处理器的性能。在Redis3.0版本,失常模式下的 Redis 只带有 serverCron 一个工夫事件, 而在 benchmark 模式下, Redis 也只应用两个工夫事件。在这种状况下, 程序简直是将无序链表进化成一个指针来应用, 所以应用无序链表来保留工夫事件, 并不影响事件处理器性能。工夫事件的数据结构 ...

September 4, 2020 · 2 min · jiezi

redis-集群模式安装本地模拟

1、参考官网教程https://redis.io/topics/clust...2、下载redis-5.0.83、解压源码包,编译4、新建一个redis目录 用来模拟安装redis集群在redis 目录下新建 7000,7001,7002,7003,7004,7005这个几个目录mkdir 7000 7001 7002 7003 7004 7005 将编译后的可运行程序分别拷贝到7000、7001、7002、7003、7004、7005 目录下进入7000目录,修改配置文件redis.confvi redis.conf修改如下配置 port 7000cluster-enabled yescluster-config-file nodes.confcluster-node-timeout 5000appendonly yes保存:wq依次修改7001、7002、7003、7004、7005的配置文件其中各个目录下的端口号要配置成与目录一致,例如7001目录下的端口号要配置成7001。不能有相同的端口号,否则会启动失败。 进入7000/src/目录启动 redis服务器 ,启动命令为 ./redis-server ../redis.conf依次启动各个目录下的redis ,命令相同 进入7000/src 使用redis-cli创建redis集群 redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 \127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \--cluster-replicas 1当集群创建成功可以看到 [OK] All 16384 slots covered使用redis-cli连接集群的一个节点 redis-cli -c -p 7000设置一个key 为foo value 为bar的键值对 set foo bar-> Redirected to slot [12182] located at 127.0.0.1:7002OK实际上会根据key的值,具体存在某个几点,虽然连接的事7000这个节点,但是实际数据存储在老了7002节点上查看集群节点情况使用一下命令 redis-cli -p 7000 cluster nodes其中slave为从节点,master为主节点 往集群中添加新的节点 ...

June 21, 2020 · 1 min · jiezi

Redis4014-Cluster-安装部署

Redis-4.0.14 Cluster 安装部署本教程安装环境是kvm虚拟机,nat模式下内网环境。这里为了节约服务器资源采用了在单实例Linux主机上进行演示。生产环境大流量下请勿将多台Redis实例部署到一台Linux服务器上(小流量也不推荐一机多redis实例)。idc机器环境下要保证网卡的并发性、云主机要选用内存型(或者内存io型)。毕竟Redis是高io服务。 Tips:如果Redis是3主3从及以上规模集群建议关闭主服务器上的bgsave操作,改为在从上进行。降低流量高峰时bgsave产生延时影响io吞吐量。Redis下载、安装 cd ~ yum install gcc gcc-c++ -y wget http://download.redis.io/releases/redis-4.0.14.tar.gz tar -zxf redis-4.0.14.tar.gz cd redis-4.0.14 cd deps make hiredis jemalloc linenoise lua geohash-int cd .. make PREFIX=/app/coohua/redis-9701 install make install将redis-cli加入系统路径中[root@redis-nec001 ~]# cp /app/coohua/redis-9701/bin/redis-cli /usr/local/bin/redis-cli[root@redis-nec001 ~]# redis-cli --versionredis-cli 4.0.14制作多副本,生产环境建议每个实例配置一个单独的服务器cd /app/coohua/mkdir -p redis-9701/confcp ~/redis-4.0.14/redis.conf ./redis-9701/conf/cp -arp redis-9701 redis-9702cp -arp redis-9701 redis-9703cp -arp redis-9701 redis-9704cp -arp redis-9701 redis-9705cp -arp redis-9701 redis-9706cp -arp redis-9701 redis-9707修改配置文件redis.conf修改或开启以下几项bind 192.168.100.214port 9701daemonize yespidfile /app/coohua/redis-9701/conf/redis_9701.pidlogfile "/data/coohua/redis-9701/redis.log"dir /data/coohua/redis-9701/maxmemory 4096Mcluster-enabled yescluster-config-file /app/coohua/redis-9701/conf/nodes-9701.conf配置ruby环境RVM方式gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDBcurl -sSL https://get.rvm.io | bash -s stablesource /etc/profile.d/rvm.shrvm install 2.6.4rvm 2.6.4 --defaultgem install rediscp ~/redis/redis-4.0.14/src/redis-trib.rb /usr/local/bin/设置系统内核参数echo never > /sys/kernel/mm/transparent_hugepage/enabledecho "echo never > /sys/kernel/mm/transparent_hugepage/enabled" >> /etc/rc.localcp ~/redis/redis-4.0.14/src/redis-trib.rb /usr/local/bin/cat /etc/sysctl.confnet.ipv4.ip_forward = 0net.ipv4.conf.default.accept_source_route = 0kernel.core_uses_pid = 1net.bridge.bridge-nf-call-ip6tables = 0net.bridge.bridge-nf-call-iptables = 0net.bridge.bridge-nf-call-arptables = 0kernel.msgmnb = 65536kernel.msgmax = 65536kernel.shmmax = 68719476736kernel.shmall = 4294967296vm.swappiness = 0net.ipv4.neigh.default.gc_stale_time=120net.ipv4.conf.all.rp_filter=0net.ipv4.conf.default.rp_filter=0net.ipv4.conf.default.arp_announce = 2net.ipv4.conf.all.arp_announce=2net.ipv4.conf.lo.arp_announce=2net.core.somaxconn = 262144net.core.netdev_max_backlog = 262144net.ipv4.ip_local_port_range = 1024 65000net.ipv4.tcp_max_tw_buckets = 5000net.ipv4.tcp_max_orphans = 262144net.ipv4.tcp_max_syn_backlog = 262144net.ipv4.tcp_timestamps = 0net.ipv4.tcp_synack_retries = 1net.ipv4.tcp_syn_retries = 1net.ipv4.tcp_fin_timeout = 2net.ipv4.tcp_tw_recycle = 1net.ipv4.tcp_tw_reuse = 1net.ipv4.tcp_syncookies = 0net.ipv4.tcp_keepalive_time = 30net.ipv4.tcp_orphan_retries = 2kernel.core_pattern = /data/coohua/core/core_%e_%pvm.overcommit_memory = 1kernel.sysrq = 1启动Redis(以coohua用户启动) chown -R coohua:coohua /app/coohua/redis* /data/coohua/ su - coohua /app/coohua/redis-9701/bin/redis-server /app/coohua/redis-9701/conf/redis.conf /app/coohua/redis-9702/bin/redis-server /app/coohua/redis-9702/conf/redis.conf /app/coohua/redis-9703/bin/redis-server /app/coohua/redis-9703/conf/redis.conf /app/coohua/redis-9704/bin/redis-server /app/coohua/redis-9704/conf/redis.conf /app/coohua/redis-9705/bin/redis-server /app/coohua/redis-9705/conf/redis.conf /app/coohua/redis-9706/bin/redis-server /app/coohua/redis-9706/conf/redis.conf /app/coohua/redis-9707/bin/redis-server /app/coohua/redis-9707/conf/redis.conf集群创建分为3主、3主3从(这也是最少的集群数量)redis-trib.rb create --replicas 0 192.168.100.214:9701 192.168.100.214:9702 192.168.100.214:9703 #3主模式,最小节点集群,无法提供高可用.redis-trib.rb create --replicas 1 192.168.100.214:9701 192.168.100.214:9702 192.168.100.214:9703 192.168.100.214:9704 192.168.100.214:9705 192.168.100.214:9706 #主从模式,Slave从节点即是Master的备用节点也是数据的读取节点创建1个最小的3主集群[root@redis-nec001 bin]# redis-trib.rb create --replicas 0 192.168.100.214:9701 192.168.100.214:9702 192.168.100.214:9703>>> Creating cluster>>> Performing hash slots allocation on 3 nodes...Using 3 masters:192.168.100.214:9701192.168.100.214:9702192.168.100.214:9703M: fa820855aeebad6551d09d0cd6063aeaefc8f4f9 192.168.100.214:9701 slots:0-5460 (5461 slots) masterM: 517fd7f65b7e653a91b24aa7a06f1ec360bd8220 192.168.100.214:9702 slots:5461-10922 (5462 slots) masterM: ccf082f6516ec23c1aee891358a3daf47d2b5ca7 192.168.100.214:9703 slots:10923-16383 (5461 slots) masterCan 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 clusterWaiting for the cluster to join..>>> Performing Cluster Check (using node 192.168.100.214:9701)M: fa820855aeebad6551d09d0cd6063aeaefc8f4f9 192.168.100.214:9701 slots:0-5460 (5461 slots) master 0 additional replica(s)M: 517fd7f65b7e653a91b24aa7a06f1ec360bd8220 192.168.100.214:9702 slots:5461-10922 (5462 slots) master 0 additional replica(s)M: ccf082f6516ec23c1aee891358a3daf47d2b5ca7 192.168.100.214:9703 slots:10923-16383 (5461 slots) master 0 additional replica(s)[OK] All nodes agree about slots configuration.>>> Check for open slots...>>> Check slots coverage...[OK] All 16384 slots covered.查看集群状态[root@redis-nec001 bin]# redis-cli -h 192.168.100.214 -p 9701 -c192.168.100.214:9701> cluster nodes517fd7f65b7e653a91b24aa7a06f1ec360bd8220 192.168.100.214:9702@19702 master - 0 1568616352578 2 connected 5461-10922ccf082f6516ec23c1aee891358a3daf47d2b5ca7 192.168.100.214:9703@19703 master - 0 1568616353579 3 connected 10923-16383fa820855aeebad6551d09d0cd6063aeaefc8f4f9 192.168.100.214:9701@19701 myself,master - 0 1568616352000 1 connected 0-5460192.168.100.214:9701> 添加从节点到集群中,升级为3主3从高可用方式redis-trib.rb add-node --slave 其中 --slave表示添加的节点为从节点 其Master主节点用--master-id方式,其后是要加入的从节点ip及端口,最后是随机选择一个主节点完成命令的格式完成,不知道Redis设计时是如何思考的。非要添加一个无关紧要的参数,但是又不可少。[root@redis-nec001 coohua]# redis-trib.rb add-node --slave --master-id fa820855aeebad6551d09d0cd6063aeaefc8f4f9 192.168.100.214:9704 192.168.100.214:9701>>> Adding node 192.168.100.214:9704 to cluster 192.168.100.214:9701>>> Performing Cluster Check (using node 192.168.100.214:9701)M: fa820855aeebad6551d09d0cd6063aeaefc8f4f9 192.168.100.214:9701 slots:0-5460 (5461 slots) master 0 additional replica(s)M: 517fd7f65b7e653a91b24aa7a06f1ec360bd8220 192.168.100.214:9702 slots:5461-10922 (5462 slots) master 0 additional replica(s)M: ccf082f6516ec23c1aee891358a3daf47d2b5ca7 192.168.100.214:9703 slots:10923-16383 (5461 slots) master 0 additional replica(s)[OK] All nodes agree about slots configuration.>>> Check for open slots...>>> Check slots coverage...[OK] All 16384 slots covered.>>> Send CLUSTER MEET to node 192.168.100.214:9704 to make it join the cluster.Waiting for the cluster to join.>>> Configure node as replica of 192.168.100.214:9701.[OK] New node added correctly.添加主节点分配槽位流程 主节点的添加配置略为复杂,先要将存储为空的Redis实例添加到集群,再'合理'的分配槽位给它。如果源集群是3主,又添加3主这样比较好分配,原来的节点每个分配一般槽位给新加入的节点即: 节点1分配新节点1一半 节点2分配新节点2一半 节点3分配新节点3一半非对称方式添加 前提是各主节点槽位数一致、或者接近一致现节点槽位数=总槽位数/集群节点数(包含最新的)各节点需要迁移槽位数=源各节点槽位数-现节点槽位数添加主节点redis-trib.rb add-node 192.168.100.214:9707 192.168.100.214:9701查看各主节点槽位 可以看到除了新节点外,每个主节点的槽位数基本都是5461.现节点槽位数(迁移分配后)=16384/4=4096旧主节点需要迁移槽位数=5461-4094=1365 ...

October 15, 2019 · 3 min · jiezi

Redis压缩包没有redisconf快速启动之记录一

转载请标明出处: http://dujinyang.blog.csdn.net/本文出自:【奥特曼超人的博客】Redis压缩包配置环境变量,直接CMD中启动,默认是打开redis.conf,当然,压缩包是没有的,这里是自行创建的 redis.windows-service.conf 和 redis.windows.conf 对应服务端和客户端的使用。 直接启动应该都会遇到这个提示警告warning,这里不是win7,懒得去找etc下的文件,也没找到…… Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf所以这里自己写了redis-ser.exe 和redis-ser.bat 作为调用,在命令行CMD输入就可以调起。 加入启动服务: redis-server.exe --service-install redis.windows.conf命令: redis-server redis.windows.confredis-cli –h 127.0.0.1 –p 8088<port> -a dujinyang<pwd> 快速启动: redis-ser 或 redis-cli2redis.windows.conf日志记录级别:loglevel notice (Redis支持四个级别:debug、verbose、notice、warning)日志记录方式:logfile ""数据库的数量:databases 16 (可以使用SELECT<dbid>命令在连接上指定数据库id)指定在多长时间内,有多少次更新操作,将数据同步到数据文件: save <seconds> <changes>save 900 1 //900秒至少有1次更新同步到数据文件 save 300 10 //300秒至少有10次更新同步到数据文件 save 60 10000 //60秒如果有10000次更新同步到数据文件指定存储至本地数据库时是否压缩数据: rdbcompression yes.默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大。指定本地数据库文件名: dbfilename dump.rdb指定本地数据库存放目录: dir ./设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步: slaveof <masterip> <masterport>当master服务设置了密码保护时,slav服务连接master的密码: masterauth <master-password>设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH<password>命令提供密码: requirepass foobared(默认关闭)设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。 当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息: maxclients 10000指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区: maxmemory <bytes>指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。appendonly no (默认为no)指定更新日志文件名。appendfilename "appendonly.aof"指定更新日志条件。appendfsync everysecappendfsync always //表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全) appendfsync everysec //表示每秒同步一次(折衷,默认值) appendfsync no //表示等操作系统进行数据缓存同步到磁盘(快)指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件。include /path/to/local.conf ...

September 19, 2019 · 1 min · jiezi

高并发探测四redis集群部署整理续容灾

前言前面已经部署了:redis的4对主从集群 + 1对主从session主从备份。如果redis集群中有宕机情况发生,怎么保障服务的可用性呢,本文准备在session服务器上添加启动哨兵服务,测试集群的容灾情况。 1.添加哨兵a.重新整理集群由于集群的连接操作均使用内网,实际应用同样。修改容器启动命令、配置文件,取消redis集群对外公网的端口映射、cli连接密码。另外节点太多不方便管理,所以减小点。调整为无密码的3对主从集群,删除clm4、cls4: / # redis-cli --cluster del-node 172.1.30.21:6379 c2b42a6c35ab6afb1f360280f9545b3d1761725e>>> Removing node c2b42a6c35ab6afb1f360280f9545b3d1761725e from cluster 172.1.30.21:6379>>> Sending CLUSTER FORGET messages to the cluster...>>> SHUTDOWN the node./ # redis-cli --cluster del-node 172.1.50.21:6379 6d1b7a14a6d0be55a5fcb9266358bd1a42244d47>>> Removing node 6d1b7a14a6d0be55a5fcb9266358bd1a42244d47 from cluster 172.1.50.21:6379[ERR] Node 172.1.50.21:6379 is not empty! Reshard data away and try again.#需要先清空槽数据(rebalance成weigth=0)/ # redis-cli --cluster rebalance 172.1.50.21:6379 --cluster-weight 6d1b7a14a6d0be55a5fcb9266358bd1a42244d47=0Moving 2186 slots from 172.1.50.21:6379 to 172.1.30.11:6379###Moving 2185 slots from 172.1.50.21:6379 to 172.1.50.11:6379###Moving 2185 slots from 172.1.50.21:6379 to 172.1.50.12:6379###/ # redis-cli --cluster del-node 172.1.50.21:6379 6d1b7a14a6d0be55a5fcb9266358bd1a42244d47>>> Removing node 6d1b7a14a6d0be55a5fcb9266358bd1a42244d47 from cluster 172.1.50.21:6379>>> Sending CLUSTER FORGET messages to the cluster...>>> SHUTDOWN the node.缩容成功。 ...

July 11, 2019 · 2 min · jiezi

Redis-Cluster配置传播及故障恢复笔记

本笔记是对Redis Cluster Spec - Configuration handling, propagation, and failovers的归纳总结。 Epoch可以把Epoch当作是一个版本号,是一个64位无符号整形每个Node自己有一份Cluster.currentEpoch、MySelf.configEpoch、其他Node.configEpoch,详见文档。每个Master有自己的ConfigEpoch且在整个Cluster中唯一Slave的ConfigEpoch随其MasterCluster.currentEpoch,该值等于所有Node中最大的ConfigEpoch的值Master的ConfigEpoch初始值是0,也就是说Cluster.CurrentEpoch的初始值也是0Node之间Gossip传输消息时,Receiver发现Sender的ConfigEpoch比自己大,那么就更新自己的Cluster.CurrentEpoch为该值,随时间收敛,所有Node的Cluster.CurrentEpoch都变成一样。Slave PromotionSlave的动作下面是总结的在发生Slave Promotion时,Slave做的事情。 Master的动作下面是总结的在发生Slave Promotion时,Master做的事情。 传播Slots的配置Slave赢得选举之后会在己侧更新Slots上的归属信息,然后在定时的PING/PONG中将这个信息传播出去。 PING/PONG总是会携带上Slots所属Master的信息(包括ConfigEpoch) PING的Reciever如果发现Sender的某个Slot上的Master.ConfigEpoch比自己这里记录的小,那么就会返回UPDATE告诉Sender更新Slots归属信息。 下面是两个规则: 如果一个Slot不属于任何Master,然后有一个Master宣称拥有它,那么就修改己侧的Slots信息把这个Slot关联到这个Master上。如果一个Slot已经归属一个Master,然后又有一个Master宣称拥有它,那么就看谁的ConfigEpoch大,大的那个赢Node复活后遇到的问题Node A有两个Slot,然后它死了,它被顶替了,等它复活时发现两个Slot一个被Node B接管,另一个被Node C接管了,那么它: 因为自己的ConfigEpoch已经很旧了,所以它复活后不负责任何Slot然后它会成为最后一个Slot的Master的SlaveSlave迁移算法Slave迁移时一个自动过程。 举个例子,现在有Master A、B,它们对应的Slave有A1、B1、B2。现在A死了,A1顶替上去,不过这个时候A1就是一个光棍Master(它没有Slave),B有富余的Slave(B1和B2),把其中一个匀给A1当Slave。 这个过程不需要共识,因为只是修改Slave的归属,也不会修改ConfigEpoch。 Slave迁移有两个规则: 当有多个Slave富余时,选择NodeID字典顺最小的那个来迁移只有当Master的Slave数量>=cluster-migration-barrier时,才会挑选它的Slave做Migration两个跳过共识修改ConfigEpoch的操作下面两个操作比较危险,最好确定一个成功后再执行另一个: CLUSTER_FAILOVER TAKEOVER(手动Failover)直接将一个Slave提升为Master,不需要大多数Master同意。Slot Migration同样不需要大多数Master同意。所以就有可能出现同一个Slot有两个相同ConfigEpoch的Master宣称由自己负责,这种冲突的解决算法是: 如果Master A发现Master B也宣称了对Slot X的主权,并且两者的ConfigEpoch一样如果Master A的NodeID的字典顺比Master B的小那么Master A就把己侧的CurrentEpoch+1,同时ConfigEpoch改成和CurrentEpoch一样Node重制略,见文档。 移除Node略,见文档。 一些自问自答Q:ConfigEpoch何时变化? A:Slave Promotion时、手动Failover时、Slot Migration时 Q:ConfigEpoch怎么变化? A:Node->ConfigEpoch = Cluster->CurrentEpoch + 1,结果也就是Cluster->CurrentEpoch加1了。源码见这里。 Q:两个Master的ConfigEpoch一样怎么办? A:这个会出现在两个Slave同时Promotion时,解决办法是NodeID字典序比较小的那个会再一次Bump ConfigEpoch,源码见这里。 Q:ConfigEpoch有什么用? A:当有两个Master宣称自己拥有同一个/批Slot时,ConfigEpoch大的那个赢,因为大的那个代表最新信息,其他Node只会采用赢的那方所宣称的信息。 Q:CurrentEpoch有什么用? A:1)用来判定Node所获得的Cluster信息的新旧。2)当Node要变更ConfigEpoch时派用处。 参考资料官方文档: Redis Cluster Spec - Configuration handling, propagation, and failovers下面是饿了么工程师写的文章,比较透彻: ...

July 11, 2019 · 1 min · jiezi

Redis-Cluster节点故障探测算法笔记

本笔记是对Redis Cluster Spec - Failure Detection的归纳总结 状态转换图每个Node在本地维护了一张其他Node的状态表,并根据Failure Detection算法更新这张表里的Node的状态每个Node可以自行把其他Node的状态设置为GOOD(这个状态在文档和源码中均不存在,等价于不是PFAIL也不是FAIL)、PFAIL。如果要把其他Node的状态设置为FAIL则需要大多数Master Node同意才行,一旦设置成功要将这个消息传播给所有其他能连接的Node,其他Node收到这个信息后也要更新本地Node状态表,将Failed Node的状态更新为FAIL。下面是状态转换图,例举的是Node A观察Node B的例子: 少数派和多数派多数派:拥有多数Master的一方,可含有Slave。 少数派:拥有少数Master的一方,可含有Slave。 少数派视角少数派只会看到大多数Master处于PFAIL/FAIL状态,0-所有Slave处于PFAIL/FAIL状态。 多数派视角多数派只会看到少数Master处于PFAIL/FAIL状态,0-所有Slave处于PFAIL/FAIL状态。 不会存在以下情况:多数派看到大多数Master处于FAIL状态,因为大多数Master处于FAIL就意味着活着的Master们变成了少数派,这就矛盾了。 一些自问自答Q:为何少数派能够看到Master处于FAIL状态?不是说要大多数Master同意才能变成FAIL状态吗?A:考虑这个情况,在Partition发生的前一秒某些Master被决定为FAIL,随即Partition发生,那么在少数派眼里这些Master依然是处于FAIL状态的。 Q:这里的每个Node是Slave还是Master呢?A:随便,只要是Node就行。 Q:既然每个Master独占的负责Slots,那么少数派继续工作为啥不可以,反正各自管各自的。A:因为在多数派方,这个Master有可能会被Slave顶替,如果允许少数派继续工作,那么就会形成两个Master,造成split brain Q:少数派节点是如何知道自己应该停止工作的?A:它发现大多数Master变成了PFAIL / FAIL 状态时,就知道自己不能工作了,Redis源码里是这么写的。 Q:多数派节点时如何知道自己应该停止工作的?A:如果这个Cluster要求所有Slots被覆盖,那么当有一个Master处于FAIL状态时,便停止工作,见源码。如果不要求,则继续工作,只不过部分Slots的操作会报错。

July 10, 2019 · 1 min · jiezi

高并发探测三分布式场景常见问题之速度优化数据同步问题

场景需求对于已有的mysql主从项目,应对数据量大时往往采取分库分表的做法,为了缩短页面响应采用一主多从的 主写+从读 的读写分离架构。redis:Redis中文网:内存存储的数据结构服务器,可用作数据库,高速缓存和消息队列代理...。使用redis一:使用redis作为php的缓存层,存储常用、相对固定的公共数据;二:添加redis缓存,组成 mysql写+ redis读 的架构;三:甚至直接采用 redis读+写 的架构。mysql迁移redis需要后台程序的紧密配合。“读写分离”容易出现数据不一致的问题。 一些优化相关传统前后端优化途径: 1. 前端 减少请求次数:css精灵(小图合并到大图),data-image(data-icon:src=data:image/jpg;base64;xx,小图合并到js文件); 2. 网关 web资源防盗链refer监测,nginx限流,nginx负载均衡、nginx缓存静态资源、gzip等,http2.0; 3. 后端 使用redis、memcache缓存,mysql优化;进阶: 1. 网关 mysql迁移redis,分布式集群部署; 2. 后端 添加针对高并发的消息队列,多线程、协程化,使用连接池;分布式部署常见问题: 登录session共享问题;读写分离的同步数据问题。另外还各种诸如图片数据库、对象存储等。1.分布式问题的一致处理要同时在多台服务器上处理比如:库存超卖、订单支付问题需要频繁的过程校验,所以对并行任务串行化、使用一台机器、单一线程处理这种一致性问题最为稳妥。应对大数据量的情况采用消息队列,平衡服务器压力。 a.RabbitMQ消息队列

July 7, 2019 · 1 min · jiezi

redis-系列redis-是什么

Redis 是什么Redis (Remote Dictionary Service) 即远程字典服务 。是一个由C 语言编写的基于内存的存储服务。 Redis 的基础数据结构reids 目前支持5种基础数据结构 string,list, hash,set,zsetstring(字符串) 在c 语言中的结构定义(SDS)见 "sds.h/sdshdr"注:代码结构和源码不太一样 为方便理解 有兴趣可以看 https://github.com/antirez/re...如下 stract sdshdr{ int len;// 记录sds 保存的字符串长度 int alloc; // 记录buf 数组中未使用的字节数量 char flags; // 3 lsb of type, 5 unused bits char buf[]; //字节数组,存储字符}程序员面试凡事都喜欢问为什么?你先琢磨下,后面开始给大家讲为什么? list (列表) 在c 语言中的结构定义 typedef struct listNode { struct listNode *prev; //前置节点 struct listNode *next;// 后置节点 void *value; // 值} listNode;typedef struct list { listNode *head; //头节点 listNode *tail;// 尾节点 void *(*dup)(void *ptr);// 节点值复制函数 void (*free)(void *ptr);// 节点值释放函数 int (*match)(void *ptr, void *key);//节点值对比函数 unsigned long len;// 链表包含的节点数量} list;

June 30, 2019 · 1 min · jiezi

Redis-哨兵使用以及在-Laravel-中的配置

主从配置(master-slave)复制 redis 配置文件以开启多个 slavesudo cp /etc/redis.conf /etc/redis-6381.confsudo cp /etc/redis.conf /etc/redis-6382.conf 编辑 slave 配置文件,主要修改参数port 6381pidfile "/var/run/redis-6381.pid"logfile "/var/log/redis/redis-6381.log"slaveof 11.11.11.11 6381masterauth "123456" # 主从都保持一样的密码,且 master 的配置也需要这一行,在执行切换 master 的时候好像不会去添加这一行/usr/bin/redis-server /etc/redis.conf 通过配置启动 redis哨兵配置(sentinel)复制哨兵配置,这儿开启3个哨兵sudo cp /etc/redis-sentinel.conf /etc/redis-sentinel-26381.confsudo cp /etc/redis-sentinel.conf /etc/redis-sentinel-26382.conf 编辑哨兵配置文件,主要修改参数如下,根据具体情况配置port 26381pidfile "/var/run/redis-sentinel-26381.pid"logfile "/var/log/redis/redis-sentinel-26381.log"sentinel monitor mymaster 11.11.11.11 6379 2 #主节点别名为mymaster,后面是ip和端口,2代表判断主节点失败至少需要2个sentinel节点同意sentinel auth-pass mymaster 123456sentinel down-after-milliseconds mymaster 30000 #主节点故障30秒后启用新的主节点sentinel parallel-syncs mymaster 1 #故障转移时最多可以有1个从节点同时对主节点进行数据同步,数字越大,用时越短,存在网络和 IO 开销sentinel failover-timeout mymaster 180000 #故障转移超时时间180s:a 如果转移超时失败,下次转移时时间为之前的2倍;b 从节点变主节点时,从节点执行 slaveof no one 命令一直失败的话,当时间超过180S时,则故障转移失败;c 从节点复制新主节点时间超过180S转移失败/usr/bin/redis-sentinel /etc/redis-sentinel.conf 通过配置启动哨兵laravel 哨兵配置'default' => [ 'tcp://11.11.11.11:26379', 'tcp://11.11.11.11:26381', 'tcp://11.11.11.11:26382', //这3个都是sentinel节点的地址 'options' => [ 'replication' => 'sentinel', 'service' => env('REDIS_SENTINEL_SERVICE', 'mymaster'), //sentinel 'parameters' => [ 'host' => env('REDIS_HOST', '127.0.0.1'), 'port' => env('REDIS_PORT', 6379), 'password' => env('REDIS_PASSWORD', null), //redis的密码,没有时写null 'database' => 0, ], ], ]

June 28, 2019 · 1 min · jiezi

Redis集群容器化安装

Redis集群概述Redis作为当前非常热门的内存型数据结构存储,可用于数据存储,缓存和消息代理等。本文将讲解如何基于docker搭建Redis集群,Redis的集群设计包括两个部分:主从复制和哈希Slot。1.1. 主从复制主从复制在数据库中很常见,一般用来做读写分离,Redis中也是如此。要求只有1个Master(主节点),可以有N个slaver(从节点),而且Slaver也可以有自己的Slaver,由于这种主从的关系决定他们是在配置阶段就要指定他们的上下级关系,而不是Zookeeper那种平行关系是自主推优出来的。读写分离,Master只负责写和同步数据给Slaver,Slaver承担了被读的任务,所以Slaver的扩容只能提高读效率不能提高写效率。Slaver先将Master那边获取到的信息压入磁盘,再load进内存,client端是从内存中读取信息的。当一个新的Slaver加入到这个集群时,会主动找Master来拜码头,Master发现新的小弟后将全量数据发送给新的Slaver,数据量越大性能消耗也就越大,所以尽量避免在运行时做Slaver的扩容。优点:读写分离,通过增加Slaver可以提高并发读的能力。缺点:Master写能力是瓶颈,维护Slaver开销也总将会变成瓶颈。1.2. 哈希Slot哈希Slot名字上可能不好理解,其实就是数据库中的“水平划分”。如果你之前有了解过数据库的表分区的话,就会发现下来对于哈希Slot的描述,就和数据库表分区里面的“HASH分区”原理上大致相同。对象保存到Redis之前先经过CRC16哈希到一个指定的Node上,例如图中Object4最终Hash到了Node1上。每个Node被平均分配了一个Slot段,对应着0-16384,Slot不能重复也不能缺失,否则会导致对象重复存储或无法存储。Node之间也互相监听,一旦有Node退出或者加入,会按照Slot为单位做数据的迁移。例如Node1如果掉线了,0-5640这些Slot将会平均分摊到Node2和Node3上,由于Node2和Node3本身维护的Slot还会在自己身上不会被重新分配,所以迁移过程中不会影响到 5641-16384 Slot段的使用。优点:将Redis的写操作分摊到了多个节点上,提高写的并发能力,扩容简单。缺点:每个Node承担着互相监听、高并发数据写入、高并发数据读出,工作任务繁重。1.3. 合二为一看到这里大家也就发现了,主从和哈希的设计优缺点正好是相互弥补的,将二者结合在一起,就是Redis集群的终极形态,先Hash分逻辑节点,然后每个逻辑节点内部是主从,如图:2.集群安装默认我们已经有了docker环境,现在开始基于docker安装Redis集群。redis 5.0 版本之前,网上有很多教程都是通过redis-trib.rb 来创建集群,但是redis 5.0 版本以后,就只能通过 redis-cli 来实现。本文即通过 redis-cli 创建集群,因为redis集群的节点选举方式是需要半数以上的master通过,所以建议创建奇数个节点。本例中创建3个Master节点,并为每个Master节点各分配1个Slave节点。2.1. 宿主机环境首先需要找一份原始的redis.conf文件,将其重命名为:redis-cluster.tmpl,并配置如下几个参数,此文件的目的是生成每一个redis实例的redis.conf:vi redis-cluster.tmpl然后执行下列脚本,给3个Master和3个Slave创建各自的挂载卷目录当前目录结构为2.2. 创建Redis节点假设我们只考虑单纯的docker环境,并无docker-compose和k8s之类的服务编排,每个redis容器之间必须要保证通讯,可以通过创建docker network。(使用微服务编排的情况,后续再讨论)现在我们就可以运行docker redis 的 master 和 slave 实例了查看已创建的redis容器2.3. 构建集群通过redis-cli 命令构建集群,随便找一个redis容器,运行redis-cli --cluster create --cluster-replicas 1 ip:port 命令即可记住构建集群时,要保证节点redis数据为空,否则会出现下列错误。2.4. 集群验证集群搭建完成后,我们通过 redis-cli 命令连接集群节点验证一下。redis 集群节点的连接命令是通过 redis-cli -c -h ${ip} -p ${port}可以看到通过 "cluster info"命令看到集群的基本信息,所有的slot (16384) 都分配完毕。然后通过 "cluster nodes" 命令查看到每个master节点的slot分配的区域。至此,redis集群基本安装成功。3.后期运维3.1. 基本命令集群节点槽(slot)3.2. 常见问题(1)redis-cluster 把所有的物理节点映射到[ 0 ~ 16383 ]个slot(哈希槽)上,cluster负责维护 node<->slot<->value。(2)集群任意一个节点中,如果master挂掉,但是还有slaver,slave将自动升为 master,系统正常。(3)集群任意一个节点中,如果master挂掉,并且没有slaver,集群将进入fail状态。(4)如果集群超过半数以上节点的master挂掉,不管是否有slaver,集群都将进入fail状态。(5)节点判断是否失效的选举,是集群中所有的master参与的,如果半数以上的master节点与当前被检测的master节点通讯检测超时(cluster-node-timerout),就认为当前master节点挂掉了。本人创业团队产品MadPecker,主要做BUG管理、测试管理、应用分发网址:www.madpecker.com,有需要的朋友欢迎试用、体验!本文为MadPecker团队技术人员编写,转载请标明出处

June 18, 2019 · 1 min · jiezi

Redis的哨兵

Redis-Sentinel简介Redis Sentinel是一个分布式架构,其中包含若干个 Sentinel 节点和 Redis 数据节点,每个 Sentinel 节点会对数据节点和其余 Sentinel 节点进行监控,当它发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它还会和其他 Sentinel 节点进行“协商”,当大多数 Sentinel 节点都认为主节点不可达时,它们会选举出一个 Sentinel 节点来完成自动故障转移的工作(这个选举机制一会介绍),同时会将这个变化实时通知给 Redis 应用方。整个过程完全是自动的,不需要人工来介入,所以这套方案很有效地解决了 Redis 的高可用问题,可以理解为:之前的主从复制中增加了几个哨兵(这里注意是几个而不是一个)来监控redis,如果主机挂了,哨兵会经过选举在从机中选出一个redis作为主机,这样就不必手动切换了。 Redis Sentinel 的功能1·监控:Sentinel 节点会定期检测 Redis 数据节点、其余 Sentinel 节点是否可达。2·通知:Sentinel 节点会将故障转移的结果通知给应用方。3·主节点故障转移:实现从节点晋升为主节点并维护后续正确的主从关系。4·配置提供者:在 Redis Sentinel 结构中,客户端在初始化的时候连接的是 Sentinel 节点集合,从中获取主节点信息。同时Redis Sentinel 包含了若个 Sentinel 节点,这样做也带来了两个好处:5·对于节点的故障判断是由多个 Sentinel 节点共同完成,这样可以有效地防止误判。6·Sentinel 节点集合是由若干个 Sentinel 节点组成的,这样即使个别 Sentinel 节点不可用,整个 Sentinel 节点集合依然是健壮的。Sentinel 节点本身就是独立的 Redis 节点,只不过它们有一些特殊,它们不存储数据,只支持部分命令 基本的故障转移流程:1.主节点出现故障,此时两个从节点与主节点失去连接,主从复制失败。 2.每个Sentinel 节点通过定期监控发现主节点出现了故障。 3.多个 Sentinel 节点对主节点的故障达成一致会选举出其中一个节点作为领导者负责故障转移。 4.Sentinel 领导者节点执行了故障转移,整个过程基本是跟我们手动调整一致的slaveof no one,只不过是自动化完成的。 5.故障转移后整个 Redis Sentinel 的结构,重新选举了新的主节点,并且会通知给客户端。 Sentinel 的配置sentinel monitor <master-name> <ip> <port> <count>监控的主节点的名字、IP 和端口,最后一个count的意思是有几台 Sentinel 发现有问题,就会发生故障转移,例如 配置为2,代表至少有2个 Sentinel 节点认为主节点不可达,那么这个不可达的判定才是客观的。对于设置的越小,那么达到下线的条件越宽松,反之越严格。一般建议将其设置为 Sentinel 节点的一半加1注意:最后的参数不得大于conut(sentinel) ...

June 13, 2019 · 2 min · jiezi

redis的主从复制

前言本来以前在csdn写文章,只是无意间注册了sf账号,每天逛一下这些网站,发现sf质量确实很高,而且,也没有那么多让人糟心的广告,每次在csdn看到想读的文章都第一件事拉到最下面看是不是广告,如果不是才耐心的从头开始读,这种体验很糟心,就来到sf,而且发现sf就在杭州本地,不错不错,果断支持一下。 主从复制是哨兵sentinel和cluster的基础,知其然知其所以然,所以,了解了其中的细节原理和每一步都执行了什么操作,对以后的调试也是很有帮助的。 为什么要使用主从在实际的场景当中单一节点的redis容易面临风险,比如只有一台redis,然后挂了,本来要经过缓存层的东西直接把压力转到数据库上了,即使数据库扛得住,响应速度可能也不是很快,这样业务受影响了,如果有主从复制,一台从机挂了还可以在其他从机获取数据,所以,主从复制可以很好的解决单机器故障的问题,也就是高可用问题,但是其实还存在故障转移问题,后面我会写文章解决这个。 主从复制的简单流程 1.这里我把一系列操作举例放到实际业务流程中,比如有N多用户在频繁的读取数据。2.如果用户1先去redis-slave缓存里取数据。3.缓存中没有数据,去数据库里取。4.database proxy分发读取数据操作,请求落在mysql从节点上。5.database proxy将获取到的数据(这里假如获取到了,没有获取到直接返回给app)写入redis-master 主节点。6.同时把获取到的数据返回给用户。7.后面的N个用户获取相同的数据,压力被分散在不同的redis-slave上面,即使,某一个redis-slave宕机了,其他redis-slave还可以继续提供服务。注:主从复制数据的复制是单向的,只能由主节点到从节点默认情况下,每台redis服务器都是主节点,也就是主节点挂了其他从节点可以变成主节点(这个切换过程我附在下面);且一个主节点可以有多个从节点(或没有从节点),但一个从节点只能有一个主节点。 主从复制搭建从节点开启主从复制,有3种方式: 1、配置文件启用在从服务器的配置文件中加入:slaveof masterip masterport 2、启动命令启用redis-server启动命令后加入 --slaveof masterip masterport 3、客户端命令启用redis服务器启动后,直接通过客户端执行命令:slaveof masterip masterport,则该redis实例成为从节点。通过 info replication 命令可以看到复制的一些信息注 : Log 文件位置 vi /var/log/redis/redis.log 主从复制原理主从复制过程大体可以分为3个阶段:连接建立阶段(即准备阶段)、数据同步阶段、命令传播阶段。在从节点执行 slaveof 命令后,复制过程便开始运作,复制过程大致分为6个过程。 对照日志看一下日志如下 1保存主节点(master)信息。2从节点(slave)内部通过每秒运行的定时任务维护复制相关逻辑,当定时任务发现存在新的主节点后,会尝试与该节点建立网络连接从节点会建立一个 socket 套接字,从节点建立了一个端口为xxxxx的套接字,专门用于接受主节点发送的复制命令。从节点连接成功后打印如下日志: 如果从节点无法建立连接,定时任务会无限重试直到连接成功或者执行 slaveof no one 取消复制关于连接失败,可以在从节点执行 info replication 查看 master_link_down_since_seconds 指标,它会记录与主节点连接失败的系统时间。从节点连接主节点失败时也会每秒打印如下日志,方便发现问题: Error condition on socket for SYNC: {socket_error_reason}3发送 ping 命令连接建立成功后从节点发送 ping 请求进行首次通信,ping 请求主要目的如下:检测主从之间网络套接字是否可用。检测主节点当前是否可接受处理命令。如果发送 ping 命令后,从节点没有收到主节点的 pong 回复或者超时,比如网络超时或者主节点正在阻塞无法响应命令,从节点会断开复制连接,下次定时任务会发起重连。我估计好多人的学软件的第一课 都ping pong过把,这里就不解释了。。 4权限验证。如果主节点设置了 requirepass 参数,则需要密码验证,从节点必须配置 masterauth 参数保证与主节点相同的密码才能通过验证;如果验证失败复制将终止,从节点重新发起复制流程。先ping服务器和端口,通了肯定校验一下密码 ...

June 12, 2019 · 1 min · jiezi

centos7-搭建redis-集群

1.搭建单个redis1.1 安装必要插件yum install gcc-c++ -y 1.2 下载解压redis_version=5.0.5wget http://download.redis.io/releases/redis-${redis_version}.tar.gztar xzf redis-${redis_version}.tar.gzmv ./redis-${redis_version} /usr/local/rediscd /usr/local/redismake && make install1.3 运行redis-server/usr/local/redis/src/redis-server /usr/local/redis/redis.conf & 1.4 检查是否运行ps -ef | grep redis 1.4 进入客户端/usr/local/redis/src/redis-cli 1.5 退出redis-server 服务/usr/local/redis/src/redis-cli shutdown 或者pkill /usr/local/redis/src/redis-server 或者 kill + 进程号 2 搭建redis 集群2.1 准备工作mkdir -p /usr/local/redis-clustermkdir -p {8001,8002,8003,8004,8005,8006}2.1复制redis.confps:要复制6份cp /usr/local/redis/redis.conf /usr/local/redis-cluster/8001/ 2.2 修改redis.confport 8001 #端口cluster-enabled yes #启用集群模式cluster-config-file nodes.confcluster-node-timeout 15000 #超时时间appendonly yesdaemonize yes #后台运行protected-mode no #非保护模式pidfile /var/run/redis_8001.pidbind 127.0.0.1(去掉bind绑定访问ip信息)cluster-config-file nodes-8001.conf(集群节点信息文件,这里800x最好和port对应上)ps: 先修改一个 然后用sed命令修改命令如下:sed -i s/8001/8002/g /usr/local/redis-cluster/8001/redis.conf ...

May 28, 2019 · 1 min · jiezi

Redis集群容器化安装

1. Redis集群概述Redis作为当前非常热门的内存型数据结构存储,可用于数据存储,缓存和消息代理等。本文将讲解如何基于docker搭建Redis集群,Redis的集群设计包括两个部分:主从复制和哈希Slot。 1.1. 主从复制主从复制在数据库中很常见,一般用来做读写分离,Redis中也是如此。要求只有1个Master(主节点),可以有N个slaver(从节点),而且Slaver也可以有自己的Slaver,由于这种主从的关系决定他们是在配置阶段就要指定他们的上下级关系,而不是Zookeeper那种平行关系是自主推优出来的。 读写分离,Master只负责写和同步数据给Slaver,Slaver承担了被读的任务,所以Slaver的扩容只能提高读效率不能提高写效率。 Slaver先将Master那边获取到的信息压入磁盘,再load进内存,client端是从内存中读取信息的。当一个新的Slaver加入到这个集群时,会主动找Master来拜码头,Master发现新的小弟后将全量数据发送给新的Slaver,数据量越大性能消耗也就越大,所以尽量避免在运行时做Slaver的扩容。 优点:读写分离,通过增加Slaver可以提高并发读的能力。缺点:Master写能力是瓶颈,维护Slaver开销也总将会变成瓶颈。1.2. 哈希Slot哈希Slot名字上可能不好理解,其实就是数据库中的“水平划分”。如果你之前有了解过数据库的表分区的话,就会发现下来对于哈希Slot的描述,就和数据库表分区里面的“HASH分区”原理上大致相同。 对象保存到Redis之前先经过CRC16哈希到一个指定的Node上,例如图中Object4最终Hash到了Node1上。  每个Node被平均分配了一个Slot段,对应着0-16384,Slot不能重复也不能缺失,否则会导致对象重复存储或无法存储。  Node之间也互相监听,一旦有Node退出或者加入,会按照Slot为单位做数据的迁移。例如Node1如果掉线了,0-5640这些Slot将会平均分摊到Node2和Node3上,由于Node2和Node3本身维护的Slot还会在自己身上不会被重新分配,所以迁移过程中不会影响到 5641-16384 Slot段的使用。 优点:将Redis的写操作分摊到了多个节点上,提高写的并发能力,扩容简单。缺点:每个Node承担着互相监听、高并发数据写入、高并发数据读出,工作任务繁重。1.3. 合二为一看到这里大家也就发现了,主从和哈希的设计优缺点正好是相互弥补的,将二者结合在一起,就是Redis集群的终极形态,先Hash分逻辑节点,然后每个逻辑节点内部是主从,如图: 2. 集群安装默认我们已经有了docker环境,现在开始基于docker安装Redis集群。redis 5.0 版本之前,网上有很多教程都是通过redis-trib.rb 来创建集群,但是redis 5.0 版本以后,就只能通过 redis-cli 来实现。本文即通过 redis-cli 创建集群,因为redis集群的节点选举方式是需要半数以上的master通过,所以建议创建奇数个节点。本例中创建3个Master节点,并为每个Master节点各分配1个Slave节点。 2.1. 宿主机环境首先需要找一份原始的redis.conf文件,将其重命名为:redis-cluster.tmpl,并配置如下几个参数,此文件的目的是生成每一个redis实例的redis.conf: [root@kerry2 redis]# wget https://raw.githubusercontent.com/antirez/redis/5.0/redis.conf[root@kerry2 redis]# mv redis.conf redis-cluster.tmplvi redis-cluster.tmpl # bind 127.0.0.1protected-mode noport ${PORT}daemonize nodir /data/redisappendonly yescluster-enabled yescluster-config-file nodes.confcluster-node-timeout 15000然后执行下列脚本,给3个Master和3个Slave创建各自的挂载卷目录 # 创建 master 和 slave 文件夹for port in `seq 7700 7705`; do ms="master" if [ $port -ge 7703 ]; then ms="slave" fi mkdir -p ./$ms/$port/ && mkdir -p ./$ms/$port/data \ && PORT=$port envsubst < ./redis-cluster.tmpl > ./$ms/$port/redis.conf;done当前目录结构为 ...

May 27, 2019 · 3 min · jiezi

Redis哨兵模式心跳检测模式

首先,Redis搭建集群需要一主多从哨兵模式说白了就是在主服务器挂掉之后,将一个从服务器反客为主的一系列动作自动化,不用手动操作他会在后台有一个监控,监控当前的主机,巡逻主机下面的从机,如果某一时刻主机挂掉了,哨兵会通过心跳检测判断主服务器是否失去响应,如果半数以上的哨兵认为主服务器挂了,那么就会通过一种投票的机制从从机之中选举一台作为新的主机,并且,其余的从机将会连接到这个新的主机上面。

May 13, 2019 · 1 min · jiezi

给webflux增加缓存

上一篇中已经给用户管理增加了页面,看上去比较low,但是不犯该大家学习。今天增加redis来作为缓存。1 为什么要做缓存当用户访问量增大的时候,并发量也会增大,同一时间数据库查询的压力也会增大。尽管mongodb的查询速度已经很快了,但是也会出现性能瓶颈,一般会通过集群来解决。另一方面就是增加缓存,使得不用每次查询都要经过数据库。提高响应速度。 2 缓存策略在查询方法中先从缓存中取,如果没有则查询数据库在删除方法中,同时删除缓存中的值在更新操作中,同步修改缓存中的值3 添加依赖关于redis的安装,我之前的文章中已经讲过了,可关注我的公众号mike啥都想搞在教程中查看。补充:之前我们讲了如和设置redis的密码,可是都不生效。是因为启动方式不对,需要指定备至文件 redis-server.exe redis.windows.conf <!-- Spring Boot 响应式 Redis 依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis-reactive</artifactId> </dependency>设置redis连接: spring.redis.host=localhostspring.redis.port=6379spring.redis.password=mike123spring.redis.timeout=50004 改造service中的方法package com.mike.service;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.mongodb.core.MongoTemplate;import org.springframework.data.mongodb.core.query.Criteria;import org.springframework.data.mongodb.core.query.Query;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Service;import com.mike.dao.UserDao;import com.mike.po.User;import reactor.core.publisher.Flux;import reactor.core.publisher.Mono;/** * The class UserService.java */@Servicepublic class UserService { @Autowired private UserDao userDao; @Autowired private RedisTemplate redisTemplate; @Autowired private MongoTemplate mongoTemplate; public Mono<User> saveOrUpdateUser(User user){ //删除缓存 if (redisTemplate.hasKey("user_"+user.getId())) { redisTemplate.delete("user_"+user.getId()); } return userDao.save(user); } public Mono<User> findById(String id){ redisTemplate.opsForValue().set("test","test"); //先查找缓存 boolean hashKey = redisTemplate.hasKey("user_"+id); if (hashKey) { User u = (User) redisTemplate.opsForValue().get("user_"+id); return Mono.create(rs -> rs.success(u)); } Mono<User> m = userDao.findById(id); if (m!=null) { //更新缓存 m.subscribe(u -> { redisTemplate.opsForValue().set("user_"+u.getId(), u); }); } return m; } public Flux<User> findAll(){ return userDao.findAll().cache(); } public void deleteById(String id){ // 使用mongoTemplate来做删除 直接使用提供的删除方法不行 Query query = Query.query(Criteria.where("id").is(id)); mongoTemplate.remove(query, User.class); //userDao.deleteById(id); 这样无法删除,不知道为什么 //同时删除缓存 if (redisTemplate.hasKey("user_"+id)) { redisTemplate.delete("user_"+id); } }}总结一般我们会和@cacheable注解配合使用,但是在webflux中,我们需要手动编程来实现redis的数据存储。原因是Mono / Flux 对象没有实现 Serializable。上面用的是同步操作redis,还有一个异步操作ReactiveRedisTemplate,但是因为我们操作缓存的时候要先进行key的判断后在执行后续操作,这是同步过程。使用ReactiveRedisTemplate的话感觉操作比较麻烦,谁如果有好的写法可以一起交流下。 ...

May 10, 2019 · 1 min · jiezi

redis/codis

本文redis基于3.0单机redis/内存数据结构对象类型 编码方式 选择条件 编码详情 string int long类型整数 ptr直接指向整数 embstr动态字符串 长度<=44 数组形式组织sds,len/内存预分配/结尾有\0 动态字符串 长度>44 链表形式组织sds 列表 压缩列表 长度<64&&元素数<512 数组形式组织ziplist 双端链表 长度>=64&&元素数>=512 双端链表 quicklist 3.2版本后 xx 哈希 压缩列表 长度<64&&元素数<512 字典 长度>=64&&元素数>=512 两个table/若干桶 集合 整数集合 元素数<512 字典 元素数>=512 有序集合 压缩列表 长度<64&&元素数<128 分支最小元素/分值 跳表 长度>=64&&元素数>=128 字典+跳表 sds保持0仍然可以使用部分C语言字符串的一些函数Len 获取长度,保证二进制安全;多出剩余空间,每次检查free预分配内存,杜绝缓冲区溢出,惰性释放,减少修改字符串带来的内存重分配次数struct sdshdr { len = 11; free = 0; buf = “hello world\0”; // buf 的实际长度为 len + 1};分配内存,删除才释放 # 预分配空间足够,无须再进行空间分配 if (sdshdr.free >= required_len): return sdshdr # 计算新字符串的总长度 newlen = sdshdr.len + required_len # 如果新字符串的总长度小于 SDS_MAX_PREALLOC # 那么为字符串分配 2 倍于所需长度的空间 # 否则就分配所需长度加上 SDS_MAX_PREALLOC (1M)数量的空间 if newlen < SDS_MAX_PREALLOC: newlen = 2 else: newlen += SDS_MAX_PREALLOC # 分配内存 newsh = zrelloc(sdshdr, sizeof(struct sdshdr)+newlen+1)ziplist压缩列表使用特殊的编码来标识长度,再加上连续的内存,非常节约空间area |<———————————————– entry ———————–>|size 5 byte 2 bit 6 bit 11 byte +——————————————-+———-+——–+—————+component | pre_entry_length | encoding | length | content | | | | | |value | 11111110 00000000000000000010011101100110 | 00 | 001011 | hello world | +——————————————-+———-+——–+—————+Pre_entry_length1 字节:如果前一节点的长度小于 254 字节,便使用一个字节保存它的值。5 字节:如果前一节点的长度大于等于 254 字节,那么将第 1 个字节的值设为 254 ,然后用接下来的 4 个字节保存实际长度。encodinng/length/content以 00 、 01 和 10 开头的字符数组的编码方式如下:编码编码长度content 部分保存的值00bbbbbb1 byte长度小于等于 63 字节的字符数组。01bbbbbb xxxxxxxx2 byte长度小于等于 16383 字节的字符数组。10____ aaaaaaaa bbbbbbbb cccccccc dddddddd5 byte长度小于等于 4294967295 的字符数组。具体如何省内存:相比如双向,指针加sds的len,free结尾空,24+1+24(32位指针和Int都是4字节);压缩链表2/6字节。添加节点在前面,要更新pre_entry_length,next 的 pre_entry_length 只有 1 字节长,但编码 new 的长度需要 5 字节的时候可能连锁更新。next 的 pre_entry_length 有 5 字节长,但编码 new 的长度只需要 1 字节不做处理。dictMurmurHash2 32 bit 算法:这种算法的分布率和速度都非常好当插入元素要检查是否应该rehash。渐进式rehash或在rehash中直接操作Ht[1],否则ht[0]rehash触发条件:ratio=used/size自然 rehash : ratio >= 1 ,且变量 dict_can_resize 为真(非持久化中)。强制 rehash : ratio 大于变量 dict_force_resize_ratio (目前版本中, dict_force_resize_ratio 的值为 5 )当字典的填充率低于 10% 时, 程序就可以对这个字典进行收缩操作rehash过程ht[1]大小为0的两倍,rehashidx记录ht[0]的rehash索引位置。渐进式:在 rehash 开始进行之后(d->rehashidx 不为 -1), 每次执行一次添加、查找、删除操作, _dictRehashStep 都会被执行一次。每次执行 _dictRehashStep , ht[0]->table 哈希表第一个不为空的索引上的所有节点就会全部迁移到 ht[1]->table 。当 Redis 的服务器常规任务执行时, dictRehashMilliseconds 会被执行, 在规定的时间内, 尽可能地对数据库字典中那些需要 rehash 的字典进行 rehash , 从而加速数据库字典的 rehash 进程(progress)。整数集合这里的encoding是针对整个intset的。当某元素长度超过时要整体升级编码方式。全存Int因此不需要length。只会升级不会降级。升级过程:扩展内容。从后开始移动,将新值插入bit 0 15 31 47 63 95 127value | 1 | 2 | 3 | ? | 3 | ? | | ^ | | +————-+ int16_t -> int32_t跳表相比于平衡二叉树,不需要严格的平衡,随机层数https://www.cl.cam.ac.uk/teac…int zslRandomLevel(void) { int level = 1; while ((random()&0xFFFF) < (ZSKIPLIST_P * 0xFFFF)) //这里取小于0xffff的数,有0.25的概率level+1,因此level有1/4概率为2, 1/16的概率为3等等 level += 1; return (level<ZSKIPLIST_MAXLEVEL) ? level : ZSKIPLIST_MAXLEVEL;}ZSKIPLIST_MAXLEVEL=32ZSKIPLIST_P=1/4一个节点的平均层数 = 1/(1-p),Redis 每个节点平均指针数为1.33平均时间复杂度:O(logn)zscan在rehash的算法若进行了rehash,先遍历小hash表的v & t0->sizemask索引指向的链表,再遍历大hash表中该索引rehash后的所有索引链表。因为sizemask=sizehash-1因此低位全是1,索引取决于hashkey的低K位,同一个节点的hashkey不变,若原来为8位hash,hashkey为…abcd,原索引计算为bcd,扩展到16位hash,索引变为abcd,若要找出所有原bcd索引的链表,需要在新的hash中找0bcd,1bcd。因为要循环高位,所以这样从高位到低位反向来,例如:000 –> 100 –> 010 –> 110 –> 001 –> 101 –> 011 –> 111 –> 000 0000 –> 1000 –> 0100 –> 1100 –> 0010 –> 1010 –> 0110 –> 1110 –> 0001 –> 1001 –> 0101 –> 1101 –> 0011 –> 1011 –> 0111 –> 1111 –> 0000 当rehash时,可能会有重复,但不会有遗漏do { / Emit entries at cursor / de = t1->table[v & m1]; while (de) { fn(privdata, de); de = de->next; } /这里v从0开始,加1只取前m1-m0位,再与后m0位合并/ v = (((v | m0) + 1) & ~m0) | (v & m0); / Continue while bits covered by mask difference is non-zero */ } while (v & (m0 ^ m1)); //这里异或前m1-m0位全是1,直到while中的v为全1后加1变为全0这里为0退出。因此若原来v为110,8到32,rehash的表将遍历00110,01110,10110,11110然后是下一个v的确认 v |= m0; //m0低位全是0,>m0全是1,将超出m0的置1,只保留低m0位 v = rev(v); //二进制翻转 v++; //加1,正常进位 v = rev(v); //二进制翻转,这步之后相当于将v从高位+1向低位进位过期key删除惰性删除:在每次获取键或setnx时,调用expireIfNeeded ,过期删除定期删除: 遍历每个数据库,随机从db->expires中最多检查n个Key,过期删除。 每次遍历最多16个库,记录断点对于利用率低于1%数据库跳过检查,等待缩容后处理 运行超过时长退出每个数据库连续随机抽样过期key个数<n/4,则执行下一个数据库定期删除位置:redis的crontab执行过程中,删除执行时长,一般不超cpu的1/4时间(时间可以设置)事件驱动循环中执行,间隔20ms执行,超过10ms退出(时间可以设置)持久化中过期键处理:RDB 已过期键不会保存到新创建的EDB文件中,载入时主服务器模式过滤过期键,从服务模式不过滤;AOF 生成无影响,重写过滤过期键;复制时 主服务器删除过期键后会给从服务器发DEL,从服务器除遇到DEL不会删除过期键cache淘汰机制Redis 用来当做LRU cache的几种策略(使用内存已达到maxmemory):noeviction:无策略,直接返会异常allkeys-lru:所有key进行LRU,先移除最久使用的(当前时间,减去最近访问的时间)allkeys-random:随机移除volatile-random:只随机移除有过期时间的keyvolatile-tt: 优先移除最短ttl的有过期时间的key近似的LRU。采样逐出(默认5个里淘汰一个)。https://redis.io/topics/lru-c…4.0后引入LFU(least frequently):大概原理是次数达到一个阶段给个计数器初始值,随时间递减。采样取最小淘汰(源码LFULogIncr)单机redis/持久化RDB存储:将redis的内存状态保存到磁盘里面,三种方式: save(阻塞);bgsave(子进程负责创建RDB,不阻塞,期间拒绝save,bgsave,延迟bgrewriteaof);自动保存设置saveparams(校验dirty与saveparam.changes,lastsave与saveparam.seconds)载入:启动时自动执行,已开启AOF载入AOF,否则载入RDBRDB文件结构:REDIS | db_version | database(SELECTDB | db_number | [EXPIRETIME_MS | ms] TYPE | key | value) | EOF | check_sum,value根据TYPE和ENCODING结构不一样,比如无压缩字符串5 hello, 压缩后字符串 REDIS_RDB_ENC_LZF 6 21 “?AA"AOF存储:AOF持久化功能打开时,执行完一个写命令后,会以协议格式将命令追加到aof_buf缓冲区末尾。写入:在每个外层循环,处理过文件事件和时间事件后将缓冲区内容写入AOF文件同步:是否同步由appendfsync(always,everysec,no)决定,载入:创建伪客户端重新执行一遍AOF文件中的命令重写:BGREWRITEAFO命令,整合为不浪费空间的命令。原理:遍历数据库,读取键值,用一条set命令代替记录当前键值对。当键值超过了redis.h/REDIS_AOF_REWITE_ITEMS_PER_CMD常量的值,此键值对用多条命令来存储。 非阻塞实现:为了避免使用锁保证数据安全性,用子进程进行AOF重写,父进程继续处理命令,用重写缓冲区解决AOF文件与重写时间段后数据库状态不一致问题,在创建子进程后,所有写命令即写入AOF缓冲区又写入重写缓冲区,将重写缓冲区内容也写入AOF文件,缓冲区和信号等通过管道穿传递。集群主从模式 复制 SLAVEOF接收到SLAVEOF命令执行步骤:设置masterhost,masterport发送OK给客户端创建socket connect到主服务器,主服务器accept发送ping给主服务器,收到PONG继续否则断开重连主服务器requirepass,从服务器masterauth发送端口给主服务器 REPLCONF listening-pot <port-number>同步SYNC/PSYNC命令传播1.SYNC主服务器BGSAVE命令生成一个RDB文件,并使用缓冲区开始记录写命令BGSAVE结束后后发送RDB文件给从服务器从服务器载入主服务器将和缓冲区中写命令发送给从服务器,从服务器执行2.命令传播主服务器将所有写命令传播给从服务器每秒一次频率向主服务器发送REPLCONF ACK <replication_offset>进行心跳检测。检测网络和命令丢失主服务器配置min-slaves-to-write n, min-slaves-max-lag m当从服务器数量少于3个,或者延迟大于等于10将拒绝执行写命令根据replication_offset检测是否丢失命令,补发命令3.断线后重复制的优化 PSYNC 2.8版本以上redis使用PSYNC命令代替SYNC,断线后使用部分重同步,其他使用SYNC从服务器向主服务器发送命令:首次PSYNC ? -1 ,断线后重复制 PSYNC <runid> <offset>。主服务器返回:+FULLERSYNC <runid> <offset> ,+CONTINUE , -ERR无法识别从服务器重发SYNC命令4.上面2/3都是2.8以上才支持,需要用到replication_offset,复制积压缓冲区,服务运行ID主服务器每次向从服务器传播N个字节,将自己的复制偏移量加N。从服务器每次收到N个字节,将自己的复制偏移量加N主服务器进行命令传播时,不仅会将写命令发送给从服务器,还会将写命令写入复制积压缓冲区,先进先出从服务器会记录正在复制的主服务器的运行ID,网络断开后,从服务器向主服务器发送这个ID,主服务器根据自己运行ID决定是部分重同步还是完全同步哨兵哨兵系统也是一个或多个特殊的redis服务器,监视普通服务器,负责下线主服务器和故障转移1.启动(1)初始化服务器 sentinel不适用数据库,不再如RDB/AOF(2)将普通redis服务器使用的代码替换成sentinel专用代码 使用不同端口,命令集(只有PING,SENTINEL,INFO.SUBSCRIBE,UNSUBSCRIBE,PSUBSCRIBE,PUNSUBSCRIBE)(3)初始化sentinel状态 (4)根据给定的配置文件,初始化sentinel的监视主服务器列表 (5)创建连向主服务器的网络连接 命令连接,订阅连接(在建立后发送SUBSCRIBE sentinel:hello,sentinel需求通过接收其他服务器发来的频道信息发现未知的sentinel)2.获取主服务器信息sentinel默认10s一次向主服务器发INFO命令,获取更新sentinelRedisInstance的run_id,role,slaves的等3.获取从服务器信息sentinel会对主服务器的从服务器建立命令连接和订阅连接,也是10s/次发送INFO,更新slaves的sentinelRedisInstance4.向主服务器和从服务器发送信息sentinel默认2s/次用命令连接向主服务器和从服务器发送 PUBLISH sentinel:hello “<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port>,<m_epoch>“对每个与sentinel连接的服务器,即发送信息到频道又订阅频道接收信息。收到信息后提取参数检查若是自己的丢弃,否则根据信息更新主服务sentinelRedisInstance中的sentinels,创建连接向其他sentinel的命令连接5.检测主观下线状态sentinel默认1s/次的频率向所有主/从/sentinel服务器发送PING命令,有效回复为+PONG,-LOADING,-MASTERDOWN。当一个实例在down-after-milliseconds内,连续向sentinel返回无效回复,sentinel修改实例中flags加入|SRI_S_DOWN标识主观下线6.检查客观下线状态如果被sentinel判断为主观下线,sentinel当前配置纪元为0,将向其他sentinel发送命令 SENTINEL is-master-down-by-addr <ip> <port> <current_epoch> <runid>返回<down_state><leader_runid> // 代表命令仅用于检测主服务器的下线状态<leader_epoch> //前一个只为则为0当接收到认为下线的sentinel数量超过quorum(sentinel moniter 127.0.0.1 6379 2中2设置)则flags加|SRI_O_DOWN7.选举领头Sentinel(raft)也通过SENTINEL is-master-down-by-addr 看来是要分开进行,带runid。每个发现主服务器进入客观下线的sentinel向其他sentinal发送命令在一个配置epoch中将先到的设为局部领头,不能再更改。接收回复检查epoch的值和自己的相同就取出leader_runid,如果发现自己被半数以上选择,则成为领头,epoch+1如果在规定时间内未选举成功,epoch+1重新选举8.故障转移领头进行故障转移1) 选出新的主服务器 在线的,5s内回复过INFO的,与主服务器断开连接时间足够短,优先级高,复制偏移量大,runid最小 发送SLAVEOF no one 以1s/次(其他是10s/次)的频率向该服务器发送INFO。当role变为master时继续22) 向下线的主服务的其他从服务器发送SLAVEOF命令3) 向旧的主服务器发送SLAVEOF命令集群一个集群由多个node组成,通过分片进行数据共享,CLUSTER MEET <ip> <port>将各阶段加入到cluster1.启动一个node就是运行在集群模式下的redis服务器,在启动时若cluster-enabled是yes,则开启服务器的集群模式。节点继续使用单机模式的服务器组件,只是serverCron函数会调用集群模式特有的clusterCron函数,执行集群的常规操作,例如向集群的其他节点发送Gossip消息,检查节点是否断线,或者检查是否需要对下线的节点进行故障转移操作等。节点数据库和单机完全相同除了智能使用0号出具库这和个限制,另外除了将键值对保存在数据库里边之外,节点还会用clusterState中的slots_to_keys跳跃表来保存槽和键,方便对属于某槽所有数据库键进行批量操作2.客户点向A发送CLASTER MEET <B.ip> <B.port>A创建B的clusterNode加入到clusterState.nodes中发送MEET给BB返回PONGA发送PING,握手完成A将B的信息通过Gossip传播给急群众其他节点3.槽指派,向节点发送CLUSTER ADDSLOTS <slot> [slot …]遍历所有输入槽,如果有已经指派的返回错误,如果都没有指派,再遍历一次:更新当前lusterState.slots[i]设为Myself更新自己clusterNode 的slots,numslots属性将自己的slots数组通过消息发送给集群中其他节点,A收到B后会把自己的clusterState.nodes中查找B对应的clusterNode结构,更新其中的slots数组;更新clusterNode中的slots,numslots属性维护整体slots目的:查某个槽被哪个节点处理维护单个节点slots目的:将某节点的所有槽指派信息发送给其他。4.执行命令在所有的槽都指派完毕之后,集群就会进入上线状态,这是客户端就可以向集群中的节点发送数据命令了。客户端向节点发送与数据库键相关的命令时,如果键所在的槽正好就指派给了当前节点,那么节点就直接执行命令;如果键所在的槽并没有指派给当前节点,那么节点返回一个MOVED错误,指引客户端(redirect)至正确节点,并再次发送之前想要执行的命令。1)计算键属于哪个槽 CLUSTER KEYSLOT [key] CRC16(KEY) & 163832) 若计算的i不对应Myself 返回MOVED <slot> <ip>:<port>3) 客户端根据MOVED错误,转向节点重新发送命令5.重新分片redis集群的重新分片操作可以将任意数量已经指派给某个节点的槽改为指派给另一个节点,并且相关的槽所属的键值对也会从源节点转移到目标节点。可以online下。redis的重新分片操作时由redis的集群管理软件redis-trib负责执行的,redis提供了进重新分片所需的所有命令,而redis-trib则通过向源节点和目标节点发送命令来进行重新分片操作。步骤如下: 1)redis-trib对目标节点发送CLUSTER SETLOT < slot > IMPORTING < source_id> 准备好导入2)redis-trib对源节点发送CLUSTER SETLOT < slot> MIGRATING < target_id > 准备好迁移3)redis-trib对源节点发送CLUSTER GETKEYSINSLOT < slot > < count > 获得最多count个属于槽slot的键值对的键名4)对3中每个键名,redis-trib对源节点发送MIGRATE < key_name> 0 < timeout> 迁移5)重复3和4,知道槽中的键值对迁移到目标节点 6)redis-trib向任意节点发送CLUSTER SETLOT < slot> NODE < target_id>,将槽指派给目标节点,并通过消息告知整个集群,最终所有节点都会知道槽slot已经指派给了目标节点。6.ASK错误 处理正在迁移中槽错误接到ASK错误的客户端会根据错误提供的IP地址和端口号,转向至正在导入槽的目标节点,然后向目标节点发送一个ASKING命令,再重新发送原本想要执行的命令。ASKING命令加client.flags|=REDIS_ASKING。正常客户端发送一个关于槽i的命令,而槽i又没有指派给这个节点的话,会返回MOVED错误,设置了REDIS_ASKING后,则会破例执行MOVED错误代表槽的负责权已经从一个节点转移到另一个,每次遇到都自动发到MOVED指向的节点。而ASK只是迁移槽中临时的,下次对下次有影响7.复制与故障转移1)复制redis集群中的节点分为主节点和从节点,其中主节点用于处理槽,而从节点则用于复制主节点,并在被复制的主节点下线之后代替下线的主节点继续处理命令请求。设置从节点:CLUSTER REPLICATE < node_id> 让接收命令的节点成为node_id的从节点接收到该命令的节点首先会在自己的clusterState.nodes字典里面找到node_id对应的节点clusterNode结构,并将自己的clusterState.myself.slaveof指针指向这个结构;节点会修改自己clusterState.myself.flags中的属性,关闭原来的REDIS_NODE_MASTER标识,打开REDIS_NODE_SLAVE标识;调用复制代码,相当于向从节点发送SLAVEOF <master_ip> <master_port>。2)故障检测集群中的每个节点都会定期地向集群中的其他节点发送PING消息,如果规定时间内没有返回PONG,发送消息的节点就会把接受消息的节点标记为疑似下线PFAIL。clusterNode的flags标识(REDIS_NODE_PFAIL)集群中各节点通过互相发送消息的方式交换集群中各个节点的状态信息,当A通过消息得知B认为C进入疑似下线,A在自己clusterState.nodes中找到C对应的clusterNode结构将B的下线报告添加到该clusterNode的fail_reports中半数以上主节点都报告x意思下线,则标记为FAIL,将主节点x标记为下线的节点向集群广播FAIL消息,所有接受者都将x标记为FAIL3)故障转移当一个从节点发现自己复制的主节点进入了下线状态的时候,从节点将开始对下线主节点进行故障转移,步骤如下: 选举新的主节点 新的主节点执行SLAVEOF no one命令,成为新的主节点 新的主节点将下线主节点的槽指派给自己 新的主节点向集群广播PONG消息,表明自己接管了原来下线节点的槽 新的节点开始接收和自己复制处理槽有关的命令请求。选举新的主节点 同样基于Raft实现 从节点广播CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST,未投过票的主节点返回CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK。配置纪元自增,半数以上。8.消息消息由消息头(header)和消息正文(data)组成。 cluster.h/clusterMsg结构表示消息头,cluster.h/clusterMsgData联合体指向消息的正文。节点消息分为5类:1)MEETA接到客户端发送的CLUSTER MEET B命令后,会向B发送MEET消息,B加入到A当前所处的集群里2)PING每个节点默认1s/次从已知节点随机选5个,对最长时间未发送PING的节点发送PING,或当有节点超过cluster-node-timeout的一半未收到PONG也发送PING,检查节点是否在线3)PONG确认MEET,PING;或主动发送让集群中其他节点立即刷新该节点信息,比如故障转移操作成功后以上三种消息都使用Gossip协议交换各自不同节点的信息,三种消息的正文都是由两个cluster.h/clusterMsgDataGossip结构组成发送者从自己已知节点列表中随机选择两个节点(主、从),保存在两个clusterMsgDataGossip结构中。接收者发现节点不在已知节点列表则与节点握手,否则更新信息。注意PONG也会带两个回去4)FAIL主节点判断FAIL状态,广播clusterMsgDataFail。(gossip随机会慢)5)PUBLISH当节点收到一个PUBLISH,会执行这个命令并向集群中广播一条PUBLISH。即向集群中某个节点发送PUBLISH <channel> <message>将导致集群中所有节点都向channel频道发送message消息。要让集群所有节点都执行相同命令,可以广播,但还要用PUBLISH发是因为直接广播这种做法,不符合redis集群的“各个节点通过发送和接收消息来进行通信”这一规则。clusterMsgDataPublish原生Gossip过程是由种子节点发起,当一个种子节点有状态需要更新到网络中的其他节点时,它会随机的选择周围几个节点散播消息,收到消息的节点也会重复该过程,直至最终网络中所有的节点都收到了消息。这个过程可能需要一定的时间,由于不能保证某个时刻所有节点都收到消息,但是理论上最终所有节点都会收到消息,因此它是一个最终一致性协议。每次散播消息都选择尚未发送过的节点进行散播(有冗余)codisCodis 3.x 由以下组件组成:Codis Server:基于 redis-3.2.8 分支开发。增加了额外的数据结构,以支持 slot 有关的操作以及数据迁移指令。具体的修改可以参考文档 redis 的修改。Codis Proxy:客户端连接的 Redis 代理服务, 实现了 Redis 协议。 除部分命令不支持以外(不支持的命令列表),表现的和原生的 Redis 没有区别(就像 Twemproxy)。对于同一个业务集群而言,可以同时部署多个 codis-proxy 实例;不同 codis-proxy 之间由 codis-dashboard 保证状态同步。Codis Dashboard:集群管理工具,支持 codis-proxy、codis-server 的添加、删除,以及据迁移等操作。在集群状态发生改变时,codis-dashboard 维护集群下所有 codis-proxy 的状态的一致性。这里和zk交互保证一致,数据迁移,主从增加等都通过这个对于同一个业务集群而言,同一个时刻 codis-dashboard 只能有 0个或者1个;所有对集群的修改都必须通过 codis-dashboard 完成。Codis Admin:集群管理的命令行工具。可用于控制 codis-proxy、codis-dashboard 状态以及访问外部存储。Codis FE:集群管理界面。多个集群实例共享可以共享同一个前端展示页面;通过配置文件管理后端 codis-dashboard 列表,配置文件可自动更新。Storage:为集群状态提供外部存储。提供 Namespace 概念,不同集群的会按照不同 product name 进行组织;目前仅提供了 Zookeeper、Etcd、Fs 三种实现,但是提供了抽象的 interface 可自行扩展。整个集群总共 1024 个 slot (槽)每个 redis group 对应着 个 slot range (如: 0127), slot range 存储在 zk 中每个 key 请求时,crc32(key) % 1024 映射到 个 slot_id,proxy 通过 slot_id 找到对应的 redis group 读写 数据集群的扩容缩容,都是通过 slot 迁移来实现,是 个两 段式提交的过程。迁移时并不 影响线上单 key 读写访问1.数据迁移migrateslot(slotid,from,dest)一台机器上有很多redis实例(一个实例一个groupid),迁移:sid=>gid,多个迁移会创建任务队列放zkzookeeper同步配置,放迁移队列,提供可靠的多rpc,每个codis-proxy中有配置,sid=>gid/gid=>sid的,zk 除了存储路由信息,同时还作为一个事件同步的媒介服务,比如变更master 或者数据迁移这样的事情,需要所有的proxy 通过监听特定zk 事件来实现。可以说zk 被我们当做了一个可靠的rpc 的信道来使用。因为只有集群变更的admin 时候会往zk 上发事件,proxy 监听到以后,回复在zk 上,admin 收到各个proxy 的回复后才继续。dash专门开启一个协程从zk的任务队列中获取slot迁移,一个一个进行迁移有两个阶段,第一阶段状态改为pre_m。若proxy都确认,将状态改m。向所在的redis-server发送迁移命dash修改到zk,proxy监听回复到zk,dash监听zk进行状态机的变更,同步配置做配置的下发同步三次配置更改同步:第一次fillslot将BackendAddr由OriginGroupMaster改写为TargetGroupMaster,这一步操作相当于读写临界区资源BackendAddr,所以必须带写锁,而MigrateFrom只是顺便改了而已第二次fillslot相当于取消了第一次的写锁,但是如果Promoting在执行的话,不应该取消Promoting设置的锁第三次fillslot取消了MigrateFrom迁移过程中读写请求:分发请求时存在一个prepare方法,这一步会获取到该key对应的slot是否有MigrateFrom如果有的话,会使用SLOTSMGRTTAGONE将这个key从MigrateFrom代表的redis强制迁移到Backend代表的redis里去,迁移完成以后再去访问Backend获得这个ke这样就能解决,如果被迁移的slot中的key,刚好被访问时,产生的一致性问题了迁移与主从切换的冲突:migrate基本上不依赖lock,当发生数据冲突时,由强制迁移这个key来解决一致性问题,和lock基本上没太大关系,lock主要是针对promote设计的2.主从切换哨兵模式3.自平衡算法1.使用自动负载均衡需要满足一个前提:所有codis-server的分组master必须配置maxmemory。 2.各组codis-server分配多少个slot是由其maxmemory决定。比如:A组maxmemory为10G, B组maxmory为1G,进行自动均衡处理后,A组分配的slot会是B组的10倍。 3.自动负载均衡并不会达到绝对意义上的均衡,其只做到maxmemory与分配的slot个数的比例均衡。无法达到操作次数的均衡。 4.自动负载均衡的处理过程中,如果发现存在maxmemory与分配的slot个数比例不均衡时,则会进行发起slot迁移的操作。达到均衡目的的前提下,此过程中会做到尽量减少slot的迁移。codis和twemproxy最大的区别有两个:一个是codis支持动态水平扩展,对client完全透明不影响服务的情况下可以完成增减redis实例的操作;一个是codis是用go语言写的并支持多线程而twemproxy用C并只用单线程。 后者又意味着:codis在多核机器上的性能会好于twemproxy;codis的最坏响应时间可能会因为GC的STW而变大,不过go1.5发布后会显著降低STW的时间;如果只用一个CPU的话go语言的性能不如C,因此在一些短连接而非长连接的场景中,整个系统的瓶颈可能变成accept新tcp连接的速度,这时codis的性能可能会差于twemproxy。某自研集群数据量的限制。1024.变更比想象的频繁zk依赖,zk出问题,路由错误无法发现,redis没有路由信息 =>某自研集群基于redis集群加一层proxy,客户端与集群连接,单进程单线程(mget岂不是很慢=》用了异步 epoll的模式,发完了不等)1.建立链接模块。与客户端和redis-clsuster建立链接。2.命令处理模块。区分单次操作命令和多次操作命令(Mget/Mset)。3.路由模块。维护redis-cluster的nodes路由信息。(原来client随便打,错了moved这记录了下)基本都靠redis-cluster关于双活方案1:方案2:codis收到命令后发送给两个机房的redis方案3:redis一些优化和问题白名单动能可以用Bit-array/bitmaps代替set实现setex代替set,expire,有两次网络内存提出策略:近似lru,对性能妥协,qps太大时也淘汰不过来,可能会有一些写不进来大key无法分片导致无法水平扩容来解决问题。所以一个key不要存特别大的数据,原则上一个key<100k同一个实例,同一个Key,QPS特别大的时候,仍然扛不住,要避免热key,静态配置类的Key迁出redis放代码里,其他热key要考虑高并发写入的问题。4万的pks针对同一key扛不住,可以转为双缓存或者改一个有常驻内存的语言写。Php的双缓存可以通过fpm的apcu,redis,mysql来做。应用redis时要预测qps,数据容量,内存中的量和磁盘代码可能还不一致。热key请求多,部分key集中于同一机器,无法通过增加机器解决=》1. 读:本地缓存(客户端本地 redis/程序/mysql 服务端本地 就是副本扩展多份多机器) 需要提前获取热点,容量有限,不一致时间长,热点key遗漏。 写:取租约,限流 2.读写分离。读复制多份,负载均衡未知热key热点key的采样。线程抢锁开始采样,外层N次,分1024个桶,看每个桶中是否有热点,用标准差。再对桶进行M此采样,选出热点key.客户端从服务器获取这批key进行本地缓存写操作删除本地缓存。根据容量或者过期淘汰,在过期淘汰时为防止击穿,首次发现过期后延长一点过期时间,只有首次的去获取新的key更新。 ...

April 8, 2019 · 4 min · jiezi

Redis持久化存储详解(一)

为什么要做持久化存储?持久化存储是将 Redis 存储在内存中的数据存储在硬盘中,实现数据的永久保存。我们都知道 Redis 是一个基于内存的 nosql 数据库,内存存储很容易造成数据的丢失,因为当服务器关机等一些异常情况都会导致存储在内存中的数据丢失。持久化存储分类在 Redis 中,持久化存储分为两种。一种是 aof 日志追加的方式,另外一种是 rdb 数据快照的方式。RDB持久化存储什么是RDB持久化存储RDB持久化存储即是将redis存在内存中的数据以快照的形式保存在本地磁盘中。.RDB持久化存储分为自动备份和手动备份1.手动备份通过 save 命令和 bgsave 命令。save是同步阻塞,而 bgsave 是非阻塞(阻塞实际发生在 fork 的子进程中)。因此,在我们实际过程中大多是使用bgsave命令实现备份.redis> SAVEOKredis> BGSAVEBackground saving started2.自动备份a.修改配置项 save m n即表示在 m 秒内执行了 n 次命令则进行备份.b.当Redis 从服务器项主服务器发送复制请求时,主服务器则会使用 bgsave命令生成 rbd 文件,然后传输给从服务器.c.当执行 debug reload 命令时也会使用 save 命令生成rdb文件.d.当使用 shutdown 命令关掉服务时,如果没有启用 aof方式实现持久化则会采用bgsave的方式做持久化.同时shutdown后面可以加备份参数[nosave|save].bgsave持久化存储实现原理1.执行bgsave命令,Redis父进程判断当前是否存在正在执行的子进程,如果存在则直接返回.2.父进程fork一个子进程(fork的过程中会造成阻塞的情况),这个过程可以使用info stats命令查看latest_fork_usec选项,查看最近一次fork操作小号的时间,单位是微妙.3.父进程fork完之后,则会返回Background saving started信息提示,此时fork阻塞解除.4.fork出的子进程开始根据父进程内存数据生成临时的快照文件,然后替换原文件.使用lastsave命令可以查看最后一次生成rdb的时间,对应info的rdb_last_save_time选项.5.当备份完毕之后向父进程发送完成信息,具体可以见info Persistence下的rbd_*选项.RDB持久化的优势与劣势优势:1.文件实现的数据快照,全量备份,便于数据的传输.比如我们需要把A服务器上的备份文件传输到B服务器上面,直接将rdb文件拷贝即可.2.文件采用压缩的二进制文件,当重启服务时加载数据文件,比aof方式更快.劣势:1.rbd采用加密的二进制格式存储文件,由于Redis各个版本之间的兼容性问题也导致rdb由版本兼容问题导致无法再其他的Redis版本中使用.2.时效性差,容易造成数据的不完整性.因为rdb并不是实时备份,当某个时间段Redis服务出现异常,内存数据丢失,这段时间的数据是无法恢复的,因此易导致数据的丢失.RDB文件常见的处理方式1.当遇到磁盘写满情况,可以使用如下命令来切换存储磁盘// dirName则是新的存储目录名(该方式同样适用于aof格式)config set dir dirName2.文件压缩处理,虽然对CPU具有消耗,但是减少体积的暂用,同时做文件传输(主从复制)也减少消耗.// 修改压缩开启或关闭config set rdbcompression yes|no3.rbd备份文件损坏检测.可以使用redis-check-rdb工具检测rdb文件,该工具默认在/usr/local/bin/目录下面.[root@syncd redis-data]# /usr/local/bin/redis-check-rdb ./6379-rdb.rdb [offset 0] Checking RDB file ./6379-rdb.rdb[offset 26] AUX FIELD redis-ver = ‘5.0.3’[offset 40] AUX FIELD redis-bits = ‘64’[offset 52] AUX FIELD ctime = ‘1552061947’[offset 67] AUX FIELD used-mem = ‘852984’[offset 83] AUX FIELD aof-preamble = ‘0’[offset 85] Selecting DB ID 0[offset 105] Checksum OK[offset 105] \o/ RDB looks OK! \o/[info] 1 keys read[info] 0 expires[info] 0 already expiredAOF持久化存储AOF持久化存储是什么AOF持久化存储便是以日志的形式将redis存储在aof_buf缓冲区中的数据写入到磁盘中。简而言之,就是记录redis的操作日志,将redis执行过的命令记录下载,当我们需要数据恢复时,redis去重新执行一次日志文件中的命令.如何配置持久化存储// 将no改为yes,控制aof开启与否appendonly no// 控制aof文件名称,存储的目录便是dir配置项appendfilename “appendonly.aof”// 三种备份策略(三者只需要开启以一个即可)# appendfsync always // 命令写入立即写入磁盘appendfsync everysec // 每秒实现文件的同步,写入磁盘# appendfsync no // 随机进行文件的同步,同步操作则交给操作系统来负责,通常时间是最长30sAOF持久化存储实现原理aof日志追加方式实现持久化存储,需要经历如下四个过程.命令写入->文件同步->文件重写->文件重载1.redis命令写入,此时会将redis命令写入aof_buf换从区.2.缓冲区中数据根据备份策略实现写入日志文件.3.当aof的文件越来越庞大,会根据我们的配置策略来实现aof的重写,实现文件的压缩,减少体积.4.当redis重新启动时,在去重写加载aof文件,达到数据恢复的目的.命令写入命令写入主要是将文件执行过的命令写入到日志文件中.并且日志文件尊徐文本协议格式,下面示例代码便是aof日志文件中存储的内容格式.*3\r\n$3\r\nset\r\n$5\r\nhello\r\n$5\r\nworld\r\naof采用的是文本协议格式。主要是原因根据资料提示,可以能使由于如下原因.1.文本协议的兼容性好.前面我们提及到了rdb文件是进行二进制加密,可能不同版本之间会出现不兼容的情况,采用文本协议可以加避免该问题。同时文本协议也可以减少跨平台使用所带来的诸多问题.2.可读性强.由于aof是将命令写入文件中,我们可以直接查看命令内容,同时也可以修改日志文件内容.3.开启aof后,所有的文件文件都包含追加操作,直接采用文本协议,减少二次开销(这一点,个人不是很理解.因为我们的aof是保存的是命令,当我们再次去加载的时候,会去执行一次里面的命令,当文件大的时候应该是比较耗时的吧。如果没有做好文件重写策略,大量重复无效的命令执行,对于二进制加密的rdb格式,不需要再去转换,这一点确实可以减少二次开销).文件写入文件写入是将aof_buf缓冲区的命令写入到文件中.文件写入的策略有如下三种方式配置项配置说明always命令写入到aof_buf缓冲区中之后立即调用系统的<font color=‘red’>fsync操作</font>同步到aof文件中,fsync完成后线程返回.everysec命令写入到aof_buf缓冲区后<font color=‘red’>每隔一秒</font>调用系统的<font color=‘red’>write操作</font>,write完成后线程返回.no命令写入aof_bug缓冲区后调用系统write操作,不对aof文件做fsync同步,同步硬盘操作由<font color=‘red’>系统操作</font>完成,时间一般最长为30s.系统调用write和fsync说明:·write操作会触发延迟写( delayed write) 机制。 Linux在内核提供页缓冲区用来提高硬盘IO性能。 write操作在写入系统缓冲区后直接返回。 同步硬盘操作依赖于系统调度机制, 例如: 缓冲区页空间写满或达到特定时间周期。 同步文件之前, 如果此时系统故障宕机, 缓冲区内数据将丢失.·fsync针对单个文件操作( 比如AOF文件) , 做强制硬盘同步, fsync将阻塞直到写入硬盘完成后返回, 保证了数据持久化.文件写入策略分析配置为always时, 每次写入都要同步AOF文件, 在一般的SATA硬盘上, Redis只能支持大约几百TPS写入, 显然跟Redis高性能特性背道而驰,不建议配置.配置为no。由于操作系统每次同步AOF文件的周期不可控, 而且会加大每次同步硬盘的数据量, 虽然提升了性能, 但数据安全性无法保证.配置为everysec。是建议的同步策略, 也是默认配置, 做到兼顾性能和数据安全性。 理论上只有在系统突然宕机的情况下丢失1秒的数据.文件重载1.为什么要文件做文件重载操作?由于aof采用的是日志追加,我们redis命令不断的写入,aof文件的体积也也会不断的增加.因此redis引入了aof重写机制达到减小aof文件体积.<font color=“blue”>aof文件重写是把redis进程内的数据转换为写命令同步到新的aof文件的过程(这一点其实不是特别明白,文件重写不是针对aof文件文件做操作的吗?为什么这里是将redis进程内的数据转换为命令写入文件,这里的进程内的数据不是太明白,还有待深入研究.个人理解的就是将旧的aof文件内容根据重写策略,进行优化生成新的aof文件。).</font>2.文件重载有什么好处?文件重载主要优化的地方有如下三点。使用文件重载既可以减少文件的体积,同时去掉了一些无效的操作,可以加快文件重载效率.a.将一些在进程内无效的数据不在写入新的文件.如过期的键.b.去掉一些无效的命令.如del key1.c.简化操作.如lpush list a,lpush list b.直接可以简化为lpush list a b.3.文件重载由那些方式?文件重载有自动触发机制和手动触发机制.手动触发机制:直接使用bgrewriteaof命令即可.该命令在fork子进程的时候会发生阻塞.自动触发机制:auto-aof-rewrite-min-size:aof重写时文件最小的体积,默认的是64M.auto-aof-rewrite-percentage:代表当前AOF文件空间( aof_current_size) 和上一次重写后AOF文件空间( aof_base_size) 的比值.自动触发时机=aof_current_size>auto-aof-rewrite-minsize&&( aof_current_size-aof_base_size) /aof_base_size>=auto-aof-rewritepercentage其中aof_current_size和aof_base_size可以在info Persistence统计信息中查看.4.文件重载实现的原理是怎样的?1.执行重写命令,判断是否存在子进程。如果已经有子进程在进行aof重写,则会提示如下信息.ERR Background append only file rewriting already in progress如果已经存在子进程在进行bgsave操作,重写命令会延迟到bgsave命令完成之后进行,会返回如下信息.Background append only file rewriting scheduled2.父进程会fork一个子进程,在fork子进程的过程中会造成阻塞.3.fork子进程结束阻塞解除,进行其他新的命令操作.新的命令依旧根据文件写入策略同步数据,保证aof机制正确进行(图中3.1).4.子进程在进行写的过程中,由于fork操作运用的是写时复制技术,子进程只能共享fork操作时内存保留的数据,新的数据是无法操作的.父进程在这过程中仍然在响应其他的命令,于是Redis会使用aof重写缓存区来保存这部分新的数据(图中3.2).5.子进程进行根据重写规则将数据写入到新的aof文件中,并且每次写入有大小限制,通过aof-rewrite-incremental-fsync配置项来控制,默认是32M,这样可以见减少单次刷盘(I/O写)造成硬盘阻塞.6.子进程在完成重写之后,会向父进程发送信息,父进程更新统计信息.可参看info persistence下的aof_*相关统计。7.父进程会把新写入存在aof重写缓冲区的数据写入到aof文件中(图5.2).8.将新的aof文件替换掉旧的aof文件.<font color=‘blue’>在第3和4中,其实不是特别理解.不理解的是为什么父进程在响应新的命令会写入旧的aof文件,还要aof重写缓存区.个人理解的是,父进程在进行新命令写入处理的策略是,按照正常的备份策略写入旧的aof的同时也把新的命令写入重写缓冲区,在第5.2中将这部分新的数据写入到新的aof文件中,这样保证数据的完整性.</font>文件重载文件重载就是将文件重新加入到redis服务中.比如redis服务重启用于数据恢复.redis的重载机制非常完善,具体流程如下.AOF文件常见的问题处理1.文件损坏我们在加载损坏的文件是可能提示如下信息.Bad file format reading the append only file: make a backup of your AOF file,then use ./redis-check-aof –fix <filename>此时我们可以使用redis-check-aof –fix命令进行修复(记得对文件做个备份).修复后使用diff-u进行数据对比,找出部分丢失的数据.2.文件加载不完整这可能是数据在备份的时候,redis服务异常,导致备份不完整.可以使用redis的aof-load-truncated兼容该异常AOF的优缺点优点:多种文件写入(fsync)策略.数据实时保存,数据完整性强.即使丢失某些数据,制定好策略最多也是一秒内的数据丢失.可读性强,由于使用的是文本协议格式来存储的数据,可有直接查看操作的命令,同时也可以手动改写命令.缺点:文件体积过大,加载速度比rbd慢.由于aof记录的是redis操作的日志,一些无效的,可简化的操作也会被记录下来,造成aof文件过大.但该方式可以通过文件重写策略进行优化.选择AOF还是RDB进行数据的持久化1.针对不同的情况来选择,建议使用两种方式相结合.2.针对数据安全性、完整性要求高的采用aof方式.3.针对不太重要的数据可以使用rdb方式.4.对于数据进行全量备份,便于数据备份的可以采用rdb方式.原文转自微信公众号:浪子编程走四方 ...

March 10, 2019 · 1 min · jiezi

codis proxy处理流程

proxy启动cmd/proxy/main.go文件解析配置文件之后重点是proxy.New(config)函数该函数中,首先会创建一个Proxy结构体,如下:type Proxy struct { mu sync.Mutex … config *Config router *Router //Router中比较重要的是连接池和slots … lproxy net.Listener //19000端口的Listener ladmin net.Listener //11080端口的Listener …}然后起两个协程,分别处理11080和19000端口的请求 go s.serveAdmin() go s.serveProxy()我们重点看s.serveProxy()的处理流程,即redis client连接19000端口后proxy如何分发到codis server并且将结果返回到客户端Proxy处理s.serverProxy也启动了两个协程,一个协程对router中连接池中的连接进行连接可用性检测,另一个协程是一个死循环,accept lproxy端口的连接,并且启动一个新的Session进行处理,代码流程如下: go func(l net.Listener) (err error) { defer func() { eh <- err }() for { c, err := s.acceptConn(l)//accept连接 if err != nil { return err } NewSession(c, s.config).Start(s.router)//启动一个新的session进行处理 } }(s.lproxy)//s为proxy,s.lproxy即19000端口的监听首先介绍一下Request结构体,该结构体会贯穿整个流程type Request struct { Multi []*redis.Resp //保存请求命令,按redis的resp协议类型将请求保存到Multi字段中 Batch *sync.WaitGroup //返回响应时,会在Batch处等待,r.Batch.Wait(),所以可以做到当请求执行完成后才会执行返回函数 Group *sync.WaitGroup Broken *atomic2.Bool OpStr string OpFlag Database int32 UnixNano int64 *redis.Resp //保存响应数据,也是redis的resp协议类型 Err error Coalesce func() error //聚合函数,适用于mget/mset等需要聚合响应的操作命令}Start函数处理流程如下: tasks := NewRequestChanBuffer(1024)//tasks是一个指向RequestChan的指针,RequestChan结构体中有一个data字段,data字段是个数组,保存1024个指向Request的指针 go func() { s.loopWriter(tasks)//从RequestChan的data中取出请求并且返回给客户端,如果是mget/mset这种需要聚合相应的请求,则会等待所有拆分的子请求执行完毕后执行聚合函数,然后将结果返回给客户端 decrSessions() }() go func() { s.loopReader(tasks, d)//首先根据key计算该key分配到哪个slot.在此步骤中只会将slot对应的连接取出,然后将请求放到连接的input字段中。 tasks.Close() }()可以看到,s.loopWriter只是从RequestChan的data字段中取出请求并且返回给客户端,通过上文Request结构体的介绍,可以看到,通过在request的Batch执行wait操作,只有请求处理完成后loopWriter才会执行下边我们看loopReader的执行流程 r := &Request{} //新建一个Request结构体,该结构体会贯穿请求的始终,请求字段,响应字段都放在Request中 r.Multi = multi r.Batch = &sync.WaitGroup{} r.Database = s.database r.UnixNano = start.UnixNano() if err := s.handleRequest(r, d); err != nil { //执行handleRequest函数,处理请求 r.Resp = redis.NewErrorf(“ERR handle request, %s”, err) tasks.PushBack(r) if breakOnFailure { return err } } else { tasks.PushBack(r) //如果handleRequest执行成功,将请求r放入tasks(即上文的RequestChan)的data字段中。loopWriter会从该字段中获取请求并且返回给客户端 }看handleRequest函数如何处理请求,重点是router的dispatch函数func (s *Router) dispatch(r *Request) error { hkey := getHashKey(r.Multi, r.OpStr)//hkey为请求的key var id = Hash(hkey) % MaxSlotNum //hash请求的key之后对1024取模,获取该key分配到哪个slot slot := &s.slots[id] //slot都保存在router的slots数组中,获取对应的slot return slot.forward(r, hkey)//执行slot的forward函数}forward函数调用process函数,返回一个BackendConn结构,然后调用其PushBack函数将请求放入bc.input中func (d *forwardSync) Forward(s *Slot, r *Request, hkey []byte) error { s.lock.RLock() bc, err := d.process(s, r, hkey) //返回一个连接,并且将请求放入BackendConn的input中 s.lock.RUnlock() if err != nil { return err } bc.PushBack(r) return nil}bc.PushBack(r)函数如下:func (bc *BackendConn) PushBack(r *Request) { if r.Batch != nil { r.Batch.Add(1) //将请求的Batch执行add 1的操作,注意前文中的loopWriter会在Batch处等待 } bc.input <- r //将请求放入bc.input channel}至此可以看到,Proxy的处理流程loopWriter->RuquestChan的data字段中读取请求并且返回。在Batch处等待loopReader->将请求放入RequestChan的data字段中,并且将请求放入bc.input channel中。在Batch处加1很明显,Proxy并没有真正处理请求,肯定会有goroutine从bc.input中读取请求并且处理完成后在Batch处减1,这样当请求执行完成后,loopWriter就可以返回给客户端端响应了。BackendConn的处理流程从上文得知,proxy结构体中有一个router字段,类型为Router,结构体类型如下:type Router struct { mu sync.RWMutex pool struct { primary *sharedBackendConnPool //连接池 replica *sharedBackendConnPool } slots [MaxSlotNum]Slot //slot …}Router的pool中管理连接池,执行fillSlot时会真正生成连接,放入Slot结构体的backend字段的bc字段中,Slot结构体如下:type Slot struct { id int … backend, migrate struct { id int bc *sharedBackendConn } … method forwardMethod}我们看一下bc字段的结构体sharedBackendConn:type sharedBackendConn struct { addr string //codis server的地址 host []byte //codis server主机名 port []byte //codis server的端口 owner *sharedBackendConnPool //属于哪个连接池 conns [][]*BackendConn //二维数组,一般codis server会有16个db,第一个维度为0-15的数组,每个db可以有多个BackendConn连接 single []*BackendConn //如果每个db只有一个BackendConn连接,则直接放入single中。当每个db有多个连接时会从conns中选一个返回,而每个db只有一个连接时,直接从single中返回 refcnt int}每个BackendConn中有一个 input chan *Request字段,是一个channel,channel中的内容为Request指针。也就是第二章节loopReader选取一个BackendConn后,会将请求放入input中。下边我们看看处理BackendConn input字段中数据的协程是如何启动并处理数据的。代码路径为pkg/proxy/backend.go的newBackendConn函数func NewBackendConn(addr string, database int, config *Config) BackendConn { bc := &BackendConn{ addr: addr, config: config, database: database, } //1024长度的管道,存放1024个Request bc.input = make(chan *Request, 1024) bc.retry.delay = &DelayExp2{ Min: 50, Max: 5000, Unit: time.Millisecond, } go bc.run() return bc}可以看到,在此处创建的BackendConn结构,并且初始化bc.input字段。连接池的建立是在proxy初始化启动的时候就会建立好。继续看bc.run()函数的处理流程func (bc *BackendConn) run() { log.Warnf(“backend conn [%p] to %s, db-%d start service”, bc, bc.addr, bc.database) for round := 0; bc.closed.IsFalse(); round++ { log.Warnf(“backend conn [%p] to %s, db-%d round-[%d]”, bc, bc.addr, bc.database, round) if err := bc.loopWriter(round); err != nil { //执行loopWriter函数,此处的loopWriter和第二章节的loopWriter只是名称相同,是两个不同的处理函数 bc.delayBeforeRetry() } } log.Warnf(“backend conn [%p] to %s, db-%d stop and exit”, bc, bc.addr, bc.database)} func (bc BackendConn) loopWriter(round int) (err error) { … c, tasks, err := bc.newBackendReader(round, bc.config) //调用newBackendReader函数。注意此处的tasks也是一个存放Request的channel,用来此处的loopWriter和loopReader交流信息 if err != nil { return err } … for r := range bc.input { //可以看到,此处的loopWriter会从bc.input中取出数据并且处理 … if err := p.EncodeMultiBulk(r.Multi); err != nil { //将请求编码并且发送到codis server return bc.setResponse(r, nil, fmt.Errorf(“backend conn failure, %s”, err)) } if err := p.Flush(len(bc.input) == 0); err != nil { return bc.setResponse(r, nil, fmt.Errorf(“backend conn failure, %s”, err)) } else { tasks <- r //将请求放入tasks这个channel中 } } return nil}注意此处的loopWriter会从bc.input中取出数据发送到codis server,bc.newBackendReader会起一个loopReader,从codis server中读取数据并且写到request结构体中,此处的loopReader和loopWriter通过tasks这个channel通信。func (bc *BackendConn) newBackendReader(round int, config *Config) (*redis.Conn, chan<- *Request, error) { … tasks := make(chan *Request, config.BackendMaxPipeline)//创建task这个channel并且返回给loopWriter go bc.loopReader(tasks, c, round)//启动loopReader return c, tasks, nil}func (bc *BackendConn) loopReader(tasks <-chan *Request, c *redis.Conn, round int) (err error) { … for r := range tasks { //从tasks中取出响应 resp, err := c.Decode() if err != nil { return bc.setResponse(r, nil, fmt.Errorf(“backend conn failure, %s”, err)) } … bc.setResponse(r, resp, nil)//设置响应数据到request结构体中 } return nil}func (bc *BackendConn) setResponse(r *Request, resp *redis.Resp, err error) error { r.Resp, r.Err = resp, err //Request的Resp字段设置为响应值 if r.Group != nil { r.Group.Done() } if r.Batch != nil { r.Batch.Done() //注意此处会对Batch执行减1操作,这样proxy中的loopWriter可以聚合响应并返回 } return err}总结一下,BackendConn中的函数功能如下loopWriter->从bc.input中取出请求并且发给codis server,并且将请求放到tasks channel中loopReader->从tasks中取出请求,设置codis server的响应字段到Request的Resp字段中,并且将Batch执行减1操作小结一图胜千言,图片版权归李老师,如下 ...

February 20, 2019 · 3 min · jiezi

使用Sentinel配置Redis 3.x主从高可用服务

Redis-Sentinel是Redis官方推荐的高可用性(HA)解决方案,当用Redis做Master-slave的高可用方案时,假如master宕机了,Redis本身(包括它的很多客户端)都没有实现自动进行主备切换,而Redis-sentinel本身也是一个独立运行的进程,它能监控多个master-slave集群,发现master宕机后能进行自动切换。它的主要功能有以下几点不时地监控redis是否按照预期良好地运行;如果发现某个redis节点运行出现状况,能够通知另外一个进程(例如它的客户端);能够进行自动切换。当一个master节点不可用时,能够选举出master的多个slave(如果有超过一个slave的话)中的一个来作为新的master,其它的slave节点会将它所追随的master的地址改为被提升为master的slave的新地址1.集群规划172.16.1.42 6379 Master172.16.1.43 6379 Slave1172.16.1.43 6380 Slave22.集群配置2.1 安装reids# yum install gcc gcc-c++ tcl# cd /opt/redis/# tar xvf redis-3.2.8.tar.gz# cd redis-3.2.8# make install# make test# cd utils/# 初始化数据# ./install_server.sh2.2 配置并启动redisMaster:# vim master.confport 6379bind 0.0.0.0masterauth 123456requirepass 123456slave-read-only yes#启动# redis-server /etc/redis/master.confSlave1:# vim slave1.confport 6379bind 0.0.0.0slaveof 172.16.1.42 6379masterauth 123456requirepass 123456slave-read-only yesprotected-mode no# 启动# redis-server slave1.confSlave2:# vim slave2.confport 6379bind 0.0.0.0slaveof 172.16.1.42 6379masterauth 123456requirepass 123456slave-read-only yesprotected-mode no# 启动# redis-server slave2.conf3. 验证主从复制Master:[root@localhost redis]# redis-cli -a 123456 -p 6379127.0.0.1:6379> set name1 zhangsanOKSlave1:[root@localhost ~]# redis-cli -p 6379127.0.0.1:6379> auth 123456OK127.0.0.1:6379> get name1"zhangsan"Slave2:[root@localhost ~]# redis-cli -a 123456 -p 6380127.0.0.1:6380> get name1"zhangsan" 4. 主从切换(Redis Sentinel)Redis SentinelSentinel(哨兵)是用于监控redis集群中Master状态的工具4.1 Sentinel作用Master状态检测如果Master异常,则会进行Master-Slave切换,将其中一个Slave作为Master,将之前的Master作为SlaveMaster-Slave切换后,master_redis.conf、slave_redis.conf和sentinel.conf的内容都会发生改变,即master_redis.conf中会多一行slaveof的配置,sentinel.conf的监控目标会随之调换4.2 Sentinel工作方式:每个Sentinel以每秒钟一次的频率向它所知的Master,Slave以及其他 Sentinel 实例发送一个 PING 命令如果一个实例(instance)距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被 Sentinel 标记为主观下线。如果一个Master被标记为主观下线,则正在监视这个Master的所有 Sentinel 要以每秒一次的频率确认Master的确进入了主观下线状态。当有足够数量的 Sentinel(大于等于配置文件指定的值)在指定的时间范围内确认Master的确进入了主观下线状态, 则Master会被标记为客观下线在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有Master,Slave发送 INFO 命令当Master被 Sentinel 标记为客观下线时,Sentinel 向下线的 Master 的所有 Slave 发送 INFO 命令的频率会从 10 秒一次改为每秒一次若没有足够数量的 Sentinel 同意 Master 已经下线, Master 的客观下线状态就会被移除。若 Master 重新向 Sentinel 的 PING 命令返回有效回复, Master 的主观下线状态就会被移除。主观下线和客观下线主观下线:Subjectively Down,简称 SDOWN,指的是当前 Sentinel 实例对某个redis服务器做出的下线判断。客观下线:Objectively Down, 简称 ODOWN,指的是多个 Sentinel 实例在对Master Server做出 SDOWN 判断,并且通过 SENTINEL is-master-down-by-addr 命令互相交流之后,得出的Master Server下线判断,然后开启failover.SDOWN适合于Master和Slave,只要一个 Sentinel 发现Master进入了ODOWN, 这个 Sentinel 就可能会被其他 Sentinel 推选出, 并对下线的主服务器执行自动故障迁移操作。ODOWN只适用于Master,对于Slave的 Redis 实例,Sentinel 在将它们判断为下线前不需要进行协商, 所以Slave的 Sentinel 永远不会达到ODOWN。4.3 配置1:指定监听Master(三个节点)所有节点都需要运行,所有节点配置一样,且必须启动# vim /etc/redis/sentinel.conf(可以从安装包里面找到这个文件)protected-mode nologfile “/mejust/logs/sentinel.log"port 26379dir “/tmp"sentinel monitor mymaster 172.16.1.42 6379 2sentinel down-after-milliseconds mymaster 5000sentinel failover-timeout mymaster 60000sentinel auth-pass mymaster !@#123mejust.com上面配置文件说明如下:第二行指定sentinel端口号第五行指定Sentinel去监视一个名为 mymaster 的Master,Master的IP地址为172.16.1.42,端口号为6379,最后的2表示当有2个Sentinel检测到Master异常时才会判定其失效,即只有当2个Sentinel都判定Master失效了才会自动迁移,如果Sentinel的数量不达标,则不会执行自动故障迁移。第六行指定Sentinel判定Master断线的时间。(单位为毫秒,判定为主观下线SDOWN)第七行指定在执行故障转移时,最多可以有多少个Slave同时对新的Master进行同步。这个数字设置为1,虽然完成故障转移所需的时间会变长,但是可以保证每次只有1个Slave处于不能处理命令请求的状态2:启动sentinel(三个节点):# redis-sentinel /etc/redis/sentinel.conf3:设置开机启动(三个节点)# echo “/opt/redis/src/redis-sentinel /main/redis/sentinel.conf” >> /etc/rc.local4.4 注意点首次启动时,必须先启动MasterSentinel 只在 server 端做主从切换,app端要自己开发(例如Jedis库的SentinelJedis,能够监控Sentinel的状态)若Master已经被判定为下线,Sentinel已经选择了新的Master,也已经将old Master改成Slave,但是还没有将其改成new Master。若此时重启old Master,则Redis集群将处于无Master状态,此时只能手动修改配置文件,然后重新启动集群5. 集群状态返回结构是PONG,则表示服务运行正常127.0.0.1:6379> pingPONG127.0.0.1:6379> info Replication查看sentine状态[root@localhost log]# redis-cli -p 26379127.0.0.1:26379> info主从切换:127.0.0.1:26379> slaveof NO ONE ...

January 4, 2019 · 2 min · jiezi

Redis Cluster

Redis3.0以后,节点之间通过去中心化的方式提供了完整的sharding(数据分片)、replication(复制机制、Cluster具备感知准备的能力)、failover解决方案。拓扑结构Redis Cluster由多个Redis节点组构成。不同节点组服务的数据无交集,每一个节点组对应数据sharding的一个分片。节点组内分为主备两类节点,两者数据准实时一致,通过异步化的主备复制机制。master节点对用户提供读写服务,slave节点对用户提供读服务。Redis Cluster总共有16384个slot,每一个节点负责一部分slot。Redis Cluster中所有的几点之间两两通过Redis Cluster Bus交互,主要交互以下关键信息:数据分片(slot)和节点的对应关系集群中每个节点可用状态集群结构发生变更时,通过一定的协议对配置信息达成一致。数据分片的迁移、故障发生时的主备切换决策、单点master的发现和其发生主备关系的变更等场景均会导致集群结构变化publish和subscribe(发布/订阅)功能在cluster版的内容实现所需要交互的信息。Redis Cluster Bus通过单独的端口进行连接,bus是节点间的内部通信机制,交互的是字节序列化信息,而不是client到Redis服务器的字符序列化以提升交互效率。Redis Cluster是去中心化的分布式实现方案,客户端可以和集群中的任一节点连接。配置一致性去中心化意味着集群的拓扑结构并不保存在单独的配置节点上,Redis Cluster通过引入两个自增的epoch变量来使得集群配置在各个节点间达成最终一致。配置信息的数据结构Redis Cluster中的每一个节点都保存了集群的配置信息,这些信息存储在clusterState中。clusterState记录了从集群中某个节点的视角看来的集群配置状态currentEpoch表示整个集群中的最大版本号,集群信息每变更一次,该版本号都会自增以保证每个信息的版本号唯一nodes是一个列表,包含了本节点所知的集群所有节点的信息(clusterNode),其中也包含本节点自身clusterNode记录了每个节点的信息,比较关键的信息包括该信息的版本epoch,该版本信息的描述:该节点对应的数据分片(slot),当该节点为master节点时对应的slave节点列表、当该节点为slave时对应的master节点每个clusterNode还包含了一个全局唯一的nodeId当集群的数据分片信息发生变更时,Redis Cluster仍然保持对外服务,在迁移过程中,通过分片迁移相关状态的一组变量来管控迁移过程当集群中的某个master出现宕机时,Redis Cluster会自动发现并触发故障转移的操作,将宕机master的某个slave升级为master,这个过程同样需要一组failover相关状态的变量来管控故障转移。Redis Cluster通过epoch作为版本号来实现集群配置的一致性。信息交互去中心化的架构不存在统一的配置中心,各个节点对集群状态的认知来自于节点间的信息交互。在Redis Cluster中,该信息的交互通过Redis Cluster Bus来完成。clusterMsg的type字段指明了消息的类型。配置信息的一致性主要依靠PING和PONG,两者除了type不同,其余字段语义均相同,消息体为Gossip数据。每一个节点向其他节点较为频繁的周期性发送PING消息和接受PONG响应。在这些下拍戏的Gossip部分,包含了发送者节点(或者响应者节点)所知的集群其他节点信息,接收节点可以根据这些Gossip信息更新自己对于集群的认知。规模较大的集群可能存在上千个节点,但是这些节点在正常情况下都是稳定的,因此每次都发送全量数据并不必要,而且还会造成网络负担。作为优化,Redis Cluster在每次的PING和PONG包中,只包含全集群部分节点信息,节点随机选取,以此控制网络流量。由于交互频繁,短时间的几次交互之后,集群状态就会以Gossip协议的方式被扩散到了集群中的所有节点。一致性达成集群结构稳定不发生变化时,各个节点通过Gossip协议在几轮交互之后便可得知全集群的信息并且达到一致的状态。但是,当发生故障转移、分片迁移等情况将会造成集群结构变更,变更的信息需要各个节点之间自行协调,优先得知变更信息的节点利用epoch变量将自己的最新信息扩散到整个集群,达到最终一致。配置信息clusterNode的epoch属性描述的粒度是单个节点配置信息clusterState的currentEpoch属性的粒度是整个集群,它的存在用来辅助epoch自增的生成。由于currentEpoch信息也是维护在各个几点自身的,Redis Cluster结构在发生变更时,通过一定时间窗口控制和更新规则保证每个节点看到的currentEpoch都是最新的。集群信息的更新规则:当某个节点率先知道了信息变更时,这个节点将currentEpoch自增使之成为集群中的最大值,再用自增后的currentEpoch作为新的epoch版本当某个节点收到了比自己大的currentEpoch时,更新自己的currentEpoch值使之保持最新当收到的Redis Cluster Bus消息中某个节点信息的epoch值大于接收者自己内部的配置信息存储的值时,意味着自己的信息太旧,此时接收者直接将自己的映射信息更新为消息的内容当收到的Redis Cluster Bus消息中某个节点信息未包含在接收节点的内部配置信息中时,意味着接受者尚未意识到该节点的存在,此时接收者直接将消息的信息添加到自己的内部配置信息中。sharding不同的节点组服务于相互无交互的数据子集(sharding,分片)。数据分片(slot)Redis Cluster将所有的数据划分为16384个分片(slot),每个分片负责其中一部分。每一条数据根据key值通过数据分布算法映射到16384个slot中的一个。数据分布算法:slotId=crc(key)%16384客户端根据slotId决定将请求路由到哪个Redis节点。Cluster不支持跨节点的单命令。为此,Redis引入HashTag的概念,使得数据分布算法可以根据key的某一部分进行计算,让相关的两条记录落到同一个数据分片,例如:某条商品交易记录的key值为:product_trade_{prod123}这个商品的详情记录的key值为:product_detail_{prod123}Redis会根据{}之间的子字符串作为数据分布算法的输入。客户端路由Redis Cluster的客户端需要具备一定的路由能力。当一个Client访问的key不在对应Redis节点的slot中,Redis返回给Client一个moved命令,告知其正确的路由信息。从Client收到moved响应,到再次向moved响应中指向的节点发送请求期间,Redis Cluster的数据分布可能又发生了变更,此时,指向的节点会继续响应moved。Client根据moved响应更新其内部的路由缓存信息,以便下一次请求时直接路由到正确的节点,降低交互次数。当Cluster处在数据重分布(目前由人工触发)过程中时,可以通过ask命令控制客户端路由。ask命令和moved命令的不同语义在于,后者会更新路由缓存,前者只是本条操作重定向到新节点,后续的相同slot操作仍路由到旧节点。ask类型将重定向和路由缓存更新分离,避免客户端的路由缓存信息频繁更新。分片迁移在稳定的Redis Cluster下,每一个slot对应的节点是确定的。但是在某些情况下,节点和分片的对应关系要发生变更:新的节点作为master加入某个节点分组需要下线负载不均需要调整slot分布此时需要进行分片的迁移。分片迁移的触发和过程由外部系统完成,Redis Cluster只提供迁移过程中需要的原语供外部系统调用。这些原语主要有两种:节点迁移状态设置:迁移前标记源/目标节点key迁移的原子化命令:迁移的具体步骤向节点B发送状态变更命令,将B的对应slot状态置为IMPORTING向节点A发送状态变更命令,将A的对应slot状态置为MIGRATING针对A的slot上的所有的key,分别向A发送MIGRATE命令,告知A将对应key的数据迁移到B。当节点A的状态被设置为了MIGRATING后,表示对应的slot正在从A迁出,为保证该slot数据的一致性,A此时对slot内部数据提供读写服务的行为和通常状态下有所区别,对于某个迁移中的slot:如果客户端访问的key尚未迁移出,则正常地处理key如果key已经被迁移出或者根本不存在该key,则回复客户端ASK信息让其跳转到B执行当节点B的状态被设置为了IMPORTING之后,表示对应的slot正在向B迁入中,即使B仍能对外提供该slot的读写服务,但行为和通常状态下也有所区别:当来自客户端的正常访问不是从ASK跳转而来时,说明客户端尚不知道迁移正在进行,很有可能操作了一个目前尚未迁移完成的正处在A上的key,如果此时key已经在A上被修改了,那么B和A的修改值将在未来发生冲突。对于该slot上的所有非ASK跳转而来的操作,B不会进行处理,而是通过MOVED命令让客户端跳转至A执行这样的状态控制可以保证同一个key在迁移之前总是在源节点执行,迁移后总是在目标节点执行,杜绝了两边同时写导致值冲突的可能性。且迁移过程中新增的key总是在目标节点执行,源节点不会再有新增的key,使得迁移过程时间有界。Redis单机对于命令的处理是单线程的,同一个key在MIGRATE的过程中不会处理对该key的其他操作,从而保证了迁移的原子性。当slot的所有key从A迁移至B上之后,客户端通过CLUSTER SETSLOT命令设置B的分片信息,使之包含迁移的slot。设置的过程中会自增一个epoch,它大于当前集群中的所有epoch值,这个新的配置信息会传播到集群中的其他每一个节点,完成分片节点映射关系的更新。failoverRedis Cluster同Sentinel一样,具备完整的节点故障发现、故障状态一致性保证、主备切换机制。failover状态变迁failover的过程如下:故障发现:当某个master宕机时,宕机事件如何被集群其他节点感知故障确认:多个节点就某个master是否宕机如何达成一致slave选举:集群确认了某个master确实宕机后,如何将它的slave升级成新的master;如果原master有多个slave,选择谁升级集群结构变更:选举成功的slave升级成新的master后如何让全集群的其他节点知道以更新他们的集群结构信息故障发现Redis Cluster节点间通过Redis Cluster Bus两两周期性地进行PING/PONG交互,当某个节点宕机时,其他发向它的PING消息将无法及时响应,当PONG的响应超过一定时间(NODE_TIMEOUT)未收到,则发送者认为接受节点故障,将其置为PFAIL状态,后续通过Gossip发出的PING/PONG消息中,这个节点的PFAIL状态将会被转播到集群的其他节点。Redis Cluster的节点间通过TCP保持Redis Cluster Bus连接,当对端无PONG回复时,除了节点故障外,还有可能是TCP连接断开。对于TCP连接断开导致的响应超时,将会产生节点状态误报。因此Redis Cluster通过预重试机制排除此类误报:当NODE_TIMEOUT/2过去了却还未收到PING对应的PONG消息,则重建连接重发PING消息,如果对端正常,PONG会在很短时间内抵达。故障确认对于网络分割的节点,某个节点(假设叫B节点)并没有故障,但可能和A无法连接,但是和C/D等其他节点可以正常联通,此时只有A会将B标记为PFAIL,其他节点扔人认为B是正常的。此时A和C/D等其他节点信息不一致。Redis Cluster通过故障确认协议达成一致。A会受到来自其他节点的Gossip消息,被告知节点B是否处于PFAIL状态,当A受到的来自其他master节点的B的PFAIL达到一定数量后,会将B的PFAIL升级为FAIL状态,表示B已确认为故障,后续将会发起slave选举流程slave选举上例中,如果B是A的master,且B已经被集群公认是FAIL状态,那么A将发起竞选,期望替代B成为新的master。如果B有多个slave A/E/F都意识到B处于FAIL状态了,A/E/F可能会同时发起竞选,当B的slave数量>=3个时,很有可能因为票数均匀无法选出胜者,延长B上的slot不可用时间。为此,slave间会在选举前协商优先级,优先级高的slave更有可能早地发起选举,优先级较低的slave发起选举的时间越靠后,避免和高优先级的slave竞争,提升一轮完成选举的可能性。优先级最重要的决定因素是slave最后一次同步master信息的时间,越新标识这个slave的数据越新,竞选优先级越高。slave通过向其他master节点发送FAILOVER_AUTH_REQUEST消息发起竞选,master收到之后回复FAILOVER_AUTH_ACK消息告知自己是否同意改slave成为新的master。slave发送FAILOVER_AUTH_REQUEST前会将currentEpoch自增并将最新的epoch带入到AILOVER_AUTH_REQUEST消息中,master收到FAILOVER_AUTH_REQUEST消息后,如果发现对于本轮(本epoch)自己尚未投过票,则回复同意,否则回复拒绝。集群结构变更通知当slave收到超过半数的master的同意回复时,该slave顺利的替代B成为新master,此时它会以最新的epoch通过PONG消息广播自己成为master的信息,让集群中的其他节点更快地更新拓扑信息。当B恢复可用之后,它首先仍然认为自己是master,但逐渐得通过Gossip协议得知A已经替代自己的事实之后降级为A的slave。主备复制Redis采用主备复制的方式保持一致性,即所有节点中,有一个节点为master,对外提供写入服务,所有的数据变更由外界对master的写入触发,之后Redis内部异步地将数据从主节点复制到其他节点上。主备复制流程Redis包含master和slave节点:master节点对外提供读写服务;slave节点作为master的数据备份,拥有master的全量数据,对外不提供写服务。主备复制由slave主动触发。slave向master发起SYNC命令。这一步在slave启动后触发,master被动地将新进slave节点加入自己的主备复制集群master收到SYNC后,开启BGSAVE操作。BGSAVE是Redis的一种全量模式的持久化机制BGSAVE完成后,master会将快照信息发送给slave发送期间,master收到的来自客户端的新的写命令,除了正常响应外,都再存入一份到backlog队列快照信息发送完成后,master继续发送backlog队列信息backlog发送完成后,后续的写操作同时发送给slave,保持实时地异步复制slave侧的处理逻辑:发送完SYNC后,继续对外提供服务开始接收master的快照信息,此时,将slave现有数据清空,并将master快照写入自身内存接收backlog内容并执行它,即回放,期间对外提供读请求继续接收后续来自master的命令副本并继续回放,以保证数据和master一致如果有多个slave节点并发发送SYNC命令给master,只要第二个slave的SYNC命令发生在master完成BGSAVE之前,第二个slave将受到和第一个slave相同的快照和后续的backlog;否则,第二个slave的SYNC将触发master的第二次BGSAVE。断点续传slave通过SYNC命令和master进行数据同步时,master都会dump全量数据。假设master和slave断开很短的时间,数据只有很少的差异,重连后也会发送这些全量数据导致大量的无效开销。最好的方式就是,master-slave只同步断开期间的少量数据。Redis的PSYNC可用于替代SYNC,做到master-slave基于断点续传的主备同步协议。master-slave两端通过维护一个offset记录当前已经同步过的命令,slave断开期间,master的客户端命令会保持在缓存中,在slave命令重连后,告知master断开时的最新offset,master则将缓存中大于offset的数据发送给slave,而断开前已经同步过的数据,则不再重新同步,这样减少了数据传输开销。可用性和性能Redis Cluster读写分离对于有读写分离需求的场景,应用对于某些读的请求允许舍弃一定的数据一致性,以换取更高的读吞吐量,此时希望将读的请求交由slave处理以分担master的压力。默认情况下,数据分片映射关系中,某个slot对应的节点一定是一个master节点,客户端通过MOVED消息得知的集群拓扑结构也只会将请求路由到各个master中,即便客户将读请求直接发送到slave上,后者也会回复MOVED到master的响应。Redis Cluster引入了READONLY命令。客户端向slave发送该命令后,slave对于读操作,将不再MOVED回master而不是直接处理,这被称为slave的READONLY模式。通过READWRITE命令,可将slave的READONLY模式重置。master单点保护集群只需要保持2*master+1个节点,就可以在任一节点宕机后仍然自动地维持,称为master的单点保护。

January 1, 2019 · 1 min · jiezi

Redis单机版搭建

首先放一张Redis的架构图:架构细节:(1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.(2)节点的fail是通过集群中超过半数的节点检测失效时才生效.(3)客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可(4)redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->valueRedis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点。所以搭建redis集群的时候最少要使用六台服务器才可以(如果少于三台服务器则不能投票)。因为是在虚拟机上面模拟搭建,所以我们可以设置六个不同的端口来模拟六台虚拟机。第一步:首先在虚拟机上面安装一个Redis 因为Redis是c语言开发的,安装redis需要c语言的编译环境。如果没有gcc需要在线安装。yum install gcc-c++。 我这里使用时Redis5.02的安装包redis-5.0.2.tar.gz。大家可以直接去官网下载就可以。Redis官网然后将压缩包上传的虚拟机的服务器上,这里我用的是Centos7.0版本。具体的上传和解压就不演示了。 把Redis解压到/usr/local/的目录下面。进入Redis里面直接执行make命令进行编译。(因为里面的带有Makefile)编译完成之后接下来就安装 执行命令代码make install PREFIX=/usr/local/redis在这里我们使用后端启动模式:这里需要进入到我们刚才把Redis解压的目录中把redis.conf文件复制到编译的目录中复制完成之后需要修改redis.conf的配置。把这了的no改为yes接下来执行执行这条命令就可以后台启动了[root@localhost bin]# ./redis-server redis.conf查看进程是否启动成功[root@localhost bin]# ps aux|grep redis好了,单机版的Redis版的已经搭建成功了。连接服务测试:输入这个指令:./redis-cli设置String类型的数据,然后取出OK,Redis单机版服务器搭建完成,下一篇文章讲解集群搭建,其实,集群版主要就是在不同的服务器上面安装而已,模拟的话:需要在一台服务器上面设置不同的端口。

December 20, 2018 · 1 min · jiezi

Redis集群环境下的-RedLock(真分布式锁) 实践

在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段。 有很多三方库和文章描述如何用Redis实现一个分布式锁管理器,但是这些库实现的方式差别很大,而且很多简单的实现其实只需采用稍微增加一点复杂的设计就可以获得更好的可靠性。 这篇文章的目的就是尝试提出一种官方权威的用Redis实现分布式锁管理器的算法,我们把这个算法称为RedLock。Redlock是redis官方提出的实现分布式锁管理器的算法。这个算法会比一般的普通方法更加安全可靠。关于这个算法的讨论可以看下官方文档。https://github.com/antirez/re…安全和可靠性保证在描述我们的设计之前,我们想先提出三个属性,这三个属性在我们看来,是实现高效分布式锁的基础。1、一致性:互斥,不管任何时候,只有一个客户端能持有同一个锁。2、分区可容忍性:不会死锁,最终一定会得到锁,就算一个持有锁的客户端宕掉或者发生网络分区。3、可用性:只要大多数Redis节点正常工作,客户端应该都能获取和释放锁。为什么基于故障切换的方案不够好为了理解我们想要提高的到底是什么,我们先看下当前大多数基于Redis的分布式锁三方库的现状。 用Redis来实现分布式锁最简单的方式就是在实例里创建一个键值,创建出来的键值一般都是有一个超时时间的(这个是Redis自带的超时特性),所以每个锁最终都会释放。而当一个客户端想要释放锁时,它只需要删除这个键值即可。 表面来看,这个方法似乎很管用,但是这里存在一个问题:在我们的系统架构里存在一个单点故障,如果Redis的master节点宕机了怎么办呢?有人可能会说:加一个slave节点!在master宕机时用slave就行了!但是其实这个方案明显是不可行的,因为这种方案无法保证第1个安全互斥属性,因为Redis的复制是异步的。 总的来说,这个方案里有一个明显的竞争条件(race condition),举例来说:1、客户端A在master节点拿到了锁。2、master节点在把A创建的key写入slave之前宕机了。3、slave变成了master节点4、B也得到了和A还持有的相同的锁(因为原来的slave里还没有A持有锁的信息)当然,在某些特殊场景下,前面提到的这个方案则完全没有问题,比如在宕机期间,多个客户端允许同时都持有锁,如果你可以容忍这个问题的话,那用这个基于复制的方案就完全没有问题,否则的话我们还是建议你采用这篇文章里接下来要描述的方案。Redlock 简介在不同进程需要互斥地访问共享资源时,分布式锁是一种非常有用的技术手段。实现高效的分布式锁有三个属性需要考虑:1、安全属性:互斥,不管什么时候,只有一个客户端持有锁2、效率属性A:不会死锁3、效率属性B:容错,只要大多数redis节点能够正常工作,客户端端都能获取和释放锁。Redlock 算法在分布式版本的算法里我们假设我们有N个Redis master节点,这些节点都是完全独立的,我们不用任何复制或者其他隐含的分布式协调算法。我们已经描述了如何在单节点环境下安全地获取和释放锁。因此我们理所当然地应当用这个方法在每个单节点里来获取和释放锁。在我们的例子里面我们把N设成5,这个数字是一个相对比较合理的数值,因此我们需要在不同的计算机或者虚拟机上运行5个master节点来保证他们大多数情况下都不会同时宕机。一个客户端需要做如下操作来获取锁:1、获取当前时间(单位是毫秒)。2、轮流用相同的key和随机值在N个节点上请求锁,在这一步里,客户端在每个master上请求锁时,会有一个和总的锁释放时间相比小的多的超时时间。比如如果锁自动释放时间是10秒钟,那每个节点锁请求的超时时间可能是5-50毫秒的范围,这个可以防止一个客户端在某个宕掉的master节点上阻塞过长时间,如果一个master节点不可用了,我们应该尽快尝试下一个master节点。3、客户端计算第二步中获取锁所花的时间,只有当客户端在大多数master节点上成功获取了锁(在这里是3个),而且总共消耗的时间不超过锁释放时间,这个锁就认为是获取成功了。4、如果锁获取成功了,那现在锁自动释放时间就是最初的锁释放时间减去之前获取锁所消耗的时间。5、如果锁获取失败了,不管是因为获取成功的锁不超过一半(N/2+1)还是因为总消耗时间超过了锁释放时间,客户端都会到每个master节点上释放锁,即便是那些他认为没有获取成功的锁。Redisson 实现方式(红锁 RedLock)github Redisson https://github.com/redisson/r...Maven<!-- JDK 1.8+ compatible –><dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.9.0</version></dependency> <!– JDK 1.6+ compatible –><dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>2.14.0</version></dependency>集群模式配置集群模式除了适用于Redis集群环境,也适用于任何云计算服务商提供的集群模式,例如AWS ElastiCache集群版、Azure Redis Cache和阿里云(Aliyun)的云数据库Redis版。程序化配置集群的用法:@Beanpublic RedissonClient redissonClient() { Config config = new Config(); config.useClusterServers() .setScanInterval(2000) // 集群状态扫描间隔时间,单位是毫秒 //可以用"rediss://“来启用SSL连接 .addNodeAddress(“redis://127.0.0.1:7000”, “redis://127.0.0.1:7001”) .addNodeAddress(“redis://127.0.0.1:7002”); return Redisson.create(config);}基于Redis的Redisson红锁RedissonRedLock对象实现了Redlock介绍的加锁算法。该对象也可以用来将多个RLock对象关联为一个红锁,每个RLock对象实例可以来自于不同的Redisson实例。RLock lock1 = redissonClient1.getLock(“lock1”);RLock lock2 = redissonClient2.getLock(“lock2”);RLock lock3 = redissonClient3.getLock(“lock3”);RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);// 同时加锁:lock1 lock2 lock3// 红锁在大部分节点上加锁成功就算成功。lock.lock();…lock.unlock();Redisson 监控锁大家都知道,如果负责储存某些分布式锁的某些Redis节点宕机以后,而且这些锁正好处于锁住的状态时,这些锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);// 给lock1,lock2,lock3加锁,如果没有手动解开的话,10秒钟后将会自动解开lock.lock(10, TimeUnit.SECONDS);// 为加锁等待100秒时间,并在加锁成功10秒钟后自动解开boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);…lock.unlock();往期精彩文章Redis服务器被攻击后该如何安全加固MySQL从删库到恢复,还用跑路吗?理解JWT鉴权的应用场景及使用建议浅谈偏向锁、轻量级锁、重量级锁算法:一致性哈希算法的理解与实践架构:通过案例读懂 RESTful 架构风格架构:一文读懂Apache Flink架构及特性分析架构:大数据推荐系统实时架构和离线架构微服务:架构下静态数据通用缓存机制微服务:小型系统如何“微服务”开发微服务:深入理解为什么要设计幂等性的服务中间件:应用消息中间件设计可以解决哪些实际问题? ...

November 12, 2018 · 1 min · jiezi