Redis是如何高效存储数据的
要想理解Redis,首先得弄明白它怎么存东西。想象一下,我们平时用的一个简单键值对,比如名字对应年龄,Redis内部是用一种叫做"字典"的结构来存的。这个字典,在源码里主要是由两个东西组成:一个叫哈希表,另一个叫跳跃表。哈希表可以让你根据键的名字,快速找到对应的值,就像查字典一样。但有时候,如果很多键都挤在同一个位置,速度会变慢,这时候Redis会自动进行一种叫"rehash"的操作,大概意思就是把旧表里的数据慢慢搬到新表里,而且这个过程是在后台悄悄进行的,不会一下子卡住你的操作。另外,对于需要排序的数据,比如分数排行榜,Redis就用跳跃表。你可以把它想象成一座多层的大楼,从高层快速定位到大概位置,再下楼找到准确房间,这样找起来比一层层爬楼梯快多了。
为什么Redis处理命令这么快
很多人知道Redis快,但为什么快呢?一个关键原因是,它的大部分操作都是在内存里操作的,所以特别快。这些设计,在黄健宏的《Redis设计与实现》这本书里讲得很细。
Redis为什么这么快?单线程的秘诀
很多人听说Redis是单线程的,第一反应是:那不会很慢吗?其实恰恰相反,这正是它快的一个关键原因。这里的单线程,主要是指处理网络请求和读写数据命令的那个核心流程,是排队一个一个来的。这样做的好处是,完全避免了多线程之间为了抢资源而打架(也就是线程切换和锁竞争的开销)。那么它怎么同时处理那么多客户端的连接呢?秘诀在于它用了I/O多路复用技术。你可以把这个技术理解成一个超级高效的接线员,这个接线员(在Linux里通常是epoll)同时守着很多个电话(客户端连接),哪个电话有消息来了(数据可读或可写),他就立刻通知Redis去处理那个电话,处理完一个再处理下一个。这样,虽然命令是一个一个执行的,但等待网络数据的时间没有被浪费。这种设计让Redis在绝大多数场景下,CPU都不会成为瓶颈,反而把速度发挥到了极致。这个核心机制在Redis的源码文件ae.c(事件循环)中体现得非常清楚。
数据的持久化:如何保证不丢失
Redis的数据都在内存里,万一服务器断电或者重启,数据不就全没了吗?不用担心,Redis提供了两种主要的持久化方式,相当于给内存数据拍照片存档。第一种叫RDB(快照)。你可以设定一个时间,比如每隔一小时,Redis就会把当前内存里的数据整个打包成一个文件,保存在硬盘上。这个过程就像给数据库拍一张完整的照片。源码中这个功能主要由rdb.c文件负责。第二种叫AOF(追加日志)。这种方式更细致,它不是拍照片,而是像写日记一样,把每一次修改数据的命令都记录下来,追加到一个文件末尾。这样即使服务器突然宕机,重启后只要把日记里的命令重新执行一遍,就能恢复数据。为了防止日记文件太大,Redis还会定期对日记进行压缩重写。通常,生产环境中会把这两种方式结合起来用,用RDB做定期的全量备份,用AOF保证两次备份之间的数据安全。这些持久化策略在Redis的配置文件中都可以灵活设置。
核心对象与通信协议
在Redis眼里,所有的数据,不管是字符串、列表还是哈希表,都被封装成一种统一的东西,叫做"Redis对象"。在源码里,这个对象的结构体(robj)有几个关键的字段。比如,字符串对象不仅存着字符本身,还会记录这个字符串的长度、剩余可用空间等信息,这样在进行追加操作时就能判断是否需要重新分配内存,非常高效。当客户端和Redis服务器通信时,它们使用的是RESP(Redis序列化协议)。这个协议设计得非常简单,人类几乎能直接读懂。比如,客户端发送一个命令,会以数组的形式打包,前面用星号*表示数组的元素个数。这种文本化的协议使得调试和开发第三方客户端都很容易。了解对象的结构和通信的协议,是理解Redis如何工作的重要一环。这些内容在《Redis设计与实现》以及源码的object.c、 networking.c等文件中都有深入的阐述。