使用 Sqlite3内存增长原因复盘

问题还原

0. 场景

  在做嵌入式产品时使用了Sqlite3的数据库,发现内存会异常增长,所有写了个Demo进行验证测试。此处仅提出个人的问题、分析和解决的方法,欢迎大神提出建议,共同进步。

1. 环境

CPU:A40i
架构:Cortex-A7
主频:1.2GHz
内存:2GB DDR3
ROM:8GB eMMC

2. 运行代码

创建表结构不在代码中体现,创建表方法:
CREATE TABLE Table_0_1 (
[Index] INTEGER PRIMARY KEY AUTOINCREMENT
UNIQUE,
Id INT NOT NULL,
Name TEXT (128) NOT NULL,
ValType INT NOT NULL,
Value TEXT (64),
Csq INT,
CreatedTime TIMESTAMP NOT NULL
DEFAULT (datetime(‘now’, ‘localtime’) )
);

代码V0.0版本:

  1. 打开数据库;
  2. 每秒执行一次插入动作;
  3. 关闭数据库
#include <iostream>

#include <stdint.h>
#include <sqlite3.h>

using namespace std;

bool g_bAppExitFalge = true;

static void exitSignalCall(const int32_t nSig)
{
   g_bAppExitFalge = false;
}

static int32_t registerSignal(void)
{
   signal(SIGINT, exitSignalCall);
   signal(SIGKILL, exitSignalCall);
   signal(SIGTERM, exitSignalCall);

   return 0;
}

int main(int argc, char const *argv[])
{
   sqlite3 *pDb = NULL;
   
   registerSignal();
    
   int32_t nRet = sqlite3_open("./MySqlite.db", &pDb);
   if (SQLITE_OK != nRet) {
       cout << "Open err!!!" << endl;
       return -1;
   }

   char szSql[128] = "INSERT INTO Table_0_1 VALUES (1,'TagTest',10,'100',1)";
   
   while(g_bAppExitFalge) {
       
   	nRet = sqlite3_exec(pDb, szSql, NULL, NULL, &errMsg);
       if (SQLITE_OK != nRet) {
           cout << "数据插入错误:" << errMsg << endl;
           sqlite3_free(errMsg);
           errMsg = NULL;
       } else {
           if (errMsg) {
               cout << "执行正常:" << errMsg << endl;
               sqlite3_free(errMsg);
               errMsg = NULL;
           }
       }
       sleep(1);
       
   }

   sqlite3_close(pDb);

   return 0;
}

3. 现象

内存会不断增长,直至将内存耗尽;

问题分析

分析1

  问题初期一直认为是Sqlite3库的问题,所有使用了更高版本的库,还是会出现该问题。所有将代码放到PC虚拟机中执行,发现不会出现内存泄露,会稳定在一个定值。所以分析可能的原因:

  1. Arm架构的A40i的性能问题,因为1秒的插入周期可能太短了,因为插入操作,Sqlite 数据库肯定是需要进行一系列的操作比如:I/O开销,事务的打开和关闭。CPU没办法尽快的将数据正确插入,在内存中缓存了插入数据;
  2. 写入数据库的位置是外置电子硬盘,速率不高;

所以使用复合的SQL语句,代码如下:

代码V1.0版本

#include <iostream>

#include <stdint.h>
#include <sqlite3.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>

using namespace std;

bool g_bAppExitFalge = true;

static void exitSignalCall(const int32_t nSig)
{
	g_bAppExitFalge = false;
}


/********************************************************************************************************************
 函数描述  : 注册信号处理函数
 参    数  : int32_t nSig: 触发信号
 返 回 值  : eSuccess: 成功		其他:失败
********************************************************************************************************************/
static int32_t registerSignal(void)
{
	signal(SIGINT, exitSignalCall);
	signal(SIGKILL, exitSignalCall);
	signal(SIGTERM, exitSignalCall);

	return 0;
}

int main(int argc, char const *argv[])
{
    
    sqlite3 *pDb = NULL;
	char *errMsg = NULL;
    int32_t nIndex = 199;

    registerSignal();

    int32_t nRet = 0; 
    nRet = sqlite3_open("/extp/DefineTag.db", &pDb);
    if (SQLITE_OK != nRet) {
        cout << "Open err!!!" << endl;
        return -1;
    }

    printf("Open every one...\n");
	char szSql[2048] = "INSERT INTO Table_0_1 (Id,Name,ValType,Value,Csq) "
        "SELECT 1,'TagTest',10,'100',1 "
        "UNION ALL SELECT 2,'TagTest',10,'100',1 "
        "UNION ALL SELECT 3,'TagTest',10,'100',1 "
        "UNION ALL SELECT 4,'TagTest',10,'100',1 "
        "UNION ALL SELECT 5,'TagTest',10,'100',1 "
        "UNION ALL SELECT 6,'TagTest',10,'100',1 "
        "UNION ALL SELECT 7,'TagTest',10,'100',1 "
        "UNION ALL SELECT 8,'TagTest',10,'100',1 "
        "UNION ALL SELECT 9,'TagTest',10,'100',1 "
        "UNION ALL SELECT 10,'TagTest',10,'100',1 "
        "UNION ALL SELECT 11,'TagTest',10,'100',1 "
        "UNION ALL SELECT 12,'TagTest',10,'100',1 "
        "UNION ALL SELECT 13,'TagTest',10,'100',1 "
        "UNION ALL SELECT 14,'TagTest',10,'100',1 "
        "UNION ALL SELECT 15,'TagTest',10,'100',1 "
        "UNION ALL SELECT 16,'TagTest',10,'100',1 "
        "UNION ALL SELECT 17,'TagTest',10,'100',1 "
        "UNION ALL SELECT 18,'TagTest',10,'100',1 "
        "UNION ALL SELECT 19,'TagTest',10,'100',1 "
        "UNION ALL SELECT 20,'TagTest',10,'100',1;";
    
    while(g_bAppExitFalge) {
        
		nRet = sqlite3_exec(pDb, szSql, NULL, NULL, &errMsg);
        if (SQLITE_OK != nRet) {
            cout << "数据插入错误:" << errMsg << endl;
            sqlite3_free(errMsg);
            errMsg = NULL;
        } else {
            if (errMsg) {
                cout << "执行正常:" << errMsg << endl;
                sqlite3_free(errMsg);
                errMsg = NULL;
            }
        }
        sleep(10);
	}

    printf("close success...\n");
    sqlite3_close(pDb);
    pDb = NULL;
    sleep(1);
    return 0;
}

结果

内存的消耗比V0.0版本的速度明显减慢,但是经过一晚上的运行,发现内存还是会增长。

分析2

针对于V1.0 版本的代码内存还是会增长的问题查资料,看到有人提出这样的分析,所有继续改良代码:

	---内存持续增加研究---
    
    原来,数据库是这样设计的:用内存保存数据,以提高增删查改的速度,同时把数据写入磁盘,让数据落地。
    如果不删除数据库里的数据,随着数据不断地添加到数据库,数据库越来越大,RES内存也越来越大。
    
    开启如下的删除数据库数据的功能代码后,虽然不断地有新数据添加到数据库,但也一直从数据库中删除着数据。
    数据库的数据总量一直保持一定数据的总量,VIRT、RES、SHR长期保存不变。
    
    所以,数据库里已经使用过的数据,应该尽量删除。
    或者,每次操作数据库的数据后,要关闭数据库连接,下一次要操作数据库的数据时,再打开连接。
    即重复如下逻辑:"打开数据->操作数据库数据->关闭数据库"。
    
    当然,数据库文件也要定期缩减。因为即使你删除了数据库的数据,sqlite3也不会自动缩减数据库。
    所以,需要定期执行sql缩减数据库文件命令:"vacuum;"。

代码V2.0 版本

  1. 定期执行数据库缩减;
  2. 定期删除数据,使数据量总数保持一定;
#include <iostream>

#include <stdint.h>
#include <sqlite3.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>

using namespace std;

bool g_bAppExitFalge = true;

static void exitSignalCall(const int32_t nSig)
{
	g_bAppExitFalge = false;
}


/********************************************************************************************************************
 函数描述  : 注册信号处理函数
 参    数  : int32_t nSig: 触发信号
 返 回 值  : eSuccess: 成功		其他:失败
********************************************************************************************************************/
static int32_t registerSignal(void)
{
	signal(SIGINT, exitSignalCall);
	signal(SIGKILL, exitSignalCall);
	signal(SIGTERM, exitSignalCall);

	return 0;
}

int main(int argc, char const *argv[])
{
    
    sqlite3 *pDb = NULL;
	char *errMsg = NULL;
    int32_t nIndex = 199;

    registerSignal();

    int32_t nRet = 0; 
    nRet = sqlite3_open("/extp/DefineTag.db", &pDb);
    if (SQLITE_OK != nRet) {
        cout << "Open err!!!" << endl;
        return -1;
    }

    printf("Open every one...\n");
	char szSql[2048] = "INSERT INTO Table_0_1 (Id,Name,ValType,Value,Csq) "
        "SELECT 1,'TagTest',10,'100',1 "
        "UNION ALL SELECT 2,'TagTest',10,'100',1 "
        "UNION ALL SELECT 3,'TagTest',10,'100',1 "
        "UNION ALL SELECT 4,'TagTest',10,'100',1 "
        "UNION ALL SELECT 5,'TagTest',10,'100',1 "
        "UNION ALL SELECT 6,'TagTest',10,'100',1 "
        "UNION ALL SELECT 7,'TagTest',10,'100',1 "
        "UNION ALL SELECT 8,'TagTest',10,'100',1 "
        "UNION ALL SELECT 9,'TagTest',10,'100',1 "
        "UNION ALL SELECT 10,'TagTest',10,'100',1 "
        "UNION ALL SELECT 11,'TagTest',10,'100',1 "
        "UNION ALL SELECT 12,'TagTest',10,'100',1 "
        "UNION ALL SELECT 13,'TagTest',10,'100',1 "
        "UNION ALL SELECT 14,'TagTest',10,'100',1 "
        "UNION ALL SELECT 15,'TagTest',10,'100',1 "
        "UNION ALL SELECT 16,'TagTest',10,'100',1 "
        "UNION ALL SELECT 17,'TagTest',10,'100',1 "
        "UNION ALL SELECT 18,'TagTest',10,'100',1 "
        "UNION ALL SELECT 19,'TagTest',10,'100',1 "
        "UNION ALL SELECT 20,'TagTest',10,'100',1;";
    
    while(g_bAppExitFalge) {
        
		nRet = sqlite3_exec(pDb, szSql, NULL, NULL, &errMsg);
        if (SQLITE_OK != nRet) {
            cout << "数据插入错误:" << errMsg << endl;
            sqlite3_free(errMsg);
            errMsg = NULL;
        } else {
            if (errMsg) {
                cout << "执行正常:" << errMsg << endl;
                sqlite3_free(errMsg);
                errMsg = NULL;
            }
        }
        sleep(10);

#if 1
        nIndex++;
        if (nIndex > 200) {
            
            cout << "开始执行删除" << endl;
            nRet = sqlite3_exec(pDb, "DELETE FROM Table_0_1 "\
                "WHERE julianday('now', 'localtime')-julianday(CreatedTime) >= 0.05;", \
                NULL, NULL, &errMsg);
            if (SQLITE_OK != nRet) {
                cout << "执行删除错误:" << errMsg << endl;
                sqlite3_free(errMsg);
            } else {
                cout << "执行删除成功:" << endl;
            }

            cout << "开始执行VACUUM:" << endl;
            nRet = sqlite3_exec(pDb, "VACUUM;", NULL, NULL, &errMsg);
            if (SQLITE_OK != nRet) {
                cout << "执行VACUUM错误:" << errMsg << endl;
                sqlite3_free(errMsg);
            } else {
                cout << "执行VACUUM成功:" << endl;
            }
            nIndex = 0;
        }
#endif

	  
	}

    printf("close success...\n");
    sqlite3_close(pDb);
    pDb = NULL;
    sleep(1);
    return 0;
}

结果

  1. 在做嵌入式场景时尽量降低数据插入的频率,如果确认有大量数据需要插入,采用复合语句;
  2. 对数据库的及时缩减;
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值