-
Notifications
You must be signed in to change notification settings - Fork 0
Description
在数据库中用到 buffer 的地方很多(比如最常见的网络输入输出 buffer、redo log buffer),并且是高频使用,实现得不好也严重影响性能。lealone 直到今天才完美解决 buffer 管理的问题,前后用了4种方案:
最开始使用全局的 buffer pool,里面用一个支持多线程并发访问的队列实现,池里的 buffer 大小不是固定的,会先按需求的容量大小从队列里找到一个最适合的 buffer,用完后再放回池中。这个方案的缺点是多线程访问并不高效;另外 buffer 的大小不一样,有的 buffer 用不到,有的被拿去用了但是依然有空闲空间浪费了。
为了解决方案1的问题,每个调度线程使用自己的 buffer pool,这样就不需要做并发控制了,只有单个线程访问,队列可以用普通的链表来实现。这个方案虽然访问队列快了,但是依然有出队和入队的开销,并且 buffer 内存空间浪费的问题也没有解决。
不使用 buffer pool 了,给每条 tcp 连接分配两个专属 buffer,每个 buffer 默认是 4k,这样就没有访问队列的开销了,处理完一次请求后,两个 buffer 都能快速复位,所以也不浪费内存空间。这个方案的唯一缺点就是不能支持太多条 tcp 长连接,一旦数量多了依然很占用内存。
这是目前的终极方案,给每个调度线程各分配两个专属 buffer,不给每条 tcp 连接分配 buffer 了。调度线程负责的所有 tcp 连接都使用调度线程的 buffer,每条 tcp 连接的输入输出包按顺序存放到调度线程的 输入包 buffer 和 输出包 buffer 中,调度线程解析完这些输入包或把输出包写到 socket 后,只需要把 buffer 的 pos 和 limit 设置一下就能复用了。不但没有内存碎片,也没有方案1和方案2访问队列的开销,也不像方案3那样 tcp 连接多了浪费内存,真的就是完美的解决方案。
这里的理解还是针对旧方案。
新方案应该是真的11月4日的所有的tcp连接都共用调度器的全局buffer开始的一系列实现。这次的学习就从这里先开始吧。之前的思路依然可以借鉴——
- lealone的启动过程,包含了server服务的启动
- client端如何与server端建立连接
- 连接创建之后如何传输数据
这三步完成,才可以看到buffer中网络传输中的使用场景。结果使用场景看buffer是如何管理的。