+
专题寄语
>>
Redis应用专题
Redis各版本及各种应用模式介绍
ID:12,
创建:2019-08-27 17:37,
更新:2022-02-21 17:24,
版本:9,
排序号:100
# redis应用介绍 附件是给单位介绍做的演示文稿 - 版本特性演化 2.6 -> 3.x -> 4.x -> 5.x - 基于客户端的分布式缓存系统 - 传统主从 - twemproxy - 官方哨兵 - 官方集群 ## 0. 主要应用方式 ### 缓存 ### 内存型数据库 ## 1. 版本特性演化 2.x -> 3.x -> 4.x -> 5.x (只选核心重点提示) ### 1.1 历史演进及各版本特性 官方个版本下载页:http://download.redis.io/releases/。<br> 目前官方已经没有2.6版本以前的下载链接。<br> 笔者曾用的最早的2.4版本使用的稳定性确实不如2.6。<br> - 1.1 2013~2015主推2.6,2.8两个版本 - 1.2 2015年4月发布版本3稳定版 - 1.3 2017年7月发布版本4稳定版 - 1.4 2018年10月发布版本5稳定版 特性列表如下 | 版本 | 特性(部分关键点) | | --- | --- | | 2.6.x | 1.支持lua脚本<br>2.支持新命令dump以及restore<br>3.内存优化及存储优<br>4.提供了BITCOUNT与BITOP,前者支持位值count,后者支持了位操作 | | 2.8.x | 1.增量主从复制。<br>2.哨兵模式生产可用<br>3.可以通过config set命令设置maxclients。<br>4.config rewrite命令可以将config set持久化到Redis配置文件中。<br>5.发布订阅添加了pub/sub | | 3.x.x | 1.Redis Cluster。<br>2.内存优化与部分命令性能提升。<br> | | 4.x.x | 1.允许开发者自定义模块<br>2.异步 DEL、FLUSHDB和FLUSHALL(http://blog.huangz.me/diary/2016/redis-4-outline.html)<br>3.Mixed RDB-AOF format<br>4.新添加MEMORY 命令。可以使用不一样的方式执行内存分析<br>5.兼容 NAT 和 Docker<br>6.Redis Cluster 的故障检测方式改变,node之间的通讯减少<br> | | 5.x.x | 1.新的流数据类型(Stream data type) https://redis.io/topics/streams-intro<br>2.redis-cli 中的集群管理器从 Ruby (redis-trib.rb) 移植到了 C 语言代码。执行 `redis-cli --cluster help`命令以了解更多信息<br>3.新的有序集合(sorted set)命令:ZPOPMIN/MAX 和阻塞变体(blocking variants)<br>4.客户端频繁连接和断开连接时,性能表现更好<br>5.对 Redis 核心代码进行了重构<br> | ### 1.2 简单尝试几个提到的特性(均在5) ####(1)2.6版本的 dump 与 restore 发现并不是那么美好。是方便传输单个键值对的 客户端模式 ```bash redis> SET mykey 10 "OK" redis> DUMP mykey "\u0000\xC0\n\t\u0000\xBEm\u0006\x89Z(\u0000\n" redis> RESTORE mykey1 0 "\u0000\xC0\n\t\u0000\xBEm\u0006\x89Z(\u0000\n" ``` 命令行模式 ```bash # 备份键c的值到文件yt-c.data(会多一个换行符,在restore的时候需要删除掉) ./src/redis-cli dump c > yt-c.data # 恢复数据为另一个键值(通过管道操作删除了最后一个字符) cat yt-c.data |head -c-1|./src/redis-cli restore c1 0 ``` #### (2)最基本且传统的备份与恢复方式 ```bash # 同步备份(可选择在slave节点备份) 127.0.0.1:6379> save OK # 后台备份(fork一个子进程运行,不会阻塞主进程) 127.0.0.1:6379> bgsave Background saving started ``` 恢复方式很简单,直接放在redis的dir目录即可(“config get dir”可获取或者根据配置文件制定)。 rdb对于不严格的缓存系统而言,比较试用,他带来了较高的性能,同时兼顾一部分数据的不丢失从而有利于恢复和数据迁移。<br> 对于数据一致性比较高的应用场景,当开启AOF。 ## 2. 基于客户端的分布式缓存系统(基本灵感来自memcached) redis最先应用的场景就是缓存,而且常常拿来和memcached对比。同时期的redis也确实在某些特性上向memcached对齐,比如没有服务端分区方案。于是,人们在期初应用的时候都的分布式(分片应用)都是基于客户端的。几乎所有的主流客户端sdk也都支持基于客户端的分片方案。<br> 优点: - 小型缓存及大面积分片缓存应用;部署简单,维护简单。 应用注意点: - 对于需要扩容和增加节点时,也就需要客户端的相应对策; - 同时自己负责进行故障节点排除。(客户端维护一个节点列表,一般客户端都有相对成熟的包装); - 不通语言客户端需要使用相同的分片算法; 客户端分片应用示例: ```go func ExampleNewRing() { rdb := redis.NewRing(&redis.RingOptions{ Addrs: map[string]string{ "shard1": "centos7-local:6379", "shard2": "centos7-local:6380", }, PoolSize: 2, }) rdb.Ping() rdb.Set("key1", "值1", 0) fmt.Println(rdb.Get("key1")) } ``` ## 3. 传统主从(字典与高可用性) ### 3.1 对于字典型(小型)KV数据库,或者要求数据持久化成都比较高的缓存比较试用。 ### 3.2 高可用方案也就可以结合平时的运维手段来完成(有些需要手动切换)。比如可以 1 主 1 从,加lvs/keepalived就可以 ### 3.3 redis的主从复制遵循master(Replication ID, offset)标记。slave发送PSYNC命令发送自己的标记,master能回溯到backlog中的标记则只同步增量数据,否则就会做全量同步。 replication配置 ```conf slaveof 127.0.0.1 6379 ``` ### 3.4 只读slave。默认配置的slave是只读模式的,需要通过如下配置决定(推荐保持默认) ```conf slave-read-only yes ``` ### 3.4 slave支持链式复制,如下面 A ---> B ---> C 当然也支持下面这中方式。方案可根据自己应用特点自选 ```text A ---> B \ ---> C ``` 查看主从状态就通过info就可以 ./src/redis-cli info ## 4. twemproxy(在官方哨兵之前就已经普及) twitter的twemproxy方案是业界较早的方案。最核心特点 - 把分布式(分片)集中在代理端(如代理端负责一致性hash) - 高吞吐、高可用集中到代理端(代理端负责管理后端健康redis节点列表,代理端可做集群) - 优化连接(较早版本对过多客户端连接支持不好,通过proxy解决并加以保护后端redis节点) 本身也是以 <b>缓存</b> 为核心应用的。不过是部署还是日常维护,都比较简单方便,节省资源。 开源项目地址https://github.com/twitter/twemproxy 示例配置 ```yml alpha: listen: 0.0.0.0:22121 hash: murmur distribution: ketama auto_eject_hosts: true redis: true server_retry_timeout: 2000 server_failure_limit: 3 servers: - 127.0.0.1:6379:1 - 127.0.0.1:6380:1 ``` 指定配置文件启动 ./src/nutcracker -d -c conf/ytshard.yml 监控(监控端口可以通过启动命令行参数“-s”指定) nc localhost 22222 ## 5. 官方哨兵(在2.8开始引入,集主从与高可用性) - 2.8 版本哨兵正式被推荐放入生产环境。 - 本质上是对redis的replication应用主从切换的自动化管理。在特大规模分布式应用上具有局限性。 - 为了保证哨兵的高可用,哨兵本身也需要至少 3 个节点(有一点点资源消耗对不对)。 基本配置 sentinel-mymaster.conf ```conf port 5000 daemonize yes logfile "sentinel-5000.log" # 最后一个参数是sentinel的合法数量 sentinel myid 19413eb915d1197971ba3f186c33ac3d2c6dde99 sentinel deny-scripts-reconfig yes sentinel monitor mymaster 10.21.6.114 6379 1 sentinel down-after-milliseconds mymaster 5000 # Generated by CONFIG REWRITE dir "/opt/redis-tutorial/redis-5.0.5-sentinel-5000" protected-mode no sentinel failover-timeout mymaster 60000 sentinel config-epoch mymaster 0 sentinel leader-epoch mymaster 0 sentinel known-replica mymaster 10.21.6.114 6380 sentinel known-replica mymaster 10.21.6.114 6381 sentinel known-replica mymaster 127.0.0.1 6381 sentinel known-replica mymaster 127.0.0.1 6380 sentinel current-epoch 0 ``` 启动 ./src/redis-sentinel sentinel-mymaster.conf sentinel log(可以观测到slave节点的加入) ```logs 2153:X 22 Aug 2019 18:26:32.271 # +monitor master mymaster 10.21.6.114 6379 quorum 1 2153:X 22 Aug 2019 18:26:32.271 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 10.21.6.114 6379 2153:X 22 Aug 2019 18:26:32.273 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 10.21.6.114 6379 ``` 获取信息命令 ```bash ./src/redis-cli -p 5000 sentinel get-master-addr-by-name mymaster ./src/redis-cli -p 5000 sentinel master mymaster ./src/redis-cli -p 5000 sentinel slaves mymaster ./src/redis-cli -p 5000 sentinel sentinels mymaster ``` golang客户端应用 ```go package main import ( "fmt" "time" ) import "github.com/go-redis/redis" func main() { ExampleSentinel() } func ExampleSentinel() { client := redis.NewFailoverClient(&redis.FailoverOptions{ MasterName: "mymaster", SentinelAddrs: []string{"10.21.6.114:5000"}, DB: 0, }) fmt.Println(client.Ping()) fmt.Println(client.Get("key1")) } ``` ## 6. 官方集群(无中心的cluster方案) ### 6.1 集群的核心功能。 - 自动分片数据集到不同节点 - 高可用性(允许部分节点经历宕机) ### 6.2 核心用途 - 常规缓存(部分冗余资源消耗) - 内存型kv数据库(对一致性和持久化有一定要求的) ### 6.3 端口与协议 redis增加一个bus port(常规端口+10000)用于集群通信,且使用二进制写通信从而减少内部数据交换带宽应用。 ### 6.4 分片原理(可参考 https://juejin.im/post/5c1bb40a6fb9a049f36211b0) - redis cluster不适用一致性hash分片。而分片规则是把所有的key都归到集群的一个“分部”,这个“分部”被命名为hash slot(哈希槽)。 - 总共有16384个hash slot,每个key属于哪个slot通过公式“CRC16(key) / 16384”计算得出。 - 每个redis节点都包含一部分slot,而节点的增加和减少涉及到slot的复制迁移,是一个在线过程,不需要停机维护时间。 - 对于多key操作事物,lua脚本的执行等,需要确保操作的key都分配的相同的slot上,而redis cluster给这种操作涉及的语法糖就是hash tag。比如this{foo}key和that{foo}key,都是通过{}中间的值计算分配到哪个slot的。也就是给应用者带来了注意事项 - 集群中的各节点遵循主从模式,主节点挂掉,从节点提升为主节点,两者都挂点,则cluster不能正常服务。 - redis cluster集群并不能完全保证数据的强一致性。其中redis cluster的从节点是异步复制是原因之一。 ### 6.5 操作路由过程 - MOVED重定向(稳态应用): ``` 从应用性能促进上讲,slot位置计算客户端,当集群有变化时,服务端做纠正保护 (1)redis客户端发送指令到任一节点 -> 节点计算slot位置 -> slot位置在该节点,直接执行并返回 (2)redis客户端发送指令到任一节点 -> 节点计算slot位置 -> slot位置不在该节点,返回MOVED重定向(包含所在槽位及节点信息) -> 客户端从获取的新节点执行指令(同时利用CLUSTER NODES,CLUSTER SLOTS来获取信息更新本地缓存) ``` - ASK重定向(迁移态应用): ``` 过程部分和MOVED重定向类似,但避免在迁移时期对不存在的key做反复重定向 ``` ### 6.6 redis cluster的基本配置(in redis.conf)项介绍 - <b>cluster-enabled [yes/no]</b> 实例是否支持集群模式的开关 - <b>cluster-config-file [filename]</b> 由redis-server本身维护的一个集群状态的文件(不要手动更改本文件) - <b>cluster-node-timeout [milliseconds]</b> 节点不可及的最大时间。主节点超时则会被父节点替代,slave节点超时则不再接受任何查询。每个节点的超时时间是不能访问集群内大多数节点的状况时间。 - <b>cluster-slave-validity-factor [factor]</b> slave节点有效期因数(默认是10)。为0表示任何时候slave节点都可以failover成master。为正数则当slave节点与master节点断连超过 factor * cluster-node-timeout时,slave节点不能failover成master。当master节点没有slave时(或因factor>0引起),需要等master节点重新加入才可以正常提供服务。 - <b>cluster-migration-barrier [count]</b> master节点保持连接到“slave节点的数量” - <b>cluster-require-full-coverage [yes/no]</b> 默认值是yes。当部分slot不能正常提供服务时,整个集群则不能提供服务,否则整个进群还可以提供服务(只是特定的slot不能提供服务)。 ### 6.7 redis cluster的搭建 以官方向导为例搭建 #### 6.7.1 创建目录并准备配置文件 ```bash # 首先把redis进行标准安装以方便任何时候都可以直接调用redis-*等核心命令 # 在redis经过make命令的源码目录中执行 make install # 创建6个节点的目录(用于盛放各自的配置文件和生成的文件) mkdir 7000 7001 7002 7003 7004 7005 ``` 分别在刚创建的6个节点目录中加入redis.conf配置文件 ```conf # 配置这个主要是为了可以让别的机器也访问(默认127.0.0.1) bind 0.0.0.0 # 下面这个port分别代表6个目录中的,真正实施时注意修改 port 7000/1/2/3/4/5 cluster-enabled yes cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes ``` #### 6.7.2 启动各节点并创建集群 在每个目录中执行如下命令 redis-server redis.conf 命令完成后,在日志中可以看到每个节点会被赋予一个唯一的id值 接着执行创建cluster的指令 ```bash 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 ``` 可以看到类似如下命令结果。redis会为你根据当前节点情况规划集群拓扑。 ```text [root@master cluster]# 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 >>> Performing hash slots allocation on 6 nodes... Master[0] -> Slots 0 - 5460 Master[1] -> Slots 5461 - 10922 Master[2] -> Slots 10923 - 16383 Adding replica 127.0.0.1:7004 to 127.0.0.1:7000 Adding replica 127.0.0.1:7005 to 127.0.0.1:7001 Adding replica 127.0.0.1:7003 to 127.0.0.1:7002 >>> Trying to optimize slaves allocation for anti-affinity [WARNING] Some slaves are in the same host as their master M: 324f1e7ecca74a03e0ce848634098c6555980b00 127.0.0.1:7000 slots:[0-5460] (5461 slots) master M: 4c7a3b054b3e0163972581bcefc002c5754945f1 127.0.0.1:7001 slots:[5461-10922] (5462 slots) master M: 2d1fa13efe8ace40da34a1b00a8e8c7cc5154ad9 127.0.0.1:7002 slots:[10923-16383] (5461 slots) master S: 58e45547a6ac855f6538746ca57b0f8c3aec6ee1 127.0.0.1:7003 replicates 4c7a3b054b3e0163972581bcefc002c5754945f1 S: 373e8ad2fa88684edcb5d9570d6759e4bd9c712a 127.0.0.1:7004 replicates 2d1fa13efe8ace40da34a1b00a8e8c7cc5154ad9 S: a297c73d740d4d0aab9e3579a8bb2450bd3e5711 127.0.0.1:7005 replicates 324f1e7ecca74a03e0ce848634098c6555980b00 Can I set the above configuration? (type 'yes' to accept): ``` 可以看看上看不确定的默认参数值时什么 ```text redis-cli -c -p 7000 127.0.0.1:7000> config get cluster-slave-validity-factor 1) "cluster-slave-validity-factor" 2) "10" 127.0.0.1:7000> ``` #### 6.7.3官方还带一个实验性脚本可以直接执行创建一个6节点的和上面类似的集群(utils/create-cluster/create-cluster)。 该脚本适合在本机运行,作为一时的集群测试使用,同时也可作为手动搭建集群的的参考。 - create-cluster start 启动6个节点(端口从30001开始) - create-cluster create 把6个节点设置成集群 - create-cluster stop 停止各节点(停止集群) 通过查看这个脚本,可以发现停止集群节点的命令用的是 redis-cli -p $PORT shutdown nosave 客户端连接必须以集群模式启动,通过命令查看redis-cli不是最优的方案,因为他没有优化slot映射(缓存映射与客户端计算) #### 6.7.4应用redis-cli的示例 ```text redis-cli -c -p 7000 [root@master create-cluster]# redis-cli -c -p 7000 127.0.0.1:7000> set foo bar -> Redirected to slot [12182] located at 127.0.0.1:7002 OK 127.0.0.1:7002> get foo "bar" 127.0.0.1:7002> set hello world -> Redirected to slot [866] located at 127.0.0.1:7000 OK 127.0.0.1:7000> get foo -> Redirected to slot [12182] located at 127.0.0.1:7002 "bar" 127.0.0.1:7002> get hello -> Redirected to slot [866] located at 127.0.0.1:7000 "world" ``` #### 6.8 应用go-redis客户端访问集群 ```go package main import ( "fmt" "time" ) import "github.com/go-redis/redis" func main() { ExampleCluster() } func ExampleCluster() { rdb := redis.NewClusterClient(&redis.ClusterOptions{ Addrs: []string{"10.21.6.237:7000", "10.21.6.237:7001", "10.21.6.237:7002", "10.21.6.237:7003", "10.21.6.237:7004", "10.21.6.237:7005"}, }) fmt.Println(rdb.Ping()) fmt.Println(rdb.Get("foo").Val()) fmt.Println(rdb.Get("hello").Val()) fmt.Println(rdb.ClusterInfo()) fmt.Println(rdb.ClusterNodes()) } ``` ## 7. 自己想到的一些问题解答 ### 7.1 redis cluster中从节点会负载均衡read请求吗? 从官方自带的redis-cli工具来看,答案是否定的。原因当然因为主从复制是一个异步过程,延时问题使从节点接受读请求在这种场景下是有问题的。
附件列表
Redis应用介绍.pptx