Redis 简介
HyperLogLog 结构
HyperLogLog 主要用来做基数统计
命令 | 说明 |
---|---|
pfadd key element1 [element2 ···] | 向 HyperLogLog 中添加元素 |
pfcount key1 [key2 ···] | 计算一个或多个 HyperLogLog 的基数 |
pfmerge destkey courcekey1 [sourcekey2 ···] | 合并多个 HyperLogLog |
数据集基数,指集合中不重复的元素个数
GEO 结构
GEO 结构同集合有点类似,只不过每个元素都有相关联的经纬度值,若元素表示某个地点,那么这个结构就是存储地点坐标的集合。
-
添加位置坐标
geoadd longitude1 latitude1 member1 [longitude2 latitude2 member2 ···]
-
获取指定位置坐标
geopos key member1 [member2]
-
获取指定的两个位置的距离,单位:米(默认)、千米、英尺、英里
geodist key member1 member2 [m|km|ft|mi]
-
以给定的经纬度为中心,返回在指定半径范围内的元素
georadius key longitude latitude radius m|km|ft|mi [withcoord] [withdist] [withhash] [count count] [asc|desc] [store key] [storedist key]
-
以指定元素所表示的位置为中心,返回在指定半径范围内的元素
georadiusbymember key member radius m|km|ft|mi [withcoord] [withdist] [withhash] [count count] [asc|desc] [store key] [storedist key]
-
返回表示元素位置的 Geohash 字符串,相同前缀字符串表示的位置是相近的,反之则未必
geohash key member1 [member2 ···]
Stream 结构
针对消息的传递,Redis 在 5.0 版本中新增了 Stream 结构。
-
添加一条消息到 Stream 流中
XADD key ID field value [field value ...]
这里,key 就是键名,feild value 就是消息内容,稍微复杂的是 ID ,它作为流里每一个消息的唯一标识,分为两个部分
<millisecondsTime>-<sequenceNumber>
,即生成消息时的本地系统的毫秒数-序列号
,一般指定 ID 为 * 符号,系统就会自动生成该值。当然,我们可以自己指定 ID 的值,但是需要保证其是单调递增的,而该 ID 值单调性的判断也分为两部分,有下面三种情况:- 如果前半部分满足单调性,则可以添加消息。
- 如果前半部分不满足单调性但是值相同,则判断后半部分是否满足单调性,满足则可以添加消息,不满足则不可添加消息。
- 如果前半部分的值小于之前消息的前半部分的值,那么不可添加消息。
可以在同一个毫秒内添加多条消息,但追加消息的时间不能小于之前添加消息的时间。所以,自动生成 ID 时,如果由于系统时间的变动,导致时间部分变小,那么则自动使用上一条消息的时间部分作为要添加的消息的 ID 的前半部分。
在指定消息的 ID 时,如果不包含
-
符号,那么提供的值将作为 ID 的前半部分,而后半部分默认为 0 。这条规则同样适用于其他命令中的 ID 参数。即xadd mystream 100 name Mike
同xadd mystream 100-0 name Mike
一样,依次执行这两条命令时,会报错。添加消息时指定的 ID 必须大于 0-0
-
获取流中的消息个数
XLEN key
-
获取指定范围内的流消息
XRANGE key start end [COUNT count] XREVRANGE key end start [COUNT count]
这里的 start 和 end 是 ID 的值,当然,只指定 ID 的前半部分亦可,返回的消息,是包含两端的。特殊字符
-
和+
分别表示最小和最大的 ID 值。若不想包含获取范围内两端的消息,可以在 ID 之前或之后添加(
或)
符号。xrevrange 命令同 xrange 功能一样,只是顺序相反,所以通常用来获取流中的最后一条消息。
-
读取流中的消息
XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id [id ...]
从流中读取大于指定 ID 的消息,返回的消息不包含指定的 ID 那条消息。但与 xrange 最大的不同是该命令是可以阻塞的,如果想要读取最新的消息,则可以使用命令
xread block 0 streams mystream $
,这里$
表示最大的消息 ID 值,所以会一直阻塞等待下一条最新消息。实际上,ID 的值可以是尚未产生的,如我们预判 ID 到达某个值时,需要执行某些操作,则可以使用该命令一直阻塞等待该消息的到达。流中的消息是扇出的,其会传递给多个客户端。
-
限制消息长度
XTRIM key MAXLEN [~] count
~ 表示近似值
-
组操作
XGROUP [CREATE key groupname id-or-$] [SETID key groupname id-or-$] [DESTROY key groupname] [DELCONSUMER key groupname consumername]
create 创建消费组,setid 设置消费组消费消息最后位置,destroy 销毁消费组,delconsumer 删除消费组的消费者
-
获取流或消费组信息
XINFO [CONSUMERS key groupname] [GROUPS key] [STREAM key] [HELP]
使用
help <command-name>
命令查看命令的格式
排序
排序命令:
sort key [by pattern] [limit offset count]
[get pattern [get pattern ···]] [asc|desc] [alpha]
[store destination]
在使用命令 smembers
获取集合元素时,可以发现,当元素均为数字时,其返回结果是有序的。实际上,我们可以使用 sort
命令,明确的表示对返回结果排序。但是,要注意该命令默认会将待排的元素转为双精度值,而后进行递增排序。所以,如果要递减排序,需指定 desc
,或者要按字典排序,则要指定 alpha
。
在用 sort
获取有序集合的排序结果时,其排序依据是元素本身,而不是与元素相关联的分值。
排序时,如果待排元素的排序依据是其他键值,那么可以通过 by
来指定依据,其后的第一个 *
符号,会被替换为元素的值,而后整体作为一个键去获取具体的排序依据值,如果依据键值是散列类型,可以使用 ->
获取指定的排序依据字段。
如:sort test-list by hash-list:*->hash-key
使用 by
时,指定其后为常量,那么排序并不会执行,而是直接返回待排元素。所以可以通过这种方式获取列表、集合的所有元素。
在排序结束后,通常想要根据排序元素获取其他键值,同 by
类似,可以使用 get
选项拼接元素获取其他值的键名,允许使用多个 get
获取多个键值,并且可以使用 get #
保留元素本身。
如果后续需要访问排序的结果,可以使用 store
保存排序结果,键值类型为列表。
事务
虽然 Redis 中的命令都是原子性的,但是并不意味着多条命令的执行是不可分割的。作为数据库,其自然也有事务的概念。
multi
命令1
命令2
命令3
exec
使用 multi
表示后续命令为一个事务,使用 exec
表示开始执行这些命令。如果事务中的命令有语法错误,则所有的命令都不会执行。而如果命令在执行过程中出错,并不会影响其他命令的执行,这意味着事务需要回滚,但 Redis 为了保持简洁和快速并没有提供回滚的功能,所以需要开发人员自己恢复数据库到执行前的状态。
事务中执行的命令的结果会在最后统一返回,所以在事务中想要先获取一个命令的结果作为后续命令执行的依据是行不通的。因此,可以在使用 multi
命令前,先使用 watch
命令监控相应的键值,如果发生修改,其将阻止后续事务的执行。这就避免了在 multi
之前获取的值因其他客户端的修改而导致事务的执行错误。当然,如果要通过获取的值来决定是否执行事务,那么,如果主观决定不执行,则需要使用 unwatch
命令取消监控该键值,以防止其对其他事务造成影响。
生存时间
命令 | 说明 |
---|---|
expire key seconds | 设置键值的生存时间,单位:秒 |
pexpire key milliseconds | 设置键值的生存时间,单位:毫秒 |
expireat key timestamp | 设置键值的有效截止时间,unix 时间戳,单位:秒 |
pexpireat key milliseconds-timestamp | 设置键值的有效截止时间,unix 时间戳,单位:毫秒 |
ttl key | 获取键值的剩余生存时间,单位:秒, 如果键不存在或永久有效,则返回 -1 |
pttl key | 获取键值的剩余生存时间,单位:毫秒 |
UNIX 时间戳的 0 按照 ISO 8601 规范为 :1970-01-01T00:00:00Z
消息通知
使用 lpush 和 rpop 或者 rpush 和 lpop 来实现队列的功能,并且取队列元素时,想要在队列为空时,阻塞线程,可以使用 blpop
或 brpop
命令。
blpop|brpop key [key ···] timeout
表示从队列左|右侧弹出元素,并且在队列为空时阻塞指定的时间,timeout 设置为 0 表示一直阻塞。 由参数可知,该命令可以监控多个队列,而多个队列中都有元素时,先从第一个有元素的队列弹出,即这些队列的优先级是递减的。
通过列表存取命令的配合使用,构造一个队列,作为多个进程之间通信的载体,这种技巧最普遍的应用便是消息队列,当然,Redis 5.0 版本之后,我们可以直接使用 Stream 结构。
发布和订阅
命令 | 说明 |
---|---|
subscribe channel1 [channel2 ···] | 订阅指定的频道 |
unsubscribe [channel1 [channel2 ···]] | 取消订阅指定的频道或所有频道 |
publish channel message | 向频道中发送信息 |
psubscribe pattern1 [pattern2 ···] | 通过通配符订阅指定的频道 |
punsubscribe [pattern1 [pattern2 ···]] | 通过通配符取消通过通配符设置的订阅频道或所有通过通配符设置的频道 |
同消息通知一样,发布和订阅也可以实现进程间的通信。
内存优化
Redis 作为一个内存数据库,如何节省内存,关系着运营成本的高低。精简键名和键值可以节省内存,但效果有限,所以 Redis 为每一种数据类型提供了两种编码方式,根据数据量的多寡,Redis 会自动变更编码方式,以节省内存。
使用命令 object encoding <key-name>
可以查看键值的编码方式。
数据类型 | 内部编码方式 |
---|---|
字符串类型 | REDIS_ENCODING_RAW REDIS_ENCODING_INT |
散列类型 | REDIS_ENCODING_HT REDIS_ENCODING_ZIPLIST |
列表类型 | REDIS_ENCODING_LINKEDLIST REDIS_ENCODING_ZIPLIST |
集合类型 | REDIS_ENCODING_HT REDIS_ENCODING_INTSET |
有序集合类型 | REDIS_ENCODING_SKIPLIST REDIS_ENCODING_ZIPLIST |
持久化
Redis 作为内存数据库,需要配合持久化方案,才能保证其可靠性。
-
快照,默认的持久化方式,根据配置,在指定时间内达到了指定的键值修改个数时,便生成一个 rdb 类型的文件来替换原文件。该文件默认在配置文件目录下,默认名为
dump.rdb
,当然,可以在配置文件中的SNAPSHOTTING
注释范围内修改相关配置。RDB(Redis Database)文件是一个二进制文件,其格式可以参考 Redis RDB Dump File Format 文档。使用命令
save
或bgsave
可以主动生成快照,前者由主进程执行备份操作,会阻塞其他请求。 -
AOF(Append Only File),该持久化方式是默认关闭的,可以在配置文件中
APPEND ONLY MODE
注释范围内修改appendonly
配置,打开该持久化方式。该方式,会将每一次键值修改的命令写入到 aof 文件中,当文件过大,Redis 还会优化文件,删除冗余的命令。当 Redis 启动时,会根据该文本文件恢复数据库。实际上,该模式并不是绝对安全的,因为在写命令到 aof 文件时,实际是先写入到系统硬盘缓存中的,然后再写入到硬盘中,这其中是有时差的。所以配置中默认的appendfsync everysec
配置设置每秒进行一次缓存同步,来规避系统缓存同步时间过长带来的风险。另外两个no
和always
的配置值,表示由操作系统决定和每次更新操作都写数据到磁盘。