发布时间:2022-08-19 12:40
随着时间的增加,存储的历史记录也在不断增加,如果设备数量很多,存储间隔很短,不用多久,数据库中的记录就非常多,至少是百万级别起步,而且有些用户还是需要存储每一次的采集的数据,这数据量别说一年,就是一个月下来都是恐怖级别的,所以这就涉及到一个重要的需求,如何自动清理早期的不需要的数据,比如只保存最近10万条记录,或者保存最近30天的记录,这就需要安排个线程,在线程中打开数据库以后,每隔一段时间去查询记录数量,超过了设定的最大值,则按照时间顺序把早期的数据删除,其实就是执行一个sql语句。如果设置的是只存储最近30天的记录,则每隔一段时间执行删除sql语句,带上条件where 时间<(今天-30)。由于是在线程中打开的数据库,所以在线程中执行的sql语句都不会对主界面使用的数据库相关处理造成卡顿。
光有数据记录清理可能还是不够的,比如系统还不断的在存储报警图片或者其他数据文件,由于硬盘的大小有限,也需要一个机制做清理,尤其是对于视频监控系统尤为重要。由于和数据库记录清理功能类似,而且数据库清理线程99.99%的时间都是空余的,就算是到了需要清理的时候,也是一次性清理多条,也不会频繁的在清理中,为了不让这个线程闲着,直接也把清理文件的机制也放到了这个类,指定要监听的目录,指定最大的大小,每隔一段时间读取下大小,超过了则清理早期的文件。
int DbCleanThread::getCount()
{
int count = -1;
if (!dbOk) {
return count;
}
time.restart();
QString sql = QString("select count(%1) from %2").arg(countName).arg(tableName);
QSqlQuery query(database);
if (query.exec(sql)) {
if (query.next()) {
count = query.value(0).toInt();
QString msg = QString("(共 %1 条/用时 %2 秒)").arg(count).arg(getUseTime());
emit debug(QString("%1数据库获取记录行数%2").arg(dbFlag).arg(msg));
emit receiveCount(tableName, count, time.elapsed());
}
}
return count;
}
QStringList DbCleanThread::getCleanValue(int cleanCount)
{
QStringList list;
if (!dbOk) {
return list;
}
QSqlQuery query(database);
query.setForwardOnly(true);
QString sql = DbHelper::getSelectCountSql(dbType, tableName, whereColumnName, "", orderSql, cleanCount);
if (query.exec(sql)) {
while (query.next()) {
list << query.value(0).toString();
}
}
return list;
}
void DbCleanThread::cleanData()
{
if (!dbOk) {
return;
}
//首先查找总记录数,如果总记录数超过限制,则将超出的部分按照字段排序进行删除
int count = getCount();
int cleanCount = (count - maxCount);
if (cleanCount < 100) {
return;
}
time.restart();
//每次最大清理1000条数据
cleanCount = cleanCount > 1000 ? 1000 : cleanCount;
//将要删除的数据指定字段集合查询出来
QStringList list = getCleanValue(cleanCount);
if (list.count() == 0) {
return;
}
//删除数据
QSqlQuery query(database);
QString sql = QString("delete from %1 where %2 in(%3)").arg(tableName).arg(whereColumnName).arg(list.join(","));
dbOk = query.exec(sql);
//qDebug() << TIMEMS << sql;
QString msg = QString("(共 %1 条/用时 %2 秒)").arg(cleanCount).arg(getUseTime());
if (dbOk) {
emit debug(QString("%1数据库清理数据成功%2").arg(dbFlag).arg(msg));
} else {
QString text = database.lastError().text();
emit error(QString("%1数据库清理数据失败%2, 原因: %3").arg(dbFlag).arg(msg).arg(text));
qDebug() << TIMEMS << this->objectName() << text;
}
}
void DbCleanThread::cleanPath()
{
if (dirPath.isEmpty()) {
return;
}
//找出该文件夹下的所有文件夹
QDir dir(dirPath);
if (!dir.exists()) {
return;
}
//按照目录查找,过滤文件夹,按照文件名称排序
dir.setFilter(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot);
dir.setSorting(QDir::Name);
QStringList list = dir.entryList();
//遍历所有目录,对所有文件大小相加得到总大小,文件就在文件夹下,不会再有子目录
qint64 size = 0;
foreach (QString path, list) {
QDir d(dirPath + "/" + path);
QFileInfoList infos = d.entryInfoList(dirFileFilter);
foreach (QFileInfo info, infos) {
size += info.size();
}
//转化成MB,超过预定大小自动删除第一个文件夹,跳出循环无需继续判断
int sizeMB = size / (1024 * 1024);
if (sizeMB >= dirMaxSize) {
//删除该目录下的所有文件
QString path = dirPath + "/" + list.at(0);
QDir dir(path);
dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot);
QStringList files = dir.entryList();
foreach (QString file, files) {
dir.remove(file);
qDebug() << TIMEMS << "删除文件" << path << file;
}
//删除文件夹本身
dir.rmdir(path);
QString msg = QString("(共 %1 个文件/用时 %2 秒)").arg(files.count()).arg(getUseTime());
emit debug(QString("%1数据库自动清理目录成功%2").arg(dbFlag).arg(msg));
break;
}
}
}
void DbCleanThread::deletePath(const QString &path)
{
QDir dir(path);
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))
//这个方法可以递归彻底删除文件夹 不管文件夹下是否有文件 比较暴力
//此方法慎用 必须指定明确的文件夹 不然删除默认的目录哭都来不及 网上多个人中招
dir.removeRecursively();
#else
//循环遍历删除文件及文件夹
dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot);
QFileInfoList fileList = dir.entryInfoList();
foreach (QFileInfo fi, fileList) {
if (fi.isFile()) {
fi.dir().remove(fi.fileName());
} else {
deletePath(fi.absoluteFilePath());
dir.rmpath(fi.absoluteFilePath());
}
}
//最后删除最外层的目录
dir.rmpath(path);
#endif
}
一文搞懂Vue3中的异步组件defineAsyncComponentAPI的用法
【kaldi】chain-model的TCP server部署
视频教程-java网上书城|美妆商城|电商系统项目实战含微信支付功能-Java
【YOLOV5-6.x中文注释版】整体项目代码全中文注释导航页面-By2022
【JavaSE】面试高频String、StringBuffer、 StringBuilder坑点总结刨析
官宣:2021中国开源年报震撼首发~一篇报告带你读懂中国开源的2021~
【tph-yolov5】使用tph-Yolov5训练自己的数据集