42本地储存性能优化之道之IM实战为何微信qq聊天比我流畅的多SQLite数据库详解
发布日期:2022-03-23 00:20 点击次数:133
上篇文章详细讲了内存缓存加速在QQ微信中的使用,这篇文章重点分析微信QQ对于SQLite的使用和我们有所不同。
首先IM的两大场景一个会话列表一个是聊天详情页,会话列表上篇文章已用内存缓存进行了大幅度的优化。两大数据聊天记录和好友数据,好友数据也用内存加速了。那对于聊天详情页和聊天记录可以使用内存加速吗?答案是否定的,数据不符合之前说的两大要求:1,数据量可控,不大;2使用反复频繁,所以这方面很难用内存加速,只能靠数据库本身。
那么数据库本身有哪些可优化的点了?
其实之前SQLite详解那篇文章已经各方面提到过,下面逐条详细解释在怎么用在实际中
分表分表原理之前的文章讲的很清楚不赘述了,在IM系统中就是把不同的的会话的聊天记录分表存储,分表存储后,在进入聊天详情页后,查询聊天记录数据库扫描的数据量会大大减小,从而极大提升性能,优化了单个sql查询性能。分表原理简单,实现确实略麻烦,涉及到动态添加表,正常的数据库版本管理中,每版本的数据库表的数目是一定可预知的,但分表后是动态的不可预知的,所以这对业务实现上也是一个小难点。使用数据库也会变得复杂麻烦很多。
SQLite线程模式和数据库模式线程模式有3种:串行模式/单线程模式/多线程模式,Ios,Android默认多线程模式,这种模式下如果只要一个数据库连接不被多个线程同时使用就是安全的,否则就会报错。这也是不用框架操作数据库经常会犯的错误,这种情况下单例加锁连接或者换成串行模式可防止错误,但不能真正意义并发。修改线程模式即修改SQLITE_THREADSAFE这个参数,编译连接运行三个时期都可以修改。那如何实现真正意义上的并发了,首先sqlite的锁级别是表锁,这决定了写入一定是顺序的,所以所谓并发是指读写间并发或者读并发。达到这个先觉条件就是数据库模式要设置成wal模式。这里之前文章提过不再赘述,我阅读过微信团队一些文章,可以确定的是他们采用的是wal模式和多线程模式,不过主流开源框架都不支持,详细情况看之前文章,个人开源了一个android上支持wal多线程读写并发的orm框架可以参考,Ios没太多时间弄,有兴趣的人可以加我好友一起开发一套。这种多线程读写并发的使用数据库根本上提升了本地数据库并发性能,这也是微信QQ如此流畅的基石
索引索引对于单个sql数据库性能的重要性毋庸置疑,能带来几倍几十倍的提升,对于IM两大数用户信息,聊天记录,用户信息对用户id加索引毫无疑问,这里又有个小技巧,int类的索引比String类的索引效率高的多,所以对用户id是string的可以做映射运算转成数字存储。聊天记录用户id,时间这些经常用于查询的字段也应加上相应的索引,最好转成数字类型,可以看微信数据库基本索引大部分是数字类型。
事务和sql优化 多用事务批量插入修改删除,杜绝大表连接查询!
I/O性能优化完全无关的业务分库,减少单个数据库的文件大小保留WAL文件大小,开启WAL模式后,写入的数据会先append到WAL文件的末尾。待文件增长到一定长度后,SQLite会进行checkpoint。这个长度默认为1000个页大小,在iOS上约为4MB。checkpoint成功之后德州扑克游戏,会将WAL文件长度删除或truncate到0。下次打开数据库,并写入数据时,WAL文件需要重新增长。而对于文件系统来说,这就意味着需要消耗时间重新寻找合适的文件块。所以我们只要在数据库关闭并checkpoint成功时,不再truncate或删除WAL文件只修改WAL的文件头的Magic Number。下次数据库打开时,SQLite会识别到WAL文件不可用,重新从头开始写入。这样会大大提高io性能mmap优化,SQLite也在OS层封装了mmap的接口,可以无缝地切换mmap和普通的I/O接口使用PRAGMA mmap_size=XXX即可开启mmap。以上即是我在研究微信QQ本地数据库结合网上资料得出的结论,并应用到实际项目中,确实性能和之前不是一个量级,并且这些优化不光是在IM中有效,其它的业务场景同样可以运用到这些手段,市面上现有的SQLite框架从性能上确实远远不够,如果感兴趣的朋友可以联系我共同以这文章上描述的内容来打造一个高性能的SQLite ORM框架。