Redis相关,部分源码
1. 保存redis的数据:
SAVE 保存是阻塞主进程,客户端无法连接redis,等SAVE完成后,主进程才开始工作,客户端可以连接
BGSAVE 是fork一个save的子进程,在执行save过程中,不影响主进程,客户端可以正常链接redis,等子进程fork执行save完成后,通知主进程,子进程关闭
2. 缓存穿透
查询一个一定不存在的数据,该请求将穿透缓存达到数据库。
解决方法:最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力,简单粗暴的方法,如果一个查询返回的数据为空(不管是数据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟;对不存在的数据缓存一个空数据
3. 缓存击穿
一个key的缓存在某个时间点过期的时候,恰好在这个时间点对这个Key有大量的并发请求过来,互斥锁;分布式缓存
4. 缓存雪崩
设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB
解决方法:大多数系统设计者考虑用加锁或者队列的方式保证缓存的单线 程(进程)写,简单方法:设置一个随机的过期时间;设置分布式缓存
5. 布隆过滤器
实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。用户海量数据滤重,用户看过的新闻去重,检测重复邮箱。
6. 更新缓存的机制
cache aside(最常用)
①失效:应用从cache取数据,若没取到,则从数据库取数据,成功后,放到缓存中
②命中:应用程序从cache取数据,取到后返回
③更新:先更新数据库,成功后,让缓存失效(读比写操作慢得多)
read through
在查询过程中更新缓存,用缓存服务自己来加载
write through
当有数据更新的时候,如果没有命中缓存,直接更新数据库,然后返回,如果命中了缓存,则更新缓存,然后再由缓存自己更新数据库
write behind caching(write back)
更新数据的时候只更新缓存,不更新数据库,缓存会异步批量更新数据库,这样操作很快,带来的问题是,数据不是强一致性,而且可能会丢失
https://coolshell.cn/articles/17416.html
数据更新时的数据库和缓存的操作顺序:
https://blog.csdn.net/z50l2o08e2u4aftor9a/article/details/80976862
读操作:先读缓存,没有命中,再读从库,然后set到缓存
写操作:先操作缓存,再异步更新数据库,缓存使用delete而不是set(淘汰缓存而不是修改,因为修改要避免cache miss)
6. Redis的相关面试题
https://blog.csdn.net/yajlv/article/details/73467865
7. 强/弱一致性,顺序一致性
①强一致性:任意时刻,所有节点的数据都是一样的,对于关系型数据库,要求更新过的数据能被后续的访问看到(原子一致性/线性一致性)
②弱一致性:数据更新后,如果能容忍后续的访问访问部分数据或者全部不能访问,就是弱一致性,最终一致性就是弱一致性,不保证任意节点的同一份数据都是相同的,但是随着时间推移,这些数据总是向趋同的方向变化,即一段时间后,节点间的数据达到一致状态
8. Redis设计与实现,源码解析
①redis使用sds
(简单动态字符串)来作为redis的默认字符串表示,sds包含三个字段,len:保存字符串的长度,free:buf数组中未使用字节的数量,buf[]:字节数组,用于保存字符串,获取字符串长度时,时间复杂度为O(1),而不是O(N),是因为strlen命令执行时,redis没有对字符串长度进行计算,只是直接访问了sds中的len这个属性,free属性,利于扩容,避免缓冲区溢出,减少内存分配次数
②空间预分配:
用于优化sds字符串增长的操作,分配额外的未使用空间给sds。如果小于1M,就分配和len属性一样大小的未使用空间,free和len相同,此时buf为free+len+1,额外的1字节用于保存空字符;如果大于1M,程序会分配1M的未使用空间。此时buf为len+1M+1byte
③惰性空间释放:
用于优化sds的字符串缩短操作,sds保存的字符换缩短时,程序并不立即使用内存重分配来回收缩短后多出来的字节,而是使用free属性记录起来,等待将来使用,避免多余的内存重分配操作,sds也提供了真正释放sds未使用空间的api
redis设计和使用相关案例:
①可以用作点赞系统,统计一段时间的点赞信息,一段时间后,将信息同步到数据库,减轻对数据库的压力
继续更新中