redis特性

  • Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
  • Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储
  • Redis支持数据的备份,即master-slave模式的数据备份。

redis应用场景

缓存:redis所有的数据是放在内存中的(内存数据库)
数据库
消息中间件(队列系统)

redis数据结构及操作

字符串(string)

字符串类型是redis中最基础的数据存储类型,该类型可以接收任何格式的数据,如图像数据或json对象描述信息等。字符串类型的value最多可以容纳的数据长度是 512M
保存
如果设置的键不存在则为添加,如果键存在则为修改
设置键值:set key value 如:set name xiaowu
设置键值及过期时间,以秒为单位:setex key seconds value 如:setex name 2 xiaowu
设置多个键值:mset key1 value1 key2 value2 key3 value3…
获取
根据键获取值,如果不存在此键则返回nil
获取单个键值:get key 如:get name
获取多个键值:mget key1 key2 key3…

列表(list)

列表的元素类型为string,按照插入顺序排序
增加
在左侧插入数据:lpush key value1 value2…
在右侧插入数据:rpush key value1 value2…
在指定元素的前或后插入新元素:linsert key before或after 指定元素 新元素
linsert list1 before name 4

获取
返回列表里指定范围内的元素

  - start、stop为元素的下标索引
  - 索引从左侧开始,第⼀个元素为0
  - 索引可以是负数,表示从尾部开始计数,如-1表示最后⼀个元素

lrange key start stop

修改
设置指定索引位置的元素值

  - 索引从左侧开始,第⼀个元素为0
  - 索引可以是负数,表示尾部开始计数,如-1表示最后⼀个元素

lset key index value

删除
删除指定元素

  - 将列表中前count次出现的值为value的元素移除
  - count > 0: 从头往尾移除
  - count < 0: 从尾往头移除
  - count = 0: 移除所有

lrem key count value

散列(hash)

hash⽤于存储对象,对象的结构为属性、值的类型为string
增加、修改
设置单个属性:hset key field value
如:hset user name xiaowu 设置键user的属性name为xiaowu
设置多个属性:hmset key field1 value1 field2 value2…

获取
获取指定键的所有的属性:hkeys key
如:获取user键的所有属性 hkeys user
获取一个属性的值:hget key field 如:hget user name
获取多个属性的值:hmget key field1 field2…
获取所有属性的值:hvals key
如:hvals user 获取键user所有属性的值

删除

  • 删除整个hash键及值,使⽤del命令
    del key
  • 删除属性,属性对应的值会被⼀起删除
    hdel key field1 field2…
集合(set)

无序集合,元素为string类型,元素具有唯一性,不重复,对于集合而言没有修改操作
增加
添加元素:sadd key element1 element2 element3…

获取
返回所有元素:smembers key

删除
删除指定元素:srem key

有序集合(zset)

有序集合,元素为string类型,元素具有唯一性,每个元素都会关联一个double类型的score,表示权重,通过权重讲元素从小到达排序,没有修改操作
增加
zadd key score1 ele1 score2 ele2…
如:zadd a 4 lisi 5 wangwu 3 zhangsan 向键a的集合中添加元素lisi、wangwu、zhangsan,权重分别为4, 5,3

获取
根据下标获取

  - 返回指定范围内的元素
  - start、stop为元素的下标索引
  - 索引从左侧开始,第⼀个元素为0
  - 索引可以是负数,表示从尾部开始计数,如-1表示最后⼀个元素

zrange key start stop
根据sore获取,返回score值在min和max之间的成员
zrangebyscore key min max

删除
删除指定元素:zrem key element1 element2
删除权重在指定范围的元素: zremrangebyscore key min max

缓存架构

普通缓存

在这里插入图片描述

多级缓存

在这里插入图片描述

缓存模式

Cache Aside Pattern(旁路缓存模式)

常用的一个缓存模式,适合读请求比较多的场景,同时维持缓存和数据库,以数据库结果为准
读过程:先从缓存中读数据,读不到的话从数据库中找数据,然后把数据库读到的数据放入缓存
写过程:先更新数据库,后删除cache
两种错误的更新方式,注意:

  • 先更新数据库,再更新缓存。这种做法最大的问题就是两个并发的写操作导致脏数据。如下图(以Redis和Mysql为例),两个并发更新操作,数据库先更新的反而后更新缓存,数据库后更新的反而先更新缓存。这样就会造成数据库和缓存中的数据不一致,应用程序中读取的都是脏数据。
    在这里插入图片描述
  • 先删除缓存,再更新数据库。这个逻辑是错误的,因为两个并发的读和写操作导致脏数据。如下图(以Redis和Mysql为例)。假设更新操作先删除了缓存,此时正好有一个并发的读操作,没有命中缓存后从数据库中取出老数据并且更新回缓存,这个时候更新操作也完成了数据库更新。此时,数据库和缓存中的数据不一致,应用程序中读取的都是原来的数据(脏数据)。
    在这里插入图片描述
Read/Write Though Pattern(读写穿透模式)

读写穿透模式把缓存作为主要存储,负责将数据读取和写入到数据库中,开发少见,一方面是性能问题,另一面是redis并没有提供cache把数据写入到数据库。
读过程:缓存中读取数据,读取到直接返回,读取不到的话,先从数据库加载,然后写入到缓存之后响应。
**写过程:**先查缓存,缓存中不存在直接更新数据库,如果缓存中存在,先更新缓存,然后缓存更新到数据库
出现脏数据效率较低,依赖缓存

Write Behind Pattern (异步缓存写入)

跟读写穿透很像,都是缓存把数据读取写入到数据库中,读写穿透是同步更新缓存和数据库,异步缓存是只更新缓存不直接更新数据库,而是异步批量的方式更新数据库。
应用场景:消息队列中消息异步写入磁盘,写性能很高,适合一些数据经常变化又对数据一致性要求没那么高的场景,比如浏览量,点赞量。但是数据一致性较差,有数据丢失的可能。

缓存问题

缓存穿透

频繁去访问缓存中没有的数据,那么缓存就失去了存在的意义,瞬间所有请求的压力都落在了数据库上,这样会导致数据库连接异常
在这里插入图片描述
解决方案: 把null值缓存或者采用布隆过滤器BloomFilter:将所有的数据放入一个足够大的集合中,不存在的数据会被集合拦截掉,缓存前加一个BloomFilter,查询的时候,先查询key是否存在,不存在直接返回,存在先查询缓存后查询数据库

缓存雪崩
缓存雪崩是指缓存不可用或者大量缓存由于超时时间相同在同一时间段失效,大量请求直接访问数据库,数据库压力过大导致系统雪崩。  

解决方案:
1、给缓存加上一定区间内的随机生效时间,不同的key设置不同的失效时间,避免同一时间集体失效。
2、采用多级缓存,不同级别缓存设置的超时时间不同,及时某个级别缓存都过期,也有其他级别缓存兜底。
3、利用加锁或者队列方式避免过多请求同时对服务器进行读写操作。

缓存过期与淘汰

redis过期策略
  • 定时过期

每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。

  • 惰性过期

只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。

  • 定期过期

每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果
Redis中同时使用了惰性过期和定期过期两种过期策略。
        Redis过期删除采用的是定期删除,默认是每100ms检测一次,遇到过期的key则进行删除,这里的检测并不是顺序检测,而是随机检测。那这样会不会有漏网之鱼?显然Redis也考虑到了这一点,当我们去读/写一个已经过期的key时,会触发Redis的惰性删除策略,直接回干掉过期的key

缓存淘汰

Redis的内存淘汰策略是指在Redis的用于缓存的内存不足时,怎么处理需要新写入且需要申请额外空间的数据。

  • noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
  • allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。
  • allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。
  • volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
  • volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
  • volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。

LRU
LRU(Least recently used,最近最少使用),LRU算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
基本思路

  1. 新数据插入到列表头部;
  2. 每当缓存命中(即缓存数据被访问),则将数据移到列表头部;
  3. 当列表满的时候,将列表尾部的数据丢弃。

LFU
LFU(Least Frequently Used 最近最少使用算法),它是基于“如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小”的思路。
在这里插入图片描述
LFU需要定期衰减。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐