自己实现数据库(3)BufferPool
BufferPool 在数据的重要性不证明自明,素有的数据IO 都需通过BufferPool 来获取,同时系统也要保证多个线程并发访问的正确性,已经也要负责将BufferPool 的数据进行flush 到磁盘持久化的过程
BufferPool 的基本变量
|
pageSize 每一页的大小,数据库在读取数据的时候都是通过最小单位page 进行读取
pgBufferPool 里面存储了 pageId,page 的映射关系
lockMgr lock 是用于在多线程下面进行访问的时候对锁的管理
获取pgae
|
getPage 是通过 事务的id(TransactionId) ,pageId,读写权限(就是获取锁那种类型的锁,共享or 排它)
首先通过lockMgr,获取lock(lockMgr 等会再说)
通过pageId 从 pgBufferPool 获取 page
如果pgBufferPool 里面没有当前的pageId,同时pgBufferPool 已经满了,则需要淘汰一部分page,否则通过 DbFile(之前说过了)获取一个page,然后在放入到buffer pool 里面
插入数据
|
插入一个数据 一按照一个tuple 进行插入的,逻辑还是比较清楚简单的
- 获取的dbfile
- 通过db file 获取需要变更的 page
- 需要变更的page 从 buffer pool 里面移除,这里需要将page 标记为dirty,因为flush 是通过判断是和否进行了更改,从而判断是和否需要数据落盘
- 同时也需要更新buffer pool 里面的page 等待后面的flush
flush 数据
|
flushPage,就是将page flush 到磁盘
- 通过 pageId,从buffer pool 里面获取 page
- 判断page 的标记文dirty,是否存在(每一次更改会更改这个bit 位)
- 然后写入到getLogFile 记录 log, 之后调用force 进行存储到磁盘
- 最后写入到DbFile 里面
lock
|
lock 使用了一个 ConcurrentHashMap
同时锁分为 SLock XLock 分别为 共享锁,排它锁 就读写锁 读写冲突,写写冲突
ObjLock 里面使用了 一个list 用于存储那些transaction 正在使用它的lock
|
获取锁的流程
通过 pageId 获取lockTable 里面的 ObjLock 对象
判断lock 的类型 读or 写
如果是共享锁,调用 objlock 里面的addHolder 将 当前的transactionId 添加到list 里面 然后返回
如果是写锁,需要进行判断 holder 里面的transaction id 与当前的tranasactionid 进行比较,如果是当前的事务,则需要进行锁的升级(有可能之前的事务,是read lock,现在需要写数据,所以需要进行一个锁升级)
如果 当前的lock的transaction 与当前的transaction 不等,或者已经有其他的写死已经在holder 里面则,需要等待 block,block 的方法其实就是调用一个wait 方法
释放锁
释放比较简单,将obj lock 里面的holder 移除,如果里面还有其他的lock,则调用notifyall 对之前的 wait 进行唤醒