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

Redis那些数据结构到底是咋回事,数据库内部结构也别忽视了

说到Redis,很多人知道它快,但为啥能这么快?光说它是内存数据库可不够,得扒开它的内脏看看它那些好用的数据结构和内部的小九九,这些东西可不是花架子,而是实打实让它飞起来的原因。

聊聊它那些花里胡哨的数据结构。

你别看Redis像个简单的键值对仓库,它的“值”可是能变很多戏法的,这跟咱们平常遇到的HashMap那种只能存个字符串或者数字完全不是一回事。

Redis那些数据结构到底是咋回事,数据库内部结构也别忽视了

最基础的当然是String(字符串),这可不是普通的字符串,Redis的字符串是“动态”的,可以自己扩容,你存个“hello”,它能存;你一下子改成“hello world”,它也能在后台悄咪咪地调整空间接住,不用你操心,它还能对数字进行自增自减操作,比如用来做文章阅读量、点赞数,一个命令就搞定,原子性的,不用担心并发出问题,来源参考自Redis官方文档对String类型的描述。

接着是List(列表),你可以把它想象成一个排队的队伍,但这个队伍很神奇,队头队尾都能进也能出,所以它既能当队列用(先进先出),也能当栈用(后进先出),比如可以用来做消息队列,生产者从左边推进消息,消费者从右边取出消息,或者记录用户最新的10条浏览记录,新的从左边推进,如果超过10条就把最老的从右边踢掉。

Redis那些数据结构到底是咋回事,数据库内部结构也别忽视了

Hash(哈希) 这个就厉害了,它特别适合存对象,比如一个用户信息,有姓名、年龄、城市,如果你用String存,得序列化成JSON字符串,改个年龄得整个读出来、解析、修改、再序列化存回去,麻烦又耗资源,但用Hash,你可以直接把用户ID作为键,这个Hash里面分别设置field为name、age、city来存对应的值,这样要改年龄,直接单独改age这个field就行了,贼方便,来源参考自Redis官方文档对Hash类型的描述。

Set(集合) 最大的特点就是里面的元素都不重复,而且支持一堆数学意义上的集合操作,比如求两个集合的交集、并集、差集,这有啥用?比如你想给文章打标签,一篇文章可能有“科技”、“编程”、“Redis”好几个标签,用Set存就能保证标签不重复,更绝的是,你想找同时对“科技”和“编程”都感兴趣的用户,直接对两个Set求个交集,结果立马就出来了,这要是用关系型数据库,得写个JOIN查询,慢多了。

Redis那些数据结构到底是咋回事,数据库内部结构也别忽视了

还有个升级版的Set,叫Sorted Set(有序集合),它给每个元素都绑定了一个分数(score),然后根据这个分数来给元素从小到大排序,这简直就是为排行榜量身定做的,比如游戏玩家积分榜,玩家ID是元素,积分就是分数,你要取前十名?一个命令搞定,甚至还能查某个玩家排第几名,范围查询分数在100到200之间的玩家,都非常高效,来源参考自《Redis设计与实现》一书中对有序集合的讲解。

光有这些好用的数据结构还不够,数据库本身内部也得有料。

Redis处理那么快的另一个核心秘密是:它干活基本上都在内存里,数据主要放在内存中,读写操作就是直接操作内存,这速度比去硬盘上找数据快了成千上万倍,那你肯定要问了,断电了数据不就没了吗?Redis也有办法,它支持持久化,就是定期或者满足条件时,把内存里的数据拍个快照(RDB方式)或者把所有写命令记录到一个日志文件(AOF方式)里存到硬盘上,这样即使断电重启,也能从硬盘上把数据恢复回来,在速度和持久化之间找了个平衡。

还有一个容易被忽视但至关重要的点是,Redis是单线程处理命令的,啥意思?就是说,虽然它网络IO等等可以用多线程,但真正执行你发过来的SET、GET这些命令的核心模块,只有一个线程在干活,这听起来好像是缺点,但其实是个巨大的优点,因为它避免了多线程之间为了争抢数据而带来的锁的麻烦,大大简化了实现,保证了每个操作的原子性,不会出现数据错乱,由于数据在内存里,操作飞快,就算单线程也能轻松处理每秒数十万次的请求,这个单线程模型的说明在多篇技术分析文章中被广泛讨论和引用。

Redis的快,是“好用数据结构”和“精巧内部设计”双管齐下的结果,数据结构让你能用最自然、最高效的方式去建模你的数据,而内存存储、单线程模型这些内部机制则保证了操作这些数据时的极致速度,这两者结合在一起,才造就了Redis这个性能怪兽。