
上述为opCache缓存架构图,具体写流程如下:
1,客户端通过连接池连接
2,进入解析器对请求解析(解析出是set还是get或者delete等)
3,进入执行器执行
4,将数据写入到buffer,并且新增日志事件到log buffer(buffer pool和log buffer有checkpoint刷盘机制),响应立即返回。
5,当buffer pool满了或者到一定触发时刻会将buffer pool数据先刷到immutable(内存)中(在该空间中的数据,不可修改)
6,刷盘线程会将immutable中的数据刷到磁盘,此处刷盘分了层次(LSM架构),分为level0层、level1层、level2层等,每层有各自限制大小,当上一层满了后,会触发下沉刷盘机制,将上一层的数据下刷到下一层去。
7,level0层比较特殊,是immutable的全数据,会对key去重,也即会出现多个相同key的不同版本,其余level层的key本层唯一
具体读流程如下:
1,客户端通过连接池连接
2,解析器解析
3,执行器处理
4,查询buffer pool中是否有该key,如果没有,则查询immutable中是否存在该key
5,查询level0中是否存在该key(在该层查询出多个key的版本时,以最新版本的为准)
6,查询level1中是否有该key(以此类推)
Ps:这样一层层查询,怎么判断该key是否在该层?这里会使用布隆过滤器处理。
2,疑难点解答
编写语言为什么选择golang?
主要是之前我开源的rpc框架和MQ都是用golang写的,而且golang做并发有天然优势。
buffer pool也是存内存的,数据量大了,比如百万时,会很慢,有怎么考虑?
确实会有这问题,golang的GC机制三色法,会对变量进行扫描处理,但好的是11版本后,对map已经进行了优化,如果map的类型是基本类型不是引用,则GC不会扫描map的元素,所以1.0版我用的是map[string]int,这样可避免GC扫描。
Map你存的数据对吧?可你那里是int,怎么存具体的key-value键值对的?
对,这里的map存的是int,也即是map的key是字符串,value是int,这里其实用了一个大的全局数组来存放真正的对应键值对(value会被转成字节数组),然后map中存放的则是该全局数组中该key的下标位置。
1.0版有做什么性能方面考虑吗?比如怎么提升读写并发?
1.0版就已经做了读写并发的考虑,因为这也为后续的分布式部署打下基础。简单来说就是 shard分片。读写请求进来后,会对key进行hash加密计算出数值,然后进行分片路由,判断路由到哪一个分片上面,每一个shard分片都有一个对应的map和全局数据,这样可以提升读写并发。
当前1.0进度如何?规划是什么?
目前1.0初版已完成,码云地址https://gitee.com/opjesus/op_cache,目前支持简单的set,get(http处理器未编写),目前存在问题是,全局数组是有大小限制的,对同一个key进行set时,是会在全局数组末尾新增元素,不会去删除之前key的值,那么怎么让之前的key过期?后面会将读写buffer独立开,分成buffer pool(读缓冲)和change buffer(写缓冲),buffer pool会使用新老生代替换方式淘汰机制,change buffer则缓存修改命令。




