ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[TOC] > ### `redis`五种数据结构 * `string` ```sh set asd "123" get asd > 123 ``` * `list ` ```sh lpush asd_list aaa lpush asd_list bbb lrange asd_list 0 2 lpop/rpop asd_list //移除并返回列表中的首/尾元素 ``` * `hash ` ```sh hmset hash_asd t1 "111" t2 "222" hgetall hash_asd >1) t1 >2) 111 >3) t2 >4) 222 hget hash_asd t1 111 ``` * `set ` * `zset`(Sorted Set) * Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。 * zset都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。 <br/> > ### `Redis` [持久化](https://juejin.im/post/5b70dfcf518825610f1f5c16) * 快照(snapshotting)持久化(`RDB`),快照持久化是Redis默认采用的持久化方式 * `save`:会阻塞当前Redis服务器,直到持久化完成,线上应该禁止使用。 * `bgsave`:该触发方式会fork一个子进程,由子进程负责持久化过程,因此阻塞只会发生在fork子进程的时候。 * 自动触发:根据`save m n` 配置规则自动触发;执行`debug reload`时;执行`shutdown`时,如果没有开启aof,也会触发;从节点全量复制时,主节点发送rdb文件给从节点完成复制操作,主节点会触发 bgsave; * `AOF`(append-only file)持久化,与快照持久化相比,`AOF`持久化 的实时性更好,因此已成为主流的持久化方案。默认情况下Redis没有开启`AOF`。开启`AOF`持久化后每执行一条会更改Redis中的数据的命令,`Redis`就会将该命令写入硬盘中的`AOF`文件 <br/> > ### 缓存雪崩 * 造成缓存雪崩一般有两个原因: * 对缓存数据设置相同的过期时间,导致某段时间内缓存失效,请求全部走数据库。 * 解决方法,在缓存的时候给过期时间加上一个随机值,这样就会大幅度的减少缓存在同一时间过期。 * `Redis`挂了 * 事发前:实现`Redis`的高可用(主从架构+Sentinel 或者Redis Cluster) * 事发中:万一`Redis`真的挂了,我们可以设置本地缓存(`ehcache`) + 限流(`hystrix`),尽量避免我们的数据库被干掉 * 事发后:redis持久化,重启后自动从磁盘上加载数据,快速恢复缓存数据。 <br/> > ### 缓存穿透 * 一般是黑客故意去请求缓存中不存在的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉。 * 有很多种方法可以有效地解决缓存穿透问题,最常见的则是采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的`bitmap`中,一个一定不存在的数据会被 这个bitmap拦截掉,从而避免了对底层存储系统的查询压力。 * 一种简单的方式,如果一个查询返回的数据为空(不管是数 据不存在,还是系统故障),我们仍然把这个空结果进行缓存,但它的过期时间会很短,最长不超过五分钟。 <br/> > ### 缓存与数据库双写一致性 * 先删除缓存,再更新数据库,在高并发下表现不如意,在原子性被破坏时表现优异,如下: ``` 线程A删除了缓存 线程B查询,发现缓存已不存在 线程B去数据库查询得到旧值 线程B将旧值写入缓存 线程A将新值写入数据库 ``` * 并发下解决数据库与缓存不一致的思路,将删除缓存、修改数据库、读取缓存等的操作积压到**队列**里边,实现**串行化**。 * 先更新数据库,再删除缓存,在高并发下表现优异,在原子性被破坏时表现不如意 ``` 缓存刚好失效 线程A查询数据库,得一个旧值 线程B将新值写入数据库 线程B删除缓存 线程A将查到的旧值写入缓存 ``` <br/> > ### [`Memcached`与`Redis`比较](https://www.zhihu.com/question/19645807) * 数据类型支持不同 * `Memcached`仅支持简单的key-value结构的数据 * `redis`支持String、Hash、List、Set、ZSet * 内存管理机制不同 * 在`Redis`中,并不是所有的数据都一直存储在内存中的。这是和Memcached相比一个最大的区别。 * `Memcached`默认使用Slab Allocation机制管理内存,其主要思想是按照预先规定的大小,将分配的内存分割成特定长度的块以存储相应长度的key-value数据记录,以完全解决内存碎片问题。 * 数据持久化支持 * Redis虽然是基于内存的存储系统,但是它本身是支持内存数据的持久化的,而且提供两种主要的持久化策略:RDB快照和AOF日志。而memcached是不支持数据持久化操作的。 * 集群管理不同 * Memcached本身并不支持分布式,因此只能在客户端通过像一致性哈希这样的分布式算法来实现Memcached的分布式存储。 * Redis支持分布式存储功能,Redis Cluster <br/> > ### `Redis`底层存储原理 * **`SDS`(simple dynamic string),动态字符串**。每个`sds`都会比它真实占用的字符长度都长,通过一个空闲标识符表示`sds`当前空闲字符有多少,如此设计,在一定长度范围的内的字符串都可以使用此`sds`,而且不会频繁的进行内存分配,直到此`sds`不能容纳分配的字符串,如果遇到这种情况情况,才需要进行扩容;这是redis的最基础的,所有的redis `k-v` 中的字符串都是依托于`sds`。 * **`dict`字典**,类似于java中的`hashmap`,两个哈希数组,一个是正常使用的,另一个是扩容时需要的,`redis`的扩容是渐进式,不影响当前`dict`的访问,即扩容的数组逐渐迁移,迁移完成之后再切换。 * **跳跃表**,跳跃表是由N层链表组成,最底层是最完整的的数据,每次数据插入,率先进入到这个链表(**有序的**),然后针对这个节点`1/2`的概率被添加到上层的列表。 * 在随机搜索方面略低于`B+`树,但是对于数据插入优于`B+`树 ![](https://i.loli.net/2019/03/13/5c89244ae87b9.png) * **单线程,多路I/O复用模型**,也是java 的`NIO`体系使用的`IO`模型,也是linux诸多`IO`模型中的一种,当一个请求来访问redis后,redis去组织数据要返回给请求,这个时间段,redis的请求入口不是阻塞的,其他请求可以继续向redis发送请求,等到redis io流完成后,再向调用者返回数据,这样一来,单线程也不怕会影响速度了。**相当于多个`IO`请求复用一个真正执行`IO`读写操作的线程**。 *** 参考: [面试前必须要知道的Redis面试题](https://zhuanlan.zhihu.com/p/54840101) [一窥redis之谜](https://zhuanlan.zhihu.com/p/34762100)