C++中SQLite与文件系统IO读写性能比较

2023-02-08 15:30:44 3914人已围观 62已点赞 24人已收藏

简介本文向大家介绍一个C++实战项目:C++中SQLite与文件系统I0读写性能比较,主要介绍SQLite数据库的优缺点,以及在同样条件下SQLite与文件系统IO读写性能比较,具有一定的C++实战价值,感兴趣的朋友可以参考一下。

1、SQLite优点

SQLite是一个C语言库,它实现了一个小型、快速、自包含、高可靠性、全功能的SQL数据库引擎。SQLite 读写效率比较高,有如下优点:

  • 支持Limit语句,支持数据自动分页 ,分页场景比较方便
  • 支持Insert返回自动递增主键的ID
  • 支持数据混搭(一列中既有数值,也有文本)
  • 查询性能和效率高
  • 支持内存数据库
  • 支持压缩命令
  • 跨平台,不只局限于Windows平台
  • 代码开源
  • 支持动态建立数据库功能

主要优点总结: 轻量 高效 跨平台 单文件 开源

2、SQLite缺点

  • 它允许多个读,但是一次只允许一个写,数据量大时并发有局限性
  • 虽然SQLite数据库可以通过网络文件系统共享,但是与这种文件系统相关的潜在延时会导致性能受损

3、测试工程

为了比较C++中文件系统与SQLite读写性能,使用VS2015创建一个名为“WriteToFileAndSQLite”的对话框工程:

SQLite,SQLite优缺点,文件读写

其中,实现写入功能代码如下:

// 写入到文件
void CWriteToFileAndSQLiteDlg::OnBnClickedBtnWriteFile()
{
	OnBtnWrite(1);
}
// 写入到SQLite
void CWriteToFileAndSQLiteDlg::OnBnClickedBtnWriteSqlite()
{
	OnBtnWrite(2);
}
// 写入按钮事件
void CWriteToFileAndSQLiteDlg::OnBtnWrite(int iWriteFlag)
{
	UpdateData(TRUE);
	if (m_strWriteCount.IsEmpty())
	{
		MessageBox(_T("未输入写入行数!"), _T("系统提示"), 0);
		return;
	}
	m_iWriteFlag = iWriteFlag;
	HANDLE hThread = CreateThread(NULL, 0, WriteThread, (LPVOID)this, 0, NULL);
	CloseHandle(hThread);
}
// 获取写入内容
std::string CWriteToFileAndSQLiteDlg::GetContent(int nID)
{
	USES_CONVERSION;
	srand((unsigned)time(NULL));
	CString sTemp = _T("");
	sTemp.Format(_T("%07d"), nID);
	Json::Value student;
	student["ID"] = T2A(sTemp);
	sTemp.Format(_T("学生%d"), nID);
	student["Name"] = T2A(sTemp);
	student["Grade"] = rand() % 100;
	Json::FastWriter fw;
	return fw.write(student);
}
DWORD WINAPI WriteThread(void* pVoid)
{
	CWriteToFileAndSQLiteDlg* pMainWnd = (CWriteToFileAndSQLiteDlg*)pVoid;
	if (pMainWnd)
	{
		pMainWnd->WriteThreadFunc();
	}
	return 0;
}
void CWriteToFileAndSQLiteDlg::WriteThreadFunc()
{
	GetDlgItem(ID_BTN_WRITE_FILE)->EnableWindow(FALSE);
	GetDlgItem(ID_BTN_WRITE_SQLITE)->EnableWindow(FALSE);
	int iCount = _ttoi(m_strWriteCount);
	// 写入到文件
	if (m_iWriteFlag == 1)
	{
		USES_CONVERSION;
		CString strFilePath = Util::GetCurrentPath() + _T("\\student.txt");
		std::ofstream os;
		os.open(T2A(strFilePath), std::ios::out);
		if (!os.fail())
		{
			std::vector vecContent;
			for (int i = 0; i < iCount; ++i)
			{
				vecContent.push_back(GetContent(i + 1));
			}
			// 获取当前时间
			WriteLog("写入到文件(%d行)开始...", iCount);
			double start_time = GetTickCount();
			for (unsigned int i = 0; i < vecContent.size(); ++i)
			{
				os << vecContent[i].c_str();
			}
			double end_time = GetTickCount();
			double dSecond = end_time - start_time;
			WriteLog("写入到文件(%d行)结束 --> 用时:%.6fms", iCount, dSecond);
			os.close();
		}
		else
		{
			WriteLog("写入文件失败...");
		}
	}
	else
	{	
		CString strDbPath = Util::GetCurrentPath() + _T("\\student.db3");
		CStudentSqlite BbStudent(strDbPath);
		BbStudent.Clear();
		StudentGradeInfoVec vecGradeInfo;
		srand((unsigned)time(NULL));
		for (int i = 0; i < iCount; ++i)
		{
			STUDENT_GRADE_INFO StuGradeInfo;
			StuGradeInfo.ID.Format(_T("%07d"), i + 1);
			StuGradeInfo.Name.Format(_T("学生%d"), i + 1);
			StuGradeInfo.Grade = rand() % 100;
			vecGradeInfo.push_back(StuGradeInfo);
		}
		WriteLog("写入到SQLite(%d行)开始...", iCount);

		double start_time = GetTickCount();
		for (unsigned int i = 0; i < iCount; ++i)
		{
			BbStudent.AddRecordToTableGrade(vecGradeInfo[i]);
		}
		double end_time = GetTickCount();
		double dSecond = end_time - start_time;
		WriteLog("写入到SQLite(%d行)结束 --> 用时:%.6fms", iCount, dSecond);
	}
	GetDlgItem(ID_BTN_WRITE_FILE)->EnableWindow(TRUE);
	GetDlgItem(ID_BTN_WRITE_SQLITE)->EnableWindow(TRUE);
}

写入500行,执行完毕后在程序运行目录下会生成如下文件:

SQLite,SQLite优缺点,文件读写

测试结果如下:

SQLite,SQLite优缺点,文件读写

可见:同样的写入功能,SQLite性能要比系统IO慢很多,这里注意每次测试结果不一定一样,但结果相差不大。

为何相差如此之大,这里是因为写入数据库的时候是一条一条写入的,速度会慢很多:

// 写入一行
bool CStudentSqlite::AddRecordToTableGrade(const STUDENT_GRADE_INFO& pStuGradeInfo)
{
	try
	{
		CString strSQL = _T("");
		strSQL.Format(_T("INSERT INTO 学生成绩表 VALUES('%s', '%s', %d);"), pStuGradeInfo.ID, pStuGradeInfo.Name, pStuGradeInfo.Grade);
		execDML(strSQL);
		return true;
	}
	catch (CppSQLite3Exception &e)
	{
		TRACE("CStudentSqlite::AddRecordToTableGrade %s", e.errorMessage());
	}
	return false;
}

改成SQLite一次插入多条记录:

// 写入多行
bool CStudentSqlite::AddRecordsToTableGrade(const StudentGradeInfoVec& vStudentGradeInfo)
{
	try
	{
		if (vStudentGradeInfo.size() > 0)
		{
			if (vStudentGradeInfo.size() == 1)
			{
				return AddRecordToTableGrade(vStudentGradeInfo[0]);
			}
			else
			{
				CString strSQL = _T("");
				strSQL.Format(_T("INSERT INTO 学生成绩表(ID,Name,Grade) SELECT '%s' AS 'ID', '%s' AS 'Name', '%d' AS 'Grade' "), vStudentGradeInfo[0].ID, vStudentGradeInfo[0].Name, vStudentGradeInfo[0].Grade);
				CString sTemp = _T("");
				for (unsigned int i = 1; i < vStudentGradeInfo.size(); ++i)
				{
					sTemp.Format(_T("union select '%s','%s','%d' "), vStudentGradeInfo[i].ID, vStudentGradeInfo[i].Name, vStudentGradeInfo[i].Grade);
					strSQL += sTemp;
				}
				execDML(strSQL);
				return true;
			}
		}
	}
	catch (CppSQLite3Exception &e)
	{
		TRACE("CStudentSqlite::AddRecordToTableGrade %s", e.errorMessage());
	}
	return false;
}
double start_time = GetTickCount();
BbStudent.AddRecordsToTableGrade(vecGradeInfo);
double end_time = GetTickCount();
double dSecond = end_time - start_time;
WriteLog("写入到SQLite(%d行)结束 --> 用时:%.6fms", iCount, dSecond);

测试结果如下:

SQLite,SQLite优缺点,文件读写

可见多条记录一次性插入速度明显是快多了。

接下来实现读取功能代码如下:

// 从文件读取
void CWriteToFileAndSQLiteDlg::OnBnClickedBtnReadFile()
{
	m_iReadFlag = 1;
	HANDLE hThread = CreateThread(NULL, 0, ReadThread, (LPVOID)this, 0, NULL);
	CloseHandle(hThread);
}
// 从SQLite读取
void CWriteToFileAndSQLiteDlg::OnBnClickedBtnReadSqlite()
{
	m_iReadFlag = 2;
	HANDLE hThread = CreateThread(NULL, 0, ReadThread, (LPVOID)this, 0, NULL);
	CloseHandle(hThread);
}
DWORD WINAPI ReadThread(void* pVoid)
{
	CWriteToFileAndSQLiteDlg* pMainWnd = (CWriteToFileAndSQLiteDlg*)pVoid;
	if (pMainWnd)
	{
		pMainWnd->ReadThreadFunc();
	}
	return 0;
}
// 读取线程函数
void CWriteToFileAndSQLiteDlg::ReadThreadFunc()
{
	GetDlgItem(ID_BTN_READ_FILE)->EnableWindow(FALSE);
	GetDlgItem(ID_BTN_READ_SQLITE)->EnableWindow(FALSE);
	USES_CONVERSION;
	// 从文件读取
	if (m_iReadFlag == 1)
	{
		USES_CONVERSION;
		CString strFilePath = Util::GetCurrentPath() + _T("\\student.txt");
		// 只读
		std::ifstream ifs(T2A(strFilePath), std::ios_base::in); 
		if (ifs.is_open())
		{
			WriteLog("从文件读取 开始...");
			double start_time = GetTickCount();
			char* pBuf = new char[256];
			while (!ifs.eof())
			{		
				ifs.getline(pBuf, 256);
				//WriteLog("%s", pBuf);
			}
			double end_time = GetTickCount();
			double dSecond = end_time - start_time;
			WriteLog("从文件读取 结束 --> 用时:%.6fms", dSecond);
			ifs.close();
			delete[] pBuf;
 		}
		else
		{
			WriteLog("读取文件失败...");
		}
	}
	else
	{
		CString strDbPath = Util::GetCurrentPath() + _T("\\student.db3");;
		CStudentSqlite BbStudent(strDbPath);
		std::vector<CString> vecGradeInfo;
		WriteLog("从SQLite读取 开始...");
		double start_time = GetTickCount();
		BbStudent.ReadRecordFromTableGrade(vecGradeInfo);
		double end_time = GetTickCount();
		double dSecond = end_time - start_time;
		WriteLog("从SQLite读取 结束 --> 用时:%.6fms", dSecond);
	}
	GetDlgItem(ID_BTN_READ_FILE)->EnableWindow(TRUE);
	GetDlgItem(ID_BTN_READ_SQLITE)->EnableWindow(TRUE);
}

测试结果如下:

SQLite,SQLite优缺点,文件读写

可见:同样读取500行,SQLite性能要和系统I/O几乎相同,但随着数据量的增大,SQLite的读取性能会比系统I/O差很多。

最后总结,在考虑是否采用SQLite数据库的时候,建议考虑以下场景:

  • (1)数据是否通过网络与应用程序分开? → 选择客户端/服务器
  • (2)多个并发用户? → 选择客户端/服务器
  • (3)大数据? → 选择客户端/服务器
  • (4)否则 → 选择 SQLite!
源码下载
  • 最近更新:   2022-06-16开发环境:   Visual Studio 2015
  • 源码大小:   686.97KB下载次数:  29 

更多为你推荐