写点什么

建议收藏!看完全面掌握,最详细的 Redis 总结(2021 最新版)

用户头像
民工哥
关注
发布于: 2021 年 04 月 15 日

Redis 简介

Redis 是一个使用 C 语言编写的,开源的(BSD 许可)高性能非关系型(NoSQL)的键值对数据库。


Redis 可以存储键和五种不同类型的值之间的映射。键的类型只能为字符串,值支持五种数据类型:字符串、列表、集合、散列表、有序集合。


与传统数据库不同的是 Redis 的数据是存在内存中的,所以读写速度非常快,因此 redis 被广泛应用于缓存方向,每秒可以处理超过 10 万次读写操作,是已知性能最快的 Key-Value DB。另外,Redis 也经常用来做分布式锁。除此之外,Redis 支持事务 、持久化、LUA 脚本、LRU 驱动事件、多种集群方案(Redis 6.0 集群搭建实践)。


从 2010 年 3 月 15 日起,Redis 的开发工作由 VMware 主持。从 2013 年 5 月开始,Redis 的开发由 Pivotal 赞助。

Redis 的优缺点

优点

  • 读写性能优异, Redis 能读的速度是 110000 次/s,写的速度是 81000 次/s。

  • 支持数据持久化,支持 AOF 和 RDB 两种持久化方式。

  • 支持事务,Redis 的所有操作都是原子性的,同时 Redis 还支持对几个操作合并后的原子性执行。

  • 数据结构丰富,除了支持 string 类型的 value 外还支持 hash、set、zset、list 等数据结构。

  • 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。

缺点

  • 数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此 Redis 适合的场景主要局限在较小数据量的高性能操作和运算上。

  • Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的 IP 才能恢复。

  • 主机宕机,宕机前有部分数据未能及时同步到从机,切换 IP 后还会引入数据不一致的问题,降低了系统的可用性。

  • Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。

数据类型

Redis 主要有 5 种数据类型,包括 String,List,Set,Zset,Hash,满足大部分的使用要求。详细的可参考:Redis 的 8 大数据类型,写得非常好!

使用场景

由于 Redis 优异的读写性能,持久化支持等优势,Redis 的使用场景非常多,主要包括计数器,缓存,消息队列,分布式锁等,具体使用场景如下:


  • 计数器

  • 可以对 String 进行自增自减运算,从而实现计数器功能。

  • Redis 这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。

  • 缓存

  • 将热点数据放到内存中,设置内存的最大使用量以及淘汰策略来保证缓存的命中率。

  • 会话缓存

  • 可以使用 Redis 来统一存储多台应用服务器的会话信息。

  • 当应用服务器不再存储用户的会话信息,也就不再具有状态,一个用户可以请求任意一个应用服务器,从而更容易实现高可用性以及可伸缩性。

  • 全页缓存(FPC)

  • 除基本的会话 token 之外,Redis 还提供很简便的 FPC 平台。

  • 以 Magento 为例,Magento 提供一个插件来使用 Redis 作为全页缓存后端。此外,对 WordPress 的用户来说,Pantheon 有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面。

  • 查找表

  • 例如 DNS 记录就很适合使用 Redis 进行存储。

  • 查找表和缓存类似,也是利用了 Redis 快速的查找特性。但是查找表的内容不能失效,而缓存的内容可以失效,因为缓存不作为可靠的数据来源。

  • 消息队列(发布/订阅功能)

  • List 是一个双向链表,可以通过 lpush 和 rpop 写入和读取消息

  • 不过最好使用 Kafka、RabbitMQ 等消息中间件。

  • 分布式锁实现

  • 在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。

  • 可以使用 Redis 自带的 SETNX 命令实现分布式锁,除此之外,还可以使用官方提供的 RedLock 分布式锁实现。

  • 其它

  • Set 可以实现交集、并集等操作,从而实现共同好友等功能。

  • ZSet 可以实现有序性操作,从而实现排行榜等功能。

持久化

Redis 是内存型数据库,为了之后重用数据(比如重启机器、机器故障之后回复数据),或者是为了防止系统故障而将数据备份到一个远程位置,需要将内存中的数据持久化到硬盘上。


Redis 提供了 RDB 和 AOF 两种持久化方式。默认是只开启 RDB,当 Redis 重启时,它会优先使用 AOF 文件来还原数据集。


Redis 持久化详解可以参考:Redis持久化

过期键的删除策略

Redis 中有个设置时间过期的功能,即对存储在 redis 数据库中的值可以设置一个过期时间。作为一个缓存数据库,这是非常实用的。如我们一般项目中的 token 或者一些登录信息,尤其是短信验证码都是有时间限制的,按照传统的数据库处理方式,一般都是自己判断过期,这样无疑会严重影响项目性能。


Redis 有三种不同的删除策略:立即删除,惰性删除,定时删除


  • (1):立即删除。在设置键的过期时间时,创建一个回调事件,当过期时间达到时,由时间处理器自动执行键的删除操作。

  • (2):惰性删除。键过期了就过期了,不管。每次从 dict 字典中按 key 取值时,先检查此 key 是否已经过期,如果过期了就删除它,并返回 nil,如果没过期,就返回键值。

  • (3):定时删除。每隔一段时间,对 expires 字典进行检查,删除里面的过期键。


可以看到,第二种为被动删除,第一种和第三种为主动删除,且第一种实时性更高。下面对这三种删除策略进行具体分析。


  • 立即删除


立即删除能保证内存中数据的最大新鲜度,因为它保证过期键值会在过期后马上被删除,其所占用的内存也会随之释放。但是立即删除对 cpu 是最不友好的。因为删除操作会占用 cpu 的时间,如果刚好碰上了 cpu 很忙的时候,比如正在做交集或排序等计算的时候,就会给 cpu 造成额外的压力。


而且目前 redis 事件处理器对时间事件的处理方式–无序链表,查找一个 key 的时间复杂度为 O(n),所以并不适合用来处理大量的时间事件。


  • 惰性删除


惰性删除是指,某个键值过期后,此键值不会马上被删除,而是等到下次被使用的时候,才会被检查到过期,此时才能得到删除。所以惰性删除的缺点很明显:浪费内存。dict 字典和 expires 字典都要保存这个键值的信息。


举个例子,对于一些按时间点来更新的数据,比如 log 日志,过期后在很长的一段时间内可能都得不到访问,这样在这段时间内就要拜拜浪费这么多内存来存 log。这对于性能非常依赖于内存大小的 redis 来说,是比较致命的。


  • 定时删除


从上面分析来看,立即删除会短时间内占用大量 cpu,惰性删除会在一段时间内浪费内存,所以定时删除是一个折中的办法。


定时删除是:每隔一段时间执行一次删除操作,并通过限制删除操作执行的时长和频率,来减少删除操作对 cpu 的影响。另一方面定时删除也有效的减少了因惰性删除带来的内存浪费。


  • Redis 使用的策略


redis 使用的过期键值删除策略是:惰性删除加上定期删除,两者配合使用。

数据淘汰策略

可以设置内存最大使用量,当内存使用量超出时,会施行数据淘汰策略。


Redis 具体有 6 种淘汰策略:



作为内存数据库,出于对性能和内存消耗的考虑,Redis 的淘汰算法实际实现上并非针对所有 key,而是抽样一小部分并且从中选出被淘汰的 key。


Redis 4.0 引入了 volatile-lfu 和 allkeys-lfu 淘汰策略,LFU 策略通过统计访问频率,将访问频率最少的键值对淘汰。


您需要根据系统的特征,来选择合适的淘汰策略。 当然,在运行过程中也可以通过命令动态设置淘汰策略,并通过 INFO 命令监控缓存的 miss 和 hit,来进行调优。


淘汰策略的内部实现


  • 客户端执行一个命令,导致 Redis 中的数据增加,占用更多内存

  • Redis 检查内存使用量,如果超出 maxmemory 限制,根据策略清除部分 key

  • 继续执行下一条命令,以此类推


在这个过程中,内存使用量会不断地达到 limit 值,然后超过,然后删除部分 key,使用量又下降到 limit 值之下。


如果某个命令导致大量内存占用(比如通过新 key 保存一个很大的 set),在一段时间内,可能内存的使用量会明显超过 maxmemory 限制。

Redis 与 Memcached 的区别

两者都是非关系型内存键值数据库,现在公司一般都是用 Redis 来实现缓存,而且 Redis 自身也越来越强大了!Redis 与 Memcached 的区别请参考:Redis与Memcached的区别

事务

Redis 通过 MULTI、EXEC、WATCH 等命令来实现事务(transaction)功能。事务提供了一种将多个命令请求打包,然后一次性、按顺序地执行多个命令的机制,并且在事务执行期间,服务器不会中断事务而改去执行其他客户端的命令请求,它会将事务中的所有命令都执行完毕,然后才去处理其他客户端的命令请求。


事务中的多个命令被一次性发送给服务器,而不是一条一条发送,这种方式被称为流水线,可以减少客户端与服务器之间的网络通信次数从而提升性能。


在传统的关系式数据库中,常用 ACID 性质来检验事务功能的可靠性和安全性。在 Redis 中,事务总是具有原子性(Atomicity)、一致性(Consistency)和隔离性(Isolation),并且当 Redis 运行在某种特定的持久化模式下时,事务也具有持久性(Durability)。


  • 事件


Redis 服务器是一个事件驱动程序。


  • 文件事件


服务器通过套接字与客户端或者其它服务器进行通信,文件事件就是对套接字操作的抽象。


Redis 基于 Reactor 模式开发了自己的网络事件处理器,使用 I/O 多路复用程序来同时监听多个套接字,并将到达的事件传送给文件事件分派器,分派器会根据套接字产生的事件类型调用相应的事件处理器。



  • 时间事件


服务器有一些操作需要在给定的时间点执行,时间事件是对这类定时操作的抽象。


时间事件又分为:


  • 定时事件:是让一段程序在指定的时间之内执行一次

  • 周期性事件:是让一段程序每隔指定时间就执行一次


目前 Redis 只使用周期性事件,而没有使用定时事件。 一个事件时间主要由三个属性组成:


  • id:服务器为时间事件创建的全局唯一 ID

  • when:毫秒精度的 UNIX 时间戳,记录了时间事件的到达时间

  • timeProc:时间事件处理器,一个函数


实现服务器将所有时间事件都放在一个无序链表中,每当时间事件执行器运行时,遍历整个链表,查找所有已到达的时间事件,并调用相应的事件处理器。(该链表为无序链表,不按 when 属性的大小排序)


  • 事件的调度与执行


服务器需要不断监听文件事件的套接字才能得到待处理的文件事件,但是不能一直监听,否则时间事件无法在规定的时间内执行,因此监听时间应该根据距离现在最近的时间事件来决定。


Sentinel

Sentinel(哨兵)可以监听集群中的服务器,并在主服务器进入下线状态时,自动从从服务器中选举出新的主服务器。


Redis集群生产环境高可用方案实战过程

分片

分片是将数据划分为多个部分的方法,可以将数据存储到多台机器里面,这种方法在解决某些问题时可以获得线性级别的性能提升。


假设有 4 个 Redis 实例 R0,R1,R2,R3,还有很多表示用户的键 user:1,user:2,… ,有不同的方式来选择一个指定的键存储在哪个实例中。


最简单的方式是范围分片,例如用户 id 从 0~1000 的存储到实例 R0 中,用户 id 从 1001~2000 的存储到实例 R1 中,等等。但是这样需要维护一张映射范围表,维护操作代价很高。


还有一种方式是哈希分片,使用 CRC32 哈希函数将键转换为一个数字,再对实例数量求模就能知道应该存储的实例。


根据执行分片的位置,可以分为三种分片方式:


  • 客户端分片:客户端使用一致性哈希等算法决定键应当分布到哪个节点。

  • 代理分片:将客户端请求发送到代理上,由代理转发请求到正确的节点上。

  • 服务器分片:Redis Cluster。

复制

通过使用 slaveof host port 命令来让一个服务器成为另一个服务器的从服务器。


一个从服务器只能有一个主服务器,并且不支持主主复制。


  • 连接过程

  • 主服务器创建快照文件,发送给从服务器,并在发送期间使用缓冲区记录执行的写命令。快照文件发送完毕之后,开始向从服务器发送存储在缓冲区中的写命令

  • 从服务器丢弃所有旧数据,载入主服务器发来的快照文件,之后从服务器开始接受主服务器发来的写命令

  • 主服务器每执行一次写命令,就向从服务器发送相同的写命令

  • 主从链


随着负载不断上升,主服务器可能无法很快地更新所有从服务器,或者重新连接和重新同步从服务器将导致系统超载。为了解决这个问题,可以创建一个中间层来分担主服务器的复制工作。中间层的服务器是最上层服务器的从服务器,又是最下层服务器的主服务器。

Redis 中缓存雪崩、缓存穿透等问题的解决方案

缓存雪崩

缓存雪崩是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。

解决方案
  • 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。

  • 一般并发量不是特别多的时候,使用最多的解决方案是加锁排队。

  • 给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存。

缓存穿透

缓存穿透是指缓存和数据库中都没有的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。

解决方案
  • 接口层增加校验,如用户鉴权校验,id 做基础校验,id<=0 的直接拦截;

  • 从缓存取不到的数据,在数据库中也没有取到,这时也可以将 key-value 对写为 key-null,缓存有效时间可以设置短点,如 30 秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个 id 暴力攻击

  • 采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力


附加


  • 对于空间的利用到达了一种极致,那就是 Bitmap 和布隆过滤器(Bloom Filter)。

  • Bitmap: 典型的就是哈希表

  • 缺点是,Bitmap 对于每个元素只能记录 1bit 信息,如果还想完成额外的功能,恐怕只能靠牺牲更多的空间、时间来完成了。


布隆过滤器(推荐)


  • 就是引入了 k(k>1)k(k>1)个相互独立的哈希函数,保证在给定的空间、误判率下,完成元素判重的过程。

  • 它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

  • Bloom-Filter 算法的核心思想就是利用多个不同的 Hash 函数来解决“冲突”。

  • Hash 存在一个冲突(碰撞)的问题,用同一个 Hash 得到的两个 URL 的值有可能相同。为了减少冲突,我们可以多引入几个 Hash,如果通过其中的一个 Hash 值我们得出某元素不在集合中,那么该元素肯定不在集合中。只有在所有的 Hash 函数告诉我们该元素在集合中时,才能确定该元素存在于集合中。这便是 Bloom-Filter 的基本思想。

  • Bloom-Filter 一般用于在大数据量的集合中判定某元素是否存在。

缓存击穿

缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。

解决方案
  • 设置热点数据永远不过期。

  • 加互斥锁,互斥锁

缓存预热

缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!

解决方案
  • 直接写个缓存刷新页面,上线时手工操作一下;

  • 数据量不大,可以在项目启动的时候自动进行加载;

  • 定时刷新缓存;

缓存降级

当访问量剧增、服务出现问题(如响应时间慢或不响应)或非核心服务影响到核心流程的性能时,仍然需要保证服务还是可用的,即使是有损服务。系统可以根据一些关键数据进行自动降级,也可以配置开关实现人工降级。


缓存降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。


在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:


一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;


警告:有些服务在一段时间内成功率有波动(如在 95~100%之间),可以自动降级或人工降级,并发送告警;


错误:比如可用率低于 90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;


严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。


服务降级的目的,是为了防止 Redis 服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis 出现问题,不去数据库查询,而是直接返回默认值给用户。

热点数据和冷数据

热点数据,缓存才有价值


对于冷数据而言,大部分数据可能还没有再次访问到就已经被挤出内存,不仅占用内存,而且价值不大。频繁修改的数据,看情况考虑使用缓存


对于热点数据,比如我们的某 IM 产品,生日祝福模块,当天的寿星列表,缓存以后可能读取数十万次。再举个例子,某导航产品,我们将导航信息,缓存以后可能读取数百万次。


数据更新前至少读取两次,缓存才有意义。这个是最基本的策略,如果缓存还没有起作用就失效了,那就没有太大价值了。


那存不存在,修改频率很高,但是又不得不考虑缓存的场景呢?有!比如,这个读取接口对数据库的压力很大,但是又是热点数据,这个时候就需要考虑通过缓存手段,减少数据库的压力,比如我们的某助手产品的,点赞数,收藏数,分享数等是非常典型的热点数据,但是又不断变化,此时就需要将数据同步保存到 Redis 缓存,减少数据库压力。

缓存热点 key

缓存中的一个 Key(比如一个促销商品),在某个时间点过期的时候,恰好在这个时间点对这个 Key 有大量的并发请求过来,这些请求发现缓存过期一般都会从后端 DB 加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端 DB 压垮。

解决方案

对缓存查询加锁,如果 KEY 不存在,就加锁,然后查 DB 入缓存,然后解锁;其他进程如果发现有锁就等待,然后等解锁后返回数据或者进入 DB 查询


以上内容参考链接:https://blog.csdn.net/ThinkWon/article/details/99999584

Redis 优化最佳实践

我们就来总结一下,在使用Redis时的最佳实践方式,主要包含两个层面:业务层面、运维层面。


由于我之前写过很多 UGC 后端服务,在大量场景下用到了Redis,这个过程中也踩过很多坑,所以在使用过程中也总结了一套合理的使用方法。


后来做基础架构,开发 Codis、Redis相关的中间件,在这个阶段关注领域从使用层面下沉到 Redis 的开发和运维,更多聚焦在 Redis 的内部实现和运维过程中产生的各种问题,在这块也积累了一些经验。


下面就针对这两块,分享一下我认为比较合理的 Redis 使用和运维方法,不一定最全面,也可能与你使用 Redis 的方法不同,但以下这些方法都是我在踩坑之后总结的实际经验,供你参考。关注公众号 Java 技术栈回复 redis 获取系列 Redis 教程。


业务层面主要是开发人员需要关注,也就是开发人员在写业务代码时,如何合理地使用 Redis。开发人员需要对Redis有基本的了解,才能在合适的业务场景使用 Redis,从而避免业务层面导致的延迟问题。

在开发过程中,业务层面的优化建议如下:

  • key 的长度尽量要短,在数据量非常大时,过长的 key 名会占用更多的内存

  • 一定避免存储过大的数据(大 value),过大的数据在分配内存和释放内存时耗时严重,会阻塞主线程

  • Redis 4.0 以上建议开启 lazy-free 机制,释放大 value 时异步操作,不阻塞主线程

  • 建议设置过期时间,把 Redis 当做缓存使用,尤其在数量很大的时,不设置过期时间会导致内存的无限增长

  • 不使用复杂度过高的命令,例如 SORT、SINTER、SINTERSTORE、ZUNIONSTORE、ZINTERSTORE,使用这些命令耗时较久,会阻塞主线程

  • 查询数据时,一次尽量获取较少的数据,在不确定容器元素个数的情况下,避免使用 LRANGE key 0 -1,ZRANGE key 0 -1 这类操作,应该设置具体查询的元素个数,推荐一次查询 100 个以下元素

  • 写入数据时,一次尽量写入较少的数据,例如 HSET key value1 value2 value3...,-控制一次写入元素的数量,推荐在 100 以下,大数据量分多个批次写入

  • 批量操作数据时,用 MGET/MSET 替换 GET/SET、HMGET/MHSET 替换 HGET/HSET,减少请求来回的网络 IO 次数,降低延迟,对于没有批量操作的命令,推荐使用 pipeline,一次性发送多个命令到服务端

  • 禁止使用 KEYS 命令,需要扫描实例时,建议使用 SCAN,线上操作一定要控制扫描的频率,避免对 Redis 产生性能抖动

  • 避免某个时间点集中过期大量的 key,集中过期时推荐增加一个随机时间,把过期时间打散,降低集中过期 key 时 Redis 的压力,避免阻塞主线程

  • 根据业务场景,选择合适的淘汰策略,通常随机过期要比 LRU 过期淘汰数据更快

  • 使用连接池访问 Redis,并配置合理的连接池参数,避免短连接,TCP 三次握手和四次挥手的耗时也很高

  • 只使用 db0,不推荐使用多个 db,使用多个 db 会增加 Redis 的负担,每次访问不同的 db 都需要执行 SELECT 命令,如果业务线不同,建议拆分多个实例,还能提高单个实例的性能

  • 读的请求量很大时,推荐使用读写分离,前提是可以容忍从节数据更新不及时的问题

  • 写请求量很大时,推荐使用集群,部署多个实例分摊写压力

运维层面

目的是合理规划 Redis 的部署和保障 Redis 的稳定运行,主要优化如下:


  • 不同业务线部署不同的实例,各自独立,避免混用,推荐不同业务线使用不同的机器,根据业务重要程度划分不同的分组来部署,避免某一个业务线出现问题影响其他业务线

  • 保证机器有足够的 CPU、内存、带宽、磁盘资源,防止负载过高影响 Redis 性能

  • 以 master-slave 集群方式部署实例,并分布在不同机器上,避免单点,slave 必须设置为 readonly

  • master 和 slave 节点所在机器,各自独立,不要交叉部署实例,通常备份工作会在 slave 上做,做备份时会消耗机器资源,交叉部署会影响到 master 的性能

  • 推荐部署哨兵节点增加可用性,节点数量至少 3 个,并分布在不同机器上,实现故障自动故障转移

  • 提前做好容量规划,一台机器部署实例的内存上限,最好是机器内存的一半,主从全量同步时会占用最多额外一倍的内存空间,防止网络大面积故障引发所有 master-slave 的全量同步导致机器内存被吃光

  • 做好机器的 CPU、内存、带宽、磁盘监控,在资源不足时及时报警处理,Redis 使用 Swap 后性能急剧下降,网络带宽负载过高访问延迟明显增大,磁盘 IO 过高时开启 AOF 会拖慢 Redis 的性能

  • 设置最大连接数上限,防止过多的客户端连接导致服务负载过高

  • 单个实例的使用内存建议控制在 20G 以下,过大的实例会导致备份时间久、资源消耗多,主从全量同步数据时间阻塞时间更长

  • 设置合理的 slowlog 阈值,推荐 10 毫秒,并对其进行监控,产生过多的慢日志需要及时报警 设置合理的复制缓冲区 repl-backlog 大小,适当调大 repl-backlog 可以降低主从全量复制的概率

  • 设置合理的 slave 节点 client-output-buffer-limit 大小,对于写入量很大的实例,适当调大可以避免主从复制中断问题

  • 备份时推荐在 slave 节点上做,不影响 master 性能

  • 不开启 AOF 或开启 AOF 配置为每秒刷盘,避免磁盘 IO 消耗降低 Redis 性能

  • 当实例设置了内存上限,需要调大内存上限时,先调整 slave 再调整 master,否则会导致主从节点数据不一致

  • 对 Redis 增加监控,监控采集 info 信息时,使用长连接,频繁的短连接也会影响 Redis 性能,redis性能监控指标,参考这个文章

  • 线上扫描整个实例数时,记得设置休眠时间,避免扫描时 QPS 突增对 Redis 产生性能抖

  • 做好 Redis 的运行时监控,尤其是 expired_keys、evicted_keys、latest_fork_usec 指标,短时间内这些指标值突增可能会阻塞整个实例,引发性能问题


以上就是我在使用 Redis 和开发 Redis 相关中间件时,总结出来 Redis 推荐的实践方法,以上提出的这些方面,都或多或少在实际使用中遇到过。


可见,要想稳定发挥 Redis 的高性能,需要在各个方面做好工作,但凡某一个方面出现问题,必然会影响到 Redis 的性能,这对我们使用和运维提出了更高的要求。


如果你在使用 Redis 过程中,遇到更多的问题或者有更好的使用经验,可以留言一起探讨!


出处:kaito-kidd.com/2020/07/04/redis-best-practices/

Redis 其它总结

阿里云Redis开发规范学习总结


目前 Redis 可视化工具最全的横向评测


Redis 性能测试工具介绍


Redis 可视化管理工具


Redis 越来越慢?常见延迟问题定位与分析

Redis 分布式集群部署

原理:

  • Redis 集群采用一致性哈希槽的方式将集群中每个主节点都分配一定的哈希槽,对写入的数据进行哈希后分配到某个主节点进行存储。

  • 集群使用公式(CRC16 key)& 16384 计算键 key 数据那个槽。

  • 16384 个 slot 均匀分布在各个节点上。

  • 集群中每个主节点将承担一部分槽点的维护,而槽点中存储着数据,每个主节点都有至少一个从节点用于高可用。

节点通信方式:
  • 开启一个端口 设置的端口号+10000,用于集群之间节点通信交换信息。

  • 每个节点默认每秒 10 次选择随机 5 个节点发送 ping 消息,将自身信息和知道的集群信息传递,收到 ping 消息后返回 pong 消息做回复,最后通过这种随机的消息交换,最终每个节点将获得所有信息。

  • 当某个主节点挂掉,所有节点将会发现主节点挂掉了,作为主节点的从节点,就会接替主节点的工作,然后告诉所有其它节点,他成为了主。这样其它存活节点,就将它们维护的信息表更新从节点将接任做主,如果都挂掉集群将报错。当从一个节点操作,根据一致性哈希计算后将存储在其中一个主节点中,从节点将同步主的数据。



  • redis cluster 是去中心化的,集群中的每个节点都是平等的关系,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃。

  • 搭建集群时,会为每一个分片的主节点,对应一个从节点。实现 slaveof 功能,同时当主节点 down,实现 sentinel 哨兵的自动 failover 切换功能

Redis 分布式集群(部署):

端口号:7000-7005


本次分布式分片集群在一台 LInux 系统即可,只需要安装多个实例作为集群配置。

安装 ruby 环境支持:
yum -y install ruby rubygems
复制代码


yum 安装 2.0.0 版本,但是 gem 需要 2.2.2 版本以上,所以选择编译

下载并安装 ruby 环境:
wget https://cache.ruby-lang.org/pub/ruby/2.6/ruby-2.6.1.tar.gztar xf ruby-2.6.1.tar.gz && cd ruby-2.6.1/./configure --prefix=/usr/local/rubymake && make install && echo $?echo 'export PATH=$PATH:/usr/local/ruby/bin' >> /etc/profilesource /etc/profile
复制代码
修改 gem 工具国内源:
# 查看gem工具源地址gem sources -l# 添加一个阿里云的gem工具源gem sources -a http://mirrors.aliyun.com/rubygems/# 删除gem工具默认国外源gem sources --remove https://rubygems.org/# 下载当前最新版本集群插件gem install redis -v 4.1.0
复制代码
集群节点准备:
mkdir /data/700{0..5}
复制代码
配置 7000 端口实例:
vim /data/7000/redis.confport 7000daemonize yespidfile /data/7000/redis.pidloglevel noticelogfile "/data/7000/redis.log"dbfilename dump.rdbdir /data/7000protected-mode nocluster-enabled yescluster-config-file nodes.confcluster-node-timeout 5000appendonly yes
复制代码
拷贝其他端口实例:
# 拷贝配置文件cp /data/7000/redis.conf /data/7001/cp /data/7000/redis.conf /data/7002/cp /data/7000/redis.conf /data/7003/cp /data/7000/redis.conf /data/7004/cp /data/7000/redis.conf /data/7005/
# 修改配置文件内容sed -i 's#7000#7001#g' /data/7001/redis.confsed -i 's#7000#7002#g' /data/7002/redis.confsed -i 's#7000#7003#g' /data/7003/redis.confsed -i 's#7000#7004#g' /data/7004/redis.confsed -i 's#7000#7005#g' /data/7005/redis.conf
复制代码
启动所有实例:
redis-server /data/7000/redis.confredis-server /data/7001/redis.confredis-server /data/7002/redis.confredis-server /data/7003/redis.confredis-server /data/7004/redis.confredis-server /data/7005/redis.conf
复制代码
创建命令软链接:
(这个命令过期了,现在使用 redis-cli 命令)(可选执行命令)
ln -s /usr/local/redis-5.0.2/src/redis-trib.rb /usr/sbin/
复制代码
查看进程:
ps -ef |grep redis-server
复制代码


加入所有实例节点到集群管理:
# --replicas 1",1是代表每一个主有一个从,后面的是所有节点的地址与端口信息redis-cli --cluster create --cluster-replicas 1 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
复制代码


分布式主从规则为,前三个实例节点是主,对应的后面三个实例节点为从节点,如果 replicas 2,那就多加 3 个实例节点

查看主节点状态:
redis-cli -p 7000 cluster nodes|grep master
复制代码


查看从节点状态:
redis-cli -p 7000 cluster nodes|grep slave
复制代码


Redis-分布式集群(管理)

集群节点增加准备:

mkdir /data/700{6..7}
拷贝其他端口实例:
# 拷贝配置文件cp /data/7000/redis.conf /data/7006/cp /data/7000/redis.conf /data/7007/
# 修改配置文件内容sed -i 's#7000#7006#g' /data/7006/redis.confsed -i 's#7000#7007#g' /data/7007/redis.conf
复制代码


启动新节点实例:


redis-server /data/7006/redis.confredis-server /data/7007/redis.conf
复制代码
查看进程:
ps -ef |grep redis-server
复制代码


添加主节点:(7000 实例是管理节点)
#'把7006实例添加到7000实例这个主节点所在集群内(此时已经有了4个主节点)redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
复制代码
查看主节点状态:
redis-cli -p 7000 cluster nodes|grep master
复制代码


转移 slot(重新分片):
#'操作集群管理节点从新分配,并在交互界面指定分片大小、选择接收分片的节点IDredis-cli --cluster reshard 127.0.0.1:7000
How many slots do you want to move (from 1 to 16384)? 4096#通过人工手动计算数据分片总大小除以主节点后的数字
What is the receiving node ID? 2129d28f0a86fc89571e49a59a0739812cff7953#选择接收数据分片的节点ID,(这是新增节点7006实例的ID号)
Source node #1: all#选择从哪些源主节点重新分片给新主节点)(all是所有节点)
Do you want to proceed with the proposed reshard plan (yes/no)? yes #确认修改以上的操作
复制代码
重新查看主节点状态:(可以看到集群数据的重新分片)
redis-cli -p 7000 cluster nodes|grep master
复制代码


添加从节点:
#'把7007实例节点添加到7006实例主节点内,并指定对应7006实例主节点坐在集群的管理节点redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7000 --cluster-slave --cluster-master-id 2129d28f0a86fc89571e49a59a0739812cff7953
复制代码
查看从节点状态:
redis-cli -p 7000 cluster nodes|grep slave
复制代码


集群节点删除准备:

移动要删除节点的数据分片:
#'操作集群管理节点从新分配,并在交互界面指定分片大小、选择接收分片的节点IDredis-cli --cluster reshard 127.0.0.1:7000
#方法是根据要删除master节点的分片位置,然后一个组分一个节点 , 也可以直接移动所有数据片到一个节点
How many slots do you want to move (from 1 to 16384)? 1365 #通过人工手动查看数据分片总大小
What is the receiving node ID? e64f9074a3733fff7baa9a4848190e56831d5447#选择接收数据分片的节点ID,(这是新增节点7006实例的ID号)
Source node #1: 2129d28f0a86fc89571e49a59a0739812cff7953#选择从哪些源主节点重新分片给新主节点(这是要删除的主节点的ID号)
Source node #2: done#这是结束命令
Do you want to proceed with the proposed reshard plan (yes/no)? yes #确认修改以上的操作
复制代码
重新查看主节点状态:(可以看到集群数据的重新分片)
redis-cli -p 7000 cluster nodes|grep master
复制代码


继续移动数据片:
#'操作集群管理节点从新分配,并在交互界面指定分片大小、选择接收分片的节点IDredis-cli --cluster reshard 127.0.0.1:7000
# 方法是根据要删除master节点的分片位置,然后一个组分一个节点 , 也可以直接移动所有数据片到一个节点
How many slots do you want to move (from 1 to 16384)? 1366 #通过人工手动查看数据分片总大小
What is the receiving node ID? f6c1aaea3a8c56e0c7dee8ad7ae17e26dd04244c#选择接收数据分片的节点ID,(这是新增节点7006实例的ID号)
Source node #1: 2129d28f0a86fc89571e49a59a0739812cff7953#选择从哪些源主节点重新分片给新主节点(这是要删除的主节点的ID号)
Source node #2: done#这是结束命令
Do you want to proceed with the proposed reshard plan (yes/no)? yes #确认修改以上的操作
复制代码
重新查看主节点状态:(可以看到集群数据的重新分片)
redis-cli -p 7000 cluster nodes|grep master
复制代码


最后一次移动数据片:
#'操作集群管理节点从新分配,并在交互界面指定分片大小、选择接收分片的节点IDredis-cli --cluster reshard 127.0.0.1:7000
#方法是根据要删除master节点的分片位置,然后一个组分一个节点 , 也可以直接移动所有数据片到一个节点
How many slots do you want to move (from 1 to 16384)? 1365 #通过人工手动查看数据分片总大小
What is the receiving node ID? 5a0df4ea0af5f35c1248e45e88d44c3f2e10169fSource node #1: 2129d28f0a86fc89571e49a59a0739812cff7953Source node #2: done
复制代码
重新查看主节点状态:(可以看到集群数据的重新分片)
redis-cli -p 7000 cluster nodes|grep master
复制代码


删除清空数据片的主节点:
#'删除已经清空数据的7006实例redis-cli --cluster del-node 127.0.0.1:7006 2129d28f0a86fc89571e49a59a0739812cff7953
#删除没有主库的7007实例redis-cli --cluster del-node 127.0.0.1:7007 821bcf78c5e4c0b08aa7a5d514214b657ebec4ab
复制代码


其他配置管理:

#内存信息查看redis-cli -p 6382 -a redhat info memory
#设置最大只能使用100MB的内存redis-cli -p 6382 -a redhat config set maxmemory 102400000
复制代码


史上最全、最新的Redis面试题(最新版)

发布于: 2021 年 04 月 15 日阅读数: 441
用户头像

民工哥

关注

公众号:民工哥技术之路 微信mingongge2019 2018.04.16 加入

10多年 IT 职场老司机,坚持自学一路从技术小白成长为互联网企业信息技术部门的负责人。 《Linux系统运维指南 从入门到企业实战》作者。专注Linux系统、架构、集群、分布式、中间件等后端开源技术分享。

评论 (1 条评论)

发布
用户头像
优质好文!我来推一推!
2021 年 04 月 16 日 20:38
回复
没有更多了
建议收藏!看完全面掌握,最详细的Redis总结(2021最新版)