Redis读书笔记 - 基础使用篇

目前在看钱文品老师的《Redis深度历险》,感觉蛮基础蛮有趣,就随手记录一些干货


五种基础用法

kv键值对(string)

键值对读写
“get {k}”, “set {k} {v}”
可以批量读写
“mget {k1} {k2}”, “mset {k1} {v1} {k2} {v2}”
删除
“del {k}”
可以查看和设置超时时间
“ttl {k}”, “expire {k} {sec}”
检查某key是否存在
“exist {k}”
可以防止覆盖
“setnx {k} {v}”
用于记数的自增(可以指定大小)
“incr {k}”, “incrby {k} {x}”

list

可以用作消息队列,但是机制过于简单功能过少,所以list不常用,因为涉及到索引遍历的操作都是n的时间复杂度

可以左右进出
“lpush {k} {v1} {v2}”, “lpop {k}”, 和"rpush",“rpop”
求大小
“llen {k}”
通过索引值获得元素
“lindex {k} {idx}”
通过索引值的区间获得多个元素
“lrange {k} {idx-begin} {idx-end}” -1表示结尾
通过索引值的区间修改list的内容
“ltrim {k} {idx-begin} {idx-end}}” 负区间的话则清空

hash

优点是有独立的kv空间,如果对应到业务数据结构只是经常修改某字段,会省掉整体序列化很方便。
缺点是相较于kv会消耗额外空间。
ps:可以使用"structKey_propertyKey"方式拼接key把hash转为kv,酌情而定。

“hset {k} {hk1} {hv1} {hk2} {hv2}”
“hget {k} {hk}”, “hgetall {k}”
求大小
“hlen {k}”
子key下的自增
“hincrby {k} {hk} {x}”

set

set集合内的数据为无序去重的状态

“sadd {k} {v1} {v2}”
“smembers {k}” (全部返回), 弹出一个 “spop {k}”
球大小
“scard {k}”
查是否存在
“sismember {k} {v}”

zset

相对于set来说,zset内数据则为有序状态,需要给每个value一个score用于排序

“zadd {k} {score} {v}”
按照排名读
“zrange {k} {idx-begin} {idx-end}” 小到大顺序返回value,"zrevrange"倒叙
按照score区间读(可以把score一并取出)
“zrangebyscore {k} {score-low} {score-high} (withscores)”
球大小
“zcard {k}”
获取value的score(double类型)或排名(index)
“zscore/zrank {k} {v}”
删除value
“zrem {k} {v}”

常用场景

分布式锁

使用"setnx"来进行加锁,如果已有锁则会返回失败。但是在加锁之后产生异常会导致死锁,则需要再设置超时时间。2.8版本中加入了将两针合并的操作**“set {k} {v} ex {sec} nx”**。还可以在加锁解锁时value给签名来保证解锁解的是自己的锁。

消息队列

可以使用list结构的push和pop来实现消息队列。
防止一直pop空结果使cpu瞎忙,可以使用blpop/brpop阻塞弹出。

延时队列

使用zset结构的zrangebyscore和zrem实现。
存入任务时把score设置为开始处理的时间戳,然后处理线程每次都用当前时间戳做限制只取一条,取失败说明没有可处理休眠即可。取成功则用zrem删除,只有删除成功才能说明是自己抢到任务才可以处理。
因为两者不是原子操作并发高了时候会争抢严重,可以用lua封装。

有趣的工具

位图

对应“布尔型数组”,使用在用index来取bool值的场景,在kv键值对的基础上增加了对value字符串按位修改的操作。

: “getbit {k} {idx}”

“setbit {k} {idx} 0/1”, “set {k} {v}” 可以整存(按照{v}的二进制存)
查找第一个0或1
“bitpos {k} 0/1 ({start} {end})” 可以指定[start, end]范围,但是是按照字节索引的
统计1的个数
“bitcount {k} ({start} {end})” 同上
bitfield 使位图支持区间上的操作,3.2更新
“bitfield {k} {cmd}” 其中,cmd有三种:

“set u/i{len} {idx} {v}” 使用无符号/有符号的v的二进制覆盖从idx开始长为len的部分

“get u/i{len} {idx}” 返回的是所取的二进制位区间按照u/i转换后的int
自增
“(overflow {op}) incrby u/i{len} {idx} {x}” x为自增的值
可以通过{op}选择当自增产生溢出时 保持最大值或者 失败不执行: sat/fail

HLL 基数估算

基数是一个数据集中,不重复的数据的个数
通常的场景都是使用一个set,然后把所有的数据都丢进去,最后求一个set的大小就结束了,费时费力的去重了的结果也没有用到。因此常用于数据统计。在这种情况下使用hyperloglog会很方便,但是注意这是估算会产生误差

原理
对于一个集合, 其中所有元素的二进制形式中,连续后缀零的个数的最大值与集合的基数是成正比的,例如1-8中最长的是8=>00001000,因此可以从最长的3个零估算出基数为2^3 = 8,1-8和1-15的集合求出来都是8,所以看的出来误差还是有的。
当然集合的元素不可能只是连续的1-8,如果出现一些特例比如8换成65536,就会导致最大值不是3而是更大。
因此要分成多份,每一份取各自的最大值,然后求得平均数来带入幂指函数。而平均数的算法使用调和平均数,这样会更加偏其中的小值。最终用平均数带入幂指函数算得了估计的基数。hyperloglog的讲解可以参考这里。 1
使用
“pfadd {k} {v1} {v2}” 给k集合添加元素
“pfcount {k}” 获取k集合基数
“pfmerge {k} {k1} {k2}” k为k1 k2的合并后的结果
实现
在redis内部,对于每一个pf计数都会分2^14个桶分组计数,然后每一个桶使用6位来记录后缀零的长度,最大能表示63位0,因此计算得内存占用12kb,所以在数量很大的时候使用这种方式会很简单。

布隆过滤器 4.0

布隆过滤器BloomFilter使用的场景一般是“垃圾过滤”。是在垃圾请求过多(不存在的请求过多的时候,多次的exist查询会拖慢数据库)或者不存在的请求代价过大。布隆过滤器就是可以过滤掉大部分肯定不存在的。而没被过滤掉的并不是一定存在,而是有一定的误差率会出现不存在的情况。

原理
布隆过滤器也是通过哈希实现的。hash在发生碰撞的时候需要定制方案去解决碰撞,而bf则不会去解决,误差就是因此而产生的。
bf使用一个大型的位数组用于散列,因为其结果只需要表示是否存在,则可以使用位来表示。然后bf使用多个哈希函数将结果散列在数组上,添加元素时只需将所有的散列结果位置1即可。查找的话只有当所有散列结果位都为1时才表示可能存在。
散列结果为0的可以知道是肯定没有出现过,是过滤掉的。而散列结果全为1的时候,可能是出现过也可能是全部发生碰撞产生的误差。
使用
“bf.add {k} {v}” 给过滤器k增加元素v
“bf.exists {k} {v}” 检查k中是否存在v元素
“bf.madd/mexists {k} {v1} {v2}” 也可以批量处理
“bf.reserve {k} {rate} {size}” 在add之前初始化,要指定key,错误率,基数大小
实现
因为bf的位数组只记录了哈希结果的位置,因此在在初始化的时候要确定大小,如果超过了规定的大小,错误率会上升但是不会飙升,也给了重建的时间。而因为bf内部完全没有存哈希结果,因此没有办法像hash一样重建。所以bf的重建只能 使用全部历史数据重新建立

限流 4.0

对分布式集群的限流可以使用Redis-Cell,内部使用使用的是漏斗算法

使用
“cl.throttle {k} {size} {op} {time} {ask}”

参数
:
key
漏斗总容量
操作次数
时间范围(用于计算速率“操作次数/时间范围”,例如“15 60”表示每60s可使用15个单位)
本次申请的大小(默认1)

返回
:
分配结果(1拒绝/0成功)
容量
剩余容量
多久后可重试(如果被拒绝)
多久后完全空

地理计算 3.2

geo指令,是普通的zset结构,元素的删除可以使用zrem。
geo内元素不宜太多,若需要一个数量级很大的geo,单独布置会避免redis卡顿

使用
:
“geoadd {k} {lon} {lat} {hk}” 在{k}下增加点{hk}, {lon}经度double[-180,180], {lat}纬度double[-90,90],东北为正。
“geodist {k} {hk1} {hk2} {type}” 用于计算{k}下两点之间距离,{type}为单位可选m/km/ml(英里)/ft(尺)
“geopose {k} {hk1} {hk2}” 取点的坐标,可取多个
“geohash {k} {hk}” 取点的哈希值
“georadiusbymember {k} {hk} {dis} {type} (withcoord/dist/hash) count {n} asc/desc”
取指定点附近的其他点,自己点也会返回
dis是附近的范围,type是单位,n为取的数量,可升序降序,可以一并取坐标/距离/哈希值
“georadius” 含义相同,将上面的{hk}某点改成{lon}{lat}坐标

遍历

用于管理全量的key的场景

使用
:
“keys {k}” 支持对key正则模式匹配,缺点是O(n)的时间而且还会卡住redis
“scan {iter} (match {k}) (count {n})” 使用{iter}游标遍历,返回新游标加数据list
第一次时用游标写0,后续则用返回的新游表,直到为0时遍历结束。支持模式匹配k和指定返回数量n

scan通过高位加法的方式遍历哈希表的slot,例如增序 000 -> 100 -> 010 -> 110
因为这种方式的遍历在rehash的前后顺序不变,不需要重新遍历。因为000rehash后变成0000和1000,新表中也是相邻的。
注意,在rehash的过程中可能出现重复的数据。

PS

redis的key的value不应该过大,zset这样的结构也是,否则会在rehash时候产生卡顿。要寻找这样的大key可以使用:redis-cli -h 127.0.0.1 -p 7001 --bigkeys -i 0.1
可选参数-i每隔100条scan休眠0.1s可以防止线上ops过高。


  1. http://dqyuan.top/2018/08/22/hyperloglog.html ↩︎

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值