1.Redis是什么?
Redis
是⼀种基于键值对(key-value)的 NoSQL 数据库,与很多键值对数据库不同的是,Redis
中的值可以是由 string(字符串)、hash(哈希)、list(列表)、set(集合)、zset(有序集合)、 Bitmaps(位图)、HyperLogLog、GEO(地理信息定位)等多种数据结构和算法组成,因此 Redis 可以满⾜很多的应⽤场景,⽽且因为 Redis 会将所有数据都存放再内存中,所以它的读写性能⾮常惊 ⼈。不仅如此,Redis 还可以将内存的数据利⽤快照和⽇志的形式保存到硬盘上,这样在发⽣类似断电 或者机器故障的时候,内存中的数据不会“丢失”。除了上述功能以外,Redis 还提供了键过期、发布 订阅、事务、流⽔线、Lua 脚本等附加功能。总之,如果在合适的场景使⽤号 Redis,它就会像⼀把瑞⼠军⼑⼀样所向披靡。
1.1 Redis 特性
Redis 之所以受到如此多公司的⻘睐,必然有之过⼈之处,下⾯是关于 Redis 的 8 个重要特性。
1. 速度快
正常情况下,Redis 执⾏命令的速度⾮常快,官⽅给出的数字是读写性能可以达到 10 万 / 秒,当
然这也取决于机器的性能,但这⾥先不讨论机器性能上的差异,只分析⼀下是什么造就了 Redis 如此 之快,可以⼤概归纳为以下四点:
•
Redis 的所有数据都是存放在内存中的
•
Redis 是⽤ C 语⾔实现的,⼀般来说 C 语⾔实现的程序 “距离” 操作系统更近,执⾏速度相对会
更快。
•
Redis 使⽤了单线程,预防了多线程可能产⽣的竞争问题。
Redis 在 6.0 版本引⼊了多线程机制,但主要也是在处理⽹络和 IO,不涉及到数据命令,即命令
的执⾏仍然采⽤了单线程模式。
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(地理信息定位)的功能,总之在这些数据结构的帮助下,开发者可以开发出各种 “有意 思” 的应⽤。
3. 丰富的功能
除了 5 种数据结构,Redis 还提供了许多额外的功能:
•
提供了键过期功能,可以⽤来实现缓存。
•
提供了发布订阅功能,可以⽤来实现消息系统。
•
⽀持 Lua 脚本功能,可以利⽤ Lua 创造出新的 Redis 命令。
•
提供了简单的事务功能,能在⼀定程度上保证事务特性。
•
提供了流⽔线(Pipeline)功能,这样客⼾端能将⼀批命令⼀次性传到 Redis,减少了⽹络的开
销。
4. 简单稳定
Redis 的简单主要表现在三个⽅⾯。⾸先,Redis 的源码很少,早期版本的代码只有 2 万⾏左右,
3.0 版本以后由于添加了集群特性,代码增⾄ 5 万⾏左右,相对于很多 NoSQL 数据库来说代码量相对 要少很多,也就意味着普通的开发和运维⼈员完全可以 “吃透” 它。其次,Redis 使⽤单线程模型, 这样不仅使得 Redis 服务端处理模型变得简单,⽽且也使得客⼾端开发变得简单。最后,Redis 不需要 依赖于操作系统中的类库(例如 Memcache 需要依赖 libevent 这样的系统类库),Redis ⾃⼰实现了 事件处理的相关功能。
但与简单相对的是 Redis 具备相当的稳定性,在⼤量使⽤过程中,很少出现因为 Redis ⾃⾝ BUG
⽽导致宕掉的情况。
5. 客⼾端语⾔多
Redis 提供了简单的 TCP 通信协议,很多编程语⾔可以很⽅便地接⼊到 Redis,并且由于 Redis 受 到社区和各⼤公司的⼴泛认可,所以⽀持 Redis 的客⼾端语⾔也⾮常多,⼏乎涵盖了主流的编程语
⾔,例如 C、C++、Java、PHP、Python、NodeJS 等,后续我们会对 Redis 的客⼾端使⽤做详细说
明。
6. 持久化(Persistence)
通常看,将数据放在内存中是不安全的,⼀旦发⽣断电或者机器故障,重要的数据可能就会丢
失,因此 Redis 提供了两种持久化⽅式:RDB 和 AOF,即可以⽤两种策略将内存的数据保存到硬盘中 这样就保证了数据的可持久性,后续我们将对 Redis 的持久化进⾏详细说明。
7. 主从复制(Replication)
Redis 提供了复制功能,实现了多个相同数据的 Redis 副本
(Replica)复制 功能是分布式 Redis 的基础。后续我们会对 Redis 的复制功能进⾏详细演⽰。
8. ⾼可⽤(High Availability)和分布式(Distributed)
Redis 提供了⾼可⽤实现的 Redis 哨兵(Redis Sentinel),能够保证 Redis 结点的故障发现和故
障⾃动转移。也提供了 Redis 集群(Redis Cluster),是真正的分布式实现,提供了⾼可⽤、读写和 容量的扩展性。
1.2 Redis 使⽤场景
上面我们已经了解了 Redis 的若⼲个特性,接下来看⼀下 Redis 的典型应⽤场景有哪些?
1.2.1 Redis 可以做什么
1. 缓存(Cache)
缓存机制⼏乎在所有⼤型⽹站都有使⽤,合理地使⽤缓存不仅可以加速数据的访问速度,⽽且能
够有效地降低后端数据源的压⼒。Redis 提供了键值过期时间设置,并且也提供了灵活控制最⼤内存和 内存溢出后的淘汰策略。可以这么说,⼀个合理的缓存设计能够为⼀个⽹站的稳定保驾护航。
2. 排⾏榜系统
排⾏榜系统⼏乎存在于所有的⽹站,例如按照热度排名的排⾏榜,按照发布时间的排⾏榜,按照
各种复杂维度计算出的排⾏榜,Redis 提供了列表和有序集合的结构,合理地使⽤这些数据结构可以很 ⽅便地构建各种排⾏榜系统。
3. 计数器应⽤
计数器在⽹站中的作⽤⾄关重要,例如视频⽹站有播放数、电商⽹站有浏览数,为了保证数据的
实时性,每⼀次播放和浏览都要做加 1 的操作,如果并发量很⼤对于传统关系型数据的性能是⼀种挑 战。Redis 天然⽀持计数功能⽽且计数的性能也⾮常好,可以说是计数器系统的重要选择。
4. 社交⽹络
赞 / 踩、粉丝、共同好友 / 喜好、推送、下拉刷新等是社交⽹站的必备功能,由于社交⽹站访问量
通常⽐较⼤,⽽且传统的关系型数据不太合适保存这种类型的数据,Redis 提供的数据结构可以相对⽐ 较容易地实现这些功能。
5. 消息队列系统
消息队列系统可以说是⼀个⼤型⽹站的必备基础组件,因为其具有业务解耦、⾮实时业务削峰等
特性。Redis 提供了发布订阅功能和阻塞队列的功能,虽然和专业的消息队列⽐还不够⾜够强⼤,但是 对于⼀般的消息队列功能基本可以满⾜。
1.2.2 Redis 不可以做什么
实际上和任何⼀⻔技术⼀样,每个技术都有⾃⼰的应⽤场景和边界,也就是说 Redis 并不是万⾦
油,有很多合适它解决的问题,但是也有很多不合适它解决的问题。我们可以站在数据规模和数据冷 热的⻆度来进⾏分析。
站在数据规模的⻆度看,数据可以分为⼤规模数据和⼩规模数据,我们知道 Redis 的数据是存放
在内存中的,虽然现在内存已经⾜够便宜,但是如果数据量⾮常⼤,例如每天有⼏亿的⽤⼾⾏为数
据,使⽤ Redis 来存储的话,基本上是个⽆底洞,经济成本相当⾼。
站在数据冷热的⻆度,数据分为热数据和冷数据,热数据通常是指需要频繁操作的数据,反之为
冷数据,例如对于视频⽹站来说,视频基本信息基本上在各个业务线都是经常要操作的数据,⽽⽤⼾ 的观看记录不⼀定是经常需要访问的数据,这⾥暂且不讨论两者数据规模的差异,单纯站在数据冷热 的⻆度上看,视频信息属于热数据,⽤⼾观看记录属于冷数据。如果将这些冷数据放在 Redis 上,基 本上是对于内存的⼀种浪费,但是对于⼀些热数据可以放在 Redis 中加速读写,也可以减轻后端存储 的负载,可以说是事半功倍。
所以,Redis 并不是万⾦油,相信随着我们对 Redis 的逐步学习,能够清楚 Redis 真正的使⽤场
景。
1.3 安装并启动 Redis
我们在本教程中选择 5.0 版本,原因是 5.0 已经 ⽀持了⼤部分的功能特性,⽽且相⽐较于 7.0 版本,更容易进⾏安装使⽤。
Redis 的官⽅并不⽀持微软的 Windows 操作系统,因为 Redis 的许多特性都是和操作系统相
关的,所以⽀持 Windows 会增加维护成本,⽽且更重要的是⼤部分公司都在使⽤ Linux 操作
系统,⽽ Redis 在 Linux 操作系统上的表现已经得到实践的证明。当然 Redis 作为⼀款优秀
的开源技术,还是吸引到微软公司的注意,微软公司的开源技术组在 Github 上维护了⼀个
Redis 分⽀:
https://github.com/MSOpenTech/redis
。不过我们还是强烈建议⼤家在 Linux
上使⽤ Redis。
1.3.1引入软件源(centos7自带可以安装的redis版本只有3,所以要额外引入软件源)
1.3.2安装redis5
这样就安装成功了。
1.3.3创建符号链接
默认安装的⽬录为 /opt/rh/rh-redis5/root/usr/bin/ , 藏的太深了, 不⽅便使⽤. 我们通过符号链接, 把需 要⽤到的关键内容设置到⽅便使⽤的⽬录中.
1.3.4针对可执⾏程序设置符号链接
cd /usr/bin
ln -s /opt/rh/rh-redis5/root/usr/bin/redis-server ./redis-server
ln -s /opt/rh/rh-redis5/root/usr/bin/redis-sentinel ./redis-sentinel
ln -s /opt/rh/rh-redis5/root/usr/bin/redis-cli ./redis-cli
1.3.5针对配置⽂件设置符号链接
cd /etc/
ln -s /etc/opt/rh/rh-redis5/ ./redis
修改配置⽂件
1.3.6 设置 ip 地址
先进入配置文件
1.3.6关闭保护模式
1.3.7启动守护进程
1.3.8设置⼯作⽬录
先创建⼯作⽬录
mkdir -p /var/lib/redis
再在配置⽂件中, 设置⼯作⽬录
dir /var/lib/redis
1.3.9设置⽇志⽬录
先创建⽇志⽬录
mkdir -p /var/log/redis/
再在配置⽂件中, 设置⽇志⽬录
logfile /var/log/redis/redis-server.log
1.3.10启动 redis
redis-server /etc/redis/redis.conf
至此便成功安装和启动redis了!
二.使用Redis
2.1.1 常⻅命令
我们先来学习最常用的两个命令GET和SET
SET
将 string 类型的 value 设置到 key 中。如果 key 之前存在,则覆盖,⽆论原来的数据类型是什么。之 前关于此 key 的 TTL 也全部失效。
语法:
SET key value [expiration EX seconds|PX milliseconds] [NX|XX]
时间复杂度:O(1)
选项:
SET 命令⽀持多种选项来影响它的⾏为:
•
EX
seconds⸺使⽤秒作为单位设置 key 的过期时间。
•
PX
milliseconds
⸺使⽤毫秒作为单位设置 key 的过期时间。
•
NX
⸺只在 key 不存在时才进⾏设置,即如果 key 之前已经存在,设置不执⾏。
•
XX
⸺只在 key 存在时才进⾏设置,即如果 key 之前不存在,设置不执⾏。
注意:由于带选项的 SET 命令可以被
SETNX
、
SETEX
、
PSETEX
等命令代替,所以之后的版本 中,Redis 可能进⾏合并。
返回值:
•
如果设置成功,返回 OK。
•
如果由于 SET 指定了 NX 或者 XX 但条件不满⾜,SET 不会执⾏,并返回 (nil)。
GET
获取 key 对应的 value。如果 key 不存在,返回 nil。如果 value 的数据类型不是 string,会报错。
语法:
GET key
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:key 对应的 value,或者 nil 当 key 不存在。
KEYS
返回所有满⾜样式(pattern)的 key。⽀持如下统配样式。
•
h?llo
匹配
hello
,
hallo
和
hxllo
•
h*llo
匹配
hllo
和
heeeello
•
h[ae]llo
匹配
hello
和
hallo
但不匹配
hillo
•
h[^e]llo
匹配
hallo
,
hbllo
, ... 但不匹配
hello
•
h[a-b]llo
匹配
hallo
和
hbllo
语法:
KEYS pattern
命令有效版本:1.0.0 之后
时间复杂度:O(N)
返回值:匹配 pattern 的所有 key。
EXISTS
判断某个 key 是否存在。
语法:EXISTS key [key ...]
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:key 存在的个数。
DEL
删除指定的 key。
语法:
DEL key [key ...]
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:删除掉的 key 的个数。
EXPIRE
为指定的 key 添加秒级的过期时间(Time To Live TTL)
语法:
EXPIRE key seconds
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:1 表⽰设置成功。0 表⽰设置失败
TTL
获取指定 key 的过期时间,秒级。
语法:
TTL key
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:剩余过期时间。-1 表⽰没有关联过期时间,-2 表⽰ key 不存在。
EXPIRE 和 TTL 命令都有对应的⽀持毫秒为单位的版本:PEXPIRE 和 PTTL,详细⽤法就不再
介绍了。
TYPE
返回 key 对应的数据类型。
语法:
TYPE key
时间复杂度:O(1)
返回值:
none
,
string
,
list
,
set
,
zset
,
hash
and
stream
.。
MGET
⼀次性获取多个 key 的值。如果对应的 key 不存在或者对应的数据类型不是 string,返回 nil。
语法:
MGET key [key ...]
时间复杂度:O(N) N 是 key 数量
返回值:对应 value 的列表
MSET
⼀次性设置多个 key 的值。
时间复杂度:O(N) N 是 key 数量
返回值:永远是 OK
语法:
MSET key value [key value ...]
2.1.2 数据结构和内部编码
type 命令实际返回的就是当前键的数据结构类型,它们分别是:string(字符串)、list(列
表)、hash(哈希)、set(集合)、zset(有序集合),但这些只是 Redis 对外的数据结构.
实际上 Redis 针对每种数据结构都有⾃⼰的底层内部编码实现,⽽且是多种实现,这样 Redis 会
在合适的场景选择合适的内部编码。
可以看到每种数据结构都有⾄少两种以上的内部编码实现,例如 list 数据结构包含了 linkedlist 和
ziplist 两种内部编码。同时有些内部编码,例如 ziplist,可以作为多种数据结构的内部实现,可以通 过 object encoding 命令查询内部编码
Redis 这样设计有两个好处:
1)可以改进内部编码,⽽对外的数据结构和命令没有任何影响,这样⼀旦开发出更优秀的内部编码, ⽆需改动外部数据结构和命令,例如 Redis 3.2 提供了 quicklist,结合了 ziplist 和 linkedlist 两者的优 势,为列表类型提供了⼀种更为优秀的内部编码实现,⽽对⽤⼾来说基本⽆感知。
2)多种内部编码实现可以在不同场景下发挥各⾃的优势,例如 ziplist ⽐较节省内存,但是在列表元素 ⽐较多的情况下,性能会下降,这时候 Redis 会根据配置选项将列表类型的内部实现转换为
linkedlist,整个过程⽤⼾同样⽆感知。
2.1.3 单线程架构
Redis 使⽤了单线程架构来实现⾼性能的内存数据库服务,本节通过分析 Redis 单线程模型为什么性能如此之⾼,最终给出为什 么理解单线程模型是使⽤和运维 Redis 的关键。
为什么单线程还能这么快
通常来讲,单线程处理能⼒要⽐多线程差,例如有 10 000 公⽄货物,每辆⻋的运载能⼒是每次
200 公⽄,那么要 50 次才能完成;但是如果有 50 辆⻋,只要安排合理,只需要依次就可以完成任 务。那么为什么 Redis 使⽤单线程模型会达到每秒万级别的处理能⼒呢?可以将其归结为三点:
a.
纯内存访问。Redis 将所有数据放在内存中,内存的响应时⻓⼤约为 100 纳秒,这是 Redis 达
到每秒万级别访问的重要基础。
b.
⾮阻塞 IO。Redis 使⽤ epoll 作为 I/O 多路复⽤技术的实现,再加上 Redis ⾃⾝的事件处理模型
将 epoll 中的连接、读写、关闭都转换为事件,不在⽹络 I/O 上浪费过多的时间。
c.
单线程避免了线程切换和竞态产⽣的消耗。单线程可以简化数据结构和算法的实现,让程序模
型更简单;其次多线程避免了在线程竞争同⼀份共享数据时带来的切换和等待消耗。
虽然单线程给 Redis 带来很多好处,但还是有⼀个致命的问题:对于单个命令的执⾏时间都是有
要求的。如果某个命令执⾏过⻓,会导致其他命令全部处于等待队列中,迟迟等不到响应,造成客⼾ 端的阻塞,对于 Redis 这种⾼性能的服务来说是⾮常严重的,所以 Redis 是⾯向快速执⾏场景的数据 库
2.2 String 字符串
字符串类型是 Redis 最基础的数据类型,关于字符串需要特别注意:1)⾸先 Redis 中所有的键的
类型都是字符串类型,⽽且其他⼏种数据结构也都是在字符串类似基础上构建的,例如列表和集合的 元素类型是字符串类型,所以字符串类型能为其他 4 种数据结构的学习奠定基础。2)其次,字符串类型的值实际可以是字符串,包含⼀般格式的字符串或者类似 JSON、XML 格式的字符
串;数字,可以是整型或者浮点型;甚⾄是⼆进制流数据,例如图⽚、⾳频、视频等。不过⼀个字符 串的最⼤值不能超过 512 MB。
由于 Redis 内部存储字符串完全是按照⼆进制流的形式保存的,所以 Redis 是不处理字符集
编码问题的,客⼾端传⼊的命令中使⽤的是什么字符集编码,就存储什么字符集编码。
2.2.2 计数命令
INCR
将 key 对应的 string 表⽰的数字加⼀。如果 key 不存在,则视为 key 对应的 value 是 0。如果 key 对 应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错。
INCR key
时间复杂度:O(1)
返回值:integer 类型的加完后的数值
INCRBY
将 key 对应的 string 表⽰的数字加上对应的值。如果 key 不存在,则视为 key 对应的 value 是 0。如 果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错。
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:integer 类型的加完后的数值。
DECR
将 key 对应的 string 表⽰的数字减⼀。如果 key 不存在,则视为 key 对应的 value 是 0。如果 key 对 应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错
DECR key
时间复杂度:O(1)
返回值:integer 类型的减完后的数值
DECYBY
将 key 对应的 string 表⽰的数字减去对应的值。如果 key 不存在,则视为 key 对应的 value 是 0。如 果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错。
语法:
DECRBY key decrement
时间复杂度:O(1)
返回值:integer 类型的减完后的数值。
DECYBY
将 key 对应的 string 表⽰的数字减去对应的值。如果 key 不存在,则视为 key 对应的 value 是 0。如 果 key 对应的 string 不是⼀个整型或者范围超过了 64 位有符号整型,则报错。
DECRBY key decrement
时间复杂度:O(1)
返回值:integer 类型的减完后的数值。
INCRBYFLOAT
将 key 对应的 string 表⽰的浮点数加上对应的值。如果对应的值是负数,则视为减去对应的值。如果 key 不存在,则视为 key 对应的 value 是 0。如果 key 对应的不是 string,或者不是⼀个浮点数,则报 错。允许采⽤科学计数法表⽰浮点数。
语法:
INCRBYFLOAT key increment
时间复杂度:O(1)
返回值:加/减完后的数值。
很多存储系统和编程语⾔内部使⽤ CAS 机制实现计数功能,会有⼀定的 CPU 开销,但在 Redis 中完全
不存在这个问题,因为 Redis 是单线程架构,任何命令到了 Redis 服务端都要顺序执⾏。
2.2.3 其他命令
APPEND
如果 key 已经存在并且是⼀个 string,命令会将 value 追加到原有 string 的后边。如果 key 不存在, 则效果等同于 SET 命令。
语法:
APPEND KEY VALUE
时间复杂度:O(1). 追加的字符串⼀般⻓度较短, 可以视为 O(1).
返回值:追加完成之后 string 的⻓度。(单位是字节)
GETRANGE
返回 key 对应的 string 的⼦串,由 start 和 end 确定(左闭右闭)。可以使⽤负数表⽰倒数。-1 代表 倒数第⼀个字符,-2 代表倒数第⼆个,其他的与此类似。超过范围的偏移量会根据 string 的⻓度调整 成正确的值。
语法:
GETRANGE key start end
时间复杂度:O(N). N 为 [start, end] 区间的⻓度. 由于 string 通常⽐较短, 可以视为是 O(1)
返回值:string 类型的⼦串
SETRANGE
覆盖字符串的⼀部分,从指定的偏移开始。
语法:
1
SETRANGE key offset value
时间复杂度:O(N), N 为 value 的⻓度. 由于⼀般给的 value ⽐较短, 通常视为 O(1).
返回值:替换后的 string 的⻓度。
STRLEN
获取 key 对应的 string 的⻓度。当 key 存放的类似不是 string 时,报错。
语法:
STRLEN key
时间复杂度:O(1)
返回值:string 的⻓度。或者当 key 不存在时,返回 0。
2.2.4指令总结
2.2.4 内部编码
字符串类型的内部编码有 3 种:
•
int:8 个字节的⻓整型。
•
embstr:⼩于等于 39 个字节的字符串。
•
raw:⼤于 39 个字节的字符串。
Redis 会根据当前值的类型和⻓度动态决定使⽤哪种内部编码实现。
2.2.5 典型使⽤场景
缓存(Cache)功能
⽐较典型的缓存使⽤场景,其中 Redis 作为缓冲层,MySQL 作为存储层,绝⼤部分请
求的数据都是从 Redis 中获取。由于 Redis 具有⽀撑⾼并发的特性,所以缓存通常能起到加速读写和 降低后端压⼒的作⽤。
Redis + MySQL 组成的缓存存储架构
通过增加缓存功能,在理想情况下,每个⽤⼾信息,⼀个⼩时期间只会有⼀次 MySQL 查询,极⼤地提 升了查询效率,也降低了 MySQL 的访问数。
与 MySQL 等关系型数据库不同的是,Redis 没有表、字段这种命名空间,⽽且也没有对键名
有强制要求(除了不能使⽤⼀些特殊字符)。但设计合理的键名,有利于防⽌键冲突和项⽬
的可维护性,⽐较推荐的⽅式是使⽤ "业务名:对象名:唯⼀标识:属性" 作为键名。例如
MySQL 的数据库名为 vs,⽤⼾表名为 user_info,那么对应的键可以使⽤
"vs:user_info:6379"、"vs:user_info:6379:name" 来表⽰,如果当前 Redis 只会被⼀个业务
使⽤,可以省略业务名 "vs:"。如果键名过程,则可以使⽤团队内部都认同的缩写替代,例如
"user:6379:friends:messages:5217" 可以被 "u:6379:fr:m:5217" 代替。毕竟键名过⻓,还
是会导致 Redis 的性能明显下降的。
计数(Counter)功能
许多应⽤都会使⽤ Redis 作为计数的基础⼯具,它可以实现快速计数、查询缓存的功能,同时数
据可以异步处理或者落地到其他数据源。如图 2-11 所⽰,例如视频⽹站的视频播放次数可以使⽤
Redis 来完成:⽤⼾每播放⼀次视频,相应的视频播放数就会⾃增 1。
实际中要开发⼀个成熟、稳定的真实计数系统,要⾯临的挑战远不⽌如此简单:防作弊、按
照不同维度计数、避免单点问题、数据持久化到底层数据源等。
共享会话(Session)
如图 2-12 所⽰,⼀个分布式 Web 服务将⽤⼾的 Session 信息(例如⽤⼾登录信息)保存在各⾃
的服务器中,但这样会造成⼀个问题:出于负载均衡的考虑,分布式服务会将⽤⼾的访问请求均衡到 不同的服务器上,并且通常⽆法保证⽤⼾每次请求都会被均衡到同⼀台服务器上,这样当⽤⼾刷新⼀ 次访问是可能会发现需要重新登录,这个问题是⽤⼾⽆法容忍的。
为了解决这个问题,可以使⽤ Redis 将⽤⼾的 Session 信息进⾏集中管理,如图 2-13 所⽰,在这种模 式下,只要保证 Redis 是⾼可⽤和可扩展性的,⽆论⽤⼾被均衡到哪台 Web 服务器上,都集中从 Redis 中查询、更新 Session 信息。
⼿机验证码
很多应⽤出于安全考虑,会在每次进⾏登录时,让⽤⼾输⼊⼿机号并且配合给⼿机发送验证码,
然后让⽤⼾再次输⼊收到的验证码并进⾏验证,从⽽确定是否是⽤⼾本⼈。为了短信接⼝不会频繁访 问,会限制⽤⼾每分钟获取验证码的频率,例如⼀分钟不能超过 5 次。
以上介绍了使⽤ Redis 的字符串数据类型可以使⽤的⼏个场景,但其适⽤场景远不⽌于此,开发
⼈员可以结合字符串类型的特点以及提供的命令,充分发挥⾃⼰的想象⼒,在⾃⼰的业务中去找到合 适的场景去使⽤ Redis 的字符串类型。
2.3 Hash 哈希
⼏乎所有的主流编程语⾔都提供了哈希(hash)类型,它们的叫法可能是哈希、字典、关联数
组、映射。在 Redis 中,哈希类型是指值本⾝⼜是⼀个键值对结构,形如 key = "key",value = { {
field1, value1 }, ..., {fieldN, valueN } }.
HSET
设置 hash 中指定的字段(field)的值(value)。
HSET key field value [field value ...]
时间复杂度:插⼊⼀组 field 为 O(1), 插⼊ N 组 field 为 O(N)
返回值:添加的字段的个数。
HGET
获取 hash 中指定字段的值。
语法:
HGET key field
时间复杂度:O(1)
返回值:字段对应的值或者 nil。
HEXISTS
判断 hash 中是否有指定的字段。
HEXISTS key field
时间复杂度:O(1)
返回值:1 表⽰存在,0 表⽰不存在。
HDEL
删除 hash 中指定的字段。
语法:
HDEL key field [field ...]
时间复杂度:删除⼀个元素为 O(1). 删除 N 个元素为 O(N).
返回值:本次操作删除的字段个数。
HKEYS
获取 hash 中的所有字段。
语法:
HKEYS key
命令有效版本:2.0.0 之后
时间复杂度:O(N), N 为 field 的个数.
返回值:字段列表。
HVALS
获取 hash 中的所有的值。
语法:
HVALS key
时间复杂度:O(N), N 为 field 的个数.
返回值:所有的值。
HMGET
⼀次获取 hash 中多个字段的值。
语法:
HMGET key field [field ...]
命令有效版本:2.0.0 之后
时间复杂度:只查询⼀个元素为 O(1), 查询多个元素为 O(N), N 为查询元素个数.
返回值:字段对应的值或者 nil。
在使⽤ HGETALL 时,如果哈希元素个数⽐较多,会存在阻塞 Redis 的可能。如果开发⼈员只
需要获取部分 field,可以使⽤ HMGET,如果⼀定要获取全部 field,可以尝试使⽤ HSCAN
命令,该命令采⽤渐进式遍历哈希类型,HSCAN 会在后续章节介绍。
HLEN
获取 hash 中的所有字段的个数。
语法:
HLEN key
命令有效版本:2.0.0 之后
时间复杂度:O(1)
返回值:字段个数。
HSETNX
在字段不存在的情况下,设置 hash 中的字段和值。
语法:
HSETNX key field value
时间复杂度:O(1)
返回值:1 表⽰设置成功,0 表⽰失败。
HINCRBY
将 hash 中字段对应的数值添加指定的值。
语法:
HINCRBY key field increment
时间复杂度:O(1)
返回值:该字段变化之后的值
HINCRBYFLOAT
HINCRBY 的浮点数版本。
语法:
HINCRBYFLOAT key field increment
命令有效版本:2.6.0 之后
时间复杂度:O(1)
返回值:该字段变化之后的值。
2.3.2 命令⼩结
下表是哈希类型命令的效果、时间复杂度,开发⼈员可以参考此表,结合⾃⾝业务需求和数据
⼤⼩选择合适的命令。
2.3.3 内部编码
哈希的内部编码有两种:
•
ziplist(压缩列表):当哈希类型元素个数⼩于 hash-max-ziplist-entries 配置(默认 512 个)、
同时所有值都⼩于 hash-max-ziplist-value 配置(默认 64 字节)时,Redis 会使⽤ ziplist 作为哈
希的内部实现,ziplist 使⽤更加紧凑的结构实现多个元素的连续存储,所以在节省内存⽅⾯⽐
hashtable 更加优秀。
•
hashtable(哈希表):当哈希类型⽆法满⾜ ziplist 的条件时,Redis 会使⽤ hashtable 作为哈希
的内部实现,因为此时 ziplist 的读写效率会下降,⽽ hashtable 的读写时间复杂度为 O(1)。
2.3.4 使⽤场景
图为关系型数据表记录的两条⽤⼾信息,⽤⼾的属性表现为表的列,每条⽤⼾信息表现为
⾏。如果映射关系表⽰这两个⽤⼾信息
关系型数据表保存⽤⼾信息
2.4 List 列表
列表类型是⽤来存储多个有序的字符串,如图 所⽰,a、b、c、d、e 五个元素从左到右组成
了⼀个有序的列表,列表中的每个字符串称为元素(element),⼀个列表最多可以存储
个元 素。在 Redis 中,可以对列表两端插⼊(push)和弹出(pop),还可以获取指定范围的元素列表、 获取指定索引下标的元素等。列表是⼀种⽐较灵活的数据结构,它可以 充当栈和队列的⻆⾊,在实际开发上有很多应⽤场景。
列表类型的特点:
第⼀、列表中的元素是有序的,这意味着可以通过索引下标获取某个元素或者某个范围的元素列表, 例如要获取图 2-20 的第 5 个元素,可以执⾏ lindex user:1:messages 4 或者倒数第 1 个元素,lindex user:1:messages -1 就可以得到元素 e。
第⼆、区分获取和删除的区别,例如图 2-20 中的 lrem 1 b 是从列表中把从左数遇到的前 1 个 b 元素删 除,这个操作会导致列表的⻓度从 5 变成 4;但是执⾏ lindex 4 只会获取元素,但列表⻓度是不会变化 的。
第三、列表中的元素是允许重复的。
2.4.1 命令
LPUSH
将⼀个或者多个元素从左侧放⼊(头插)到 list 中。
语法:
1
LPUSH key element [element ...]
命令有效版本:1.0.0 之后
时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数.
返回值:插⼊后 list 的⻓度。
LPUSHX
在 key 存在时,将⼀个或者多个元素从左侧放⼊(头插)到 list 中。不存在,直接返回
语法:
1
LPUSHX key element [element ...]
命令有效版本:2.0.0 之后
时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数.
返回值:插⼊后 list 的⻓度。
RPUSH
将⼀个或者多个元素从右侧放⼊(尾插)到 list 中。
语法:
1
RPUSH key element [element ...]
命令有效版本:1.0.0 之后
时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数.
返回值:插⼊后 list 的⻓度。
RPUSHX
在 key 存在时,将⼀个或者多个元素从右侧放⼊(尾插)到 list 中。
语法:
1
RPUSHX key element [element ...]
命令有效版本:2.0.0 之后
时间复杂度:只插⼊⼀个元素为 O(1), 插⼊多个元素为 O(N), N 为插⼊元素个数.
返回值:插⼊后 list 的⻓度。
LRANGE
获取从 start 到 end 区间的所有元素,左闭右闭。
语法:
1
LRANGE key start stop
命令有效版本:1.0.0 之后
时间复杂度:O(N)
返回值:指定区间的元素。
LPOP
从 list 左侧取出元素(即头删)。
语法:
1
LPOP key
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:取出的元素或者 nil
RPOP
从 list 右侧取出元素(即尾删)。
语法:
1
RPOP key
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:取出的元素或者 nil。
LINSERT
在特定位置插⼊元素。
语法:
1
LINSERT key <BEFORE | AFTER> pivot element
命令有效版本:2.2.0 之后
时间复杂度:O(N)
返回值:插⼊后的 list ⻓度。
LLEN
获取 list ⻓度。
语法
LLEN key
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:list 的⻓度。
2.4.2 阻塞版本命令
blpop 和 brpop 是 lpop 和 rpop 的阻塞版本,和对应⾮阻塞版本的作⽤基本⼀致,除了:
•
在列表中有元素的情况下,阻塞和⾮阻塞表现是⼀致的。但如果列表中没有元素,⾮阻塞版本会理 解返回 nil,但阻塞版本会根据 timeout,阻塞⼀段时间,期间 Redis 可以执⾏其他命令,但要求执 ⾏该命令的客⼾端会表现为阻塞状态(如图 2-22 所⽰)。
•
命令中如果设置了多个键,那么会从左向右进⾏遍历键,⼀旦有⼀个键对应的列表中可以弹出元
素,命令⽴即返回。
•
如果多个客⼾端同时多⼀个键执⾏ pop,则最先执⾏命令的客⼾端会得到弹出的元素。
BLPOP
LPOP 的阻塞版本。
语法:
1
BLPOP key [key ...] timeout
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:取出的元素或者 nil。
BRPOP
RPOP 的阻塞版本。
语法:
1
BRPOP key [key ...] timeout
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:取出的元素或者 nil。
2.4.3 内部编码
列表类型的内部编码有两种:
•
ziplist(压缩列表):当列表的元素个数⼩于 list-max-ziplist-entries 配置(默认 512 个),同时
列表中每个元素的⻓度都⼩于 list-max-ziplist-value 配置(默认 64 字节)时,Redis 会选⽤
ziplist 来作为列表的内部编码实现来减少内存消耗。
•
linkedlist(链表):当列表类型⽆法满⾜ ziplist 的条件时,Redis 会使⽤ linkedlist 作为列表的内
部实现。
2.4.4 使⽤场景
消息队列
如图 所⽰,Redis 可以使⽤ lpush + brpop 命令组合实现经典的阻塞式⽣产者-消费者模型队列, ⽣产者客⼾端使⽤ lpush 从列表左侧插⼊元素,多个消费者客⼾端使⽤ brpop 命令阻塞式地从队列中 "争抢" 队⾸元素。通过多个客⼾端来保证消费的负载均衡和⾼可⽤性。
分频道的消息队列
如图所⽰,Redis 同样使⽤ lpush + brpop 命令,但通过不同的键模拟频道的概念,不同的消费 者可以通过 brpop 不同的键值,实现订阅不同频道的理念。
2.5 Set 集合
集合类型也是保存多个字符串类型的元素的,但和列表类型不同的是,集合中 1)元素之间是⽆序
的 2)元素不允许重复,如图所⽰。⼀个集合中最多可以存储
2^
32
1
个元素。Redis 除了⽀持
集合内的增删查改操作,同时还⽀持多个集合取交集、并集、差集,合理地使⽤好集合类型,能在实 际开发中解决很多问题。
2.5.1 普通命令
SADD
将⼀个或者多个元素添加到 set 中。注意,重复的元素⽆法添加到 set 中。
语法:
1
SADD key member [member ...]
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:本次添加成功的元素个数
SMEMBERS
获取⼀个 set 中的所有元素,注意,元素间的顺序是⽆序的。
语法:
SMEMBERS key
命令有效版本:1.0.0 之后
时间复杂度:O(N)
返回值:所有元素的列表。
SISMEMBER
判断⼀个元素在不在 set 中。
语法:
1
SISMEMBER key member
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:1 表⽰元素在 set 中。0 表⽰元素不在 set 中或者 key 不存在
SCARD
获取⼀个 set 的基数(cardinality),即 set 中的元素个数。
语法:
1
SCARD key
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:set 内的元素个数。
SPOP
从 set 中删除并返回⼀个或者多个元素。注意,由于 set 内的元素是⽆序的,所以取出哪个元素实际是
未定义⾏为,即可以看作随机的。
语法:
1
SPOP key [count]
命令有效版本:1.0.0 之后
时间复杂度:O(N), n 是 count
返回值:取出的元素。
SMOVE
将⼀个元素从源 set 取出并放⼊⽬标 set 中。
语法:
1
SMOVE source destination member
命令有效版本:1.0.0 之后
时间复杂度:O(1)
返回值:1 表⽰移动成功,0 表⽰失败
SREM
将指定的元素从 set 中删除。
语法:
1
SREM key member [member ...]
命令有效版本:1.0.0 之后
时间复杂度:O(N), N 是要删除的元素个数.
返回值:本次操作删除的元素个数。
2.5.2 集合间操作
交集(inter)、并集(union)、差集(diff)的概念如图 2-25 所⽰。
图 2-25 集合求交集、并集、差集
SINTER
获取给定 set 的交集中的元素。
语法:
1
SINTER key [key ...]
命令有效版本:1.0.0 之后
时间复杂度:O(N * M), N 是最⼩的集合元素个数. M 是最⼤的集合元素个数.
返回值:交集的元素。
SINTERSTORE
获取给定 set 的交集中的元素并保存到⽬标 set 中。
语法:
1
SINTERSTORE destination key [key ...]
命令有效版本:1.0.0 之后
时间复杂度:O(N * M), N 是最⼩的集合元素个数. M 是最⼤的集合元素个数.
返回值:交集的元素个数。
SUNION
获取给定 set 的并集中的元素。
语法:
1
SUNION key [key ...]
命令有效版本:1.0.0 之后
时间复杂度:O(N), N 给定的所有集合的总的元素个数.
返回值:并集的元素。
SUNIONSTORE
获取给定 set 的并集中的元素并保存到⽬标 set 中。
语法:
1
SUNIONSTORE destination key [key ...]
命令有效版本:1.0.0 之后
时间复杂度:O(N), N 给定的所有集合的总的元素个数.
返回值:并集的元素个数。
SDIFF
获取给定 set 的差集中的元素。
语法:
1
SDIFF key [key ...]
命令有效版本:1.0.0 之后
时间复杂度:O(N), N 给定的所有集合的总的元素个数.
返回值:差集的元素。
SDIFFSTORE
获取给定 set 的差集中的元素并保存到⽬标 set 中。
语法:
1
SDIFFSTORE destination key [key ...]
命令有效版本:1.0.0 之后
时间复杂度:O(N), N 给定的所有集合的总的元素个数.
返回值:差集的元素个数
SDIFFSTORE
获取给定 set 的差集中的元素并保存到⽬标 set 中。
语法:
1
SDIFFSTORE destination key [key ...]
命令有效版本:1.0.0 之后
时间复杂度:O(N), N 给定的所有集合的总的元素个数.
返回值:差集的元素个数
2.5.4 内部编码
集合类型的内部编码有两种:
•
intset(整数集合):当集合中的元素都是整数并且元素的个数⼩于 set-max-intset-entries 配置
(默认 512 个)时,Redis 会选⽤ intset 来作为集合的内部实现,从⽽减少内存的使⽤。
•
hashtable(哈希表):当集合类型⽆法满⾜ intset 的条件时,Redis 会使⽤ hashtable 作为集合
的内部实现
当元素个数较少并且都为整数时,内部编码为 intset:
当元素个数超过 512 个,内部编码为 hashtable:
当存在元素不是整数时,内部编码为 hashtable:
2.5.5 使⽤场景
集合类型⽐较典型的使⽤场景是标签(tag)。例如 A ⽤⼾对娱乐、体育板块⽐较感兴趣,B ⽤⼾
对历史、新闻⽐较感兴趣,这些兴趣点可以被抽象为标签。有了这些数据就可以得到喜欢同⼀个标签 的⼈,以及⽤⼾的共同喜好的标签,这些数据对于增强⽤⼾体验和⽤⼾黏度都⾮常有帮助。 例如⼀个 电⼦商务⽹站会对不同标签的⽤⼾做不同的产品推荐。
2.6 Zset 有序集合
有序集合相对于字符串、列表、哈希、集合来说会有⼀些陌⽣。它保留了集合不能有重复成员的
特点,但与集合不同的是,有序集合中的每个元素都有⼀个唯⼀的浮点类型的分数(score)与之关 联,着使得有序集合中的元素是可以维护有序性的,但这个有序不是⽤下标作为排序依据⽽是⽤这个 分数。
有序集合中的元素是不能重复的,但分数允许重复。类⽐于⼀次考试之后,每个⼈⼀定有⼀
个唯⼀的分数,但分数允许相同
2.6.1 普通命令
ZADD
添加或者更新指定的元素以及关联的分数到 zset 中,分数应该符合 double 类型,+inf/-inf 作为正负 极限也是合法的。
ZADD 的相关选项:
•
XX:仅仅⽤于更新已经存在的元素,不会添加新元素。
•
NX:仅⽤于添加新元素,不会更新已经存在的元素。
•
CH:默认情况下,ZADD 返回的是本次添加的元素个数,但指定这个选项之后,就会还包含本次更 新的元素的个数。
•
INCR:此时命令类似 ZINCRBY 的效果,将元素的分数加上指定的分数。此时只能指定⼀个元素和 分数。
语法:
ZADD key [NX | XX] [GT | LT] [CH] [INCR] score member [score member ...]
命令有效版本:1.2.0 之后
时间复杂度:O(log(N))
返回值:本次添加成功的元素个数
ZCARD
获取⼀个 zset 的基数(cardinality),即 zset 中的元素个数。
语法:
1
ZCARD key
命令有效版本:1.2.0 之后
时间复杂度:O(1)
返回值:zset 内的元素个数。
ZCOUNT
返回分数在 min 和 max 之间的元素个数,默认情况下,min 和 max 都是包含的,可以通过 ( 排除。
语法:
1
ZCOUNT key min max
命令有效版本:2.0.0 之后
时间复杂度:O(log(N))
返回值:满⾜条件的元素列表个数。
ZRANGE
返回指定区间⾥的元素,分数按照升序。带上 WITHSCORES 可以把分数也返回。
语法:
ZRANGE key start stop [WITHSCORES]
此处的 [start, stop] 为下标构成的区间. 从 0 开始, ⽀持负数.
命令有效版本:1.2.0 之后
时间复杂度:O(log(N)+M)
返回值:区间内的元素列表。
ZPOPMAX
删除并返回分数最⾼的 count 个元素。
语法:
1
ZPOPMAX key [count]
命令有效版本:5.0.0 之后
时间复杂度:O(log(N) * M)
返回值:分数和元素列表。
BZPOPMAX
ZPOPMAX 的阻塞版本。
语法:
1
BZPOPMAX key [key ...] timeout
命令有效版本:5.0.0 之后
时间复杂度:O(log(N))
返回值:元素列表。
ZPOPMIN
删除并返回分数最低的 count 个元素。
语法:
1
ZPOPMIN key [count]
命令有效版本:5.0.0 之后
时间复杂度:O(log(N) * M)
返回值:分数和元素列表。
ZRANK
返回指定元素的排名,升序。
语法:
1
ZRANK key member
命令有效版本:2.0.0 之后
时间复杂度:O(log(N))
返回值:排名。
ZSCORE
返回指定元素的分数。
语法:
1
ZSCORE key member
命令有效版本:1.2.0 之后
时间复杂度:O(1)
返回值:分数。
ZREMRANGEBYRANK
按照排序,升序删除指定范围的元素,左闭右闭。
语法:
1
ZREMRANGEBYRANK key start stop
命令有效版本:2.0.0 之后
时间复杂度:O(log(N)+M)
返回值:本次操作删除的元素个数。
2.6.2 集合间操作
ZINTERSTORE
求出给定有序集合中元素的交集并保存进⽬标有序集合中,在合并过程中以元素为单位进⾏合并,元 素对应的分数按照不同的聚合⽅式和权重得到新的分数。
语法:
ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight
[weight ...]] [AGGREGATE <SUM | MIN | MAX>]
命令有效版本:2.0.0 之后
时间复杂度:O(N*K)+O(M*log(M)) N 是输⼊的有序集合中, 最⼩的有序集合的元素个数; K 是输⼊了 ⼏个有序集合; M 是最终结果的有序集合的元素个数.
返回值:⽬标集合中的元素个数
ZUNIONSTORE
求出给定有序集合中元素的并集并保存进⽬标有序集合中,在合并过程中以元素为单位进⾏合并,元 素对应的分数按照不同的聚合⽅式和权重得到新的分数。
语法:
ZUNIONSTORE destination numkeys key [key ...] [WEIGHTS weight
[weight ...]] [AGGREGATE <SUM | MIN | MAX>]
命令有效版本:2.0.0 之后
时间复杂度:O(N)+O(M*log(M)) N 是输⼊的有序集合总的元素个数; M 是最终结果的有序集合的元素 个数
返回值:⽬标集合中的元素个数
2.6.4 内部编码
有序集合类型的内部编码有两种:
•
ziplist(压缩列表):当有序集合的元素个数⼩于 zset-max-ziplist-entries 配置(默认 128 个),
同时每个元素的值都⼩于 zset-max-ziplist-value 配置(默认 64 字节)时,Redis 会⽤ ziplist 来作
为有序集合的内部实现,ziplist 可以有效减少内存的使⽤。
•
skiplist(跳表):当 ziplist 条件不满⾜时,有序集合会使⽤ skiplist 作为内部实现,因为此时
ziplist 的操作效率会下降
当元素个数较少且每个元素较⼩时,内部编码为 ziplist:
当元素个数超过 128 个,内部编码 skiplist:
当某个元素⼤于 64 字节时,内部编码 skiplist:
2.6.5 使⽤场景
有序集合⽐较典型的使⽤场景就是排⾏榜系统。例如常⻅的⽹站上的热榜信息,榜单的维度可能
是多⽅⾯的:按照时间、按照阅读量、按照点赞量。本例中我们使⽤点赞数这个维度,维护每天的热 榜:
Redis ⽀持哪些数据类型
五种最核⼼的类型:
•
String
•
List
•
Hashmap
•
Set
•
ZSet
在后续版本中也逐渐新增了⼀些新的数据类型:
•
Bitmap, 通过⼆进制位表⽰某个数字是否存在.
•
Bitfield, 把字符串当做位图, 并进⾏位操作.
•
Hyperloglog, 基于位图的结构实现 "计数" 效果(统计某个数字出现⼏次, ⽐ hashmap 节省空间).
•
Geospatial, 地理信息, 存储经纬度. 并且可以进⾏⼀些空间计算 (⽐如找出某个点附近 1km 内都有 哪些点).
•
Stream, 消息队列.
这⼏个类型都属于特定场景中才会使⽤的类型. 不像前⾯五个类型通⽤性那么强. 因此我们没
有介绍. ⼤家感兴趣可以⾃⾏了解.
参考资料:
https://redis.io/docs/data-types/
2.7 渐进式遍历
Redis 使⽤ scan 命令进⾏渐进式遍历键,进⽽解决直接使⽤ keys 获取键时可能出现的阻塞问
题。每次 scan 命令的时间复杂度是 O(1),但是要完整地完成所有键的遍历,需要执⾏多次 scan。
SCAN
以渐进式的⽅式进⾏键的遍历。
语法:
SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]
命令有效版本:2.8.0 之后
时间复杂度:O(1)
返回值:下⼀次 scan 的游标(cursor)以及本次得到的键。
除了 scan 以外,Redis ⾯向哈希类型、集合类型、有序集合类型分别提供了 hscan、sscan、zscan 命 令,它们的⽤法和 scan 基本类似,感兴趣的读者可以⾃⾏做扩展学习。
渐进性遍历 scan 虽然解决了阻塞的问题,但如果在遍历期间键有所变化(增加、修改、删
除),可能导致遍历时键的重复遍历或者遗漏,这点务必在实际开发中考虑。
2.8 数据库管理
Redis 提供了⼏个⾯向 Redis 数据库的操作,分别是 dbsize、select、flushdb、flushall 命令,
本机将通过具体的使⽤常⻅介绍这些命令。
2.8.1 切换数据库
1
select dbIndex
许多关系型数据库,例如 MySQL ⽀持在⼀个实例下有多个数据库存在的,但是与关系型数据库⽤
字符来区分不同数据库名不同,Redis 只是⽤数字作为多个数据库的实现。Redis 默认配置中是有 16 个数据库。select 0 操作会切换到第⼀个数据库,select 15 会切换到最后⼀个数据库。0 号数据库和 15 号数据库保存的数据是完全不冲突的,即各种有各⾃的键值对。默认情况下,我 们处于数据库 0。
Redis 中虽然⽀持多数据库,但随着版本的升级,其实不是特别建议使⽤多数据库特性。如
果真的需要完全隔离的两套键值对,更好的做法是维护多个 Redis 实例,⽽不是在⼀个
Redis 实例中维护多数据库。这是因为本⾝ Redis 并没有为多数据库提供太多的特性,其次
⽆论是否有多个数据库,Redis 都是使⽤单线程模型,所以彼此之间还是需要排队等待命令
的执⾏。同时多数据库还会让开发、调试和运维⼯作变得复杂。所以实践中,始终使⽤数据
库 0 其实是⼀个很好的选择。
2.8.2 清除数据库
flushdb / flushall
命令⽤于清除数据库,区别在于 flushdb 只清除当前数据库,flushall 会清楚所有数 据库。
❗
永远不要在线上环境执⾏清除数据的操作,除⾮你想体验⼀把 “从删库到跑路” 的操作。
上面就是redis的入门和基础了。感谢观看!