• 关于redis


    1、Redis阻塞

    	导致阻塞的场景:
    	a、不合理使用api,如在一个有上万个元素的hash结构上执hgetall,由于数据量大而且算法复杂度是O(n),所以该命令执行很慢
    		redis原生提供慢查询统计,可用slowlog get {n} 获取最近n条慢查询命令
    	b、CPU饱和。单线程的redis在处理命令时只使用一个cpu,如果redis把单核CPU使用率跑到100%,则会导致CPU饱和问题,使redis无法处理更多的命令,从而影响吞吐率
    	c、持久化相关阻塞。
    		fork阻塞:fork阻塞发生在RDB和AOF重写时,在fork子线程完成持久化重写工作时,如果fork本身很耗时,必然会导致主线程阻塞
    		AOF刷盘阻塞:在开启AOF持久化时,文件刷盘一般使用一秒一次。后台线程每秒对AOF文件执行fsync操作,当硬盘压力过大时,fsync操作需要等待,直到写入完成。
    					 如果主线程发现距离上一次AOF操作完成成功超过了2秒,为保证数据安全它会阻塞,直到AOF操作完成
    		CPU竞争。redis时CPU密集型应用,不建议和其他CPU秘籍型应用部署在一起,防止其他进程严重消耗CPU影响redis吞吐量
    		内存交换。内存交换(swap)对于redis来说是致命的。当内存不够用时,redis会吧内存换出到硬盘,由于硬盘和内存的io相差很大,将导致redis吞吐量急剧下降
    				  解决方法:确保有足够的内存,确保redis实例设置了最大可用内存
    		网络问题。常见的网络问题有:网络延迟、链接拒绝、网卡软终端等
    

    2、过期策略

    	redis的过期策略主要分为主动和被动两种。
    		主动过期:就是redis会定期测试一批设置了过期时间的key并进行过期处理,测试到已经过期的key将被删除。具体算法如下:
    			1)redis配置项hz定义了serverCron任务的执行周期,默认为10,即CPU空闲时每秒执行10次
    			2)每次过期key清理的时间不超过cpu时间的25%,即若hz=1,则一次清理时间最大为250毫秒,若hz=10,则一次清理最大时间为25ms
    			3)清理时一次遍历所有db;
    			4)从db中随机抽取20个设置了过期时间的key,判断是否过期,若过期,则清除
    			5)若有5个以上的key过期,则执行步骤4,否则遍历下一个db
    			6)在清理过程中,如果达到了25%的CPU时间,则退出清理
    		
    		被动过期:即用户访问一个指定的key时,判断该key是否过期,如果过期,则清除,返回空值
    

    3、数据逐出策略:

    	volatile-lru:从已经设置过期时间的数据集中挑选最近最少使用的数据淘汰
    	volatile-ttl:从已经设置过期时间的数据集中挑选将要过期的数据淘汰
    	volatile-random:从已经设置过期时间的数据集中随机选择任意数据淘汰
    	allkey-lru:从数据集中挑选最近最少使用的数据淘汰
    	allkey-radom:从数据集中选择任意数据淘汰
    	no-eviction:禁止驱逐数据
    

    4、redis数据持久化的两种方式:RDB和AOF

       RDB:快照的形式,根据配置,redis定时将内存数据刷新到磁盘(save N M),N秒内,有M个key被修改,就刷新到磁盘。
       每次备份都会生成一个临时的RDB文件,完成之后替换掉原有的RDB文件。全量更新
    		缺点:可能导致一段时间内的数据丢失;每次备份都会fork出一个子进程来做持久化的操作,当数据量很大时,可能会导致短时间内阻塞
    		优点:定期备份,非常适用于灾备恢复,恢复大的数据集时相对较快,占用更少的磁盘空间,相对AOF更快
       AOF:AOF同步有三种fsync策略,直接将更新命令追加到AOF文件末尾。增量更新
    		appendfsync always  每次更新都将更新命令刷新到AOF文件。这种方式非常慢,但是数据安全非常高,几乎不会丢失数据
    		appendfsync everysec 每秒钟将内存中的更新命令刷新到AOF文件。可能丢失最多一秒钟的数据,足够快
    		appendfsync no 关闭AOF持久化功能
    		
    		优点:更加可靠;AOF日志只能追加,即使因为停电导致数据只写到一半,redis-check-aof工具也可以轻松修复它;自动触发aof重写,对aof文件进行压缩节省空间;
    		如果你吴用了flushall操作,如果在此期间没有执行重写操作,只需要停止服务,删除最新命令,然后重启redis,就可以恢复数据
    		缺点:aof文件通常比rdb文件更加占用空间;aof比rdb更慢;
    

    5、Redis数据结构

    	在Redis中,key-value的关系是通过dict数据结构来维护的,其中key固定为String类型,value有多种数据类型
    	dict的key固定使用sds数据结构,value的通用数据结构是redisObject。如果value是一个list,那么它的内部存储结构是一个quicklist
    	1)String。Redis中String底层采用动态字符串SDS来存储。在追加时,采用动态扩容来避免缓冲区溢出。基于数组实现。
    		对于一个string类型的value,如果它的值是一个数字,那么Redis内部还会把它转成long型来存储,从而减小内存使用
    		
    	2)dict。Redis采用dict来维护数据的key-value结构。dict本质上是为了解决算法中的查找问题。
    		在Redis中,dict也是一个基于哈希表的算法。和传统的哈希算法类似,它采用某个哈希函数从key计算得到在哈希表中的位置,采用拉链法解决冲突,并在装载因子(load factor)超过预定值时自动扩展内存,引发重哈希(rehashing)。Redis的dict实现最显著的一个特点,就在于它的重哈希。它采用了一种称为增量式重哈希(incremental rehashing)的方法,在需要扩展内存时避免一次性对所有key进行重哈希,而是将重哈希操作分散到对于dict的各个增删改查的操作中去。这种方法能做到每次只对一小
    		部分key进行重哈希,而每次重哈希之间不影响dict的操作。dict之所以这样设计,是为了避免重哈希期间单个请求的响应时间剧烈增加,这与前面提到的“快速响应时间”的设计原则是相符的。
    		为了实现增量式重哈希(incremental rehashing),dict的数据结构里包含两个哈希表。在重哈希期间,数据从第一个哈希表向第二个哈希表迁移
    		
    String 底层是sds或者int
    list 底层是quicklist或者ziplist
    set 底层是intset或者hashtable,只有当set中的元素全是整数时,才会是inset
    sortedset 底层是ziplist或者skiplist
    hash 底层是ziplist或hashtable
    		
    		
    		
    		
    总结:Redis的数据结构粗狂的概括起来其实就是一个key-value,内部使用dict来实现,key固定使用sds。value的类型比较丰富,有sds、ziplist、quicklist、skiplist、dict
    什么是sds呢?sds就是动态字符串,它的结构体中有三个属性,len字符串长度,free空余空间,char[]buf字符数组,说明sds底层是基于字符数组来实现的,当向字符串中追加新的内容时,首先判断是否有足够的空间,如果没有,进行一次扩容,从而避免字符串缓冲区的溢出
    

    redis面试题

    1、redis有哪些数据结构?

    基本数据结构:String、list、hash、set、sorted。高级:Geo、Pub/Sub
    

    2、如何实现redis分布式锁?

    通过set(key, value, setIfNotExist, setWithExpireTime, expiredTime)指令完成锁的设置,通过lua脚本实现原子性的解锁
    
    public boolean tryGetRedisLock(String key, String value, int expiredTime){
        String result = jedisService.set(key, value, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expiredTime);
        if (LOCK_SUCCESS.equals(result)){
            return true;
        }
        return false;
    }
    
    public boolean releaseRedisLock(String key, String value){
        String script = "if redis.call('get', KEYS[1]) == KEYS[2] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedisService.eval(script, 2, key, value);
        if (RELEASE_SUCCESS.equals(result)){
            return true;
        }
        return false;
    }
    

    3、假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如果将它们全部找出来?

    可以使用redis key的模糊匹配,但是由于redis是单线程模型,所以该命令会导致阻塞,可改用scan指令,scan指令可以无阻塞的提取指定模式的key列表,不过所需时间会比直接使用keys时间长;还有scan可能会导致数据重复,需要在客户端做去重处理
    

    4、如何用redis实现异步队列?

    使用list结构做队列,rpush生产消息,lpop消费消息。当lpop没有消息时,应该适当的sleep一会儿。也可以使用blpop当list中没有消息时,同步等待。
    可通过pub/sub主题订阅模式,实现1:N的消息队列
    

    5、 pub/sub的缺点?

    消费者下线的情况下,生产的数据会丢失
    

    6、如果大量的key设置同一过期时间,需要注意什么?

    如果大量的key在同一时间过期,可能导致在过期那个时间点出现卡顿现象。可以在过期时间后加一个随机值,使得过期时间分散一些
    

    7、redis如何持久化?

    redis有两种持久化机制:aof和rdb
    aof:直接将更新命令追加到aof文件末尾,实现增量更新。aof有三种 fsync策略,分别是always,每次更新都刷新到aof文件,优点是可靠性高,缺点是性能低;everysec 每秒刷新一次,最多丢失1秒的数据,可靠性高,性能也相对更好;no 关闭aof
    rdb:没隔一段时间,将内存快照刷新到磁盘。每次刷新都会创建一个临时的rdb文件,保存好之后直接删除之前的rdb文件,然后重命名新的rdb文件,以此替换原来的rdb文件。优点,性能优越,rdb文件相对aof文件更节省磁盘空间,定期备份,非常适用于灾备恢复,缺点:系统当机会导致大量数据丢失,每次都fork出一个子进程进行持久化操作,当数据量很大时,肯能导致阻塞
  • 相关阅读:
    用才情绽放的幸福之花
    我的爱车,你在哪里
    爱在网络,有没有错
    假如能抱着美女写诗
    只想爱你
    创业者和爱因斯坦的10大共同点(不是不可比的)
    心的感谢
    成大事必备9种能力.9种手段.9种心态
    一颗新星在陨落
    C++/C学习笔记(九)
  • 原文地址:https://www.cnblogs.com/canmeng-cn/p/9011017.html
Copyright © 2020-2023  润新知