1. SQLite Vacuum
VACUUM命令通过复制主数据库中的内容到一个临时数据库文件,然后清空主数据库,并从副本中重新载入原始的数据库文件。这消除了空闲页,把表中的数据排列为连续的,另外会清理数据库文件结构。如果表中没有明确的整型主键(INTEGER PRIMARY KEY),VACUUM 命令可能会改变表中条目的行 ID(ROWID)。VACUUM 命令只适用于主数据库,附加的数据库文件是不可能使用 VACUUM 命令。如果有一个活动的事务,VACUUM 命令就会失败。VACUUM 命令是一个用于内存数据库的任何操作。由于 VACUUM 命令从头开始重新创建数据库文件,所以 VACUUM 也可以用于修改许多数据库特定的配置参数。
1.1 手动 VACUUM
1.1.1 在命令提示符中对整个数据库发出 VACUUM 命令的语法:
$sqlite3 database_name "VACUUM;"
1.1.2 您也可以在 SQLite 提示符中运行 VACUUM,如下所示:
sqlite> VACUUM;
1.1.3 您也可以在特定的表上运行 VACUUM,如下所示:
sqlite> VACUUM table_name;
1.1.4 调用sqlite3_exe()数据库系统API:
sqlte3_exe(..., "VACUUM table_name", ...);
2. Auto-VACUUM
2.1 SQLite 的 Auto-VACUUM 与 VACUUM 差异:
Auto-VACUUM它只是把空闲页移到数据库末尾,从而减小数据库大小。通过这样做,它可以明显地把数据库碎片化,而 VACUUM 则是反碎片化。所以 Auto-VACUUM 只会让数据库更小---???。
2.2 在 SQLite 提示符中,您可以通过下面的编译运行,启用/禁用 SQLite 的 Auto-VACUUM:
sqlite> PRAGMA auto_vacuum = NONE; -- 0 means disable auto vacuum
sqlite> PRAGMA auto_vacuum = INCREMENTAL; -- 1 means enable incremental vacuum
sqlite> PRAGMA auto_vacuum = FULL; -- 2 means enable full auto vacuum
您可以从命令提示符中运行下面的命令来检查 auto-vacuum 设置:
$sqlite3 database_name "PRAGMA auto_vacuum;"
3. 关于PRAGRM AUTO_VACUUM如何使用?
3.1 该命令的具体用法:
PRAGMA auto_vacuum = 0 或 NONE;
PRAGMA auto_vacuum = 1 或 FULL;
PRAGMA auto_vacuum = 2 或 INCREMENTAL;
备注:这里,0和NONE表示的含义相同。
3.2 AUTO_VACUUM命令详解:
PRAGMA auto_vacuum = 0 : AUTO_VACUUM命令缺省值为0,表示禁用AUTO_VACUUM。除非在编译的时候定义了宏SQLITE_DEFAULT_AUTOVACUUM。数据删除的时候,数据库大小不会改变。没用的数据库文件页面会被添加到freelist里头,用于将来重用。这时,使用VACUUM命令,可以重建整个数据库,以回收无用的磁盘空间。
PRAGMA auto_vacuum = 1: 所有的freelist页会被移动到文件末尾,每次事务提交的时候文件会被截短。注意,自动vacuum只是从文件是截断freelist页,并没有进行碎片重整等操作,也就是说,它没有VACUUM命令来得彻底。事实上,自动vacuum会让碎片更多。 只有在数据库存储某些附加信息的时候,它允许每个数据库页来跟踪它的引用页,自动vacuum才用得上。它必须在没有创建任何表的情况下启用。在一个表已经创建了之后,是不能启用和停用auto-vacuum的。
PRAGMA auto_vacuum = 2: 表示增量vacuum,意味着并不是在每次提交事务的时候自动vacuum,需要调用一个独立的incremental_vacuum语句来触发auto-vacuum。
数据库可以在1和2两种vacuum模式下进行切换。但是不能从none到full或incremental间切换。要想切换,要么数据库是全新的数据库(没有任何表),或者单独运行vacuum命令以后。改变自动vacuum模式,首先执行auto_vacuum语句设置新的模式,然后调用VACUUM来重整数据库。 不带参数的auto_vacuum语句返回当前的auto_vacuum模式值。
4. 用例
4.1 新建数据库
char *pszSqliteName = USER_DATA_SQLITE3_PATH"gwooDb";
iRet = sqlite3_open(pszSqliteName, &db);
if (iRet)
{
TRACE("can't open db!/n", sqlite3_errmsg(db));
sqlite3_close(db);
}
4.2 新建数据库表
//建表
char *sqlCmd = "CREATE TABLE RealTable1(\
StartTime TEXT(15),\
SuccTime TEXT(15),\
SaveTime TEXT(15),\
MtrAddr BLOB,\
x20000200 BLOB,\
x20010200 BLOB,\
x20020200 BLOB)";
iRet = sqlite3_exec(db, sqlCmd, 0, 0, &errmsg);
4.3 新建数据库表索引
char *sql="CREATE INDEX RealTable1_INDEX ON RealTable1(StartTime, SuccTime, SaveTime, MtrAddr)";
iRet = sqlite3_exec(db, sql, 0, 0, &errmsg);
上面的数据库表在增加索引后会导致数据库文件的空间增大一倍,实际在使用时一定要注意!!!
4.4 设置PRAGRM AUTO_VACCUM=0
char *sql = "PRAGRAM auto_vacuum = 0";
iRet = sqlite3_exec(db, sql, 0, 0, &errmsg);
4.5 增加事务接口
//描述:执行SQL命令
int ExecSqlCmd(sqlite3 *db, char *sql)
{
int iRet;
char *err_msg = 0;
char buf[1024];
if ((iRet = sqlite3_exec(db, sql, NULL, 0, &err_msg)) != SQLITE_OK)
sqlite3_free(err_msg);
memset(buf, 0, sizeof(buf));
sprintf(buf, "Exec sql: \"%s\", code:%d\n", sql, iRet);
TRACE("%s", buf);
return iRet;
}
//描述:开始事务处理
void BeginTransaction(sqlite3 *db)
{
ExecSqlCmd(db, "BEGIN TRANSACTION");
}
//描述:结束事务处理
void CommitTransaction(sqlite3 *db)
{
ExecSqlCmd(db, "COMMIT");
}
//描述:回滚事务处理
void RollBackTransaction(sqlite3 *db)
{
ExecSqlCmd(db, "ROLLBACK");
}
4.6 增加SQL执行时间打印(毫秒级别)
void StartTestSqliteTime()
{
TMillTime tMillTime;
char cStr[32];
WORD wStrLen;
memset(cStr, 0, sizeof(cStr));
GetCurMillTime(&tMillTime);
MillTimeToStr(tMillTime, cStr);
TRACE("Start test time=%s.\n", cStr);
}
void FinishTestSqliteTime()
{
TMillTime tMillTime;
char cStr[32];
WORD wStrLen;
memset(cStr, 0, sizeof(cStr));
GetCurMillTime(&tMillTime);
MillTimeToStr(tMillTime, cStr);
TRACE("Finish test time=%s.\n", cStr);
}
4.7 添加10万条记录
StartTestSqliteTime();
BeginTransaction(db);
for (int i=0; i<100000; i++)
{
char *insertData2 = "INSERT INTO RealTable1(StartTime, SuccTime, SaveTime, MtrAddr, x20020200) VALUES(\"20190104095030\", \"20190105095031\", \"20190105095030\", \"222222222222\", \"222222222222222222222222\")";
iRet = sqlite3_exec(db, insertData2, 0, 0, &errmsg);
//char *insertData3 = "INSERT INTO RealTable1 VALUES(\"20190106095030\", \"20190106095031\", \"20190106095030\", \"333333333333\", \"3333333333333333333\", \"444444444444444444\", \"55555555555555555\")";
//iRet = sqlite3_exec(db, insertData3, 0, 0, &errmsg);
}
CommitTransaction(db);
FinishTestSqliteTime();
4.8 删除部分记录
BeginTransaction(db);
char *sql = "DELETE FROM RealTable1 WHERE StartTime=\"20190104095030\"";
iRet = sqlite3_exec(db, sql, 0, 0, &errmsg);
CommitTransaction(db);
//RollBackTransaction(db);
4.9 删除数据库表
BeginTransaction(db);
char *sql = "DROP TABLE RealTable1";
iRet = sqlite3_exec(db, sql, 0, 0, &errmsg);
CommitTransaction(db);
//RollBackTransaction(db);
4.10 设置VACCUM,释放数据库文件空间
char *sql = "vacuum";
iRet = sqlite3_exec(db, sql, 0, 0, &errmsg);