1. mmap高性能存取的基石
MMKV通过mmap 内存映射文件来进行读写操作的,这是其效率高于普通IO的原因。
普通IO
传统的read首先将文件内容从硬盘拷贝到内核空间的一个缓冲区,然后再将这些数据拷贝到用户空间,这个过程中,实际上完成了。
内存映射
mmap将文件直接映射到用户空间,所以中断处理函数根据这个映射关系,直接将文件从硬盘拷贝到用户空间,只进行了 ,因此,mmap内存映射的效率要比read/write效率高。
为什么不直接用内存映射代替IO?
既然MMKV使用的内存映射优于IO,为什么还要使用IO?
首先要明白,直接将文件映射到虚拟内存,意味着没有数据没有缓存在内核缓存空间,而是直接读到了用户空间,而系统的IO和内核缓存搭配可以使得部分的文件使用效率更高。(OS会根据局部性原理在一次read()系统调用的时候预读取更多的文件数据到内核空间缓冲区中,这样当下一次read()系统调用的时候发现要读取的数据已经存在于内核空间缓冲区中的时候只要直接拷贝数据到用户空间缓冲区中即可,无需再进行一次低效的磁盘I/O操作,且磁盘的大小要远远超过内存)
而且mmap映射的文件是大于一个内存页大小的(),并且是。
也就是说两个方式都是有优缺点的,所以不存在代替这个说法,只能通过分析其场景而选择不同的方式。
2.采用Protobuf协议存储key-value结构
Protobuf协议
protobuf 是google开源的一个序列化框架,类似xml,json,最大的特点是基于二进制,比SharedPreferences使用的传统的XML表示同样一段内容要短小得多。同样这也不能说明Protobuf优于XML,关于Protobuf的更多内容如下:
写入优化
标准 protobuf 和SharedPreferences 一样,每次写入kv对象都必须全量写入。也就是写入之前将所有数据加载到内存中,然后判断新增的key是否已经存在,完成更新或增加后在全部写入文件。
MMKV中采用增量更新的方式处理protobuf,当需要写入kv对象时,不论是新增还是更新都将其直接加入文件的末尾,这样大大增加了写入效率。
上面的做法必然带来两个问题
1.必然导致同一key值会有新旧若干份数据,最新的数据在最后。
2.文件大小会增长得不可控。同一个 key 不断更新的话,是可能耗尽几百 M 甚至上 G 空间。
针对第一个问题,在读取时,针对同一个 key使用后读入的 value 替换之前的值,就可以保证数据是最新有效的。
针对第二个问题,有上文可知MMKV的文件必然是稍大于(一个内存页的大小)的倍数,当写入的数据小于4k时,可以继续写入,因为本身文件大小就已经略大于4k了,有点很小的浪费,当写入数据超过4k的倍数后,进行文件重整、key 排重,尝试序列化保存排重结果;排重后空间还是不够用的话,将文件在增加4k,直到空间足够。
3.通过crc 校验确保数据有效性
文件系统、操作系统都有一定的不稳定性,MMKV使用crc 校验确保数据有效性,关于crc 校验,可以参考:
4.多进程设计与实现
这一步官方有详尽的说明,如下:
多进程设计与实现
网友评论