Redis知识点
2021-11-29 17:03:28 0 举报
AI智能生成
Redis缓存相关知识点
作者其他创作
大纲/内容
关于网络IO
网络IO的本质是socket的读取,socket在linux系统被抽象为流,IO可以理解为对流的操作,<br>对于一次IO访问(以read举例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间<br>
所以IO操作可以分为两部分
第一阶段:等待数据准备 (Waiting for the data to be ready)。
第二阶段:将数据从内核拷贝到进程中
对于IO的实现方式--socket 而言,也是两部分:
第一步:通常涉及等待网络上的数据分组到达,然后被复制到内核的某个缓冲区。
第二步:把数据从内核缓冲区复制到应用进程缓冲区。
IO模型的类型:
同步模型(synchronous IO)
异步IO(asynchronous IO)
阻塞IO(bloking IO)
非阻塞IO(non-blocking IO)
多路复用IO(multiplexing IO)
由于同步非阻塞方式需要不断主动轮询,轮询占据了很大一部分过程,轮询会消耗大量的CPU时间,<br>而 “后台” 可能有多个任务在同时进行,人们就想到了循环查询多个任务的完成状态,<br>只要有任何一个任务完成,就去处理它。如果轮询不是进程的用户态,而是有人帮忙就好了。<br>那么这就是所谓的 “IO 多路复用”。UNIX/Linux 下的 select、poll、epoll 就是干这个的<br>(epoll 比 poll、select 效率高,做的事情是一样的)<br>
FD:File Descriptor,文件描述符,判断文件是否可读、可写。<br> IO多路复用的底层select 就是根据FD的状态来做判断的
信号驱动式IO(signal-driven IO)
数据结构
Redis 使用数组作为其存储结构,value是数组元素,key是数组索引
几个核心的数据结构
typedef struct redisDb {<br> dict *dict; //存储了key和value<br> dict *expires; //存储了key的过期信息<br> dict *watched_keys; //被wathch 的key<br> int id; //数据库id<br> .......<br>} redisDb;<br>
typedef struct dict {<br> //(dict hash table)存储数据的 hash 表<br> // 数据在ht[0]中,ht[1]在 rehash 时会被使用<br> dictht ht[2]; <br>} dict;
typedef struct dictht {<br> // 哈希表节点的指针数组<br> // key 的哈希值最终映射到这个数组的某个位置上<br> dictEntry **table;<br>} dictht;
typedef struct dictEntry {<br> void *key; // 键, void 代表任意类型<br> union { // 值,union表示结构内的数据类型是同一种<br> void *val; <br> uint64_t u64;<br> int64_t s64;<br> } v;<br> struct dictEntry *next; // 链往后继节点<br>} dictEntry;<br>
key
key 索引了 value 在内存中的位置,通过对客户端的 rehash 来存储或查找key
key在请求过来的时候会判断是否过期,过期会被移除
value
string
SDS(动态字符串)
// 3.0<br>struct sdshdr {<br> unsigned int len;// 记录buf数组中已使用字节的数量,即SDS所保存字符串的长度<br> unsigned int free;// 记录buf数据中未使用的字节数量<br> char buf[];// 字节数组,用于保存字符串<br>};<br>
//3.0 之后会根据字符长度选择不同的构造<br>static inline char sdsReqType(size_t string_size) {<br> if (string_size < 1<<5) // 32<br> return SDS_TYPE_5;<br> if (string_size < 1<<8) // 256<br> return SDS_TYPE_8;<br> if (string_size < 1<<16) // 65536 64k<br> return SDS_TYPE_16;<br> if (string_size < 1ll<<32) // 4294967296 4G<br> return SDS_TYPE_32;<br> return SDS_TYPE_64;<br>}<br>
list
双向链表
typedef struct listNode {<br> struct listNode *prev;// 前置节点<br> struct listNode *next;// 后置节点<br> void *value;// 节点值<br>} listNode;<br>
map
字典
typedef struct dictht {<br> dictEntry **table;// 哈希表数组<br> unsigned long size;// 哈希表大小<br> unsigned long sizemask;// 哈希表大小掩码,用于计算索引值,等于size-1<br> unsigned long used;// 哈希表已有节点的数量<br>} dictht;<br>
set
类似hashset,他是一个value=null的字典
它内部的键值对是无序、唯一的。<br>它的内部实现相当于一个特殊的字典,字典中所有的 value 都是一个值 NULL<br>
zset
跳跃表
这个数据结构很复杂,zset用这个实现的
Redis要注意的问题
缓存雪崩
原因:Redis宕机或者缓存失效,大量请求会发送到数据库层,导致数据库层的压力激增。<br>
解决方式:<br>1、让缓存过期时间错开。<br>2、通过服务降级让一部分请求返回默认值<br>3、集群配置,防止所有节点宕机
缓存穿透
原因:访问的某个数据,既不在缓存,也不再数据库,这种情况可能是:<br>1、恶意攻击,专门访问数据库中没有的数据;<br>2、业务请求的误操作,缓存和数据库中的数据都被误删除了,导致都没有数据;<br>3、新业务上线时,缓存和数据库都没有用户的业务数据;<br>
解决方式:<br>1、设置空值或缺省值,防止不存在的数据访问到数据库<br>2、使用布隆过滤器,对不存在的数据进行处理<br>3、拦截恶意请求,前端/后端都可以做<br>
缓存击穿
原因:某个热点数据的缓存失效,导致请求一直访问数据库
解决方式:这种请求一般是缓存到时间过期导致的,所以将相关数据的缓存设置为永不过期,就可以了<br>
key的控制
控制 key 的长度:<br>最简单直接的内存优化,就是控制 key 的长度。<br>如果 key 数量达到了百万级别,那么,过长的 key 名也会占用过多的内存空间。<br>
避免存储 bigkey<br>如果大量存储 bigkey,也会导致 Redis 内存增长过快。<br>
Redis锁
INCR
这种加锁的思路是, key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作进行+1。
加锁和设置过期时间是分开的,不是原子性。<br>如果没设置过期时间,这个锁会一直存在,后期操作会受影响。
SETNX
如果 key 不存在,将 key 设置为 value<br>如果 key 已存在,则 SETNX 不做任何动作<br>返回0/1表示是否存在
用法-设置key名为lock的锁:<br>加锁成功:setnx lock java -> 1<br>加锁失败:setnx lock c++ -> 0<br>设置过期时间100s:EXPIRE lock 100<br>释放锁成功:del lock --> 1<br>
加锁和设置过期时间是分开的,不是原子性。<br>如果没设置过期时间,这个锁会一直存在,后期操作会受影响。<br>
SETEX
相对于setnx 增加了过期时间的设置,使得加锁和过期时间设置保持原子性<br>
用法-用法-设置key名为lock的锁,30秒过期:<br>加锁成功:setex lock 30 java -> OK<br>解锁成功:del lock -> 1
PSETEX
PSETEX key milliseconds value
同setex 不过这里用的是毫秒
SET
2.6.12 版本可以使用set 调用nx、ex
set key value [EX seconds|PX milliseconds] [NX|XX] [KEEPTTL]
Redis线程
redis 线程分两部分
在执行io操作,也就是网络读写的时候,是多线程<br>这是基于IO多路复用,也就是reactor 模式的<br>
详细的看下面的《关于网络IO》
Redis 服务采用 Reactor 的方式来实现文件事件处理器(每一个网络连接其实都对应一个文件描述符)
请求作为FD
使用了事件处理器
在网络请求和IO操作部分使用的是多线程,<br>在执行命令方面使用的是单线程
key的模糊检索会因为单线程的问题而阻塞,所以要使用scan<br>
redis业务上的使用
缓存
分布式锁
全页缓存(FPC)
排行榜/计数器
会话缓存(Session Cache)
会话过期
0 条评论
下一页