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

Redis里Key到底是怎么存的,存储特点那些事儿浅谈

基于网络公开技术博客与社区讨论综合整理,例如开发者社区、CSDN博客等平台的相关文章)

今天咱们就来聊聊Redis里最基础也最核心的一个东西:Key是怎么存的,别看Key就是一个小小的字符串,它在Redis肚子里过的可是不一样的生活,有很多有意思的特点。

Redis里Key到底是怎么存的,存储特点那些事儿浅谈

最根本的一点是,Redis本质上是一个巨大的“字典”或者叫“键值对”数据库,你可以把它想象成一个超级大的HashMap,Key就是这个HashMap里的索引,当你输入一个命令,比如SET username "张三",Redis要做的第一件事就是找到“username”这个Key在它庞大的字典里对应的“位置”,然后把值“张三”塞进去。

这个“位置”是怎么确定的呢?这就涉及到Key的存储结构了,Redis为了快,使用了一种叫做“哈希表”的东西来存所有的Key,简单理解,就是Redis会给你的Key通过一个算法算出一个编号(哈希值),然后根据这个编号决定把它放在哪个“格子”里(哈希桶),这样查找的时候,不需要遍历所有Key,直接算一下编号就能直奔主题,速度极快,这也是为什么Redis能支持那么高并发访问的原因之一。

Redis里Key到底是怎么存的,存储特点那些事儿浅谈

这里就有个问题,如果不同的Key算出来的编号碰巧一样,都挤在同一个“格子”里怎么办?这种现象叫“哈希冲突”,Redis的解决办法很直接,就是在这个“格子”里拉一个链表,把冲突的Key一个个串起来挂在那,当查找的时候,先找到对应的“格子”,再在这个小小的链表里挨个对比一下真正的Key值,就能找到目标了,如果同一个“格子”里挂的Key太多,链表就会变得很长,查找效率就会下降,Redis还会在后台监控,当链表太长的时候,它会自动进行“扩容”(rehash),也就是换一个更大的哈希表,把原来的Key重新计算编号再放进去,让Key分布得更散开一些,保证查询速度。

接下来聊聊Key本身的特点,Redis的Key是“二进制安全”的,这话听起来专业,其实意思很简单:Key可以是任何二进制数据,不光是咱们平时写的文字,一个图片的二进制内容、一个序列化后的对象,都可以直接当作Key来用,之所以能这样,是因为Redis不关心Key里面具体是啥,它只把它当作一串字节数组来处理,我们最常用的还是字符串形式的Key,比如user:10001:profile,这种用冒号分隔的命名方式是一种约定俗成的规范,看起来清晰,有点像文件路径,方便我们 mentally 归类和管理。

Redis里Key到底是怎么存的,存储特点那些事儿浅谈

Key在内存中是怎么存在的呢?Redis为了节省内存,对不同长度的Key采取了不同的策略,对于比较短的字符串(目前版本一般是小于44字节),Redis会采用一种叫“embstr”的编码方式,这种方式会把Key和它对应的值(如果值也是短字符串的话)在内存中紧挨着分配,这样能减少内存碎片,提高缓存局部性,访问起来更快,而对于更长的Key,就会用更通用的方式来存储。

还有一个非常重要的特点是,Redis的所有Key都是存在内存里的,这是它读写速度飞快的根本原因,但同时也带来了一个限制:能存多少数据,取决于你的机器有多大内存,用Redis的时候,可不能像用硬盘数据库那样随心所欲地存海量数据,我们需要精心设计Key,避免存储过大的Value,有时候还得设置过期时间,让不常用的Key自动被清理掉(Redis的过期策略也是另一个有趣的话题)。

关于Key的命名空间,需要注意的是,Redis不像有些数据库有“数据库”(Database)的概念来隔离数据,它默认有16个编号0-15的数据库,你可以用SELECT命令切换,但这更像是一种逻辑上的隔离,更重要的是,Redis没有像“文件夹”那样的层级结构,所有的Key都生活在同一个扁平的哈希表空间里,我们看到的user:10001:profile这种带冒号的Key,只是“看起来”有层次,对Redis来说,它依然是一个完整的、普通的字符串Key,管理这些Key主要靠我们自己的命名约定。

Redis里的Key虽然简单,但它的存储方式(哈希表、解决冲突、内存分配优化)都是为了一个终极目标:快,它的二进制安全特性和内存存储的特性,也决定了我们在使用它时需要遵循的一些最佳实践,比如使用简洁且有意义的命名、控制Key和Value的大小、善用过期时间等,理解了这些,你就能更好地驾驭Redis这个高性能的利器了。