1 🍑Redis 背景🍑
Redis 是⼀种基于键值对的非关系数据库,与很多键值对数据库不同的是,Redis中的值可以是由 string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)、Bitmaps(位图)、HyperLogLog、GEO(地理信息定位)等多种数据结构和算法组成,因此 Redis可以满⾜很多的应⽤场景,⽽且因为 Redis 会将所有数据都存放再内存中,所以它的读写性能⾮常惊⼈。
不仅如此,Redis 还可以将内存的数据利⽤快照和⽇志的形式保存到硬盘上,这样在发⽣类似断电或者机器故障的时候,内存中的数据不会“丢失”。除了上述功能以外,Redis 还提供了键过期、发布订阅、事务、流⽔线、Lua 脚本等附加功能。总之,如果在合适的场景使⽤号 Redis,它就会像⼀把瑞⼠军⼑⼀样所向披靡。
2008 年,Redis 的作者 Salvatore Sanfilippo 在开发⼀个叫 LLOOGG 的⽹站时,需要实现⼀个⾼性能的队列功能,最开始是使⽤ MySQL 来实现的,但后来发现⽆论怎么优化 SQL 语句等都不能使⽹站的性能提⾼上去,再加上⾃⼰囊中羞涩,于是他决定⾃⼰做⼀个专属于 LLOOGG 的数据库,这个就是 Redis 的前⾝。后来,Salvatore Sanfilippo 将 Redis 1.0 的源码发布到 Github 上,可能连他⾃⼰都没想到,Redis 后来如此受欢迎。
假如现在有⼈问 Redis 的作者都有谁在使⽤ Redis,我想他可以开句玩笑的回答:还有谁不使⽤Redis,当然这只是开玩笑,但是从 Redis 的官⽅公司统计来看,有很多重量级的公司都在使⽤Redis,如国外的 Twitter、Instagram、Stack Overflow、Github 等,国内就更多了,如果单单从体量来统计,新浪微博可以说是全球最⼤的 Redis 使⽤者,除了新浪微博,还有像阿⾥巴巴、腾讯、搜狐、优酷⼟⾖、美团、⼩⽶、唯品会等公司都是 Redis 的使⽤者。除此之外,许多开源技术像 ELK 等已经把 Redis 作为它们组件中的重要⼀环,⽽且 Redis 还提供了模块系统让第三⽅⼈员实现功能扩展,让 Redis 发挥出更⼤的威⼒。所以,可以这么说,熟练使⽤和运维 Redis 已经成为开发运维⼈员的⼀个必备技能。
2 🍑Redis 特性🍑
我们首先打开Redis官网上看看,官方对于Redis的描述:
上面介绍说Redis被数百万开发人员用作缓存、矢量数据库、文档数据库、流媒体引擎和消息代理的开源内存数据存储。
那么Redis又具有哪些特性呢?接下来我们一个一个来看:
2.1 🍎速度快🍎
俗话说:天下武功,唯快不破。正常情况下,Redis 执⾏命令的速度⾮常快,官⽅给出的数字是读写性能可以达到 10 万 / 秒,当然这也取决于机器的性能,但这⾥先不讨论机器性能上的差异,只分析⼀下是什么造就了 Redis 如此之快,可以⼤概归纳为以下几点:
1️⃣Redis 的所有数据都是存放在内存中的。
相比于传统的MySQL来说,Redis是直接访问内存,相对于访问硬盘那肯定快了不少。下面是⾕歌公司 2009 年给出的各层级硬件执⾏速度,大家可以参考下:
层级速度L1 cache reference0.5 nsBranch mispredict5 nsL2 cache reference7 nsMutex lock/unlock25 nsMain memory reference100 nsCompress 1 K bytes with Zippy3 000 nsSend 2 K bytes over 1 Gbps network20 000 nsRead 1 MB sequentially from Memory250 000 nsRound trip within same datacenter500 000 nsDisk seek10 000 000 nsRead 1 MB sequentially from disk20 000 000 nsSend packet CA -> Netherlands -> CA150 000 000 ns2️⃣Redis 使⽤了单线程,预防了多线程可能产⽣的竞争问题。
虽然Redis 在 6.0 版本引⼊了多线程机制,但主要也是在处理⽹络和 IO,不涉及到数据命令,即命令的执⾏仍然采⽤了单线程模式。
2.2 🍎基于键值对的数据结构服务器🍎
⼏乎所有的编程语⾔都提供了类似字典的功能,例如 C++ ⾥的 map、Java ⾥的 map、Python ⾥的 dict 等,类似于这种组织数据的⽅式叫做基于键值对的⽅式,与很多键值对数据库不同的是,Redis 中的值不仅可以是字符串,⽽且还可以是具体的数据结构,这样不仅能便于在许多应⽤场景的开发,同时也能提⾼开发效率。
Redis 的全程是 REmote Dictionary Server,它主要提供了 5 种数据结构:字符串(string)、哈希(hash)、列表(list)、集合(set)、有序集合(ordered set /zet),同时在字符串的基础之上演变出了位图(Bitmaps)和 HyperLogLog 两种神奇的 ”数据结构“,并且随着 LBS(Location Based Service,基于位置服务)的不断发展,Redis 3.2. 版本种加⼊有关 GEO(地理信息定位)的功能,总之在这些数据结构的帮助下,开发者可以开发出各种 “有意思” 的应⽤。
2.3 🍎丰富的功能🍎
除了 5 种数据结构,Redis 还提供了许多额外的功能:
提供了键过期功能,可以⽤来实现缓存。提供了发布订阅功能,可以⽤来实现消息系统。⽀持 Lua 脚本功能,可以利⽤ Lua 创造出新的 Redis 命令。提供了简单的事务功能,能在⼀定程度上保证事务特性。提供了流⽔线(Pipeline)功能,这样客户端能将⼀批命令⼀次性传到 Redis,减少了⽹络的开销。
2.4 🍎简单稳定🍎
Redis 的简单主要表现在三个⽅⾯。⾸先,Redis 的源码很少,早期版本的代码只有 2 万⾏左右,3.0 版本以后由于添加了集群特性,代码增⾄ 5 万⾏左右,相对于很多 NoSQL 数据库来说代码量相对要少很多,也就意味着普通的开发和运维⼈员完全可以 “吃透” 它。
其次,Redis 使⽤单线程模型,这样不仅使得 Redis 服务端处理模型变得简单,⽽且也使得客⼾端开发变得简单。最后,Redis 不需要依赖于操作系统中的类库(例如 Memcache 需要依赖 libevent 这样的系统类库),Redis ⾃⼰实现了事件处理的相关功能。
但与简单相对的是 Redis 具备相当的稳定性,在⼤量使⽤过程中,很少出现因为 Redis ⾃⾝ BU⽽导致宕掉的情况。
2.5 🍎客户端语⾔多🍎
Redis 提供了简单的 TCP 通信协议,很多编程语⾔可以很⽅便地接⼊到 Redis,并且由于 Redis 受到社区和各⼤公司的⼴泛认可,所以⽀持 Redis 的客⼾端语⾔也⾮常多,⼏乎涵盖了主流的编程语⾔,例如 C、C++、Java、PHP、Python、NodeJS 等,后续我们会对 Redis 的客户端使⽤做详细说明。
2.6 🍎持久化🍎
通常看,将数据放在内存中是不安全的,⼀旦发⽣断电或者机器故障,重要的数据可能就会丢失,因此 Redis 提供了两种持久化⽅式:RDB 和 AOF,即可以⽤两种策略将内存的数据保存到硬盘中,这样就保证了数据的可持久性,后续我们将对 Redis 的持久化进⾏详细说明。
2.7 🍎主从复制🍎
Redis 提供了复制功能,实现了多个相同数据的 Redis 副本(Replica),复制功能是分布式 Redis 的基础。后续我们会对 Redis 的复制功能进⾏详细演⽰。
2.8 🍎高可用和分布式🍎
Redis 提供了⾼可⽤实现的 Redis 哨兵(Redis Sentinel),能够保证 Redis 结点的故障发现和故障⾃动转移。也提供了 Redis 集群(Redis Cluster),是真正的分布式实现,提供了⾼可⽤、读写和容量的扩展性。
3 🍑Redis 使用场景🍑
要充分理解 Redis 的作⽤,需要读者对⽹站的架构有⼀定的基础理解,博主之前已经写了这方面的博客大家可以看一下:
3.1 🍎Redis 可以做什么?🍎
3.1.1 🍋缓存(Cache)🍋
缓存机制⼏乎在所有⼤型⽹站都有使⽤,合理地使⽤缓存不仅可以加速数据的访问速度,⽽且能够有效地降低后端数据源的压⼒。Redis 提供了键值过期时间设置,并且也提供了灵活控制最⼤内存和内存溢出后的淘汰策略。可以这么说,⼀个合理的缓存设计能够为⼀个⽹站的稳定保驾护航。
3.1.2 🍋计数器应⽤🍋
计数器在⽹站中的作⽤⾄关重要,例如视频⽹站有播放数、电商⽹站有浏览数,为了保证数据的实时性,每⼀次播放和浏览都要做加 1 的操作,如果并发量很⼤对于传统关系型数据的性能是⼀种挑战。Redis 天然⽀持计数功能⽽且计数的性能也⾮常好,可以说是计数器系统的重要选择。
3.1.3 🍋排⾏榜系统🍋
排⾏榜系统⼏乎存在于所有的⽹站,例如按照热度排名的排⾏榜,按照发布时间的排⾏榜,按照各种复杂维度计算出的排⾏榜,Redis 提供了列表和有序集合的结构,合理地使⽤这些数据结构可以很⽅便地构建各种排⾏榜系统。
3.1.4 🍋社交⽹络🍋
赞 / 踩、粉丝、共同好友 / 喜好、推送、下拉刷新等是社交⽹站的必备功能,由于社交⽹站访问量通常⽐较⼤,⽽且传统的关系型数据不太合适保存这种类型的数据,Redis 提供的数据结构可以相对⽐较容易地实现这些功能。
3.1.5 🍋消息队列系统🍋
消息队列系统可以说是⼀个⼤型⽹站的必备基础组件,因为其具有业务解耦、⾮实时业务削峰等特性。Redis 提供了发布订阅功能和阻塞队列的功能,虽然和专业的消息队列⽐还不够⾜够强⼤,但是对于⼀般的消息队列功能基本可以满⾜。
3.2 🍎Redis 不可以做什么?🍎
实际上和任何⼀⻔技术⼀样,每个技术都有⾃⼰的应⽤场景和边界,也就是说 Redis 并不是万⾦油,有很多合适它解决的问题,但是也有很多不合适它解决的问题。我们可以站在数据规模和数据冷热的⻆度来进⾏分析。
站在数据规模的⻆度看,数据可以分为⼤规模数据和⼩规模数据,我们知道 Redis 的数据是存放在内存中的,虽然现在内存已经⾜够便宜,但是如果数据量⾮常⼤,例如每天有⼏亿的用户⾏为数据,使⽤ Redis 来存储的话,基本上是个⽆底洞,经济成本相当⾼。
站在数据冷热的⻆度,数据分为热数据和冷数据,热数据通常是指需要频繁操作的数据,反之为冷数据,例如对于视频⽹站来说,视频基本信息基本上在各个业务线都是经常要操作的数据,⽽⽤⼾的观看记录不⼀定是经常需要访问的数据,这⾥暂且不讨论两者数据规模的差异,单纯站在数据冷热的⻆度上看,视频信息属于热数据,⽤⼾观看记录属于冷数据。如果将这些冷数据放在 Redis 上,基本上是对于内存的⼀种浪费,但是对于⼀些热数据可以放在 Redis 中加速读写,也可以减轻后端存储的负载,可以说是事半功倍。
4 🍑Redis 的重大版本🍑
Redis 借鉴了 Linux 操作系统对于版本号的命名规则:版本号第⼆位如果是奇数,则为⾮稳定版本(例如 2.7、2.9、3.1),如果是偶数,则为稳定版本(例如 2.6、2.8、3.0、3.2)。当前奇数版本就是下⼀个稳定版本的开发版本,例如 2.9 版本是 3.0 版本的开发版本。所以我们⽣产环境通常选取偶数版本的 Redis,如果对于某些新的特性想提前了解和使⽤,可以选择最新的奇数版本。⽬前最新的版本是 7.0 版本。本⼩节将对 Redis 发展过程中的⼀些重要版本及特性进⾏说明。
4.1 🍎Redis 2.6🍎
Redis 2.6 在 2012 年正式发布,相⽐于 Redis 2.4,主要特性如下:
服务端⽀持 Lua 脚本去掉虚拟内存相关功能。放开对客⼾端连接数的硬编码限制。键的过期时间⽀持毫秒。从结点提供只读功能。两个新的位图命令:bitcount 和 bitop。增强了 redis-benchmark 的功能:⽀持定制化的压测、CSV 格式输出等功能。基于浮点数⾃增命令:incrbyfloat 和 hincrbyfloat。redis-cli 可以使⽤ --eval 参数实现 Lua 脚本执⾏。shutdown 命令增强。Info 可以按照 setction 输出,并且添加了⼀些统计项。重构了⼤量的核⼼代码,所i有集群相关的代码都去掉了,会在 3.0 ⽀持 cluster 功能。sort 命令优化。
4.2 🍎Redis 2.8🍎
Redis 2.8 在 2013 年正式发布,相⽐于 Redis 2.6,主要特性如下:
添加部分主从复制的功能,在⼀定程度上降低了由于⽹络问题,造成频繁全量复制⽣成 RDB 对系统造成的压⼒。尝试性地⽀持 IPv6。可以通过 config set 命令设置 maxclients。可以⽤ bind 命令绑定多个 IP 地址。Redis 设置了明显的进程名,⽅便使⽤ ps 命令查看系统进程。config rewrite 命令可以将 config set 持久化到 Redis 配置⽂件中。发布订阅添加了 pubsub 命令。Redis Sentinel 第⼆版,相⽐于 Redis 2.6 的 Redis Sentinel,此版本已经变成⽣产可⽤。
4.3 🍎Redis 3.0🍎
Redis 3.0 在 2015 年正式发布,相⽐于 Redis 2.8,主要特性如下:
Redis Cluster:Redis 提供的官⽅分布式实现。全新的 embedded string 对象编码结果,优化了⼩对象内存访问,在特定的⼯作负载时,下载速度⼤幅提⾼。优化了 LRU 算法,⼤幅提供性能。migrate 链接缓存,⼤幅提供键迁移的速度。migrate 命令新增两个参数:copy 和 replace。client pause 命令,在指定时间内中⽌处理客⼾端请求。bitcount 命令性能提⾼。config set 设置 maxmemory 时候能够设置不⼀样的单位(以前只能是字节)。Redis⽇志⼩作调整:⽇志中会反应当前实例的⻆⾊(master 或者 slave)。incr命令性能提⾼。
4.4 🍎Redis 3.2🍎
Redis 3.2 在 2016 年正式发布,相⽐于 Redis 3.0,主要特性如下:
添加 GEO 相关功能。SDS 在速度和节省空间上都作了优化。⽀持⽤ upstart 或者 systemd 管理 Redis 进程。新的 List 编码类型:quicklist。从节点读取过时数据保证⼀致性。添加了 hstrlen 命令。加强了 debug 命令,⽀持了更多的参数。Lua 脚本功能加强。添加了 Lua Debugger。config set ⽀持更多的配置参数。优化了 Redis 崩溃后的相关报告。新的 RDB 格式,可是仍然兼容旧的 RDB。加速 RDB 的加载速度。spop 命令⽀持个数参数。cluster nodes 命令获得加速。Jemalloc 更新到 4.0.3 版本。
4.5 🍎Redis 4.0🍎
Redis 4.0 在 2017 年正式发布,相⽐于 Redis 3.2,主要特性如下:
提供了模块系统(module),⽅便第三⽅开发者拓展 Redis 的功能。PSYNC 2.0:优化了以前版本中,主从节点切换必然引发全量复制的问题。提供了新的缓存剔除算法:LFU(Last Frequently Used),注意 LFU 和 LRU 算法的不同之处,LRU 的淘汰规则是基于访问时间,⽽ LFU 是基于访问次数的,并对已有算法进⾏了优化。提供了⾮阻塞 del 和 flushall / flushdb 功能,新添加了 unlink 命令, 这个命令是 del 命令的异步版本, 它可以将删除指定键的操作放在后台线程⾥⾯执⾏。提供了 memory 命令,实现对内存更为全⾯的监控统计。提供了交互数据库功能,实现 Redis 内部数据库的数据置换。提供了 RDB-AOF 混合持久化格式,充分利⽤了 AOF 和 RDB 各⾃优点。Redis Cluster 兼容 NAT 和 docker。
4.6 🍎Redis 5.0🍎
Redis 5.0 在 2018 年正式发布,相⽐于 Redis 4.0,主要特性如下:
新的流数据类型(stream)。新的 Redis 模块 API:定时器、集群和字典 API。RDB 现在可存储 LFU 和 LRU 信息。redis-cli 中的集群管理器从 Ruby(redis-trib.rb)移植到了 C 语⾔代码。执⾏ redis-cli --cluster help 命令以了解更多信息。新的有序集合(sorted set)命令:zpopmin / zpopmax 和阻塞变体(blocking variants)。升级 Active defragmentation ⾄ v2 版本。增强 HyperLogLog 的实现。更好的内存统计报告。许多包含⼦命令的命令现在都有⼀个 help ⼦命令。客⼾端频繁连接和断开连接时,性能表现更好。许多错误修复和其他⽅⾯的改进。升级 Jemalloc ⾄ 5.1 版本。引⼊ client unblock 和 client id。新增 lolwut 命令。在不存在需要保持向后兼容性的地⽅,弃⽤ “slave” 术语。⽹络层中的差异优化。增强对 Lua 的⽀持:将 Lua 脚本更好地传播到 replicas / AOF、Lua 脚本现在可以超时并在副本中进⼊ -BUSY 状态。引⼊动态的 HZ(Dynamic HZ)以平衡空闲 CPU 使⽤率和响应性。对 Redis 核⼼代码进⾏了重构并在许多⽅⾯进⾏了改进.
4.7 🍎Redis 6.0🍎
Redis 6.0 在 2020 年正式发布,相⽐于 Redis 5.0,主要特性如下:
Redis 6.0 引⼊多线程 IO,但多线程部分只是⽤来处理⽹络数据的读写和协议解析,执⾏命令仍
然是单线程。实现了client-side-caching(客⼾端缓存)功能。放弃了caching slot,⽽只使⽤ key names。Redis 6.0 开始在兼容 RESP 2 的基础上,开始⽀持 RESP 3(RESP,Redis Serialization Protocol 是 Redis 服务端与客⼾端之间通信的协议)。连接⽀持 SSL,更加安全。增强 ACL 权限控制:⽀持对客⼾端的权限控制,实现对不同的 key 授予不同的操作权限、新增⼀个新的ACL ⽇志命令,允许查看所有违反 ACL 的客⼾机、访问不应该访问的命令、访问不应该访问的密钥,或者验证尝试失败。这对于调试 ACL 问题⾮常有⽤。提升了RDB⽇志加载速度。发布官⽅的 Redis 集群代理模块 Redis Cluster Proxy。提供了众多的新模块(modules)API。
4.8 🍎Redis 7.0🍎
Redis 7.0 在 2022 年正式发布,相⽐于 Redis 6.0,主要特性如下:
将 AOF ⽂件的存储⽅式改为在⼀个⽂件夹下存储多个⽂件。将持久化⽂件 RDB 的版本升级为 10,与之前的 RDB ⽂件版本不再兼容。在读取⽼的 RDB ⽂件格式的时候将 ziplist 转换为 listpack,这种转换发⽣于两种情况之下:从
磁盘读取⽂件或者从⼀个主节点进⾏复制⽂件的时候。在 redis.conf 配置⽂件中,protected-mode 默认更改为 yes,只有当你希望你的客⼾端在没有授权的情况下可以连接到 Redis server 的时候可以将 protected-mode 设置为 no。在 ACL 中,pub / sub channel 默认是被阻塞的。在从节点中,TTL 的时间标识的是绝对时间,不再是相对时间,从⽽保证了过期数据被及时删除。不再⽀持 gopher 协议。当在配置⽂件中设置 replica-serve-stale-data=no, 当主节点不再提供服务时,ping 命令得不
到返回值。