UE5.2.1中用C+++SQLite3逐条更新12万行时游戏卡顿无响应
问题分析与解决方案
核心问题
你在使用Unreal Engine 5.2.1 + C++ + SQLite3批量更新12万行数据时出现卡顿且无结果,少量行正常,代码存在以下关键问题:
1. 数据库只读模式导致无法写入
代码中使用ESQLiteDatabaseOpenMode::ReadOnly打开数据库,只读模式下完全无法执行UPDATE等写入操作,这是批量更新无结果的根本原因。
2. 未使用事务导致IO过载
SQLite默认会为每条UPDATE语句自动提交事务,12万次单独提交会触发大量磁盘IO,这是游戏卡顿的核心原因。
3. 可优化的细节
- 每次循环创建新的
TArray<FString> Values会产生不必要的内存分配 - 错误处理不够完善(如PreparedStatement创建失败后仍执行Destroy)
- 循环中拷贝
FMyColour对象可改为引用减少开销
关键结论:无需为每行单独创建语句
你的代码复用PreparedStatement的做法是正确的——PreparedStatement就是设计用来预编译一次、多次绑定参数执行的,比每行创建新语句高效得多,完全不需要修改这一点。
修改后的优化代码
void UCatalogueGameInstanceSubsystem::InsertMyColourToDatabase(TArray<FMyColour> MyColour) { FSQLiteDatabase* MyDb = new FSQLiteDatabase(); // 改为读写模式(若需自动创建数据库可改用ReadWriteCreate) if (MyDb->Open(*DbFileFullPath, ESQLiteDatabaseOpenMode::ReadWrite)) { FSQLitePreparedStatement* PreparedStatement = new FSQLitePreparedStatement(); if (PreparedStatement->Create(*MyDb, *UpdateMyColour, ESQLitePreparedStatementFlags::Persistent)) { // 开启事务,将所有更新合并为一次提交 if (MyDb->Execute("BEGIN TRANSACTION")) { const TArray<FString> Keys = {"$redRGB", "$greenRGB", "$blueRGB", "$id"}; // 提前初始化参数数组,循环内复用避免重复分配 TArray<FString> Values; Values.Init("", Keys.Num()); bool bUpdateSuccess = true; // 使用引用避免拷贝FMyColour对象 for (const FMyColour& MyColor : MyColour) { Values[0] = FString::SanitizeFloat(MyColor.RGB.R); Values[1] = FString::SanitizeFloat(MyColor.RGB.G); Values[2] = FString::SanitizeFloat(MyColor.RGB.B); Values[3] = FString::FromInt(MyColor.id); if (!BindUpdateStatement(PreparedStatement, Keys, Values)) { UE_LOG(LogTemp, Warning, TEXT("参数绑定失败,ID: %d"), MyColor.id); bUpdateSuccess = false; break; } if (!PreparedStatement->Execute()) { UE_LOG(LogTemp, Warning, TEXT("更新失败: %s"), *MyDb->GetLastError()); bUpdateSuccess = false; break; } } // 根据更新结果提交或回滚事务 if (bUpdateSuccess) { MyDb->Execute("COMMIT"); } else { MyDb->Execute("ROLLBACK"); } } else { UE_LOG(LogTemp, Warning, TEXT("开启事务失败: %s"), *MyDb->GetLastError()); } PreparedStatement->Destroy(); } delete PreparedStatement; } MyDb->Close(); delete MyDb; } bool UCatalogueGameInstanceSubsystem::BindUpdateStatement( class FSQLitePreparedStatement* Statement, const TArray<FString>& Keys, const TArray<FString>& Values) const { check(Keys.Num() == Values.Num()); for (int i = 0; i < Keys.Num(); i++) { if (!Statement->SetBindingValueByName(*Keys[i], *Values[i])) { UE_LOG(LogTemp, Warning, TEXT("绑定键失败: %s"), *Keys[i]); return false; } } return true; } const FString UpdateMyColour = "UPDATE MyTable " "SET Red_RGB = $redRGB, Green_RGB = $greenRGB, Blue_RGB = $blueRGB " "WHERE ID = $id";
额外优化建议
- 后台线程执行:即使优化后,12万行更新仍会占用一定时间,建议使用UE的
AsyncTask或FRunnable将数据库操作移到后台线程,避免阻塞主线程导致游戏卡顿。 - 添加ID索引:确保
MyTable的ID字段创建了索引,否则UPDATE的WHERE子句会触发全表扫描,大幅降低更新速度。
内容的提问来源于stack exchange,提问作者VansFannel
相关产品推荐
相关产品推荐

