Redis (c实现)
String、List、Set、ZSet、Hash、BitMap(String)、HyperLogLog(String)、Geospatial(ZSet)
String:可以存储二进制数据
list:底层使用了压缩列表和快速链表两种数据结构存储。数据较少时使用压缩列表,数据较多时使用快速链表。
本质可以将list理解为链表数据结构。
可以使用list实现队列和栈的功能。
set:类似于Java中的hashset,不允许重复元素。无序
ZSet:有序。
Hash:常用于存储对象
常用命令
Key(键)
删除建:del key1,key2…
获取所有键:keys key
设置过期时间: 秒语法:expire key seconds 毫秒语法:pexpire key milliseconds
String(字符串)
命令在指定的key不存在时,为key设置指定的值:setnx key value 返回值:1-成功 0-已存在,设置失败
将键key的值设置为value,并将键key的生存时间设置为seconds秒钟:setex key secnods value
:setbit key offset value
将键key的值设为value,并返回键key在被设置之前的旧值: getset key value
存放键值:set key value
获取键值:get key
批量存放键值:mset key1 value1 key2 value2 … 如:mset java1 1 java2 2 java3 3
批量获取键值:mget key key2 …
Set(集合)
存储值:sadd key member member2 …
获取元素:smembers key
获取集合元素个数:scard key
使用场景
- 缓存
- 分布式锁
- 分布式会话
- 消息队列
- 排行榜
- 计数器
- 最新列表
- 广播模式
持久化
RDB(默认)
RDB(Redis DataBase),是redis默认的存储方式,RDB方式是通过快照( snapshotting )完成的。
它保存的是某一时刻的数据并不关注过程。RDB保存redis某一时刻的数据的快照
AOF
AOF(append only file)是Redis的另一种持久化方式。Redis默认情况下是不开启的。
开启AOF持久化后Redis 将所有对数据库进行过写入的命令(及其参数)(RESP)记录到 AOF 文件,
以此达到记录数据库状态的目的,这样当Redis重启后只要按顺序回放这些命令就会恢复到原始状态了。
AOF会记录过程,RDB只管结果
分布式锁
- redis 命令 1.setnx命令 2.expire 3.del命令 (1.setnx和expire分2步执行,非原子操作;若setnx执行成功,但expire执行失败,就可能出现死锁
2.delete命令存在误删除非当前线程持有的锁的可能
3.不支持阻塞等待、不可重入) - redis lua脚本
数据存放
session
token
验证码
访问量高的数据
发布/订阅功能
- 实现一对一或一对多的广播功能
缓存雪崩、穿透、击穿、预热、降级
-
缓存雪崩
缓存雪崩表示在某一时间段,缓存集中失效,导致请求全部走数据库,有可能搞垮数据库,使整个服务瘫痪。
使缓存集中失效的原因:
1、redis服务器挂掉了。
2、对缓存数据设置了相同的过期时间,导致某时间段内缓存集中失效。
如何解决缓存集中失效:
1、针对原因1,可以实现redis的高可用,Redis Cluster 或者 Redis Sentinel(哨兵) 等方案。
2、针对原因2,设置缓存过期时间时加上一个随机值,避免缓存在同一时间过期。
3、使用双缓存策略,设置两个缓存,原始缓存和备用缓存,原始缓存失效时,访问备用缓存,备用缓存失效时间设置长点。 -
缓存穿透 (跳过了缓存,直接访问量数据库)
缓存穿透表示查询一个一定不存在的数据,由于没有获取到缓存,所以没写入缓存,导致这个不存在的数据每次都需要去数据库查询,失去了缓存的意义。
请求的数据大量的没有获取到缓存,导致走数据库,有可能搞垮数据库,使整个服务瘫痪。
比如文章表,一般我们的主键ID都是无符号的自增类型,有些人想要搞垮你的数据库,每次请求都用负数ID,而ID为负数的记录在数据库根本就没有。
解决方案:
1、对于像ID为负数的非法请求直接过滤掉,采用布隆过滤器(Bloom Filter)。
2、针对在数据库中找不到记录的,我们仍然将该空数据存入缓存中,当然一般会设置一个较短的过期时间。 -
缓存击穿
缓存击穿表示某个key的缓存非常热门,有很高的并发一直在访问,如果该缓存失效,那同时会走数据库,压垮数据库。
解决方案:
1、让该热门key的缓存永不过期。
2、使用互斥锁,通过redis的setnx实现互斥锁。(缓存失效,大量并发请求同时访问,然后同时走数据库[加锁,防止同时访问数据库],然后在重新设置缓存key) -
缓存预热
缓存预热是指系统上线后,提前将相关的缓存数据加载到缓存系统。避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题,用户直接查询事先被预热的缓存数据。
如果不进行预热,那么Redis初始状态数据为空,系统上线初期,对于高并发的流量,都会访问到数据库中, 对数据库造成流量的压力。
解决方案:
- 数据量不大的时候,工程启动的时候进行加载缓存动作;
- 数据量大的时候,设置一个定时任务脚本,进行缓存的刷新;
- 数据量太大的时候,优先保证热点数据进行提前加载到缓存。
-
缓存降级
缓存降级是指缓存失效或缓存服务器挂掉的情况下,不去访问数据库,直接返回默认数据或访问服务的内存数据。降级一般是有损的操作,所以尽量减少降级对于业务的影响程度。
在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级
内存过期策略(删除策略)和内存淘汰策略
过期策略
Redis中同时使用了惰性删除和定期删除两种过期策略。
我们在set key的时候,可以给它设置一个过期时间,比如expire key 60。指定这key60s后过期,60s后,redis是如何处理的嘛
定时删除
每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即对key进行清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
惰性删除
只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
定期删除
每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。
淘汰策略
noeviction(默认): 不删除策略, 达到最大内存限制时, 如果需要更多内存, 直接返回错误信息。大多数写命令都会导致占用更多的内存(有极少数会例外。
allkeys-random:所有key通用; 随机删除一部分 key。
allkeys-lru:所有key通用; 优先删除最近最少使用(less recently used ,LRU) 的 key。
allkeys-lfu:使用 LFU 算法在所有数据中进行筛选删除。
volatile-ttl: 只限于设置了 expire 的部分; 优先删除剩余时间(time to live,TTL) 短的key。
volatile-random: 只限于设置了 expire 的部分; 随机删除一部分 key。
volatile-lru:只限于设置了 expire 的部分; 优先删除最近最少使用(less recently used ,LRU) 的 key。
volatile-lfu:会使用 LFU 算法筛选设置了过期时间的键值对删除。
LRU: Least Recently Used 最近最少使用
LFU: Least Frequently Used 最不常用的(使用频率最低的)
大体公分为:lru、lfu、random、ttl
缓存与数据库的一致性问题
延时双删
- 删缓存
- 更新数据库
- 删缓存(延时根据业务而定)
先更新数据库,在删除缓存
- 先更新数据库
- 在删除缓存
- 补偿删除失败的情况(重试)
RedLock
RedLock是一种算法,RedLock也就是 Redis Distributed Lock,可用实现多节点Redis的分布式锁。
RedLock官方推荐,Redisson完成了对RedLock算法封装。
此种方式具有以下特性:
- 互斥访问:即永远只有一个 client 能拿到锁
- 避免死锁:最终 client 都可能拿到锁,不会出现死锁的情况,即使锁定资源的服务崩溃或者分区,仍然能释放锁。
- 容错性:只要大部分 Redis 节点存活(一半以上),就可以正常提供服务
pipeline
- 减少网络开销
lua脚本
- 减少网络开销
- 原子操作
- 替代redis事务功能
GEO(ZSet)
存储地理位置信息
redis 模式
- 单节点模式
- 主从模式(集群)
- 读写分离
- 负载均衡
- 故障恢复
- cluster模式(集群)
- hash取余(16384),计算key落在哪台服务器
- 哨兵模式(高可用、集群)
- 哨兵(sentinel)是一个分布式系统,用于对主从结构中的每台服务器进行监控,当出现故障时通过投票机制选择新的master并将所有slave连接到新的master
Cache Cloud
redis管理平台
分区模式(可以理解为一种特殊的集群)
分区是将数据分布在多个Redis实例(Redis主机)上,以至于每个实例只包含一部分数据。
不足:
- 分区是多台redis共同作用的,如果其中一台出现了宕机现象,则整个分片都将不能使用,虽然是在一定程度上缓减了内存的压力,但是没有实现高可用。
- 涉及多个key的操作通常是不被支持的。举例来说,当两个set映射到不同的redis实例上时,你就不能对这两个set执行交集操作。
- 涉及多个key的redis事务不能使用。
- 当使用分区时,数据处理较为复杂,比如你需要处理多个rdb/aof文件,并且从多个实例和主机备份持久化文件。
常见问题?
- redis常见数据类型?
- redis使用场景?