Redis的应用场景

缓存热点数据、排行榜(zset)、分布式锁(lua脚本)、计数器(incr)、队列(list pop、push)、点赞或好友关系存储(set)

Redis支持的数据类型

string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)

zset跳表的数据结构

链表按照顺序排列,同时给链表添加多级索引,在提高它的查找效率的同时,也能支持范围查找




如何删除redis的密码 怎么删除redis_redis关键字删除


如何删除redis的密码 怎么删除redis_Redis_02



查找x,在第k级,遍历到y结点,发现x大于y,但x小于y后面的结点z,所以先顺着y往下到第k-1级,发现y,z之间有三个节点,所以我们在k-1级索引中,遍历3个节点找到x,以此类推,在每一层需要通过3个节点找目标数,那么总的时间复杂度就为O(3*logn),因为3是常数,所以最后的时间复杂度为O(logn)。这一结构相当于让跳表实现了二分查找。

注意

跳表更新时如果在单链表的两个节点之间一直插入,会导致跳表退化成单链表。

跳表通过“随机函数”来维护前面的“高效性”,往跳表中插入数据a时,通过随机函数生成一个随机数h,a插入单链表的同时,在第1级到第h级中也同时插入索引a。

Redis的数据过期策略

处理过期数据的常用策略

定时删除:根据键的过期时间设置定时器,触发超时及删除对应键,对cpu造成压力

惰性删除:在取键时才对键进行过期检查,没法及时回收内存空间

定期删除:每隔一段时间执行一次限时限量的批量删除

redis同时采用定期删除和惰性删除策略: 使用定期策略可以更平滑的利用cpu和内存资源(每隔 100ms 就随机抽取一些设置了过期时间的 key),但是会存在过期数据失效不及时的问题。用惰性删除加以辅助便可达到定期删除下访问实时失效的效果。

持久化

rdb:生成rdb文件时: redis会检查保存的数据是否过期,如果过期则不会写入rdb文件载入rdb文件时: redis会对rdb文件中的数据进行过期检查,已过期的数据会被忽略,不写入内存

aof:追加aof文件时: 当有数据被删除,redis会追加该删除命令到aof文件中去重写aof文件时: 已过期的数据不会写入新aof文件(重写文件体积压缩的原因之一)

内存淘汰机制

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

Redis的LRU过期策略的具体实现

class LRUCache {    private int capacity;    private LinkedList list;    private Map map;    public LRUCache(int capacity) {        this.capacity = capacity;        list = new LinkedList<>();        map = new HashMap<>();    }    public String get(String key) {        if (map.containsKey(key)) {            list.remove(key);            list.addLast(key);            return map.get(key);        }        return null;    }    public void put(String key, String value) {        if (map.containsKey(key)) {            list.remove(key);            list.addLast(key);            map.put(key, value);        } else {            if (capacity == list.size()) {                map.remove(list.removeFirst());            }            map.put(key,value);            list.addLast(key);        }    }}


如何删除redis的密码 怎么删除redis_Redis_02


如何解决Redis缓存雪崩,缓存穿透问题

缓存穿透

查询一个一定不存在的数据,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到数据库去查询,造成缓存穿透。

1)布隆过滤器

位数组+k个独立hash函数,将hash函数对应的值的位数组置1。查找时如果发现所有hash函数对应位都是1说明存在。不保证查找的结果是100%正确的。同时也不支持删除一个已经插入的关键字,因为该关键字对应的位会牵动到其他的关键字。所以一个简单的改进就是counting Bloom filter,用一个counter数组代替位数组,就可以支持删除了。添加时增加计数器,删除时减少计数器。

2)缓存一个null

缓存层和存储层的数据会有一段时间窗口的不一致,可能会对业务有一定影响。例如过期时间设置为 5分钟,如果此时存储层添加了这个数据,那此段时间就会出现缓存层和存储层数据的不一致,此时可以利用消息系统或者其他方式清除掉缓存层中的空对象。

缓存雪崩

如果缓存集中在一段时间内失效,发生大量的缓存穿透,所有的查询都落在数据库上,造成了缓存雪崩。

1)控制写缓存操作

在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

业界比较常用的做法,是使用mutex。简单地来说,就是在缓存失效的时候(判断拿出来的值为空),不是立即去load db,而是先使用缓存工具的某些带成功操作返回值的操作(Redis的SETNX)去set一个mutex key,当操作返回成功时,再进行load db的操作并回设缓存。否则,就重试整个get缓存的方法。

2)数据预热

预先去更新缓存,在即将发生大并发访问前手动触发加载缓存不同的key,设置不同的过期时间,让缓存失效的时间点尽量均匀

Redis的持久化机制

aof(文件存储操作日志)、rdb(定时生成快照)

Redis的管道pipeline

redis 是 CS 模式,Redis客户端与Redis之间使用TCP协议进行连接,一个客户端可以通过一个socket连接发起多个请求命令,每个请求命令发出后client通常会阻塞并等待redis服务处理,redis处理完后请求命令后会将结果通过响应报文返回给client,因此当执行多条命令的时候都需要等待上一条命令执行完毕才能执行。

Pipelining可以满足批量的操作,把多个命令连续的发送给Redis Server,然后一一解析响应结果。Pipelining可以提高批量处理性能,提升的原因主要是TCP连接中减少了“交互往返”的时间

pipeline 底层是通过把所有的操作封装成流,redis有定义自己的出入输出流。在 sync() 方法执行操作,每次请求放在队列里面,解析响应包。