Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kiwi 线程模型,以及改进方案 #87

Open
lqxhub opened this issue Nov 25, 2024 · 9 comments
Open

kiwi 线程模型,以及改进方案 #87

lqxhub opened this issue Nov 25, 2024 · 9 comments

Comments

@lqxhub
Copy link
Collaborator

lqxhub commented Nov 25, 2024

线程模型

无标题-2024-01-13-2209

这是目前的线程模型,分为IO线程和worker线程,其中

IO线程可以分为 读线程写线程

worker线程分为 命令线程

如果没有开启读写分离,那么IO线程就只有读线程,此时读线程也会处理server向client写数据的功能


当前的命令处理逻辑

当一个client 使用 tcp连接的server时,这个client会被操作系统内核分配到一个IO线程(fd会分别放到对应的IO读和IO写线程)

当client向server发送数据的时候,会在IO读线程处理,然后解析resp协议,把得到的参数包装成一个 task 然后发到 worker线程池中
现在没有区分快慢命令,所有的命令(除了pipeline的命令)都会放到快线程中,pipeline的一组命令会放到 慢线程中

然后在对应的worker线程中,或者对应的命令对象,然后处理命令(操作rocksdb)

命令处理完成后,把数据放到 IO写线程中,然后发回client中

现在每个worker线程中到会有一份 命令对象,也就是 CmdTable 这个结构。


问题

  • 首先是内存浪费问题,如果线程池特别大,会有很多份命令对象
  • 命令无法在 IO线程处理,如果是一些简单的命令,比如不涉及读写 rocksdb的命令可以直接在IO线程中处理,没必要放到线程池中执行。如果引入缓存层,读写缓存也可以在IO线程完成

改进设想

在IO线程就能获取命令对象,并把命令对象放到 task 中以前传到线程池中

面临问题

  1. 如果每个IO线程都有一份 CmdTable 一样会有内存占用问题
  2. 如果使用全局唯一的 CmdTable 会有多线程锁竞争
  3. 每次执行命令初始化初始化一个 CmdTable 会频繁 new delete
  4. 全局 CmdTable + 线程缓存,实现复杂,功能太复杂了,性能不一定会提升

还请集思广益,想一个好的实现方案

@lqxhub lqxhub changed the title kiwi 线程模型,已经改进方案 kiwi 线程模型,以及改进方案 Nov 25, 2024
@Iam-WenYi
Copy link
Collaborator

我建议先把这种模型的设计依据先提供出来,项目现在一个很大的问题就是团队内部对各自的工作都不是很了解(这点在我去请教的时候有非常深刻的体会),然后再思考着改进。

个人对于 Kiwi 的主要矛盾与次要矛盾的分析是:主要矛盾在于 Kiwi 与 BRaft 的结合(这是它实现分布式的方法),次要矛盾在于 Kiwi 对于 Redis 指令的兼容方法,其它都是次要(网络库等我认为不是很重要的事情)。

@Iam-WenYi
Copy link
Collaborator

http://mysql.taobao.org/monthly/2019/02/09/ 看一看这篇文章

@Iam-WenYi
Copy link
Collaborator

@hahahashen
Copy link
Collaborator

hahahashen commented Dec 13, 2024

就我的理解来说,目前CmdTableManager是张预创建了每个cmd类指针的大表,为什么不把命令对象的创建放在命令执行的时候,需要执行什么样的命令就创建什么样的命令对象,命令执行完毕后销毁
这样CmdTableManager是查表的时候用的,告诉我们什么样的命令应该创建什么样的命令对象,只会有读CmdTableManager对象而不会有写CmdTableManager对象的行为存在,全局唯一实例,访问可以上读锁甚至不上锁?

@lqxhub
Copy link
Collaborator Author

lqxhub commented Dec 13, 2024

就我的理解来说,目前CmdTableManager是张预创建了每个cmd类指针的大表,为什么不把命令的创建放在命令执行的时候,需要执行什么样的命令就创建什么样的命令对象,命令执行完毕后销毁
这样CmdTableManager是查表的时候用的,告诉我们什么样的命令应该创建什么样的命令对象,只会有读CmdTableManager对象而不会有写CmdTableManager对象的行为存在,全局唯一实例,访问可以上读锁甚至不上锁?

频繁的创建和销毁对象是一个相对耗时的操作,还有对于服务这种长时间运行来说,频繁new delete会产生内存碎片。使用jemalloc这样的库能缓解,但不是最优解。
比较好的方案是实现是个类似go中sync.pool这样的结构,复用对象,重复的使用lru淘汰。这样库的问题是,不能按key分类型缓存。

@AlexStocks
Copy link
Contributor

“频繁的创建和销毁对象是一个相对耗时的操作” 这个理论上当然重要,但是建议不要当做主要任务,避免“过早优化”。

@loveyacper
Copy link
Collaborator

worker thread为啥分为快和慢两种,以什么为标准呢?某一个快命令,长尾值可能又比较慢呢?

@AlexStocks
Copy link
Contributor

AlexStocks commented Dec 19, 2024

较早的一些关于 kiwi 的线程池 doc
PikiwiDB 线程模型 OpenAtomFoundation/pikiwidb#362

PikiwiDB 网络层优化 OpenAtomFoundation/pikiwidb#223
image

@lqxhub
Copy link
Collaborator Author

lqxhub commented Dec 21, 2024

image

新想了一种线程模型, 和现在redis的多线程类似, 核心思想是尽量减少线程切换

有一个或者多个 netThread 这个线程 listen 端口, 并且维持和client的连接, 但是这个线程不支持参数网络数据的读写

每次触发了 网络事件(比如可读或者可写) 后,会把所有就绪的 fd 分配到线程池中的 worker 线程上,

每个线程得到 fd后, 负责读取读取数据, 操作 rocksdb, 完成后再把数据写回网络

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants