redis

吴龙淼 写于 2022-12-03

事务

MULTI 事务开始
EXEC 开始执行指令

服务器命令

redis-server /path/to/redis.conf  # 指定配置文件启动
redis-server --daemonize yes      # 以守护进程方式运行
ping  检测连接是否存活
shutdown 停服
auth password  # 密码验证
SAVE 同步备份
BGSAVE 异步备份
LASTSAVE          # 获取最后一次成功保存时间
info 返回redis相关信息
slowlog get 显示慢查询
client list        # 显示客户端列表
select n 切换到数据库n,redis默认有16个数据库(DB 0~DB 15),默认使用的第0个
move key n 不同数据库之间数据是不能互通的,move移动键到指定数据库
dbsize 查看当前数据库大小
flushdb 清空当前数据库中的键值对。
flushall 清空所有数据库的键值对。

管道操作
pipe = redis_client.pipeline()
pipe.set('key1', 'value1').set('key2', 'value2').get('key1').get('key2')

# 一次性获取所有命令结果
result = pipe.execute()

key 命令

keys * 查看当前数据库中所有的key
exists key 检查键是否存在
set key value 设置redis键值
del key key1… 删除键
expire key seconds 设置键的生命周期,单位为秒
ttl key 获取键的有效时长
persist key 移除键的过期时间
type key 键的数据结构类型
randomkey 随机返回数据库中一个键
rename key1 key2  重命名
renamex key1 key2  当key2不存在时,key1重命名

string

字符串类型是 Redis 最基础的数据结构,其它的几种数据结构都是在字符串类型基础上构建的,字符串的值可以是字符串、数字、二进制,但其值最大不能超过 512M

set key value 设置一个key的value值
setnx key value 仅当key不存在时进行set
setex key seconds value set 键值对并设置过期时间
mset key value key1 value1… 设置多个key value
msetnx key1 value1 key2 value2… 批量设置键值对,仅当参数中所有的key都不存在时执行,原子性操作,一起成功,一起失败
getset key value 设置一个key的value,并获取设置前的值,如果不存在则返回null

get key 返回key的value
mget key key1 批量获取多个key保存的值
exists key key1 查询一个key是否存在
strlen key 返回key的string类型value的长度。
getrange key start end 获取存储在key上的值的一个子字符串slice [start, end]

append key value 向指定的key的value后追加字符串

decr/incr key 将指定key的value数值进行+1/-1(仅对数值类型)
incrby/decrby key n 按指定的步长对数值进行加减
incrbyfloat key n 为数值加上浮点型数值

setrange key offset value 设置从指定位置开始替换字符,替换超出保留原来的字符值

list

Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边),也可以获取指定范围指定下标的元素等。一个列表最多可以包含 232 - 1 个元素 (4294967295, 每个列表超过 40 亿个元素) 不需要显示声明 list 直接操作

添加操作
lpush/rpush key value1 value2… 从左边/右边向列表中PUSH值(一个或者多个)
lpushx/rpushx key value 向已存在的列名中push值(一个或者多个),list不存在 lpushx失败
linsert key before|after pivot value 在指定列表元素的前/后 插入value

查找操作
lindex key index 通过索引获取列表元素
lrange key start end 获取slice [start, end]
llen key 查看列表长度

删除操作
ltrim key start end 截取指定范围内的列表,slice直接操作原数组
lpop/rpop key 从最左边/最右边移除值 并返回
lrem key count value 删除指定值次数 count >0头部开始, count < 0从尾部开始, count = 0删除列表中所有的指定value
lmove source target l/r l/r 将列表的尾部(右)最后一个值弹出,并返回,然后加到另一个列表的头部

修改操作
lset key index value 通过索引为元素设值

阻塞操作
blpop/brpop key1[key2] timout 移出并获取列表的第一个/最后一个元素,如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
blmove source target l/r l/r timeout 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 timeout=0表示永久阻塞

hash

几乎所有的编程语言都提供了哈希(hash)结构,Redis 中 hash 是一个 string 类型的 field 和 value 的映射表,可以将一个 Hash 表作为一个对象进行存储,表中存放对象的信息。

hset key field value 将哈希表 key 中的字段 field 的值设为 value。重复设置同一个field会覆盖,返回0
hmset key field1 value1 [field2 value2…] 同时将多个 field-value (域-值)对设置到哈希表 key 中。
hsetnx key field value 只有在字段 field不存在时,设置哈希表字段的值。
hdel key field1 [field2…] 删除哈希表key中一个/多个field字段

hget key field value 获取存储在哈希表中指定字段的值
hmget key field1 [field2…] 获取所有给定字段的值
hexists key field 查看哈希表 key 中,指定的字段是否存在。
hlen key 获取哈希表中字段的数量
hkeys key 获取所有字段field
hvals key 获取哈希表中所有值value
hgetall key 获取在哈希表key 的所有字段和值

hincrby key field n 为哈希表 key 中的指定字段的整数值加上增量n,并返回增量后结果 一样只适用于整数型字段
hincrbyfloat key field n 为哈希表 key 中的指定字段的浮点数值加上增量 n。

set

Redis 的 Set 是 string 类型的无序集合,我们不能通过索引获取元素。集合成员是唯一的,这就意味着集合中不能出现重复的数据。Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储 40 多亿个成员)。

集合内操作
sadd key member1[member2…] 向集合中无序增加一个/多个成员
srem key member1[member2…] 移除集合中一个/多个成员
scard key 获取集合的成员数
smembers key 返回集合中所有的成员
sismember key member 查询member元素是否是集合的成员,若存在返回1,不存在返回0

srandmember key [count] 随机返回集合中count个成员,count缺省值为1
spop key [count] 随机移除并返回集合中count个成员,count缺省值为1


集合间操作
sinter key1 [key2…] 返回所有集合的交集
sinterstore destination key1[key2…] 在SINTER的基础上,存储结果到destination。覆盖

sunion key1 [key2…] 返回所有集合的并集
sunionstore destination key1 [key2…] 在SUNION的基础上,存储结果到destination。覆盖

sdiff key1[key2…] 返回所有集合的差集 key1- key2 - …
sdiffstore destination key1[key2…] 在SDIFF的基础上,将结果保存到集合中。覆盖

smove source destination member 将source集合的成员member移动到destination集合
sscan key [MATCH pattern] [COUNT count] 在大量数据环境下,使用此命令遍历集合中元素,每次遍历部分

zset

在有序集合中保留了不能有重复成员的特性,但其中的成员是可以排序的,每一个元素都会关联一个 double 类型的分数(score)作为排序依据,score 相同时按字典顺序排序。redis 正是通过分数来为集合中的成员进行从小到大的排序。

集合内
zadd key score member1 [score2 member2] 向有序集合添加一个或多个成员,或者更新已存在成员的分数
zcard key 获取有序集合的成员数
zscore key member 返回有序集中,成员的分数值
zcount key min max 计算在有序集合中指定区间score的成员数
zlexcount key min max 在有序集合中计算指定字典区间内成员数量
zincrby key n member 有序集合中对指定成员的分数加上增量 n
zscan key cursor [MATCH pattern] [COUNT count] 迭代有序集合中的元素(包括元素成员和元素分值)


范围查询(正序小到大)
zrank key member 返回有序集合中指定单个成员的索引,排名0-n往后排名越高
zrevrank key member 返回有序集合中指定单个成员的索引,从大到小排序,score高到低排序

zrange key start end 通过索引区间返回有序集合成指定区间内的成员
zrevrange key start end 通过索引区间返回有序集合成指定区间内的成员,分数从高到底

zrangebylex key min max 通过字典区间返回有序集合的成员
zrevrangebylex key max min 按字典顺序倒序返回有序集合的成员

zrangebyscore key min max 返回有序集中指定分数区间内的成员 -inf 和 +inf分别表示最小最大值,只支持开区间
zrevrangebyscore key max min 返回有序集中指定分数区间内的成员,分数从高到低排序


删除操作
zrem key member1 [member2…] 移除有序集合中一个/多个成员
zremrangebylex key min max 移除有序集合中给定的字典区间的所有成员
zremrangebyrank key start stop 移除有序集合中给定的排名区间的所有成员
zremrangebyscore key min max 移除有序集合中给定的分数区间的所有成员


集合间操作
zinterstore destination numkeys key1 [key2 …] weight AGGREGATE(sum,max,min) 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中,numkeys表示参与运算的集合数,将score相加作为结果的score, weight权重新集合分数需要乘权重 默认聚合函数相加
zunionstore destination numkeys key1 [key2…] 计算给定的一个或多个有序集的并集并将结果集存储在新的有序集合 key 中

发布/订阅

发送者 (publishers) 不直接向特定的接收者发送消息 (subscribers),而是发布消息到频道(channel),不关心有没有订阅者,订阅者订阅关注一个或多个频道(channel),并且只接收他关注的消息,不管发布者是不是存在。

特定模式匹配(正则)订阅多个频道 PSUBSCRIBE

按名称订阅频道 SUBSCRIBE channel1 channel2...

客户端退订给定模式频道 PUNSUBSCRIBE pattern

客户端退订指定名称频道 UNSUBSCRIBE channel1 channel2...

没有订阅者的频道发送消息 publish bad_channel "can any body hear me?"

向有一个订阅者的频道发送信息 publish msg "good morning"

向有多个订阅者的频道发送信息 publish chat_room "hello~ everyone"

列出当前的活跃频道 PUBSUB CHANNELS pattern

redis 性能优化

持久化 主要作用是数据备份,即将数据存储在硬盘,保证数据不会因进程退出而丢失。 RDB 持久化原理是将 Reids 在内存中的数据库记录定时保存到磁盘上。(定时对内存中的数据生成快照,以文件形式保存在硬盘中) AOF 持久化(append only file)原理是将 Reids 的操作日志以追加的方式写入文件,类似于 MySQL 的 binlog。(类似于 Mysql 的二进制日志,以追加的方式将写和删的操作命令记录到 AOF 文件中) 主从复制 主从复制是高可用 Redis 的基础,哨兵和集群都是在主从复制基础上实现高可用的。主从复制主要实现了数据的多机备份(和同步),以及对于读操作的负载均衡和简单的故障恢复。 缺陷故障恢复无法自动化;写操作无法负载均衡;存储能力受到单机的限制。 哨兵 在主从复制的基础上,哨兵实现了自动化的故障恢复。(主挂了,找一个从成为新的主,哨兵节点进行监控) 缺陷写操作无法负载均衡;存储能力受到单机的限制。 Cluster 集群 通过集群,Redis 解决了写操作无法负载均衡,以及存储能力受到单机限制的问题,实现了较为完善的高可用方案。(6 台起步,成双成对,3 主 3 从)

内存碎片率=Redis向操作系统申请的内存 / Redis中的数据占用的内存

used_memory_rss是Redis向操作系统申请的内存。
used_memory是Redis中的数据占用的内存。

config set activedefrag yes    #自动碎片清理
memory purge                   #手动碎片清理

低于1说明系统开始使用虚拟内存(使用硬盘空间,速度慢),性能下降严重,需要扩容或者删除redis键值
缓存和数据库双写一致性问题
先更新数据库,然后再删除缓存  + 缓存做过期时间,数据过期后再有读请求可从数据库直接更新缓存
缓存雪崩
缓存同一时间大面积的过期失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。
解决方案

缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生。
一般并发量不是特别多的时候,使用最多的解决方案是加锁排队。
给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存。

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

设置热点数据永远不过期。
加互斥锁,互斥锁

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

接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截;
从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力