redis核心原理及应用
数据结构
1.string(字符串)
key-value形式
常用命令(可以批量操作):set(批量用mset)
key value; get(mget) key;
exists key;
del key;
expire key设置过期时间;
setnx key:如果key不存在就创建,否则执行失败,是原子操作
2.list(链表)
类似于java当中的LinkedList
右边进,左边出(队列)
rpush name values:右边进
lpop name:左边出一个元素
llen name:得到name 的长度
右边进,右边出(栈)
rpush lists values:右边进
rpop lists :右边出一个元素
lindex lists i:返回i位置的元素,需要遍历整个llist
lrange list 0 -1:获取所有元素
ltrim lists start end:返回start 和end之间的元素,其他的会被删掉,慎用
3.hash(字典)
类似于java中的hashMap,底层也是链表+数组(一维数组,二维链表)
4.set (集合)
Redis 的集合相当于 Java 语言里面的 HashSet,它内部的键值对是无序的唯一的。它的内部实现相当于一个特殊的字典,字典中所有的 value 都是一个值NULL。
set有去重的功能
相关命令:sadd sets value; smembers books:查看所有成员,结果是无序的:
sismember sets value:查看某个value是否存在; scard sets:获取长度
spop:弹出一个
5.zset(面试常问)
一方面它是一个 set,保证了内部 value 的唯一性,另一方面它可以给每个 value 赋予一个 score,代表这个 value 的排序权重。
应用场景:
1.延时队列
2.布隆过滤器
3.geohash算法(附近的人)
常用命令:以学生成绩排序为例
zadd zsets score value:score是学生分数,value是学生ID
zrange zsets 0 -1:按照score排序输出,区间为排名范围
zrevrange zsets 0 -1:逆序输出
zscore zsets value:获取指定value的score
zcard zsets # 相当于 count()
zrank zsets value: 获取value对应的排名
zrem zsets value:删除value
过期时间
Redis 所有的数据结构都可以设置过期时间,时间到了,Redis 会自动删除相应的对象。需要注意的是过期是以对象为单位,比如一个 hash 结构的过期是整个 hash 对象的过期,而不是其中的某个子 key。
还有一个需要特别注意的地方是如果一个字符串已经设置了过期时间,然后你调用了 set 方法修改了它,它的过期时间会消失。(会把过期时间改为-1)
应用场景
1.分布式锁
(1)普通分布式锁:
1. 用setnx()命令加锁,del()删除锁
问题:在运行期间出现异常,del没有运行导致锁没释放,会导致死锁
2.在setnx()之后设置expire,超过过期时间自动释放锁
问题:setnx()和expire之间不是原子操作,之间如果出错也不会设置expire,所以还是会死锁
(2)解决办法:
redis2.8版本后,官方将setnx()+expire合并为一个原子命令:setex();(原来那个项目redis版本较老,所以用value的设置时间来判断是否del)
2. 延时队列
对于那些只有一组消费者的消息队列,使用 Redis 就可以非常轻松的搞定,没有ACK保证,对可靠性较高的系统不推荐使用
数据结构:list
使用rpush/lpush操作入队列,使用lpop 和 rpop来出队列。
阻塞读在队列没有数据的时候,会立即进入休眠状态,一旦数据到来,则立刻醒过来。消息的延迟几乎为零。用blpop/brpop替代前面的lpop/rpop,解决了队列延迟的问题
延时队列的实现:通过zset数据结构实现
3. HyperLogLog--记录pv,uv(用来解决很多精度不高的统计需求)
如果统计 PV 那非常好办,给每个网页一个独立的 Redis 计数器就可以了,这个计数器的 key 后缀加上当天的日期。这样来一个请求,incrby 一次,最终就可以统计出所有的 PV 数据。但是uv不一样,他需要去重。
HyperLogLog 提供了两个指令 pfadd 和 pfcount,根据字面意义很好理解,一个是增加计数,一个是获取计数。pfacount返回的事数量。
但是注意, HyperLogLog是有误差的,在0.81%左右,不过对于统计PV,UV来说可以忽略不计。
4.布隆过滤器(Bloom Filter)
应用场景:
新闻客户端推送消息时去重,即已经推送了的不在推送。
爬虫系统中对url去重,已经爬过的不会再爬
部分nosql数据库中有类似布隆过滤器的结构
垃圾邮件过滤系统中也用到
类似于不太精确的set结构(因为可能会误判),需要redis4.0以上
命令:bf.add 添加元素,bf.exists 查询元素是否存在
5.漏斗限流
Redis 4.0 提供了一个限流 Redis 模块,它叫 redis-cell。该模块也使用了漏斗算法,并提供了原子的限流指令。
该模块只有1条指令cl.throttle
6.GeoHash算法(地理位置距离排序算法)
应用场景:可以使用 Redis 来实现摩拜单车「附近的 Mobike」、美团和饿了么「附近的餐馆」这样的功能。
结构:zset
7.scan
在平时的业务开发中要尽量避免大key的产生
应用场景:用来代替keys命令获取正则匹配的key,当数据较多时,使用keys命令会将所有的key暴力的输出,时间复杂度是o(n),但是redis是单线程模式,势必会造成其他命令卡顿,所以用scan命令
命令:scan 0 match key99* count 1000
scan 参数提供了三个参数,第一个是 cursor 整数值,第二个是 key 的正则模式,第三个是遍历的 limit hint。
zsan命令不仅可以对key进行遍历,也可以对数据集合进行遍历,如对hash结构进行遍历:hscan,zscan遍历zset
大key扫描:key较大时需要扩容会申请另一块内存,会导致卡顿,释放时也会导致卡顿,所以在平时的业务开发中要尽量避免大key的产生