set key value
常见场景:数据库分表的id值,以及计数器(string类型的incr和decr命令:自增),计数器:如微博的评论数、点赞数、分享数,抖音作品的收藏数,京东商品的销售量、评价数等。
redis的string内部格式如下:
struct sdshdr {
// buf 已占用长度
int len;
// buf 剩余可用长度
int free;
// 实际保存字符串数据的地方
char buf[];
};
注:真实看源码就知道这些大佬的牛!!!
lpush key value
常见场景:朋友圈点赞的展示,像这种有序的。
hset key filed value
常见场景:购物车,即
{
顾客:
商品1:数量,
商品2:数量
}
sadd key value
常见场景:不重复
共同好友的推荐、同类信息的关联检索
zadd key score1 value
常见场景:
范围查找、访问量的统计排序。
getbit key offset
常见场景:
一般用于是否点赞等等,并且布隆过滤器也是用的其存储,还有像位图法:从一个大量的数据中,找到某一些值,我们可以利用为数组,某一位存储一个数,则内存量大大降低。
定义: 就是在同一时间内,有大量的key到期失效,所以都访问了数据库,从而导致数据库负载增加,甚至崩溃。
分析:
1)因为是同一时间段,大量的失效,说明可能是redis崩溃了或者是有大量的请求
2)或者是很多key设置的到期时间是差不多的
解决:
1.1)针对第一种,咱们可以设置集群来减轻redis服务器的压力。
1.2)针对第一种,还可以进行限流(限制客户端的连接数量):
2)针对第二种,咱们可以对于其key的到期时间设置一个基础值+随机值,防止其同时到期。
定义: 访问大量的不存在的key,导致redis无法缓存,直接访问数据库,导致数据库访问量增大。
分析:
1)由于不存在的key,数据库没有,一般不会缓存
解决:
1)针对不存在的key,我们也缓存,但是必须设置一个到期时间,不然的话太浪费空间
2)布隆过滤器来解决:
注意:可能会有个疑问,数据什么时候写入呢,应该是当往数据库插入的时候,以及同步到缓存后。
定义: 不同于缓存雪崩,其主要是大量请求同一个失效的key/同一条数据,而缓存雪崩是不同的key。
分析: 主要是key失效,还是热点key
解决方法:
1)明显的解决方法就是增加其时间,或者说设置key永不失效
2)还有个策略就是对热点key进行加锁,保证同一时间内只有一个客户端访问它。但是加锁明显会降低效率,所以一般要慎用。
参考链接:
https://www.zhihu.com/question/399145557/answer/1270059584
写:
1)先更新数据库
2)直接删除(应该是全部删除)
顺序不能变:
如果先删除cache,在修改数据库:
如果A删除了cache,B来拿数据库拿数据,此时A在修改数据库,就出现了缓存不一致问题
而先修改数据库,在删除cache也是有问题:
B先拿到数据库的数据,而此时A完成了写操作(更新数据库,删除cache),之后B才更新cache,也是会出现问题,但是一般来说缓存的写入是比数据库的写入快很多的。
读:
上面的旁路缓存策略明显有一个问题,就是修改就会使得缓存失效,所以不适合写比较频繁:
而读写穿透策略:
注:但是有一个问题,就是如果多次写的话,则需要多次cache和数据库同时修改,而旁路缓存对于cache只修改一次,所以一般还是旁路缓存策略 + 一些方法。
其实和上面读写穿透策略几乎一样,读是一样的,但是不同的是写:
1)当cache不存在,则直接更新数据库
2)当cache存在,更新缓存,但是不同时更新数据库,而是异步更新。
缓存不一致问题:数据库更新成功后,但是缓存删除失败,则会导致缓存不一致的问题。
缓存不一致问题:删除了缓存,还没来得及更新数据库,线程B读取的是旧的数据,然后保存缓存,仍会出现缓存不一致问题
参考链接:
https://blog.csdn.net/qq_41884972/article/details/113883886?utm_term=Redis%E6%97%81%E8%B7%AF%E7%BC%93%E5%AD%98%E6%A8%A1%E5%BC%8F&utm_medium=distribute.pc_aggpage_search_result.none-task-blog-2allsobaiduweb~default-4-113883886&spm=3001.4430
Redis是单线程的,当然高版本是多线程的(6.0,就不考虑了),为什么设计成单线程,主要是:因为其是内存型数据库,所以其瓶颈不在cpu,而真正的在于网络的IO,所以单线程下也利用了IO多路复用来解决客户端和服务端之间的网络传输延迟。
虽然是单线程的,但是仍会有并发问题,因为会有多个请求,所以在处理的时候不同的请求A、B可能在处理上会有交互,所以会有问题,所以仍需要事务:
multi
set xx xx
get xxx
exec/discard
事务在exec的那一时刻,保证了set、get命令的原子执行,所以不会出现问题,当然可以用discard撤销事务。
注:另外比较重要的是,redis的事务不支持归滚,因为设计之初,对于事务出现错误主要就是程序员的编码问题,所以这需要自己回滚(所以我们写的时候要注意);当然对于一些编码错误,如:set set这种语法错发,会全部失败,但是如果出现:set key 1,2 把lpush的语法弄过来的话,则其他正确会继续执行,所以不会回滚,记住不能回滚(就是出现错误,不全部失败)。
为保证事务执行前,某个key不变化,在watch的一瞬间其值就不能变了,当你exec的时候,发现监控的监控的值进行了操作,则认为不安全,事务失败。
RDB持久化方式:是将当前内存中的所有缓存进行保存,所以明显其保存效率低,但是恢复速度是比较快的。
AOF持久化方式:保存所有的命令(当然只涉及修改),其保存速度快,但是恢复速度慢,需要一步步的执行。
三种方式:
因为涉及到许多命令,但是很有可能许多都没有了:
作用:降低磁盘的容量;恢复的时候更加快。
可以看到一般RDB用于主从复制,作为备份(全量复制);而AOF一般用于对数据比较敏感的,像everysec最多只有1s的数据损失,而RDB一般最后一次的损失挺大的。另外RDB保存慢,但是恢复快;而AOF相反;并且两者都是开启子进程进行异步操作的。
redis数据库本身存储的就是key - value,其实内部还有一个东西叫expires,而这个expires主要保存的是:每个key的value地址以及对应的到期时间,所以主要是保存设置到期时间的key
类似于操作系统的页面置换,我们要把没用的删除:
注:其中lru,是最近最久未使用算法;lfu是最近不常用的算法;类似于操作系统,简单说一下:LRU:对已存在的某些key,进行往前查找,谁用的最早就删除;LFU:在给定的一段时间内,谁用的频率越少,就删除谁。
主从复制的原因可以从这几个方面看一下:
1)读写分离,分担一个redis数据库的压力,主节点主要是写,然后同步到从节点,从节点主要负责读
2)数据备份,是持久化的另一种数据保存
3)故障恢复,当主节点凉凉的时候,可以选择一个从节点作为主节点
主要分为三步:
当slave多数掉线 + 延迟过高,则master则会拒绝同步
如:slave数量少于2个,或者所有slave的延迟都大于等于10秒时,强制关闭master写功能,停止数据同步
接着上篇的主从复制,我们直到主从复制如果主节点宕机后,我们需要人工的选择一个从节点代替它,所以不免有些费劲,所以哨兵为我们解决了这个问题,它会监控所有的主从节点,当主节点挂了后,由其自动选,自动恢复!!
主要分为三个阶段:
1.监控阶段
不断的检查主从节点是否正常运行(连接过程)
2.通知阶段
当哨兵向主从节点发送ping指令的时候,然后将收到的消息(正常)全部在哨兵中进行同步。
3.故障转移阶段
注意:在选从节点的时候,也是有策略,一般选在线的、响应快的等等。
邮件发送:100个人订阅了一个人,而这个博主发送了一个博客, 那么这100个人都会收到
读写分离场景:保证在写入的同时,进行同时的分发到各个从节点(读)的中
1.subscribe(订阅一个通道/多个通道)
2.publish(向指定通道发布消息)
3.psubscibe(模糊订阅通道)
注:
pubsub-channels 字典{
key:channel
value:[client1、client2]
}
pubsub_patterns: [client1、client2] //模糊匹配的客户端/订阅者
结果:发布者发布消息的的时候,会获取当前通道的所有的客户端,然后通过addReply函数发布给每个客户端;那个模糊订阅同理;对于addRpely函数目前就不探讨了!!!!!
参考链接:
https://blog.csdn.net/weixin_33795093/article/details/85743750?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163067045716780264085192%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=163067045716780264085192&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_v2~rank_v29-1-85743750.pc_search_result_cache&utm_term=redis%E5%8F%91%E5%B8%83%E8%AE%A2%E9%98%85&spm=1018.2226.3001.4187
上面讲到了hash(ip),这里就说一下,对于分布式来说,我们要保证分布式session的实现:
客户段请求的key,经过CRC16得到一个值,然后对16384取余,来决定放置到哪个槽,每个节点负责一部分的槽,如:
两次寻址过程:
综合来说:就是进行hash槽信息的同步的时候,保证发送的信息量少,并且一般不会超过1000个节点(一般不超过1000个也说明节点数不能太多,所以16384够用了!)
参考链接:
https://www.jianshu.com/p/de268f62f99b