【温故知新】-Redis相关知识点梳理

Redis 知识点复习 灵魂十问

 

目录

1.Redis凭什么这么快?

2.Redis的五大数据类型是什么?

3.Redis的持久化机制

4.Redis的过期策略及内存淘汰机制

5.Redis事务

6.Redis分布式锁

7.Redis的集群方案

9.缓存穿透,缓存击穿,缓存雪崩,缓存更新

10.Redis的使用场景


 

1.Redis凭什么这么快?

redis之所以这么快,总起来主要有以下几点:

①基于单线程的设计,避免了多线程上线文切换的时间和锁开销.

②基于内存,直接在内存存取,所以很快

③采用了非阻塞IO多路复用机制

④Redis精心设计的基于C语音的SDS字符串,提高了性能.(SDS字符串获取字符串长度的时间复杂度仅为O(1),且减少了修改字符串长度时内存重分配的次数,关于SDS具体可以百度,限于篇幅,这里不展开)

 

2.Redis的五大数据类型是什么?

Redis支持String,Hash,List,Set,Sorted Set五大数据类型.

String:字符串类型,最常用的一种数据类型

Hash:用于存储结构化对象,用的也比较多

List:双端链表,常备用来做轻量级的消息队列

Set:无序集合,用的比较少

Sorted Set:有序集合,常被用来做排行榜类的功能开发,比如热卖榜,热搜榜...

 

3.Redis的持久化机制

Redis支持RDB和AOF两种持久化机制,默认采用RDB持久化数据,RDB(Redis dataBase)是对Redis中存储的数据作数据快照,每隔一段时间将内存中数据的快照写入磁盘,AOF(Append only file)是采用类似于Mysql binlog那种日志的方式,将每个写操作追加到日志中.

RDB相对于AOF在数据恢复时更快,但有可能存在数据丢失的情况;AOF的日志文件大小会与日俱增,到了后期数据恢复会变得很慢,但不会丢数据,所以可以根据具体的应用场景去选择合适的持久化方式,当然也可以RDB+AOF同时开启.

 

4.Redis的过期策略及内存淘汰机制

Redis采用定期删除+惰性删除的过期策略. 定期删除是指每隔一段时间,就随机扫描一些已经设置了过期时间的key,检查是否过期,过期就删除. 惰性删除是指在获取某个key时,会先检查是否已过期,过期就删除.

内存淘汰总结起来主要有4类:LRU,LFU,RANDOM,TTL;具体分为以下8类:

volatile-lru:从已设置过期时间的数据中,挑选最近最少使用的进行淘汰

volatile-lfu:从已设置过期时间的数据中,挑选使用频率最低的进行淘汰

volatile-ttl:从已设置过期时间的数据中,挑选即将过期的进行淘汰

volatile-random:从已设置过期时间的数据中,随机淘汰

allKeys-lru:从所有数据中挑选最近最少使用的进行淘汰

allKeys-lfu:从所有数据中挑选使用频率最低的进行淘汰

allKeys-random:从所有数据中随机淘汰

no-eviction:不淘汰,如果内存满了就无法set新的数据,但不影响老数据的使用,保证数据不丢失

 

5.Redis事务

Redis可以通过Muti,exec,discard,watch命令实现事务控制. Redis的事务保证ACID(原子性,一致性,隔离性,持久性)

 

6.Redis分布式锁

Redis的分布式锁的实现思路:

锁创建:set一条数据,如果该数据的key不存在,则返回true; 若key已存在,则重试,直到超过设置的超时时间为止,期间若设置成功,则返回ture,否则返回false;为了防止死锁,一般会对数据设置默认的失效时间. 另外需要考虑,是否同一请求的重入,一般是借助value实现的.

锁释放:锁释放需要先判断key是否存在,value是否是设置的value,毕竟解铃还须系铃人,另外需要注意该锁的重入次数,如果重入次数大于1,需要先判断并递减,不能直接就删除key,而且整个过程需要原子操作,一般是借助lua脚本来实现.

//锁创建伪代码
public boolean tryLock(String lockKey,String requestId,long timeOutMillis){
    //获取锁超时时间
    Long timeOut = System.currentTimeMillis() + LOCK_MAX_WAIT_TIME;

    while(true){
        String result = jedis.set(lockKey,requestId,"NX","PX",timeOutMillis);
        if("ok".equals(result){
            return true;
        }

        maxWaitTime = timeOut - System.currentTimeMillis();
        
        //超过过期时间还没有获取到锁,则直接返回失败
        if(maxWaitTime <= 0){
            return false;
        }

        //TODO 线程适度休眠,以减轻CPU压力
    }
}
//伪代码 锁释放
public boolean release(String lockKey, String requestId){
     String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then return         redis.call('del',KEYS[1]) else return 0 end";

          Object result = jedis.eval(luaScript, Collections.singletonList(lockKey),
                    Collections.singletonList(requestId));

            if ("1".equals(result)) {
                return true;
            }
}

 

7.Redis的集群方案

Redis集群方案常见的有三种:

①Redis-client 客户端分片, 由客户端决定数据读取/写入的节点,优点是简单,性能高,缺点是可维护性差.

②Proxy 基于代理分片,由代理决定数据的读/写节点,开源的方案有twitter的twemproxy,优点是切换成本低,代理和数据写入逻辑解耦,缺点是框架失去维护,同时代理层增加了资源损耗.

③Redis-cluster 官方集群,由Redis官方提供的集群方案,优点是客户端直连redis服务,省去代理层性能损耗,缺点是数据迁移不方便.

 

8.Redis常见的性能优化

①尽量使用短且具有业务含义的key

②尽量避免使用key*去获取数据

③设置有效期及回收策略

④对于批量场景可以通过管道合并命令,批量执行.

 

9.缓存穿透,缓存击穿,缓存雪崩,缓存更新

缓存穿透:缓存穿透是指由用户恶意发起,查询数据库和缓存中不存在的值,从而查询直接请求数据库,比如用户一直查询id=-1(数据库中不存在该数据),就会给数据库带来很大压力,常见的处理方式如布隆过滤器,guava提供了实现.

缓存击穿:缓存击穿是指,同时有多条请求来查询数据,此时缓存中无数据,但数据库中有,于是存在多条线程同时查询数据库并更新缓存的情况,可以通过分布式锁来处理,只允许一个线程更新缓存,未获取到锁的线程等待一段时间后重新从缓存中获取数据.

缓存雪崩:缓存雪崩是指,同一时间内,大量缓存都过期失效了,导致所有请求瞬间都直接打到数据库上,导致数据库崩溃. 解决方案,可以设置随机的缓存失效时间,也可以分模块设置缓存过期时间,推荐前者.

缓存更新:最常用的方式是 先更新数据库,再删除缓存.

 

10.Redis的使用场景

①作为缓存,减轻底层数据库压力.

②可以支持分布式锁,分布式session等

③可以支持轻量级消息队列

④可以利用有序集合做排行榜

⑤可以用来做计数器,原子增/减,比如电商系统的库存

⑥在一些特殊场景下,redis也会被直接作为数据库使用

 

你可能感兴趣的:(【NoSQL】,【温故知新】,redis,缓存,redis面试题)