redis
穿透:缓存没有,去存储层里查。熔断:存储层挂了,直接访问缓存
多路I/O复用linux下
首先了解一下FD(文件描述符)
一个打开的文件通过唯一的描述符进行引用,该描述符是打开文件的元数据到文件本身的映射
还有其他复用函数:epoll/kqueue/evport/select
redis在不同平台中会优先选择时间复杂度O(1)的I/O多路复用函数作为底层实现
以时间复杂度O(N)的select作为保底
基于react设计模式监听I/O事件
Redis数据类型
String:最基本的数据类型,二进制安全(可以包含任何数据,jpg图片,序列化的对象)
底层是一个结构体:
struct sdshdr{
int len; //buf中已占用空间的长度 int free; //buf中剩余可用空间的长度 char buf[]; //数据空间
}
String数据结构是简单的key-value类型,value其实不仅可以是String,也可以是数字。 常规key-value缓存应用; 常规计数:微博数,粉丝数等。
Hash:String元素组成的字典,适合用于存储对象
命令:hmset buty name "buty" age 26 sex "male" 获取名字: hget buty name
hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值。 比如我们可以 hash 数据结构来存储用户信息,商品信息等等。比如下面我就用 hash 类型存放了我本人的一些信息:
key=JavaUser293847
value={
“id”: 1,
“name”: “SnailClimb”,
“age”: 22,
“location”: “Wuhan, Hubei”
}
List:列表,按照String元素插入顺序排序,类似于栈(显示的时候越新的数据越靠前,可以实现排行榜)
常用命令: lpush,rpush,lpop,rpop,lrange等
list 就是链表,Redis list 的应用场景非常多,也是Redis最重要的数据结构之一,比如微博的关注列表,粉丝列表,消息列表等功能都可以用Redis的 list 结构来实现。
Redis list 的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
另外可以通过 lrange 命令,就是从某个元素开始读取多少个元素,可以基于 list 实现分页查询,这个很棒的一个功能,基于 redis 实现简单的高性能分页,可以做类似微博那种下拉不断分页的东西(一页一页的往下走),性能高。
Set:String元素组成的无序集合,通过哈希表实现的,不允许重复
常用命令: sadd,spop,smembers,sunion 等
set 对外提供的功能与list类似是一个列表的功能,特殊之处在于 set 是可以自动排重的。
当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。可以基于 set 轻易实现交集、并集、差集的操作。
比如:在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。
Sorted Set :通过分数来为集合中的成员进行从小到大排序
底层是跳表实现的:https://www.jianshu.com/p/dd01e8dc4d1f
常用命令: zadd,zrange,zrem,zcard等
和set相比,sorted set增加了一个权重参数score,使得集合中的元素能够按score进行有序排列。
举例: 在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息,适合使用 Redis 中的 Sorted Set 结构进行存储。
更高级的数据结构:
用于计数的HyperLogLog
用于支持存储地理位置信息的Geo
常见问题:如何从海量Key里查询出某个固定前缀的Key?
陷入陷阱:KEYS pattern:查找所有符合给定模式pattern的key,查所有就是 keys *
海量数据的话必然卡住,keys指令是一次性返回所有匹配的key
正确答案:SCAN cursor [MATCH pattern] [COUNT count]
只需要记得把上次返回的游标用到下次就可以了
常见问题:如何通过Redis实现分布式锁?
分布式锁是控制分布式系统或不同系统之间共同访问共享资源的一种锁的实现,需要互斥来防止彼此干扰保证一致性
分布式锁需要解决的问题:
互斥性:任意时刻只能有一个客户端获取到锁
安全性:锁只能被持有该锁的客户端删除,不能被其他客户端删除
死锁:获取锁的客户端因为某些原因宕机,其他客户端再也没法获取锁
容错:部分redis节点宕机,客户端还能正常获取锁和释放锁
命令: SETNX key value
时间复杂度:O(1) 返回值:成功1 失败 0
先SETNX,对某key赋值,set if not exists,,一旦设置了值不会更改
咋办呢?
如果挂掉了,来不及设置过期时间咋办?
缺点就在于原子性得不到满足,虽然setnx和expire都是原子的,但是组合起来就不是了
解决方案:把setnx和expire糅合在一起
https://blog.csdn.net/kongmin_123/article/details/82080962
问题:大量KEY同时过期怎么办?
清除大量key花费时间,卡顿,把时间加上随机值
redis如何对过期时间进行自动删除?
如果假设你设置了一批 key 只能存活1个小时,那么接下来1小时后,redis是怎么对这批key进行删除的?
定期删除+惰性删除。
通过名字大概就能猜出这两个删除方式的意思了。
定期删除:redis默认是每隔 100ms 就随机抽取一些设置了过期时间的key,检查其是否过期,如果过期就删除。注意这里是随机抽取的。为什么要随机呢?你想一想假如 redis 存了几十万个 key ,每隔100ms就遍历所有的设置过期时间的 key 的话,就会给 CPU 带来很大的负载!
惰性删除 :定期删除可能会导致很多过期 key 到了时间并没有被删除掉。所以就有了惰性删除。假如你的过期 key,靠定期删除没有被删除掉,还停留在内存里,除非你的系统去查一下那个 key,才会被redis给删除掉。这就是所谓的惰性删除,也是够懒的哈!
但是仅仅通过设置过期时间还是有问题的。我们想一下:如果定期删除漏掉了很多过期 key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?如果大量过期key堆积在内存里,导致redis内存块耗尽了。怎么解决这个问题呢? redis 内存淘汰机制。
如何用Redis做异步队列
使用list作为队列,RPUSH生产消息 LPOP消费消息
如果阻塞咋办?则使用下列这两个方法:
1 BLPOP key1 [key2 ] timeout(队列用这个)
移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
2 BRPOP key1 [key2 ] timeout
移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
缺点是:只能提供一个消费者消费
优化:用Redis的主题订阅模式
发送者发送消息,订阅者接受消息
订阅者可以订阅任意数量的频道(订阅命令:subscribe topic;发布命令:publish topic “hello”)
缺点是:消息的发布是无状态的风,无法保证可达(发布,然后有一个突然下线,再上线,可能就错过了)
高级点的就用kafka分布式消息队列
Redis如何实现持久化?
AOF持久化:保存写状态
文件重写是指定期重写AOF文件,减小AOF文件的体积。需要注意的是,AOF重写是把Redis进程内的数据转化为写命令,同步到新的AOF文件;不会对旧的AOF文件进行任何读取、写入操作!
RDB的缺点:无法保存最近一次快照之后的数据
RDB的优点:全量数据快照,文件小,恢复快
AOF优点:可读性高,适合保存增量数据,数据不易丢失
AOF缺点:文件体积大,恢复时间长
混合持久化方式:
先RDB备份所有数据,再以AOF追加增量数据
Redis的主从同步机制
Redis Sentinel(哨兵)是一个用来监控redis集群中节点的状态,不用来存储数据。当集群中的某个节点有故障时,可以自动的进行故障转移的操作。为了保证sentinel的高可用,sentinel也会部署多个
Redis如何分片?