1. 首页
  2. IT资讯

一文掌握 Redis 常用知识点 – 图文结合

“u003Cpu003E优质文章,及时送达u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRcsUnd92eoboDR” img_width=”640″ img_height=”29″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cpu003E作者 | Object_u003Cu002Fpu003Eu003Cpu003E来源 | juejin.imu002Fpostu002F5d809a89e51d456206115ab3u003Cu002Fpu003Eu003Cblockquoteu003Eu003Cdivu003Eu003Cpu003E常用的 SQL 数据库的数据都是存在磁盘中的,虽然在数据库底层也做了对应的缓存来减少数据库的 IO 压力。u003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cu002Fblockquoteu003Eu003Cpu003E由于数据库的缓存一般是针对查询的内容,而且粒度也比较小,一般只有表中的数据没有发生变动的时候,数据库的缓存才会产生作用。u003Cu002Fpu003Eu003Cpu003E但这并不能减少业务逻辑对数据库的增删改操作的 IO 压力,因此缓存技术应运而生,该技术实现了对热点数据的高速缓存,可以u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-6″u003E大大u003Cu002Fiu003E缓解后端数据库的压力。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003E主流应用架构u003Cu002Fh1u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRfMIMZA4YmoxaF” img_width=”1080″ img_height=”539″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cpu003E客户端在对数据库发起请求时,先到缓存层查看是否有所需的数据,如果缓存层存有客户端所需的数据,则直接从缓存层返回,否则进行穿透查询,对数据库进行查询。u003Cu002Fpu003Eu003Cpu003E如果在数据库中查询到该数据,则将该数据回写到缓存层,以便下次客户端再次查询能够直接从缓存层获取数据。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003E缓存中间件 Memcache 和 Redis 的区别u003Cu002Fh1u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003EMemcache 的代码层类似 Hash,特点如下:u003Cu002Fh1u003Eu003Culu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003E支持简单数据类型u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003E不支持数据持久化存储u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003E不支持主从u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003E不支持分片u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003ERedis 特点如下:u003Cu002Fh1u003Eu003Culu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003E数据类型丰富u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003E支持数据磁盘持久化存储u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003E支持主从u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003E支持分片u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003E为什么 Redis 能这么快u003Cu002Fh1u003Eu003Cpu003Eu003Cstrong toutiao-origin=”span” class=”highlight-text”u003ERedis 的效率很高,官方给出的数据是 100000+QPS,这是因为:u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003ERedis 完全基于内存,绝大部分请求是纯粹的内存操作,执行效率高。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003ERedis 使用单进程单线程模型的(K,V)数据库,将数据存储在内存中,存取均不会受到硬盘 IO 的限制,因此其执行速度极快。u003Cu002Fpu003Eu003Cpu003E另外单线程也能处理高并发请求,还可以避免频繁上下文切换和锁的竞争,如果想要多核运行也可以启动多个实例。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E数据结构简单,对数据操作也简单,Redis 不使用表,不会强制用户对各个关系进行关联,不会有复杂的关系限制,其存储结构就是键值对,类似于 HashMap,HashMap 最大的优点就是存取的时间复杂度为 O(1)。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003ERedis 使用多路 Iu002FO 复用模型,为非阻塞 IO。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003E注:Redis 采用的 Iu002FO 多路复用函数:epollu002Fkqueueu002Fevportu002Fselect。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E阅读更多关于 Spring Boot、Java 、Spring全家桶 等技术文章,欢迎u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-1″u003E关注u003Cu002Fiu003Eu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-1″u003E微信u003Cu002Fiu003E公众号:Java后端u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003Eu003Cstrong toutiao-origin=”span” class=”highlight-text”u003E选用策略:u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003E因地制宜,优先选择时间复杂度为 O(1) 的 Iu002FO 多路复用函数作为底层实现。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E由于 Select 要遍历每一个 IO,所以其时间复杂度为 O(n),通常被作为保底方案。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E基于 React 设计模式监听 Iu002FO 事件。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003ERedis 的数据类型u003Cu002Fh1u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003EStringu003Cu002Fh1u003Eu003Cpu003E最基本的数据类型,其值最大可存储 512M,二进制安全(Redis 的 String 可以包含任何二进制数据,包含 jpg 对象等)。u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRfMIMZN5VqulHd” img_width=”1080″ img_height=”508″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003E注:如果重复写入 key 相同的键值对,后写入的会将之前写入的覆盖。u003Cu002Fh1u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003EHashu003Cu002Fh1u003Eu003Cpu003EString 元素组成的字典,适用于存储对象。u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp9.pstatp.comu002Flargeu002Fpgc-imageu002FRfMIMZaIAWvygW” img_width=”1080″ img_height=”256″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cpu003Eu003Cstrong toutiao-origin=”strong” class=”highlight-text”u003EListu003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E列表,按照 String 元素u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-4″u003E插入u003Cu002Fiu003E顺序排序。其顺序为后进先出。由于其具有栈的特性,所以可以实现如“最新消息排行榜”这类的功能。u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRfMIMZoGQhsNPE” img_width=”1080″ img_height=”339″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cpu003Eu003Cstrong toutiao-origin=”strong” class=”highlight-text”u003ESetu003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003EString 元素组成的无序集合,通过哈希表实现(增删改查时间复杂度为 O(1)),不允许重复。u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRfMINHZAjqyzkg” img_width=”1061″ img_height=”309″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cpu003E另外,当我们使用 Smembers 遍历 Set 中的元素时,其顺序也是不确定的,是通过 Hash 运算过后的结果。u003Cu002Fpu003Eu003Cpu003ERedis 还对集合提供了求交集、并集、差集等操作,可以实现如同共同u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-1″u003E关注u003Cu002Fiu003E,共同好友等功能。u003Cu002Fpu003Eu003Cpu003Eu003Cstrong toutiao-origin=”strong” class=”highlight-text”u003ESorted Setu003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E通过分数来为集合中的成员进行从小到大的排序。u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp9.pstatp.comu002Flargeu002Fpgc-imageu002FRfMINHpH12HQDx” img_width=”1080″ img_height=”443″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cpu003Eu003Cstrong toutiao-origin=”strong” class=”highlight-text”u003E更高级的 Redis 类型u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E用于计数的 HyperLogLog、用于支持存储地理位置信息的 Geo。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003E从海量 Key 里查询出某一个固定前缀的 Keyu003Cu002Fh1u003Eu003Cpu003Eu003Cstrong toutiao-origin=”span” class=”highlight-text”u003E假设 Redis 中有十亿条 Key,如何从这么多 Key 中找到固定前缀的 Key?u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E方法 1:u003Cu002Fstrongu003E使用 Keys [pattern]:查找所有符合给定模式 Pattern 的 Keyu003Cu002Fpu003Eu003Cpu003E使用 Keys [pattern] 指令可以找到所有符合 Pattern 条件的 Key,但是 Keys 会一次性返回所有符合条件的 Key,所以会造成 Redis 的卡顿。u003Cu002Fpu003Eu003Cpu003E假设 Redis 此时正在生产环境下,使用该命令就会造成隐患,另外如果一次性返回所有 Key,对内存的消耗在某些条件下也是巨大的。u003Cu002Fpu003Eu003Cpu003E例:u003Cu002Fpu003Eu003Cpreu003Eu003Cpu003Ekeys test* u002Fu002F返回所有以test为前缀的keyu003Cu002Fpu003Eu003Cu002Fpreu003Eu003Cpu003Eu003Cstrongu003E方法 2:u003Cu002Fstrongu003E使用 SCAN cursor [MATCH pattern] [COUNT count]u003Cu002Fpu003Eu003Cpu003Eu003Cstrong toutiao-origin=”span” class=”highlight-text”u003E注:u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003Ecursor:游标u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003EMATCH pattern:查询 Key 的条件u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003ECount:返回的条数u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003ESCAN 是一个基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程。u003Cu002Fpu003Eu003Cpu003ESCAN 以 0 作为游标,开始一次新的迭代,直到命令返回游标 0 完成一次遍历。u003Cu002Fpu003Eu003Cpu003E此命令并不保证每次执行都返回某个给定数量的元素,甚至会返回 0 个元素,但只要游标不是 0,程序都不会认为 SCAN 命令结束,但是返回的元素数量大概率符合 Count 参数。另外,SCAN 支持模糊查询。u003Cu002Fpu003Eu003Cpu003E例:u003Cu002Fpu003Eu003Cpreu003Eu003Cpu003ESCAN 0 MATCH test* COUNT 10 u002Fu002F每次返回10条以test为前缀的keyu003Cu002Fpu003Eu003Cu002Fpreu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003E如何通过 Redis 实现分布式锁u003Cu002Fh1u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003E分布式锁u003Cu002Fh1u003Eu003Cpu003E分布式锁是控制分布式系统之间共同访问共享资源的一种锁的实现。如果一个系统,或者不同系统的不同主机之间共享某个资源时,往往需要互斥,来排除干扰,满足数据一致性。u003Cu002Fpu003Eu003Cpu003Eu003Cstrong toutiao-origin=”span” class=”highlight-text”u003E分布式锁需要解决的问题如下:u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003E互斥性:u003Cu002Fstrongu003E任意时刻只有一个客户端获取到锁,不能有两个客户端同时获取到锁。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003E安全性:u003Cu002Fstrongu003E锁只能被持有该锁的客户端删除,不能由其他客户端删除。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003E死锁:u003Cu002Fstrongu003E获取锁的客户端因为某些原因而宕机继而无法释放锁,其他客户端再也无法获取锁而导致死锁,此时需要有特殊机制来避免死锁。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003E容错:u003Cu002Fstrongu003E当各个节点,如某个 Redis 节点宕机的时候,客户端仍然能够获取锁或释放锁。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cstrong toutiao-origin=”strong” class=”highlight-text”u003E如何使用 Redis 实现分布式锁u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E使用 SETNX 实现,u003Cu002Fstrongu003ESETNX key value:如果 Key 不存在,则创建并赋值。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003E该命令时间复杂度为 O(1),如果设置成功,则返回 1,否则返回 0。u003Cu002Fh1u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRfMINIm1uD3JFY” img_width=”1080″ img_height=”255″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cpu003E由于 SETNX 指令操作简单,且是原子性的,所以初期的时候经常被人们作为分布式锁,我们在应用的时候,可以在某个共享资源区之前先使用 SETNX 指令,查看是否设置成功。u003Cu002Fpu003Eu003Cpu003E如果设置成功则说明前方没有客户端正在访问该资源,如果设置失败则说明有客户端正在访问该资源,那么当前客户端就需要等待。u003Cu002Fpu003Eu003Cpu003E但是如果真的这么做,就会存在一个问题,因为 SETNX 是长久存在的,所以假设一个客户端正在访问资源,并且上锁,那么当这个客户端结束访问时,该锁依旧存在,后来者也无法成功获取锁,这个该如何解决呢?u003Cu002Fpu003Eu003Cpu003E由于 SETNX 并不支持传入 EXPIRE 参数,所以我们可以直接使用 EXPIRE 指令来对特定的 Key 来设置过期时间。u003Cu002Fpu003Eu003Cpu003E用法:u003Cu002Fpu003Eu003Cpreu003Eu003Cdivu003Eu003Cpu003EEXPIRE key secondsu003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cu002Fpreu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRfMINIz8q9GeMt” img_width=”1080″ img_height=”267″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cpu003E程序:u003Cu002Fpu003Eu003Cpreu003Eu003Cdivu003Eu003Cpu003ERedisService redisService = SpringUtils.getBean(RedisService.class);u003Cu002Fpu003Eu003Cpu003Elong status = redisService.setnx(key,”1″);u003Cu002Fpu003Eu003Cpu003Eif(status == 1){u003Cu002Fpu003Eu003Cpu003EredisService.expire(key,expire);u003Cu002Fpu003Eu003Cpu003EdoOcuppiedWork;u003Cu002Fpu003Eu003Cpu003E}u003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cu002Fpreu003Eu003Cpu003E这段程序存在的问题:假设程序运行到第二行出现异常,那么程序来不及设置过期时间就结束了,则 Key 会一直存在,等同于锁一直被持有无法释放。u003Cu002Fpu003Eu003Cpu003E出现此问题的根本原因为:原子性得不到满足。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E解决:u003Cu002Fstrongu003E从 Redis 2.6.12 版本开始,我们就可以使用 Set 操作,将 SETNX 和 EXPIRE 融合在一起执行,具体做法如下:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003EEX second:u003Cu002Fstrongu003E设置键的过期时间为 Second 秒。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003EPX millisecond:u003Cu002Fstrongu003E设置键的过期时间为 MilliSecond 毫秒。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003ENX:u003Cu002Fstrongu003E只在键不存在时,才对键进行设置操作。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003EXX:u003Cu002Fstrongu003E只在键已经存在时,才对键进行设置操作。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpreu003Eu003Cpu003ESET KEY value [EX seconds] [PX milliseconds] [NX|XX]u003Cu002Fpu003Eu003Cu002Fpreu003Eu003Cpu003E注:SET 操作成功完成时才会返回 OK,否则返回 nil。u003Cu002Fpu003Eu003Cpu003E有了 SET 我们就可以在程序中使用类似下面的代码实现分布式锁了:u003Cu002Fpu003Eu003Cpreu003Eu003Cdivu003Eu003Cpu003ERedisService redisService = SpringUtils.getBean(RedisService.class);u003Cu002Fpu003Eu003Cpu003EString result = redisService.set(lockKey,requestId,SET_IF_NOT_Eu003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-1″u003EXIu003Cu002Fiu003EST,SET_WITH_EXPIRE_TIME,expireTime);u003Cu002Fpu003Eu003Cpu003Eif(“OK.equals(result)”){u003Cu002Fpu003Eu003Cpu003EdoOcuppiredWork;u003Cu002Fpu003Eu003Cpu003E}u003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cu002Fpreu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003E如何实现异步队列u003Cu002Fh1u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003E①使用 Redis 中的 List 作为队列u003Cu002Fh1u003Eu003Cpu003E使用上文所说的 Redis 的数据结构中的 List 作为队列 Rpush 生产消息,LPOP 消费消息。u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRfMINJFDeMjo1M” img_width=”883″ img_height=”292″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cpu003E此时我们可以看到,该队列是使用 Rpush 生产队列,使用 LPOP 消费队列。u003Cu002Fpu003Eu003Cpu003E在这个生产者-消费者队列里,当 LPOP 没有消息时,证明该队列中没有元素,并且生产者还没有来得及生产新的数据。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E缺点:u003Cu002Fstrongu003ELPOP 不会等待队列中有值之后再消费,而是直接进行消费。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003E弥补:u003Cu002Fstrongu003E可以通过在应用层引入 Sleep 机制去调用 LPOP 重试。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003E②使用 BLPOP key [key…] timeoutu003Cu002Fh1u003Eu003Cpu003EBLPOP key [key …] timeout:阻塞直到队列有消息或者超时。u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRfMINrREoJZRXC” img_width=”1080″ img_height=”218″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cimg src=”http:u002Fu002Fp9.pstatp.comu002Flargeu002Fpgc-imageu002FRfMINro8vwyDcG” img_width=”524″ img_height=”199″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRfMINs0AcF3ySf” img_width=”1080″ img_height=”187″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cpu003Eu003Cstrongu003E缺点:u003Cu002Fstrongu003E按照此种方法,我们生产后的数据只能提供给各个单一消费者消费。能否实现生产一次就能让多个消费者消费呢?u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003E③Pubu002FSub:主题订阅者模式u003Cu002Fh1u003Eu003Cpu003E发送者(Pub)发送消息,订阅者(Sub)接收消息。订阅者可以订阅任意数量的频道。u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRfMINsCGXvVy40″ img_width=”1080″ img_height=”373″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cpu003Eu003Cstrongu003EPubu002FSub模式的缺点:u003Cu002Fstrongu003E消息的发布是无状态的,无法保证可达。对于发布者来说,消息是“即发即失”的。u003Cu002Fpu003Eu003Cpu003E此时如果某个消费者在生产者发布消息时下线,重新上线之后,是无法接收该消息的,要解决该问题需要使用专业的消息队列,如 Kafka…此处不再赘述。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003ERedis 持久化u003Cu002Fh1u003Eu003Ch1 toutiao-origin=”h3″u003E什么是持久化u003Cu002Fh1u003Eu003Cpu003E持久化,即将数据持久存储,而不因断电或其他各种复杂外部环境影响数据的完整性。u003Cu002Fpu003Eu003Cpu003E由于 Redis 将数据存储在内存而不是磁盘中,所以内存一旦断电,Redis 中存储的数据也随即消失,这往往是用户不期望的,所以 Redis 有持久化机制来保证数据的安全性。u003Cu002Fpu003Eu003Cpu003Eu003Cstrong toutiao-origin=”strong” class=”highlight-text”u003ERedis 如何做持久化u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003ERedis 目前有两种持久化方式,即 RDB 和 AOF,RDB 是通过保存某个时间点的全量数据快照实现数据的持久化,当恢复数据时,直接通过 RDB 文件中的快照,将数据恢复。u003Cu002Fpu003Eu003Cpu003Eu003Cstrong toutiao-origin=”span” class=”highlight-text”u003ERDB(快照)持久化u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003ERDB持久化会在某个特定的间隔保存那个时间点的全量数据的快照。u003Cu002Fh1u003Eu003Cpu003ERDB 配置文件,redis.conf:u003Cu002Fpu003Eu003Cpreu003Eu003Cdivu003Eu003Cpu003Esave 900 1 #在900s内如果有1条数据被写入,则产生一次快照。u003Cu002Fpu003Eu003Cpu003Esave 300 10 #在300s内如果有10条数据被写入,则产生一次快照u003Cu002Fpu003Eu003Cpu003Esave 60 10000 #在60s内如果有10000条数据被写入,则产生一次快照u003Cu002Fpu003Eu003Cpu003Estop-writes-on-bgsave-error yes u003Cu002Fpu003Eu003Cpu003E#stop-writes-on-bgsave-error :u003Cu002Fpu003Eu003Cpu003E如果为yes则表示,当备份进程出错的时候,u003Cu002Fpu003Eu003Cpu003E主进程就停止进行接受新的写入操作,这样是为了保护持久化的数据一致性的问题。u003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cu002Fpreu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h4″u003Eu003Cstrongu003E①RDB 的创建与载入u003Cu002Fstrongu003Eu003Cu002Fh2u003Eu003Cpu003Eu003Cstrongu003ESAVE:u003Cu002Fstrongu003E阻塞 Redis 的服务器进程,直到 RDB 文件被创建完毕。SAVE 命令很少被使用,因为其会阻塞主线程来保证快照的写入,由于 Redis 是使用一个主线程来接收所有客户端请求,这样会阻塞所有客户端请求。u003Cu002Fpu003Eu003Cpu003Eu003Cstrongu003EBGSAVE:u003Cu002Fstrongu003E该指令会 Fork 出一个子进程来创建 RDB 文件,不阻塞服务器进程,子进程接收请求并创建 RDB 快照,父进程继续接收客户端的请求。u003Cu002Fpu003Eu003Cpu003E子进程在完成文件的创建时会向父进程发送信号,父进程在接收客户端请求的过程中,在一定的时间间隔通过轮询来接收子进程的信号。u003Cu002Fpu003Eu003Cpu003E我们也可以通过使用 lastsave 指令来查看 BGSAVE 是否执行成功,lastsave 可以返回最后一次执行成功 BGSAVE 的时间。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h4″u003Eu003Cstrongu003E②自动化触发 RDB 持久化的方式u003Cu002Fstrongu003Eu003Cu002Fh2u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h4″u003Eu003Cstrong toutiao-origin=”span” class=”highlight-text”u003E自动化触发RDB持久化的方式如下:u003Cu002Fstrongu003Eu003Cu002Fh2u003Eu003Culu003Eu003Cliu003Eu003Cpu003E根据 redis.conf 配置里的 SAVE m n 定时触发(实际上使用的是 BGSAVE)。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E主从复制时,主节点自动触发。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E执行 Debug Reload。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E执行 Shutdown 且没有开启 AOF 持久化。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h4″u003Eu003Cstrongu003E③BGSAVE 的原理u003Cu002Fstrongu003Eu003Cu002Fh2u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRfMINsNHNDWznc” img_width=”911″ img_height=”561″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cpu003Eu003Cstrong toutiao-origin=”span” class=”highlight-text”u003E启动:u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003E检查是否存在子进程正在执行 AOF 或者 RDB 的持久化任务。如果有则返回 false。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E调用 Redis 源码中的 rdbSaveBackground 方法,方法中执行 fork 产生子进程执行 RDB 操作。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cdivu003Eu003Cpu003E关于 fork 中的 Copy-On-Write。u003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Efork 在 Linux 中创建子进程采用 Copy-On-Write(写时拷贝技术),即如果有多个调用者同时要求相同资源(如内存或磁盘上的数据存储)。u003Cu002Fpu003Eu003Cpu003E他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本给调用者,而其他调用者所见到的最初的资源仍然保持不变。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h4″u003Eu003Cstrongu003E④RDB 持久化方式的缺点u003Cu002Fstrongu003Eu003Cu002Fh2u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h4″u003Eu003Cstrong toutiao-origin=”span” class=”highlight-text”u003ERDB 持久化方式的缺点如下:u003Cu002Fstrongu003Eu003Cu002Fh2u003Eu003Culu003Eu003Cliu003Eu003Cpu003E内存数据全量同步,数据量大的状况下,会由于 Iu002FO 而严重影响性能。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E可能会因为 Redis 宕机而丢失从当前至最近一次快照期间的数据。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cstrong toutiao-origin=”strong” class=”highlight-text”u003EAOF 持久化:u003Cu002Fstrongu003Eu003Cstrong toutiao-origin=”strong” class=”highlight-text”u003E保存写状态u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003EAOF 持久化是通过保存 Redis 的写状态来记录数据库的。u003Cu002Fpu003Eu003Cpu003E相对 RDB 来说,RDB 持久化是通过备份数据库的状态来记录数据库,而 AOF 持久化是备份数据库接收到的指令:u003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003EAOF 记录除了查询以外的所有变更数据库状态的指令。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E以增量的形式追加保存到 AOF 文件中。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cstrong toutiao-origin=”strong” class=”highlight-text”u003E开启 AOF 持久化u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E①打开 redis.conf 配置文件,将 appendonly 属性改为 yes。u003Cu002Fpu003Eu003Cpu003E②修改 appendfsync 属性,该属性可以接收三种参数,分别是 always,everysec,no。u003Cu002Fpu003Eu003Cpu003Ealways 表示总是即时将缓冲区内容写入 AOF 文件当中,everysec 表示每隔一秒将缓冲区内容写入 AOF 文件,no 表示将写入文件操作交由操作系统决定。u003Cu002Fpu003Eu003Cpu003E一般来说,操作系统考虑效率问题,会等待缓冲区被填满再将缓冲区数据写入 AOF 文件中。u003Cu002Fpu003Eu003Cpreu003Eu003Cdivu003Eu003Cpu003Eappendonly u003Cstrong toutiao-origin=”span” class=”highlight-text”u003Eyesu003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E#appendsync alwaysu003Cu002Fpu003Eu003Cpu003Eappendfsync everysecu003Cu002Fpu003Eu003Cpu003E# appendfsync nou003Cu002Fpu003Eu003Cu002Fdivu003Eu003Cu002Fpreu003Eu003Cpu003Eu003Cstrong toutiao-origin=”strong” class=”highlight-text”u003E日志重写解决 AOF 文件不断增大u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E随着写操作的不断增加,AOF 文件会越来越大。假设递增一个计数器 100 次,如果使用 RDB 持久化方式,我们只要保存最终结果 100 即可。u003Cu002Fpu003Eu003Cpu003E而 AOF 持久化方式需要记录下这 100 次递增操作的指令,而事实上要恢复这条记录,只需要执行一条命令就行,所以那一百条命令实际可以精简为一条。u003Cu002Fpu003Eu003Cpu003ERedis 支持这样的功能,在不中断前台服务的情况下,可以重写 AOF 文件,同样使用到了 COW(写时拷贝)。u003Cu002Fpu003Eu003Cpu003Eu003Cstrong toutiao-origin=”span” class=”highlight-text”u003E重写过程如下:u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003E调用 fork,创建一个子进程。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E子进程把新的 AOF 写到一个临时文件里,不依赖原来的 AOF 文件。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E主进程持续将新的变动同时写到内存和原来的 AOF 里。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E主进程获取子进程重写 AOF 的完成信号,往新 AOF 同步增量变动。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E使用新的 AOF 文件替换掉旧的 AOF 文件。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cstrong toutiao-origin=”strong” class=”highlight-text”u003EAOF 和 RDB 的优缺点u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003EAOF 和 RDB 的优缺点如下:u003Cu002Fh1u003Eu003Culu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003ERDB 优点:u003Cu002Fstrongu003E全量数据快照,文件小,恢复快。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003ERDB 缺点:u003Cu002Fstrongu003E无法保存最近一次快照之后的数据。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003EAOF 优点:u003Cu002Fstrongu003E可读性高,适合保存增量数据,数据不易丢失。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003EAOF 缺点:u003Cu002Fstrongu003E文件体积大,恢复时间长。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cstrong toutiao-origin=”strong” class=”highlight-text”u003ERDB-AOF 混合持久化方式u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003ERedis 4.0 之后推出了此种持久化方式,RDB 作为全量备份,AOF 作为增量备份,并且将此种方式作为默认方式使用。u003Cu002Fpu003Eu003Cpu003E在上述两种方式中,RDB 方式是将全量数据写入 RDB 文件,这样写入的特点是文件小,恢复快,但无法保存最近一次快照之后的数据,AOF 则将 Redis 指令存入文件中,这样又会造成文件体积大,恢复时间长等弱点。u003Cu002Fpu003Eu003Cpu003E在 RDB-AOF 方式下,持久化策略首先将缓存中数据以 RDB 方式全量写入文件,再将写入后新增的数据以 AOF 的方式追加在 RDB 数据的后面,在下一次做 RDB 持久化的时候将 AOF 的数据重新以 RDB 的形式写入文件。u003Cu002Fpu003Eu003Cpu003E这种方式既可以提高读写和恢复效率,也可以减少文件大小,同时可以保证数据的完整性。u003Cu002Fpu003Eu003Cpu003E在此种策略的持久化过程中,子进程会通过管道从父进程读取增量数据,在以 RDB 格式保存全量数据时,也会通过管道读取数据,同时不会造成管道阻塞。u003Cu002Fpu003Eu003Cpu003E可以说,在此种方式下的持久化文件,前半段是 RDB 格式的全量数据,后半段是 AOF 格式的增量数据。此种方式是目前较为推荐的一种持久化方式。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003ERedis 数据的恢复u003Cu002Fh1u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003ERDB 和 AOF 文件共存情况下的恢复流程如下图:u003Cu002Fh1u003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRfMIOKp5Madmva” img_width=”529″ img_height=”643″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cpu003E从图可知,Redis 启动时会先检查 AOF 是否存在,如果 AOF 存在则直接加载 AOF,如果不存在 AOF,则直接加载 RDB 文件。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003EPinelineu003Cu002Fh1u003Eu003Cpu003EPipeline 和 Linux 的管道类似,它可以让 Redis 批量执行指令。u003Cu002Fpu003Eu003Cpu003ERedis 基于请求u002F响应模型,单个请求处理需要u003Ci class=”chrome-extension-mutihighlight chrome-extension-mutihighlight-style-4″u003E一一u003Cu002Fiu003E应答。如果需要同时执行大量命令,则每条命令都需要等待上一条命令执行完毕后才能继续执行,这中间不仅仅多了 RTT,还多次使用了系统 IO。u003Cu002Fpu003Eu003Cpu003EPipeline 由于可以批量执行指令,所以可以节省多次 IO 和请求响应往返的时间。但是如果指令之间存在依赖关系,则建议分批发送指令。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003ERedis 的同步机制u003Cu002Fh1u003Eu003Ch1 toutiao-origin=”h3″u003E主从同步原理u003Cu002Fh1u003Eu003Cpu003ERedis 一般是使用一个 Master 节点来进行写操作,而若干个 Slave 节点进行读操作,Master 和 Slave 分别代表了一个个不同的 Redis Server 实例。u003Cu002Fpu003Eu003Cpu003E另外定期的数据备份操作也是单独选择一个 Slave 去完成,这样可以最大程度发挥 Redis 的性能,为的是保证数据的弱一致性和最终一致性。u003Cu002Fpu003Eu003Cpu003E另外,Master 和 Slave 的数据不是一定要即时同步的,但是在一段时间后 Master 和 Slave 的数据是趋于同步的,这就是最终一致性。u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRfMIOL1222CAEj” img_width=”658″ img_height=”375″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h4″u003Eu003Cstrong toutiao-origin=”span” class=”highlight-text”u003E全同步过程如下:u003Cu002Fstrongu003Eu003Cu002Fh2u003Eu003Culu003Eu003Cliu003Eu003Cpu003ESlave 发送 Sync 命令到 Master。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003EMaster 启动一个后台进程,将 Redis 中的数据快照保存到文件中。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003EMaster 将保存数据快照期间接收到的写命令缓存起来。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003EMaster 完成写文件操作后,将该文件发送给 Slave。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E使用新的 AOF 文件替换掉旧的 AOF 文件。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003EMaster 将这期间收集的增量写命令发送给 Slave 端。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h4″u003Eu003Cstrong toutiao-origin=”span” class=”highlight-text”u003E增量同步过程如下:u003Cu002Fstrongu003Eu003Cu002Fh2u003Eu003Culu003Eu003Cliu003Eu003Cpu003EMaster 接收到用户的操作指令,判断是否需要传播到 Slave。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E将操作记录追加到 AOF 文件。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E将操作传播到其他 Slave:对齐主从库;往响应缓存写入指令。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003E将缓存中的数据发送给 Slave。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cstrong toutiao-origin=”span” class=”highlight-text”u003ERedis Sentinel(哨兵)u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Cpu003E主从模式弊端:当 Master 宕机后,Redis 集群将不能对外提供写入操作。Redis Sentinel 可解决这一问题。u003Cu002Fpu003Eu003Cpu003Eu003Cstrong toutiao-origin=”span” class=”highlight-text”u003E解决主从同步 Master 宕机后的主从切换问题:u003Cu002Fstrongu003Eu003Cu002Fpu003Eu003Culu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003E监控:u003Cu002Fstrongu003E检查主从服务器是否运行正常。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003E提醒:u003Cu002Fstrongu003E通过 API 向管理员或者其它应用程序发送故障通知。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cliu003Eu003Cpu003Eu003Cstrongu003E自动故障迁移:u003Cu002Fstrongu003E主从切换(在 Master 宕机后,将其中一个 Slave 转为 Master,其他的 Slave 从该节点同步数据)。u003Cu002Fpu003Eu003Cu002Fliu003Eu003Cu002Fulu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003ERedis 集群u003Cu002Fh1u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003E如何从海量数据里快速找到所需?u003Cu002Fh1u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h4″u003Eu003Cstrongu003E①分片u003Cu002Fstrongu003Eu003Cu002Fh2u003Eu003Cpu003E按照某种规则去划分数据,分散存储在多个节点上。通过将数据分到多个 Redis 服务器上,来减轻单个 Redis 服务器的压力。u003Cu002Fpu003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch2 toutiao-origin=”h4″u003Eu003Cstrongu003E②一致性 Hash 算法u003Cu002Fstrongu003Eu003Cu002Fh2u003Eu003Cpu003E既然要将数据进行分片,那么通常的做法就是获取节点的 Hash 值,然后根据节点数求模。u003Cu002Fpu003Eu003Cpu003E但这样的方法有明显的弊端,当 Redis 节点数需要动态增加或减少的时候,会造成大量的 Key 无法被命中。所以 Redis 中引入了一致性 Hash 算法。u003Cu002Fpu003Eu003Cpu003E该算法对 2^32 取模,将 Hash 值空间组成虚拟的圆环,整个圆环按顺时针方向组织,每个节点依次为 0、1、2…2^32-1。u003Cu002Fpu003Eu003Cpu003E之后将每个服务器进行 Hash 运算,确定服务器在这个 Hash 环上的地址,确定了服务器地址后,对数据使用同样的 Hash 算法,将数据定位到特定的 Redis 服务器上。u003Cu002Fpu003Eu003Cpu003E如果定位到的地方没有 Redis 服务器实例,则继续顺时针寻找,找到的第一台服务器即该数据最终的服务器位置。u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRfMIOLIGwzcwim” img_width=”490″ img_height=”320″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cpu003Eu003Cu002Fpu003Eu003Ch1 toutiao-origin=”h3″u003E③Hash 环的数据倾斜问题u003Cu002Fh1u003Eu003Cpu003EHash 环在服务器节点很少的时候,容易遇到服务器节点不均匀的问题,这会造成数据倾斜,数据倾斜指的是被缓存的对象大部分集中在 Redis 集群的其中一台或几台服务器上。u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FRfMIOLbGsqTadx” img_width=”389″ img_height=”414″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cpu003E如上图,一致性 Hash 算法运算后的数据大部分被存放在 A 节点上,而 B 节点只存放了少量的数据,久而久之 A 节点将被撑爆。u003Cu002Fpu003Eu003Cpu003E针对这一问题,可以引入虚拟节点解决。简单地说,就是为每一个服务器节点计算多个 Hash,每个计算结果位置都放置一个此服务器节点,称为虚拟节点,可以在服务器 IP 或者主机名后放置一个编号实现。u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp1.pstatp.comu002Flargeu002Fpgc-imageu002FRfMIOLn5EIOUNn” img_width=”409″ img_height=”421″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003Eu003Cpu003E例如上图:将 NodeA 和 NodeB 两个节点分为 Node A#1-A#3,NodeB#1-B#3。u003Cu002Fpu003Eu003Cp class=”pgc-end-source”u003E【END】u003Cu002Fpu003Eu003Cimg src=”http:u002Fu002Fp3.pstatp.comu002Flargeu002Fpgc-imageu002FReU3KFq9FXVax2″ img_width=”1071″ img_height=”742″ alt=”一文掌握 Redis 常用知识点 – 图文结合” inline=”0″u003E”

原文始发于:一文掌握 Redis 常用知识点 – 图文结合

主题测试文章,只做测试使用。发布者:逗乐男神i,转转请注明出处:http://www.cxybcw.com/17714.html

联系我们

13687733322

在线咨询:点击这里给我发消息

邮件:1877088071@qq.com

工作时间:周一至周五,9:30-18:30,节假日休息

QR code