Redis

为什么效率比较高?

答:

  1. 纯内存数据库,一般都是简单的存取操作,读取速度快

  2. 使用非阻塞IO,IO多路复用

  3. 采用单线程模型,保证每个操作的原子性,减少了线程的上下文切换和竞争

  4. 全程使用hash结构,读取速度快,再比如有序集合使用的跳表,不同场景下采用不同的底层数据结构来实现。

简述下Redis的过期策略?

答:我们都知道,Redis是key-value数据库,我们可以设置Redis中缓存的key的过期时间。Redis的过期策略就是指当Redis中缓存的key过期了,Redis如何处理。

过期策略通常有以下三种:
定时过期:每个设置过期时间的key都需要创建一个定时器,到过期时间就会立即清除。该策略可以立即清除过期的数据,对内存很友好;但是会占用大量的CPU资源去处理过期的数据,从而影响缓存的响应时间和吞吐量。
惰性过期:只有当访问一个key时,才会判断该key是否已过期,过期则清除。该策略可以最大化地节省CPU资源,却对内存非常不友好。极端情况可能出现大量的过期key没有再次被访问,从而不会被清除,占用大量内存。
定期过期:每隔一定的时间,会扫描一定数量的数据库的expires字典中一定数量的key,并清除其中已过期的key。该策略是前两者的一个折中方案。通过调整定时扫描的时间间隔和每次扫描的限定耗时,可以在不同情况下使得CPU和内存资源达到最优的平衡效果。
(expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,value是该键的毫秒精度的UNIX时间戳表示的过期时间。键空间是指该Redis集群中保存的所有键。)

Redis的过期删除策略就是:惰性删除和定期删除两种策略配合使用。

惰性删除:Redis的惰性删除策略由 db.c/expireIfNeeded 函数实现,所有键读写命令执行之前都会调用 expireIfNeeded 函数对其进行检查,如果过期,则删除该键,然后执行键不存在的操作;未过期则不作操作,继续执行原有的命令。
定期删除:由redis.c/activeExpireCycle 函数实现,函数以一定的频率运行,每次运行时,都从一定数量的数据库中取出一定数量的随机键进行检查,并删除其中的过期键。
注意:并不是一次运行就检查所有的库,所有的键,而是随机检查一定数量的键。
定期删除函数的运行频率,在Redis2.6版本中,规定每秒运行10次,大概100ms运行一次。在Redis2.8版本后,可以通过修改配置文件redis.conf 的 hz 选项来调整这个次数。

补充:1. 定期删除:1.redis每过100ms,从设置了过期时间的key中随机取出20个缓存key2.清除其中的过期key3.若过期key占比超过1/4,则重复步骤1。为什么定期删除采用的是随机策略,而不是对全部数据做清理呢?因为每100ms过滤所有缓存数据,对CPU压力较大。虽然定期删除解决了一部分过期数据的问题,但是还有不少数据并没有被清除出缓存,所以此时就有了惰性删除。2. 惰性删除:这种方式是被动触发的,部分过期缓存key未被清理出缓存,长久占用缓存资源(随机策略存在的缺陷性),随着缓存数据的不断增加,无法通过删除过期key的方式腾出空间,来储存新的热点数据。所以这里就需要我们的内存淘汰策略

简述下Redis复制?

老版实现(2.8版本以前)

1. 过程:

  • 同步(一开始启动的时候)
    1. 从服务器向主服务器发送SYNC命令
    2. 收到命令的服务器执行bgsave命令,生成一个RDB文件,并且在这个过程中,缓存所有的写命令,因为在后台线程生成RDB文件的时候,这些新产生的写命令是不会记录的
    3. 当主服务器的bgsave执行完后,将RDB文件发送给从服务器,然后从服务器载入这个RDB文件,
      到达执行bgsave的主服务器状态
    4. 主服务器再将缓存的写命令发送给从服务器,从服务器就可以恢复到主服务器当前的状态
  • 传播命令(启动之后的方式)
    1. 将命令传播给从服务器,从服务器执行即可

缺点:初次复制的时候完全没问题,但是断线后重连时效率很低,重复的内容很多,降低了效率

新版实现(2.8版本开始)
  • 完整重同步(用于处理初次复制的情况)
    1. 同上
  • 部分重同步(用于断线后重复制的情况)
    1. 当从服务器断线后,主服务去记录那些断线期间的写命令,当重连的时候只将这部分发送给从服务器
    2. 实现方式,是有一个复制偏移量,每当主服务器向从服务器发送N字节数据的时候,就将自己的复制偏移量增加N,然后从服务器收到N字节的数据时,将自己的复制偏移量加N。然后还有一个复制积压缓冲区,是一个队列,这里会存最近一些发送给从服务器的数据,每次同步的时候,从服务器会将自己的复制偏移量发送给主服务器,然后主服务器检查是否同步,如果不同步就计算偏移量的差值,并将缓冲区中相应的数据发送给服务器

简述下Redis持久化的方式?

答:

  • RDB
    1. 简述: RDB即将当前数据生成快照,并保存于硬盘中。可以通过手动命令,也可以设置自动触发。
    2. 方式: save 和 bgsave
    3. 文件保存的东西:TYPE KEY VALUE TTL(time to live 如果有的话,也会有过期时间)
  • AOF
    1. 简述: AOF通过日志,对数据的写入修改操作进行记录。这种持久化方式实时性更好。通过配置文件打开-**AOF。
    2. 保存的方式:比如
      RPUSH LIST "A" "B" [A,B]
      RPUSH LIST "C" [A,B,C]
      RPUSH LIST "D" "E" [ A,B,C,D,E]
      LPOP LIST[B,C,D,E]
      LPOP LIST[C,D,E]
      这里是5条指令,但是不是保存这五条,我们只需要保存RPUSH "C" "D" "E"

AOF 日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。因为在 rewrite log 的时候,会对其中的指令进行压缩,创建出一份需要恢复数据的最小日志出来。在创建新日志文件的时候,老的日志文件还是照常写入。当新的 merge 后的日志文件 ready 的时候,再交换新老日志文件即可。

redis优化从哪些方面入手?

答:

  1. 在没有必要开启持久化的时候,关闭持久化
  2. 缩短键值对的存储长度,从以上数据可以看出,在 key 不变的情况下,value 值越大操作效率越慢,因为 Redis 对于同一种数据类型会使用不同的内部编码进行存储,比如字符串的内部编码就有三种:int(整数编码)、raw(优化内存分配的字符串编码)、embstr(动态字符串编码),这是因为 Redis 的作者是想通过不同编码实现效率和空间的平衡,然而数据量越大使用的内部编码就越复杂,而越是复杂的内部编码存储的性能就越低。
  3. 禁用长耗时的查询命令;
  4. 使用 Pipeline(管道技术)批量操作数据,在平常都是一个命令,一个返回结果,使用管道技术我们可以一次性发送多个命令,然后等得到总的结果后再返回。

Redis 内存淘汰机制了解么?

为什么会有内存淘汰机制:其实是因为Redis的删除机制并不是一次性删除所有过期的数据,而是每次随机选一部分删除。所以最终还是有可能内存溢出,另外内存淘汰机制的合理性,最理想的就是没有过期的数据不会删除,但是还是有可能没过期的数据被删除了,但redis的定位是缓存,所以你那些强业务性的东西,本来就不应该防在redis里,他是一个有误差的东西

答:

  1. volatile-lru(least recently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰
  2. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰
  3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰
  4. allkeys-lru(least recently used):当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)
  5. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰
  6. no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!
  7. volatile-lfu(least frequently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使用的数据淘汰
  8. allkeys-lfu(least frequently used):当内存不足以容纳新写入数据时,在键空间中,移除最不经常使用的 key

Redis如何实现分布式锁?

答:
参考文章1
参考文章2
参考文章3

  • 加锁: 1、加锁本质上就是在redis上面设置一个key,这样当其他服务想获取这个锁的时候,都会执行set一个key的操作,这里这里有两个参数需要注意,nx是当key不存在的时候才进行设置,如果存在的话返回失败,然后还有一个ex参数,这里是设置key的过期时间,主要是为了防止如果某个服务获取锁后宕机了,不会导致死锁。2、解锁的时候也要注意,这里要防止一个误解锁问题,因为咋们前面说了锁是有过期时间的,那么如果锁过期了,业务代码还没有执行完,但是此时锁是已经释放了,也就是说其他人可能会去获取这把锁,此时当我们之前没执行完的业务代码执行完之后,并解锁的话,就会导致锁误解开。3、误解锁解决方案,为每个加锁生成一个uid,让解锁的时候,加锁的人可以知道这把锁是否是自己加的,这里注意,解锁此时也分为两步走了,第一步是判断是否是自己加的,第二步是进行删除。这里的两步操作同样需要保证原子性,为什么要保证原子性呢?假如A请求在获取锁对应的value值验证requestId相等后,下达删除指令。但是由于网络等原因,删除的指令阻塞住了。而此时锁因为超时自动解锁了,并且B请求获取到了锁,重新加锁。这时候A请求到删除指令执行了,结果把B请求好不容易获取到的锁给删了。
出现的问题
  1. 加锁没有保证原子性:将加锁和设置过期时间分为两步,如果加完锁后出现错误,导致过期时间没有设置,这样就会导致锁永远不会过期,解决方案:1、 最新的redis命令已经提供API同步。 2 、在redis中,lua脚本是当作命令来执行的,所以是原子的操作,所以我们可以利用lua命令来进行加锁
  2. 锁误解除:如果线程 A 成功获取到了锁,并且设置了过期时间 30 秒,但线程 A 执行时间超过了 30 秒,锁过期自动释放,此时线程 B 获取到了锁;随后 A 执行完成,线程 A 使用 DEL 命令来释放锁,但此时线程 B 加的锁还没有执行完成,线程 A 实际释放的线程 B 加的锁。
public static void wrongReleaseLock2(Jedis jedis, String lockKey, String requestId) {
    // 判断加锁与解锁是不是同一个客户端
    if (requestId.equals(jedis.get(lockKey))) {
        // 若在此时,这把锁突然不是这个客户端的,则会误解锁
        jedis.del(lockKey);
    }
}

如代码注释,问题在于如果调用jedis.del()方法的时候,这把锁已经不属于当前客户端的时候会解除他人加的锁。那么是否真的有这种场景?答案是肯定的,比如客户端A加锁,一段时间之后客户端A解锁,在执行jedis.del()之前,锁突然过期了,此时客户端B尝试加锁成功,然后客户端A再执行del()方法,则将客户端B的锁给解除了。

注意加锁和解锁的操作原子性

简述 Redis 中跳表的应用以及优缺点?

答:
跳表是一个可以快速查找的有序链表, 搜索、插入、删除操作的时间均为O(logn), 跳表虽然是非常有用的数据结构,但是很多书里都没有写这个,我在大学的数据结构课本里也没有写跳表,就导致很多人对跳表不熟悉。

跳表本质上是一个链表,因为链表的随机查找性能太差,是O(N),查找元素只能从头结点或者尾结点遍历。

跳表的优缺点:
作为快速查找的数据结构,跳表常用来和红黑树 做比较,列一下跳表和红黑树的优缺点吧

跳表的优点:

跳表实现起来相对简单。红黑树的定义和左旋右旋操作,确实复杂,我资质愚钝,理解起来还是有单困难。后面我会解释跳表实现简单的原因的.

区间查找方便。在跳表中找到一个节点后,就可以通过前后指针找到相邻的元素。红黑树则需要通过父节点,子节点去寻找,相对麻烦。

红黑树的优点:

内存占用小,只需要3个指针就可以(左子树,右子树,父节点) 而跳表有一个向后的指针,每一层都有一个向前的指针

红黑树的查找稳定,红黑树有着严格的定义,每次插入和删除数据都会通过左旋右旋来平衡树的结构,通过红黑树查找有着稳定的查找时间O(logn) ,为啥跳表是不稳定的,看到跳表是怎样确定层数的就明白了

跳表和普通链表相比,除了费内存,好像没啥缺点了

如何解决缓存与数据库不一致的问题?

Redis事务?

参考链接

Redis三种高级数据结构?

答:
bitmap相关使用
三种高级数据结构

  • HyperLogLog:
  • bitMap:
  • GEO:
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容