Sqlite嵌入式内存数据库的优化
0. 场景及问题
目前网关项目在使用Sqlite的数据库存储数据,但是我们使用的方式和一般的数据库使用有区别,导致出现了两个问题:
- Sqlite3占用内存迅速增长,这种增长是cat /proc/fd号/statm 的显示增长,区别于: 数据库缓存导致的增长。
- 数据删除时间过长,影响了其他业务;
一般使用方式是需要数据库存储或查询数据时,使用sqlite3_open()打开数据库,使用完毕后sqlite3_close()关闭数据库。 我们项目使用的方式是一次使用sqlite3_open()打开后,周期性的往数据库中插入数据和查询数据,在整个程序运行结束时才去关闭数据库。
1. 问题1分析
1.1 原因
- 多次的插入和查询,未完全处理好内存释放;
- sqlite内部机制的问题,需要执行sqlite3_close()才能完全将申请的内存释放掉;
1.2 验证
只使用一次打开,只进行插入操作,保证使用的内存正常释放sqlite3_free(),发现内存还是增长(此处不贴代码,需要代码: )。使用周期关闭的方式,目前是10分钟关闭一次,发现内存基本稳定,但是原因可能需要查看Sqlite3的源代码。
2. 问题2分析
2.1 原因
如果网关是长时间正常运行的,按照程序的正常删除历史数据的周期是不会出现这个问题的,但是如果是已经正常运行了一段时间,然后关机一段时间,重新启动后会有大量的历史数据需要清理,导致删除时间过长(也和嵌入式CPU的处理速度相关),影响其他业务。
2.2 解决方案
2.2.1 方案一(低版本的sqlite也可以使用):
删除历史数据时限制删除的条数,此处只放Sql的执行语句。使用该方案,当然需要考虑多次进行删除,这个开发人员需要注意。
DELETE FROM Table_10_1 WHERE `Index` IN(SELECT `Index` FROM Table_10_1 WHERE julianday(now, localtime)-julianday(CreatedTime) >= 1 ORDER BY `Index` ASC limit 5000)
Table_10_1: 需要删除的表; Index: 根据列的名称删除,和后面查询的语句是相关的; CreatedTime: 存储时自动保存的时间,格式是“2022-05-11 17:18:14“ julianday(‘now’, ‘localtime’)-julianday(CreatedTime) >= 1:根据时间判断,删除超过一天的数据。 ORDER BY Index ASC limit 5000: 根据Index进行排序,限制删除5000条; 注意:该Sql语句中 Index 是 带单引号,是因为Index是一个关键字,我们把这个关键字当成了一个列名,普通的列名则不需要。
2.2.2 方案二(高于Sqlite3.6.4版本的才能使用,没测试成功)
Sqlite本身是提供直接删除限制行数的,但是没测试成功,原因可能是:1. 语法问题;2. 编译Sqlite是没增加SQLITE_ENABLE_UPDATE_DELETE_LIMIT 的编译选项。
DELETE FROM Table_10_1 WHERE (julianday(now, localtime)-julianday(CreatedTime) >= 1) ORDER BY `Index` ASC limit 4000;