Redis 深度历险学习笔记

论坛 期权论坛 脚本     
已经匿名di用户   2022-2-7 16:34   3725   0

目录

开篇:Redis可以用来做什么?

基础:Redis基础数据结构

string

list

hash

set

zset

容器数据结构通用规则

应用1:分布式锁

应用2:延时队列

异步消息队列

所冲突处理

延时队列的实现

应用3:位图

应用4:HyperLogLog

应用5:布隆过滤器

应用6:简单限流

应用7:漏斗限流

应用8:GeoHash

应用9:Scan

字典结构


最近在看Redis深度历险,做个笔记,方便以后查看

开篇:Redis可以用来做什么?

基础:Redis基础数据结构

Redis的5种基础数据结构:string(字符串),list(列表),hash(哈希),set(不重复集合),zset(有序集合)

string

动态字符串,内部结构类似于java中的ArrayList,预分配冗余空间,当字符串小于1M时扩容为翻倍,大于1M时每次只会多扩容1M。字符串最大长度为512M。

常见用途:缓存用户信息。

> set name codehole
OK
> get name
"codehole"
> exists name
(integer) 1
> del name
(integer) 1
> get name
(nil)

批量操作:

> set name1 codehole
OK
> set name2 holycoder
OK
> mget name1 name2 name3 # 返回一个列表
1) "codehole"
2) "holycoder"
3) (nil)
> mset name1 boy name2 girl name3 unknown
> mget name1 name2 name3
1) "boy"
2) "girl"
3) "unknown"

过期:

设置key过期时间,到点自动删除,后续有具体的介绍

list

相当于java中的LinkedList。插入删除快,索引定位慢。

常见用途:异步队列。

队列:右进左出

> rpush books python java golang
(integer) 3
> llen books
(integer) 3
> lpop books
"python"
> lpop books
"java"
> lpop books
"golang"
> lpop books
(nil)

栈:右进右出

> rpush books python java golang
(integer) 3
> rpop books
"golang"
> rpop books
"java"
> rpop books
"python"
> rpop books
(nil)

Redis底层存储为快速列表quidcklist

当元素较少的时候使用一块连续的内存,结构为ziplist,数据较多时转为quicklist,quicklist可以理解为链表与ziplist的结合,既满足了快速插入删除性能,又不会太占内存空间。

hash

相当于java中的HashMap,内部结构也与java中的HashMap一致,是数组+链表的二维结构。

与java的HashMap不同的是,Redis的hash的key只能是字符串,并且Redis的rehash的方式不同,Redis采取了渐进式rehash策略。

渐进式rehash会在rehash的同时,同时保留新旧两个hash,查询时同时查询两个hash,在后续的定时任务中以及hash的子指令中,循序渐进的将旧hash的内容一点点的迁移到新hash中。

set

相当于java中的HashSet,内部键值对是无序且唯一,内部实现相当于一个特殊的hash,hash中所有的value都是NULL

应用:可以用来存储活动的中奖名单

zset

类似于java中SortedSet和HashMap的结合,一方面,它是一个set,保证了内部value的唯一性,另一方面可以为每个value赋一个score,代表value的排序权重。内部实现是使用的跳跃列表的数据结构。

查找按HashMap方式查找,插入按二分插入法插入。

容器数据结构通用规则

list/hash/set/zset通用以下规则:

  1. create if not exists
  2. drop if no elements

过期:

所有结构都能设置过期时间,过期是以对象为单位,如某个hash结构的过期是整个hash对象的过期,而不是某个key的过期。

需要注意的是如果一个字符串设置了过期时间,再次调用set方法修改它,过期时间会失效。

应用1:分布式锁

使用setnx指令占领锁,通过del释放锁

如果出现异常导致无法释放锁,就有可能导致del没有执行,就会陷入死锁状态,锁永远得不到释放。

可以使用指令expire在拿到锁之后加上一个锁过期时间,可以保证到期后自动释放锁。

如果在setnx和expire中间服务器挂掉了,那同样可能导致expire没有执行,陷入死锁状态。Redis 2.8 加入了set指令的扩展参数,可以使得setnx和expire指令一起执行。

应用2:延时队列

异步消息队列

使用Redis的list来实现,用lpush/rpush进行入队,rpop/lpop出队。

队列空了:通过sleep让线程休息一下。

sleep会导致延迟增加,使用blpop/brpop代替lpop/rpop,b代表blocking,阻塞读,在队列没有数据时进入休眠状态,一旦有数据过来,立刻醒过来。

所冲突处理

加锁失败处理策略:

  1. 直接排除异常,通知用户稍后重试
  2. sleep,一会再试
  3. 将请求转至延时队列,一会再试

延时队列的实现

使用Redis的zset实现,将消息序列化为一个字符串作为zset的value,将消息的到期处理时间设置为score,多个线程轮询zset获取到期任务进行处理,多线程是为了保障可用性。

应用3:位图

应用4:HyperLogLog

HyperLogLog提供不精确的去重计数方案。

指令:pfadd,pfcount,pfmerge

应用5:布隆过滤器

指令:bf.add,bf.exists,bf.madd,bf.mexists

原理:对应到Redis中就是一个大型的位数组以及几个不一样的无偏差hash函数。无偏差就是能够把元素的hash值算的比较均匀。

添加key时,使用多个hash函数对key进行hash算的一个整数索引,然后对数组长度进行取模运算得到一个长度,将位数组中这几个位置都置1。

查询key时,把hash的位置算出来,看看位数组中这几个位置是不是都为1,只要有1位为0,那么这个key不存在,如果都为1,不一定说明这个key一定存在,只说明大概率存在。

应用6:简单限流

应用7:漏斗限流

应用8:GeoHash

Geo算法:将二维经纬度映射到一维整数,所有元素都挂在一条线上。

映射算法:将地球看成二维平面,然后切割成若干小方块,类似围棋盘一样,方块越小越精确。对方块进行整数编码,越靠近编码越接近。切蛋糕法,二刀分四格,00、01、10、11,然后对每个小方块继续二刀分。然后对这个经纬度整数做一个base32编码变成一个字符串。

Redis中,使用52位整数编码,放入zset中,value是元素的key,score是GeoHash的52位整数。通过score排序可以获得附近的人,通过将score还原可以得到原始坐标。

指令:geoadd(添加元素),geodist(计算两个元素之间的距离),geopost(获取元素经纬度坐标),geohash(获取元素经纬度编码字符串),georediusbymember(查找附近的人)

注意事项:如果数据了过多建议对geohash单独使用一个redis,甚至可以对geo数据进行拆分,按国家、省份等拆分成多个redis,这样就可以显著降低单个zset的大小。

应用9:Scan

搜素key:keys指令,无offset、limit参数,Redis单线程,可能造成卡顿

指令scan:

1、复杂度虽然也是 O(n),但是它是通过游标分步进行的,不会阻塞线程;
2、提供 limit 参数,可以控制每次返回结果的最大条数,limit 只是一个 hint,返回的
结果可多可少;
3、同 keys 一样,它也提供模式匹配功能;
4、服务器不需要为游标保存状态,游标的唯一状态就是 scan 返回给客户端的游标整数;
5、返回的结果可能会有重复,需要客户端去重复,这点非常重要;
6、遍历的过程中如果有数据修改,改动后的数据能不能遍历到是不确定的;
7、单次返回的结果是空的并不意味着遍历结束,而要看返回的游标值是否为零。

scan 参数提供了三个参数,第一个是 cursor 整数值,第二个是 key 的正则模式,第三
个是遍历的 limit hint。

字典结构

Redis中所有的key都存在一个很大的hash中,scan指令返回的是一维数据的位置索引,也称为槽(slot),limit表示要遍历的槽位数

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:81
帖子:4969
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP