大话java:五旬大爷教你一窥redis裙下风采

大爷你没事吧”,

“没事没事,路有点滑,”

“大爷,你的鞋都被人追掉了”

“现在的小姑娘真是的,撩下就动手,多亏你小伙子,小伙子你是做什么工作的”

“后端java开发”

“哦,有前途,这样你救了我, 我今天就把我的不传之谜 传授于你,”

“?啥?”

说着大爷颤颤巍巍的从裤裆里掏出了一本笔记,赫然写着

大话java:五旬大爷教你一窥redis裙下风采

”小伙子 你知道 redis的速度么?“

”单机读可达10000/S, 写可达 5000/s“

”恩 ,RDS巨擘mysql ,经过了这么多年优化 才1000/500,那么有么有想过redis为何这么吊么,“

”redis 是操作内存,操作速度自然比mysql的磁盘操作要快太多了“

”哎,世人肤浅,都只看到了redis的内存操作主导了优势,在我看来,这个原因也只是1/3“

”?另外2分是?“

”哼哼,“

老爷子舔了舔手指,慢慢翻开。

”数据结构!大家都知道redis提供了5种数据类型的支持,却不知他们的其中奥妙,着实可惜,今日老夫就让你见识见识,

redis最基础的是SDS(simple dynamic string),动态字符串,与java c的字符串不同,他的定义是不单单是一个char 数组构成,每个sds都会比它真实占用的字符长度都长,通过一个空闲标识符表示sds当前空闲字符有多少,如此设计,在一定长度范围的内的字符串都可以使用此sds,而且不会频繁的进行内存分配,直到此sds不能容纳分配的字符串,如果遇到这种情况情况,才需要进行扩扩容,妙不可言;这是redis的最基础的,所有的redis k-v 中的字符串都是依托于sds,这是其一;

其二 dict,字典,类似于java中的hashmap:数组,负载因子 hash算法;

不同于它的就是,每个dict拥有两个数组,一个简单的hash算法“

“?两个数组?简单的hash算法”

大话java:五旬大爷教你一窥redis裙下风采

”java hashmap的hash算法是啥,对key进行hash,将hash值与当前的数组长度进行计算,获取当前key在数组中的索引值“

”redis的很简单,就是key和一个定值运算,简单粗暴不是不伤手,保证了最快的执行速度,至于为啥两个数组,和hashmap一样,也要扩容,初始的dic的数组长度不能太大,随着数据的增加,超过了负载因子,dic的数组必须进行扩容,hashmap怎么扩容?“

大话java:五旬大爷教你一窥redis裙下风采

”新生一个更长的数组,遍历老数组向新数组的迁移“

”原理差不多,但是hashmap的扩张是一次性的,而redis的扩容是渐进式,不影响当前使用dic的正常使用,一点点划拉,直到迁移完成,这是其二,

其三堪称一个杰作,你知道mysql的索引的结构么?“

“我知道B+树,”

”B+树,源于B树,也是自动平衡的树,随机查询性能举世无双,经过B+树的改进,压缩了整棵树的高度,在搜索性能又上一层楼,但是二叉树也有自己的弊端,数据插入的平衡维持:左旋右旋,旋的我脑壳疼,拖了性能,而我们即将说到的redis第三点,就是跳跃表,在随机搜索方面略低于B+树,但是对于数据插入,跳跃表使用了历史上最屌的算法:抛硬币,唯一能和这个算法媲美的只有掷骰子,推牌九,斗地主,扎金花等等“

大话java:五旬大爷教你一窥redis裙下风采

“。。。这算哪门子算法,尤其后面几个,最多算民间艺术”

大爷,没有理睬,继续说道,

“在跳跃表是由N层链表组成,最底层是最完整的的数据,每次数据插入,率先进入到这个链表(有序的),插入完成后,通过抛硬币的算法,判断是否将数据向上层跑,如果是1的话,就抛到上层,然后继续抛硬盘,判断是否继续向上层抛,直到抛出了0结束整个操作,每抛到一层的时候,如果当前层没有数据,就构造一个链表,将数据放进去,然后使用指针指向来源地址,就这样依次类推,形成了跳跃表,每次查询,从最上层遍历查询,如果找到就返回结果,否则就在此层找到最接近查询的值,将查询操作移到另外一层,就是刚才说到来源地址,所在层,重复查询,第一次听到这跳跃表,简直比左旋右旋还头大,渐渐的领会到其要义:没事多推牌九,斗地主 扎金花

大话java:五旬大爷教你一窥redis裙下风采

”哦,原理还有这个玩意,好神奇哦,这些数据结构就能解决redis的速度问题?“

”非也非也“

”这是存储方面巧妙的地方之一,更主要是单线程+多路 I/O 复用模型“

”单线程?单线程会更快。。。“

大话java:五旬大爷教你一窥redis裙下风采

“如果说单线程更快的话那简直是骗人的,不过单线程的模式解决了数据存储的顽疾:数据并发安全,任何运行多线程同时访问数据库都会存在这个问题,所以才有了mysql的mvcc和锁, Memcached 的cas乐观锁,来保证数据不会出现并发导致的数据问题,但是redis使用单线程就不存在这个问题:1,单线程足够简单,无论在redis的实现还是作为调用方,都不需要为数据并发提心吊胆,不需要加锁。 2.不会出现不必要的线程调度,你知道多线程,频繁切换上下文,也会带来很多性能消耗”

“额 。。。 什么是切换上下文?”

大话java:五旬大爷教你一窥redis裙下风采

”你记得上次有个帅哥讲内存模型的时候(两程序员玩“锁”,一人抢救无效身亡),有提到工作内存么?线程每次执行需要把数据从主内存读到工作内存,然而当线程被调度到阻塞的时候,这些工作内存的数据需要被快照到线程上下文中,其实就是一个记录各个线程状态的存储结构,等到线程被唤醒的时候,再从上下文中读取,称之为上下文切换;减少上下文切换操作,也是使用单线程的奥妙;”

“。。。。”

大话java:五旬大爷教你一窥redis裙下风采

“再说 多路 I/O 复用模型,这个也是java 的NIO体系使用的IO模型,也是linux诸多IO模型中的一种,说白了就是当一个请求来访问redis后,redis去组织数据要返回给请求,这个时间段,redis的请求入口不是阻塞的,其他请求可以继续向redis发送请求,等到redis io流完成后,再向调用者返回数据,这样一来,单线程也不怕会影响速度了

“哦,听起来很厉害,不过我再想redis真的是单线程就完全安全么?”

“当然不是。。。一个有趣的场景,如果请求1 2 3 同时发送修改redis的一个key值,这个是不可预期的,无法判断那个才是正确的,如果你认定第一个修改的为正确的,就需要借助redis其他的特性来完成,比如说watch,如果要保重多个操作原子性,就需要multi”

“算了,反正我也听不懂,我先走了”

大话java:五旬大爷教你一窥redis裙下风采

“少年留步,”

“大爷你松手,我听着呢”

大话java:五旬大爷教你一窥redis裙下风采

“下面我必须要叮嘱你,redis持久化一定要慎重,AOF RDB,切不可瞎鸡巴用,redis集群的数据库同步,与mysql同步不同,别有洞天,最后老夫再传授你“如何避免缓存击穿”的奥秘”

“?缓存击穿?是啥?”

大话java:五旬大爷教你一窥redis裙下风采

“缓存一般作为RDS的前置系统和服务器直连,减轻rds的负担,常理而言,如果服务器查询缓存而不得的话,需要从rds中获取然后更新到缓存中,但是如果在“从rds中获取然后更新到缓存中”,这个阶段,缓存尚未更新成功,大量请求进来的话,rds势必压力暴增,甚至雪崩,或者歹人恶意攻击,一直查询rds和缓存中未存在key,也会导致缓存机制失效,rds压力暴增,称之为缓存击穿,

”前辈 那该咋儿办?“

”缓存永不失效,定时同步rds redis,不允许应用直接请求查询rds,所有的查询以缓存中为准“

”那且不是数据不能实时展示了么,“

”对的,不过你就认为服务器临时上了一个厕所,耽误个几分钟,这样会好受些“

”如果定时器挂了怎么办“

”服务器挂了怎么办?其实道理一样的,如何避免服务器宕机,就如何避免定时器挂机“

”哦“

大话java:五旬大爷教你一窥redis裙下风采

年轻人大概有些明白,或者有些恍惚,低头间,只看到大爷的背影,渐行渐远,

“大爷,未请教尊姓大名”

“黄见红”

“前辈可留下秘籍,供晚辈瞻仰”

“拿去”

大爷从裤裆里大手一辉

书落在了少年手中,还有淡淡的体温,少年打开,呢喃着

“redis设计与实现。。。”

“小子里面有我二维码,别忘了付款”

“。。。。”

大话java:五旬大爷教你一窥redis裙下风采

惯例上图:

大话java:五旬大爷教你一窥redis裙下风采


分享到:


相關文章: