当前位置:首页 > 问答 > 正文

Redis用正负索引玩转数据,速度快到飞起,处理效率杠杠的

(引用来源:Redis官方文档、多位技术博主如“程序员小灰”、“小林coding”的科普文章)

Redis这个东西,它之所以能快到飞起,处理效率杠杠的,有一个特别简单又实用的设计功劳巨大,那就是它对数据位置的定位方式——正负索引,这玩意儿听起来可能有点技术感,但其实理解起来特别简单,就跟我们数数一样。

想象一下,你有一串糖葫芦,上面串了5个山楂果,现在你想告诉小伙伴吃哪一个,你可以从左边开始数,第一个是“1”,第二个是“2”,这叫正索引,从0开始或者从1开始都行,Redis里列表(List)是从0开始算正的,你也可以从右边开始数,最右边那个是“-1”,倒数第二个是“-2”,这叫负索引,Redis非常聪明地把这两种数数方法都支持了。

这个看似小孩都懂的功能,在Redis手里怎么就变成速度法宝了呢?主要体现在以下几个方面:

第一,找数据快到不用过脑子,你有一个存满了消息的列表,叫“message_list”,里面有100万条消息,你现在想直接看最新的一条消息是多少?如果只用正索引,你得先知道总共有多少条(这本身可能就是个耗时的操作),然后算出最后一个的位置是第100万条,再用命令去取,但在Redis里,你根本不用算,直接一个LRANGE message_list -1 -1命令,告诉它:“我就要倒数第一个”,Redis瞬间就能给你拿到,因为它内部的结构设计(一个叫双向链表的东西)让它从尾巴找和从头找一样快,根本不需要从头数到尾,这种操作在专业上叫时间复杂度O(1),意思是不管列表多长,我取头尾数据的速度都是一样的,快如闪电。

第二,删减数据特别利索,还是那个消息列表,如果我想只保留最新的100条,把旧的都删掉,该怎么做?用正索引你得算,从第1条删到第999900条?这计算和操作都麻烦,用负索引就简单粗暴了,直接一个LTRIM message_list -100 -1命令,这个命令的意思就是:“帮我把列表修剪一下,只留下从倒数第100个到倒数第1个之间的内容,其他的全扔掉”,Redis接到指令,咔嚓一下,直接就操作了,效率非常高,这种清理内存的方式,保证了Redis能一直轻装上阵,不会因为数据太多而变慢。

第三,应对各种场景灵活得像泥鳅,正负索引的结合,让很多日常需求变得异常简单。

  • 模拟最新消息队列:新消息从左边用LPUSH塞进去,要处理时用RPOP从右边取出来,如果你想看一眼最新的消息但不想取走,就用LINDEX key 0(看正数第一个)或者LINDEX key -1(看倒数第一个,也就是最早进去的)?不对,这里要注意:因为是从左边塞的,所以最新的消息在索引0的位置,最早的消息在索引-1的位置,你看,用索引可以轻松定位到任何你想看的位置。
  • 分页查询:你想看一个长列表中间的部分,比如从第50个开始看10个,用LRANGE key 49 58(因为从0开始)就能搞定,如果你想看最后10个呢?用LRANGE key -10 -1,比你先计算总长度再减10要直观和可靠得多,尤其是在列表长度不停变化的时候。

为什么这种简单的设计能带来这么高的效率呢?这就要说到Redis的老本行了——它所有的数据都放在内存里,内存的读写速度本身就是硬盘的几十上百倍,再加上Redis是单线程干活(新版本有变化,但核心逻辑处理仍是单线程),它不用像多线程程序那样费劲去处理复杂的锁的问题,避免了线程切换带来的开销,这个单线程就像一个超级专注的伙计,你给它一个指令,取倒数第一个”,它就能心无旁骛地直接去内存里那个确切的位置把数据拿给你,中间几乎没有拖泥带水的步骤。

Redis的正负索引,并不是一个多么高深莫测的黑科技,而是一个将简单思想发挥到极致的典范,它充分利用了内存高速读写的特性,通过一种对人类友好、对机器高效的定位方式,让数据的存取、修剪、遍历操作都变得直接而迅速,正是这些一个个精心打磨的细节,共同铸就了Redis“速度快到飞起,处理效率杠杠的”金字招牌,它告诉我们,最快的解决方案,恰恰是那个最直观、最简单的。

Redis用正负索引玩转数据,速度快到飞起,处理效率杠杠的