架构机制
KeyDB
众所周知
线程模型
KeyDB 使用了SO_REUSEPORT 特性,多个线程可以绑定监听同个端口。- 每个
worker 线程做了cpu 绑核,读取数据也使用了SO_INCOMING_CPU 特性,指定cpu 接收数据。 - 解析协议之后每个线程都会去操作内存中的数据,由一把全局锁来控制多线程访问内存数据。
主线程其实也是一个
- 处理统计
- 客户端链接管理
db 数据的resize 和reshard - 处理
AOF Replication 主备同步Cluster 模式下的任务
Links 管理
在int iel; /* the event loop index we're registered with */
,用来表示链接属于哪个线程接管。
- clients_pending_write:线程专属的链表,维护同步给客户链接发送数据的队列
- clients_pending_asyncwrite:线程专属的链表,维护异步给客户链接发送数据的队列
- clients_to_close:全局链表,维护需要异步关闭的客户链接
分成同步和异步两个队列,是因为
同步发送的逻辑比较简单,都是在本线程中完成,以下图来说明如何同步给客户端发送数据:
一个链接的创建、接收数据、发送数据、释放链接都必须在同个线程执行。异步发送涉及到两个线程之间的交互。
int fdCmdWrite; //写管道
int fdCmdRead; //读管道
本地线程需要异步发送数据时,先检查AE_ASYNC_OP::CreateFileEvent
的操作,要求添加写
锁机制
struct ticket
{
uint16_t m_active; //解锁+1
uint16_t m_avail; //加锁+1
};
struct fastlock
{
volatile struct ticket m_ticket;
volatile int m_pidOwner; //当前解锁的线程id
volatile int m_depth; //当前线程重复加锁的次数
};
使用原子操作 __atomic_load_2,__atomic_fetch_add,__atomic_compare_exchange
来通过比较 m_active=m_avail
判断是否可以获取锁。
- try_lock:一次获取失败,直接返回
- lock:忙等,每
1024 * 1024
次忙等后使用sched_yield 主动交出cpu ,挪到cpu 的任务末尾等待执行。
在
Active-Replica
- 每个
replica 有个uuid 标志,用来去除环形复制 - 新增加
rreplay API ,将增量命令打包成rreplay 命令,带上本地的uuid - key,
value 加上时间戳版本号,作为冲突校验,如果本地有相同的key 且时间戳版本号大于同步过来的数据,新写入失败。采用当前时间戳向左移20 位,再加上后44 位自增的方式来获取key 的时间戳版本号。