缘起
最近公司的第一个PHP转GO项目已经在生产环境稳定运行数周,又逢需求小年儿,最近可以得空分享下去年学GO过程中的练手项目Godis——用Golang实现的Redis.
Redis3.0版本,代码简明精炼,再加上是Web后端程序员使用最多组件之一,熟悉Redis原理并阅读多源码的开发者人数颇多,这个系列小文便不再对Redis细节做过多介绍。不过,有必要系统性说明的地方仍然会以较大篇幅尝试解读。
进入正题
基本流程
Godis第一版的目标是“最基本的kv缓存”,feature list如下:
- 客户端/服务端交互
- set/get 命令实现
- AOF持久化实现
已经做到,再精简就等于没写的境界。遵循实际工作中的编码流程,先设计基本架构再填充实现的方式,Godis的架构图一步到位、毫无点缀:
原理分析
1. 客户端/服务端交互
客户端与服务端通过建立网络连接,发送、处理、返回数据给对方,完成通信。Redis的单机应用中,一个服务端redis-server进程可以处理多个客户端的请求。
客户端需要一个数据结构来保存信息,接收命令,维持和服务端的连接,与服务端进行一对一的交互。
客户端具体需要哪些信息,暂且不表。
服务端为了响应多个客户端的请求,对数据进行查询、存储、更新、删除操作,也需要一个结构来保存基本信息,包括数据本身、正在连接中的客户端等。
客户端和服务端通过这两个基本数据结构,便可以在建立连接(可以简化为socket demo)之后,保存自身和对方的必要信息,维持之后的交互。
从原理分析入手,使用下图所示的结构体,可以满足存储客户端、服务端的数据存储要求:
client并非是我们用来和redis-server交互的client,而是与redis-server建立连接后,服务端在服务器创建的、用来存储当前连接的结构。与redis-server建立连接的客户端什么样,redis-server不关心,毕竟与之交互的都是协议而已。
由图,client和server结构体均有Db字段,不同的是,server.Db指向的是0号db(Redis支持多db,可以自行查阅了解);client.Db指向的是正在连接的db。如果有select切换操作,该指向也会随之变化。
2. set/get 命令实现
set、get 命令是redis最常用的命令之一,也是最能反映缓存发展历史的操作。对最简单命令代码的阅读,可以看到Redis最核心的原理。
set命令将数据以k-v键值对,保存到数据库,也就是redis-server占用的内存中,并且任何连接到此Redis服务器的客户端,都可以通过get命令查询到。
上一小节提到,保存服务器相关的信息需要一个结构体,这里set命令保存的数据,也存在这个结构体中。不过,存储的是数据的指针。
所以,set/get的实现原理可以简化为,在服务器数据结构中保存set命令的数据,get命令执行时,也从这个数据结构中查找。
set、get命令在客户端接收之后,经由协议转换传递给服务端执行。服务端执行命令前先查询是否支持该命令,以决定是否执行。所以server结构体还需要有个commands字段,记录支持的命令列表。
3. AOF持久化实现
set命令保存的数据不能一直在内存中,万一宕机或者硬件故障,数据岂不是烟消云散?
这就需要持久化技术,这也是存储领域的一大关键技术。AOF,是Append Only File的简称,代表的是“只存增量”的持久化方式。在Godis v1.0版本中,将以最简单的方式实现AOF持久化,做到下次开机可以查到上次set的数据 :)
持久化不应该对所有命令一视同仁,减少没必要的执行开销。在server中增加dirty字段,标记数据是否已经被污染,再决定是否持久化。
数据结构关联
经过如上说明,这里还有一幅Godis v1.0版数据结构图:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。