Qt 多線程數據庫操作優化

在多線程應用中,數據庫操作往往是性能瓶頸與穩定性風險的高發區。當多個線程同時讀寫數據庫時,若處理不當,輕則出現查詢卡頓、事務沖突,重則導致數據錯亂、連接泄漏甚至程序崩潰。Qt作為跨平臺框架,提供了QSql系列類支持數據庫操作,但原生API并未直接解決多線程場景下的效率與安全問題。本文將從線程安全基礎出發,深入講解數據庫連接池設計、事務優化、并發控制等核心技術,通過實戰案例展示如何將多線程數據庫操作的性能提升3-5倍,同時保證數據一致性。

一、多線程數據庫操作的核心挑戰

在單線程應用中,數據庫操作通常通過單一連接順序執行,雖簡單但無法利用多核資源,且容易阻塞UI線程。而多線程操作數據庫雖能提升并發能力,但會面臨一系列底層挑戰,這是優化的前提。

1. 連接的線程安全性:最容易踩的“坑”

Qt的QSqlDatabaseQSqlQuery并非線程安全組件,這是多線程操作的首要風險點:

  • QSqlDatabase不能跨線程使用:官方明確規定,在一個線程中創建的QSqlDatabase對象,不能在另一個線程中直接使用(即使通過指針傳遞),否則會導致數據庫驅動崩潰;
  • QSqlQuery依賴連接上下文:每個QSqlQuery必須關聯到當前線程的數據庫連接,跨線程復用查詢對象會引發未定義行為;
  • 驅動差異:不同數據庫驅動的線程安全特性不同(如SQLite默認不支持多線程寫入,MySQL需配置thread_handling參數)。

錯誤示例:跨線程使用數據庫連接

// 線程A中創建連接
QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL", "conn1");
db.setDatabaseName("testdb");
db.open();// 線程B中直接使用線程A的連接(錯誤!)
QThread *thread = new QThread;
thread->start();
QMetaObject::invokeMethod(thread, [&db](){QSqlQuery query(db); // 嚴重錯誤:跨線程使用連接query.exec("SELECT * FROM users"); 
});

上述代碼會導致數據庫驅動內部狀態混亂,可能出現查詢無結果、連接句柄泄漏甚至程序崩潰。

2. 并發操作的性能瓶頸

即使保證了線程安全,不合理的多線程數據庫操作仍會面臨性能問題:

  • 連接創建開銷:每次操作數據庫都新建連接(QSqlDatabase::open()),會觸發TCP握手、權限驗證等耗時操作(單次連接耗時可達100-500ms);
  • 事務沖突:多個線程同時寫入同一表時,會引發行鎖/表鎖競爭,導致事務等待甚至回滾;
  • 資源競爭:無限制創建線程和連接會導致數據庫連接數超限(多數數據庫默認連接上限為100-500),引發“too many connections”錯誤;
  • UI阻塞風險:若在主線程執行耗時查詢(如大表掃描),會導致界面卡頓。

3. 數據一致性挑戰

多線程并發寫入時,若缺乏有效的同步機制,會導致數據一致性問題:

  • 丟失更新:兩個線程同時更新同一條記錄,后提交的更新覆蓋前一次更新,導致數據丟失;
  • 臟讀:線程A讀取到線程B未提交的事務數據,若B回滾,A讀取的數據無效;
  • 不可重復讀:線程A兩次查詢同一數據,期間線程B修改并提交了數據,導致A兩次結果不一致。

二、數據庫連接池:線程安全的基石

解決多線程數據庫操作的核心方案是數據庫連接池——通過預創建一定數量的數據庫連接,由連接池統一管理連接的創建、復用、釋放,避免頻繁創建連接的開銷,同時保證每個線程安全使用獨立連接。

1. 連接池核心設計思想

連接池的本質是“資源復用”與“線程隔離”,其核心設計需包含以下組件:

  • 連接容器:用隊列/棧存儲可用連接,通過互斥鎖保證線程安全訪問;
  • 連接創建策略:初始化時創建最小連接數,當可用連接不足時動態擴容至最大連接數;
  • 連接復用機制:線程從池獲取連接,使用完畢后歸還(而非關閉),供其他線程復用;
  • 連接有效性檢測:歸還和獲取連接時檢查連接是否有效(如通過ping命令),失效則重建;
  • 超時控制:當所有連接都被占用時,獲取連接的線程等待超時后返回錯誤,避免無限阻塞。

2. 連接池實現代碼:Qt連接池核心框架

以下是一個可直接復用的Qt數據庫連接池實現,支持MySQL、SQLite、PostgreSQL等主流數據庫:

// DatabasePool.h
#ifndef DATABASEPOOL_H
#define DATABASEPOOL_H#include <QSqlDatabase>
#include <QMutex>
#include <QWaitCondition>
#include <QString>
#include <QQueue>
#include <QMap>class DatabasePool {
public:// 單例模式:全局唯一連接池static DatabasePool& instance();// 設置連接池參數void setConfig(const QString& driver, const QString& host, int port,const QString& dbName, const QString& user, const QString& password,int minConnections = 5, int maxConnections = 20, int waitTimeout = 5000);// 從連接池獲取連接(線程安全)QSqlDatabase getConnection();// 歸還連接到連接池(線程安全)void releaseConnection(const QSqlDatabase& db);private:DatabasePool();~DatabasePool();DatabasePool(const DatabasePool&) = delete;DatabasePool& operator=(const DatabasePool&) = delete;// 創建新連接QSqlDatabase createConnection();// 檢查連接是否有效bool isConnectionValid(const QSqlDatabase& db);// 連接池配置參數struct Config {QString driver;    // 數據庫驅動(如"QMYSQL")QString host;      // 主機地址int port;          // 端口號QString dbName;    // 數據庫名QString user;      // 用戶名QString password;  // 密碼int minConnections; // 最小連接數int maxConnections; // 最大連接數int waitTimeout;   // 獲取連接超時時間(ms)} config;QQueue<QString> freeConnections; // 可用連接名隊列QMap<QString, QSqlDatabase> allConnections; // 所有連接(連接名->連接)QMutex mutex; // 保護連接隊列的互斥鎖QWaitCondition waitCondition; // 等待可用連接的條件變量int nextConnId = 0; // 連接ID生成器
};#endif // DATABASEPOOL_H
// DatabasePool.cpp
#include "DatabasePool.h"
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
#include <QDebug>
#include <QTime>DatabasePool::DatabasePool() {}DatabasePool::~DatabasePool() {// 析構時關閉所有連接QMutexLocker locker(&mutex);for (const QString& connName : allConnections.keys()) {QSqlDatabase::removeDatabase(connName);}allConnections.clear();freeConnections.clear();
}DatabasePool& DatabasePool::instance() {static DatabasePool pool;return pool;
}void DatabasePool::setConfig(const QString& driver, const QString& host, int port,const QString& dbName, const QString& user, const QString& password,int minConnections, int maxConnections, int waitTimeout) {QMutexLocker locker(&mutex);config.driver = driver;config.host = host;config.port = port;config.dbName = dbName;config.user = user;config.password = password;config.minConnections = qMax(1, minConnections); // 最小連接數至少為1config.maxConnections = qMax(config.minConnections, maxConnections);config.waitTimeout = waitTimeout;// 初始化連接池:創建最小數量的連接for (int i = 0; i < config.minConnections; ++i) {QString connName = createConnection();if (!connName.isEmpty()) {freeConnections.enqueue(connName);}}
}QString DatabasePool::createConnection() {// 生成唯一連接名(格式:"dbpool_conn_xxx")QString connName = QString("dbpool_conn_%1").arg(++nextConnId);// 創建連接QSqlDatabase db = QSqlDatabase::addDatabase(config.driver, connName);db.setHostName(config.host);db.setPort(config.port);db.setDatabaseName(config.dbName);db.setUserName(config.user);db.setPassword(config.password);// SQLite特殊配置:開啟多線程模式if (config.driver == "QSQLITE") {db.setConnectOptions("QSQLITE_THREADSAFE=1"); // 線程安全模式}// 打開連接if (!db.open()) {qCritical() << "Create connection failed:" << db.lastError().text();QSqlDatabase::removeDatabase(connName);return "";}allConnections[connName] = db;return connName;
}bool DatabasePool::isConnectionValid(const QSqlDatabase& db) {// 檢查連接是否有效:執行ping命令或簡單查詢if (!db.isOpen()) return false;QSqlQuery query(db);// MySQL/SQLite通用檢查:查詢版本號if (query.exec("SELECT 1")) {return true;} else {qWarning() << "Connection invalid:" << query.lastError().text();return false;}
}QSqlDatabase DatabasePool::getConnection() {QMutexLocker locker(&mutex);QTime timer;timer.start();// 循環等待可用連接while (true) {// 檢查是否有可用連接if (!freeConnections.isEmpty()) {QString connName = freeConnections.dequeue();QSqlDatabase db = allConnections[connName];// 驗證連接有效性,無效則重建if (isConnectionValid(db)) {return db;} else {// 連接無效,重建并替換QSqlDatabase::removeDatabase(connName);allConnections.remove(connName);connName = createConnection();if (!connName.isEmpty()) {return allConnections[connName];}}}// 無可用連接,檢查是否可擴容if (allConnections.size() < config.maxConnections) {QString connName = createConnection();if (!connName.isEmpty()) {return allConnections[connName];}}// 無法擴容,等待可用連接(超時退出)if (timer.elapsed() >= config.waitTimeout) {qCritical() << "Get connection timeout (>" << config.waitTimeout << "ms)";return QSqlDatabase(); // 返回無效連接}// 等待100ms后重試waitCondition.wait(&mutex, 100);}
}void DatabasePool::releaseConnection(const QSqlDatabase& db) {if (!db.isValid()) return;QString connName = db.connectionName();if (!allConnections.contains(connName)) return;QMutexLocker locker(&mutex);// 檢查連接是否仍有效,無效則移除if (!isConnectionValid(db)) {QSqlDatabase::removeDatabase(connName);allConnections.remove(connName);// 若總連接數低于最小連接數,補充新連接if (allConnections.size() < config.minConnections) {QString newConnName = createConnection();if (!newConnName.isEmpty()) {freeConnections.enqueue(newConnName);}}} else {// 連接有效,歸還到可用隊列freeConnections.enqueue(connName);}// 喚醒等待連接的線程waitCondition.wakeOne();
}

3. 連接池使用方法:線程安全的數據庫操作

使用連接池時,每個線程通過getConnection()獲取獨立連接,操作完成后調用releaseConnection()歸還,無需手動關閉連接:

// 線程任務:查詢用戶信息
void queryUserTask(int userId) {// 從連接池獲取連接QSqlDatabase db = DatabasePool::instance().getConnection();if (!db.isOpen()) {qWarning() << "Get connection failed";return;}// 執行查詢QSqlQuery query(db);query.prepare("SELECT name, age FROM users WHERE id = :id");query.bindValue(":id", userId);if (query.exec() && query.next()) {QString name = query.value(0).toString();int age = query.value(1).toInt();qDebug() << "User info: name=" << name << ", age=" << age;} else {qWarning() << "Query failed:" << query.lastError().text();}// 歸還連接到池(必須調用!)DatabasePool::instance().releaseConnection(db);
}// 初始化連接池(通常在main函數或應用啟動時)
void initDatabasePool() {DatabasePool::instance().setConfig("QMYSQL",       // 驅動"localhost",    // 主機3306,           // 端口"testdb",       // 數據庫名"root",         // 用戶名"123456",       // 密碼5,              // 最小連接數20,             // 最大連接數5000            // 超時時間(ms));
}

4. 連接池關鍵參數調優

連接池的性能取決于參數配置,需根據業務場景調整:

  • 最小連接數(minConnections):建議設置為CPU核心數的1-2倍(如8核CPU設為10),保證基本并發需求;
  • 最大連接數(maxConnections):不超過數據庫服務的連接上限(MySQL默認最大連接數為151),建議設為50-200(根據服務器配置);
  • 超時時間(waitTimeout):根據業務容忍度設置,一般設為3000-10000ms(3-10秒);
  • 連接檢測頻率:在高并發場景下,可縮短連接有效性檢測的間隔(如每次歸還連接時檢測)。

三、事務優化:提升并發寫入效率

多線程寫入數據庫時,頻繁的事務提交會導致大量IO操作和鎖競爭。通過合理的事務管理,可將寫入性能提升3-5倍,同時保證數據一致性。

1. 事務粒度控制:避免“一操作一事務”

默認情況下,數據庫處于“自動提交”模式(每執行一條SQL自動提交事務),這在多線程寫入時效率極低。優化方案是增大事務粒度:將多個相關操作合并為一個事務,減少提交次數。

反例(低效):單條插入即提交

// 低效:1000條記錄,1000次事務提交
for (int i = 0; i < 1000; ++i) {QSqlQuery query(db);query.prepare("INSERT INTO logs (content) VALUES (:content)");query.bindValue(":content", QString("log %1").arg(i));query.exec(); // 自動提交,每次執行觸發磁盤IO
}

優化方案:批量插入+單次事務

// 高效:1000條記錄,1次事務提交
db.transaction(); // 手動開啟事務
QSqlQuery query(db);
query.prepare("INSERT INTO logs (content) VALUES (:content)");for (int i = 0; i < 1000; ++i) {query.bindValue(":content", QString("log %1").arg(i));if (!query.exec()) {qWarning() << "Insert failed:" << query.lastError().text();db.rollback(); // 失敗回滾return false;}
}if (!db.commit()) { // 批量提交qWarning() << "Commit failed:" << db.lastError().text();db.rollback();return false;
}
return true;

性能對比:在MySQL中插入10000條記錄,自動提交模式耗時約8-10秒,批量事務模式僅需1-2秒,效率提升80%以上。

2. 事務隔離級別選擇:平衡一致性與性能

數據庫事務隔離級別決定了并發事務之間的可見性,級別越高一致性越好,但性能越低。Qt中可通過QSqlDatabase::setTransactionIsolationLevel()設置隔離級別:

隔離級別說明適用場景
ReadUncommitted(讀未提交)允許讀取未提交的事務數據,可能臟讀、不可重復讀、幻讀對一致性要求低,追求極致性能(如日志采集)
ReadCommitted(讀已提交)只能讀取已提交的數據,避免臟讀,可能不可重復讀、幻讀大多數業務場景(如用戶信息查詢)
RepeatableRead(可重復讀)保證同一事務中多次讀取結果一致,避免臟讀、不可重復讀,可能幻讀統計分析、報表生成
Serializable(串行化)事務完全串行執行,避免所有并發問題金融交易等強一致性場景

設置示例

// 設置事務隔離級別為“讀已提交”(MySQL默認級別)
db.setTransactionIsolationLevel(QSql::ReadCommitted);

建議:多數業務場景優先選擇ReadCommitted,平衡一致性與性能;僅在強一致性需求(如支付)時使用Serializable

3. 樂觀鎖與悲觀鎖:解決并發更新沖突

多線程同時更新同一條記錄時,需通過鎖機制避免丟失更新,Qt中常用兩種鎖策略:

(1)悲觀鎖:搶占式鎖定

悲觀鎖假設沖突一定會發生,通過數據庫的行鎖/表鎖機制,在更新前鎖定記錄,阻止其他線程修改:

// 悲觀鎖實現:更新用戶余額(MySQL示例)
db.transaction();
QSqlQuery query(db);// 鎖定id=100的記錄(FOR UPDATE會加行鎖)
query.prepare("SELECT balance FROM users WHERE id = :id FOR UPDATE");
query.bindValue(":id", 100);
if (!query.exec() || !query.next()) {db.rollback();return false;
}int currentBalance = query.value(0).toInt();
int newBalance = currentBalance + 100; // 增加100元// 更新余額
query.prepare("UPDATE users SET balance = :balance WHERE id = :id");
query.bindValue(":balance", newBalance);
query.bindValue(":id", 100);
if (!query.exec()) {db.rollback();return false;
}db.commit();

注意:悲觀鎖會增加鎖競爭,長時間持有鎖會導致其他線程阻塞,建議鎖的范圍越小越好(行鎖優先于表鎖),且事務執行時間盡可能短。

(2)樂觀鎖:無鎖并發控制

樂觀鎖假設沖突很少發生,通過版本號機制實現無鎖并發,適用于讀多寫少場景:

  1. 表中新增version字段(整數,初始值0);
  2. 更新時檢查版本號,僅當版本號匹配時才更新,并自增版本號;
  3. 若版本號不匹配,說明記錄已被修改,需重試。

實現示例

// 樂觀鎖實現:更新用戶余額
int retryCount = 3; // 最多重試3次
while (retryCount-- > 0) {db.transaction();QSqlQuery query(db);// 查詢當前余額和版本號query.prepare("SELECT balance, version FROM users WHERE id = :id");query.bindValue(":id", 100);if (!query.exec() || !query.next()) {db.rollback();return false;}int currentBalance = query.value(0).toInt();int currentVersion = query.value(1).toInt();int newBalance = currentBalance + 100;int newVersion = currentVersion + 1;// 僅當版本號匹配時更新(避免覆蓋其他線程的修改)query.prepare("UPDATE users SET balance = :balance, version = :version ""WHERE id = :id AND version = :oldVersion");query.bindValue(":balance", newBalance);query.bindValue(":version", newVersion);query.bindValue(":id", 100);query.bindValue(":oldVersion", currentVersion);if (!query.exec()) {db.rollback();return false;}// 檢查是否更新成功(影響行數為1則成功)if (query.numRowsAffected() == 1) {db.commit();return true;} else {// 更新失敗(版本號不匹配),重試db.rollback();qWarning() << "Update conflict, retry..." << retryCount;}
}// 多次重試失敗
return false;

對比:悲觀鎖實現簡單但并發效率低,適合寫多讀少場景;樂觀鎖無鎖競爭,并發效率高,但需處理重試邏輯,適合讀多寫少場景(如商品庫存更新)。

四、性能優化實戰:從卡頓到流暢

結合連接池、事務優化、索引設計等技術,我們通過一個實戰案例展示多線程數據庫操作的完整優化流程。

1. 場景描述

某日志分析系統需多線程采集設備日志,每秒鐘生成約1000條日志記錄,寫入MySQL數據庫。原實現采用單線程+自動提交事務,出現以下問題:

  • 寫入延遲高達5-10秒,日志堆積嚴重;
  • 主線程頻繁卡頓,UI無響應;
  • 高峰期出現“too many connections”錯誤。

2. 優化方案實施

(1)引入連接池

配置連接池參數:最小連接數=5,最大連接數=20,超時時間=5000ms,避免頻繁創建連接。

(2)多線程寫入+批量事務
  • 創建4個日志處理線程(與CPU核心數匹配);
  • 每個線程累積100條日志后批量提交事務,減少IO次數。
(3)索引優化

為日志表的device_idcreate_time字段創建聯合索引,加速后續查詢:

CREATE INDEX idx_log_device_time ON logs(device_id, create_time);
(4)異步化處理

使用QtConcurrent::run()在后臺線程執行寫入操作,主線程通過QFutureWatcher監控進度,避免UI阻塞。

3. 優化后代碼實現

// 日志寫入任務
bool writeLogsBatch(const QList<LogRecord>& logs) {// 從連接池獲取連接QSqlDatabase db = DatabasePool::instance().getConnection();if (!db.isOpen()) return false;// 開啟事務db.transaction();QSqlQuery query(db);query.prepare("INSERT INTO logs (device_id, content, create_time) ""VALUES (:device_id, :content, :create_time)");// 批量綁定參數for (const LogRecord& log : logs) {query.bindValue(":device_id", log.deviceId);query.bindValue(":content", log.content);query.bindValue(":create_time", log.createTime);if (!query.exec()) {qWarning() << "Insert log failed:" << query.lastError().text();db.rollback();DatabasePool::instance().releaseConnection(db);return false;}}// 提交事務bool success = db.commit();if (!success) {db.rollback();qWarning() << "Commit logs failed:" << db.lastError().text();}// 歸還連接DatabasePool::instance().releaseConnection(db);return success;
}// 多線程批量寫入日志
void startLogWriter() {QFutureWatcher<bool>* watcher = new QFutureWatcher<bool>();connect(watcher, &QFutureWatcher<bool>::finished,watcher, &QFutureWatcher<bool>::deleteLater);// 啟動4個后臺線程處理日志for (int i = 0; i < 4; ++i) {// 從日志隊列獲取100條日志QList<LogRecord> batchLogs = logQueue.dequeueBatch(100);if (batchLogs.isEmpty()) break;// 異步執行批量寫入QFuture<bool> future = QtConcurrent::run(writeLogsBatch, batchLogs);watcher->setFuture(future);}
}

4. 優化效果對比

指標優化前優化后提升幅度
寫入延遲5-10秒<500ms10倍以上
每秒寫入量約200條約1500條7.5倍
UI響應性頻繁卡頓流暢無卡頓-
連接錯誤頻繁出現“too many connections”無連接錯誤-

五、最佳實踐與避坑指南

多線程數據庫操作優化需兼顧性能與穩定性,以下是經過實戰驗證的最佳實踐:

1. 連接池使用禁忌

  • 禁止跨線程歸還連接:連接必須在獲取它的線程中歸還,避免線程安全問題;
  • 避免長時間占用連接:連接是稀缺資源,耗時操作(如大文件導入)應拆分步驟,及時歸還連接;
  • 不手動關閉連接:連接的關閉由連接池統一管理,不要調用QSqlDatabase::close(),只需調用releaseConnection()

2. 事務使用原則

  • 事務盡可能小:事務中只包含必要的SQL操作,避免在事務中執行耗時任務(如網絡請求);
  • 及時提交或回滾:事務開啟后應盡快完成提交或回滾,減少鎖持有時間;
  • 異常必回滾:在try-catch或錯誤處理中,確保事務異常時能正確回滾,避免長期鎖表。

3. 線程安全編碼規范

  • 線程數據隔離:每個線程使用獨立的QSqlQuery對象,不共享查詢對象;
  • 避免UI操作:數據庫操作線程中禁止直接調用UI組件接口,通過信號槽將結果發送到主線程更新UI;
  • 參數綁定優先:使用QSqlQuery::bindValue()傳遞參數,避免字符串拼接SQL(防止SQL注入和編碼問題)。

4. 數據庫選型注意事項

  • MySQL:支持完善的事務和鎖機制,適合高并發寫入,需注意配置max_connections參數;
  • SQLite:輕量級嵌入式數據庫,多線程寫入需開啟QSQLITE_THREADSAFE=1,但不支持真正的并行寫入(寫操作串行執行),適合單機低并發場景;
  • PostgreSQL:事務隔離級別更嚴格,支持復雜查詢,適合數據量大、查詢復雜的場景。

六、總結

多線程數據庫操作優化是Qt開發中的核心難點,其本質是資源復用(連接池)、并發控制(事務與鎖)、性能平衡(批量與異步)的綜合應用。本文通過連接池設計、事務優化、鎖機制詳解和實戰案例,展示了如何將多線程數據庫操作從卡頓變為流暢,核心要點包括:

  • 連接池是線程安全的基礎,通過復用連接降低創建開銷;
  • 批量事務和合理的隔離級別是提升寫入性能的關鍵;
  • 樂觀鎖與悲觀鎖分別適用于不同并發場景,解決更新沖突;
  • 多線程+異步化處理可避免UI阻塞,提升用戶體驗。

如果你在多線程數據庫優化中遇到特殊場景或問題,歡迎在評論區留言討論!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/90887.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/90887.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/90887.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Qt 狀態機框架:復雜交互邏輯的處理

Qt狀態機框架&#xff08;Qt State Machine Framework&#xff09;是一個強大的工具&#xff0c;用于簡化復雜的交互邏輯和狀態管理。它基于UML狀態圖概念&#xff0c;提供了聲明式的方式來定義對象行為&#xff0c;特別適合處理具有多種狀態和轉換的場景&#xff08;如GUI交互…

【docker】DM8達夢數據庫的docker-compose以及一些啟動踩坑

摘要&#xff1a;本文介紹了通過docker-compose配置啟動達夢數據庫(DM8)的方法。配置包括容器鏡像、端口映射、數據卷掛載以及關鍵環境變量設置&#xff08;如字符集、大小寫敏感等&#xff09;。也說明了啟動過程可能遇到的一些問題。通過docker-compose啟動達夢數據庫可以按照…

服務器中的防火墻設置需要打開嗎

服務器中的防火墻屬于是一種保護計算機網絡不會受到未經授權的網絡設備所訪問的技術手段&#xff0c;防火墻還有著將內部網絡和外部網絡進行隔離的功能&#xff0c;在網絡之間創建安全屏障&#xff0c;以此來保護網絡中數據的安全。防火墻是一種網絡安全系統&#xff0c;可以幫…

Java項目:基于SSM框架實現的社區團購管理系統【ssm+B/S架構+源碼+數據庫+畢業論文+答辯PPT+遠程部署】

摘 要 使用舊方法對社區團購信息進行系統化管理已經不再讓人們信賴了&#xff0c;把現在的網絡信息技術運用在社區團購信息的管理上面可以解決許多信息管理上面的難題&#xff0c;比如處理數據時間很長&#xff0c;數據存在錯誤不能及時糾正等問題。 這次開發的社區團購系統有…

介紹一下static關鍵字

在Java中&#xff0c;被static修飾的成員稱為靜態成員&#xff0c;static關鍵字可以用來修飾方法或者成員變量&#xff0c;且被static修飾的方法或者成員變量屬于類方法或者類屬性&#xff0c;也就是說被static修飾的方法或者成員變量不是單獨存儲在某一個對象的空間&#xff0…

【Java學習|黑馬筆記|Day23】網絡編程、反射、動態代理

【DAY23】 文章目錄【DAY23】一.網絡編程1&#xff09;三要素1.1&#xff09;IPInetAddress類的使用1.2&#xff09;端口號1.3&#xff09;協議2.1&#xff09;UDP協議發送數據2.2&#xff09;UDP協議接收數據2.3&#xff09;UDP的三種通信方式3.1&#xff09;TCP協議的發送和接…

【Zephyr】Window下的Zephyr編譯和使用

工具下載 參考文檔&#xff08;Getting Started Guide — Zephyr Project Documentation&#xff09;中介紹&#xff0c;可以直接通過winget下載&#xff1a; winget download Kitware.CMake Ninja-build.Ninja oss-winget.gperf python Git.Git oss-winget.dtc wget 7zip.7z…

圖論(BFS)構造鄰接表(運用隊列實現搜索)

碼蹄集OJ-夏日漫步 #include<bits/stdc.h> using namespace std; int n; int a[200010],dis[200010],qaq[1000010]; vector<int>son[200010]; int que[200010]; int main( ) {memset(qaq,-1,sizeof(qaq));memset(dis,-1,sizeof(dis));cin>>n;for(int i1;i…

vue怎么實現導入excel表功能

<el-uploadref"upload":action"aaa":on-change"importProject"name"excelFile"multiple:auto-upload"false":show-file-list"false"><el-button type"warning">導入</el-button><…

Linux DNS解析3 -- DNS解析代理配置使用

當網關設備配置了 /etc/hosts 文件時&#xff0c;確實可以為終端設備提供自定義DNS解析功能&#xff0c;但具體效果取決于網關的DNS代理服務配置。下面詳細解釋其工作原理和限制&#xff1a; 一、/etc/hosts 文件的作用 /etc/hosts 是本地靜態域名解析文件&#xff0c;格式為&a…

歷史版本的vscode下載地址

我有點厭惡vscode越來越臃腫的體積&#xff0c;也不需要層出不窮的新功能&#xff0c;于是網上找尋歷史版本。 首先是這個頁面&#xff1a;https://code.visualstudio.com/updates &#xff0c;但最多只顯示兩年&#xff0c;更早的就要手工修改地址欄&#xff0c;我試了最早的…

如何規范化項目執行

要實現項目執行的規范化&#xff0c;應做到以下幾點&#xff1a;制定詳細的項目執行計劃、明確項目團隊角色職責、建立高效溝通與協調機制、實施全面的質量與風險管理、采用合適的項目管理工具。其中&#xff0c;尤其重要的是明確項目團隊角色職責&#xff0c;通過構建清晰的責…

【Rust異步】async和await異步編程實戰:高并發任務處理全解析

?? 歡迎大家來到景天科技苑?? &#x1f388;&#x1f388; 養成好習慣&#xff0c;先贊后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者簡介&#xff1a;景天科技苑 &#x1f3c6;《頭銜》&#xff1a;大廠架構師&#xff0c;華為云開發者社區專家博主&#xff0c;…

在Linux上使用DuckCP實現從csv文件匯總數據到SQLite數據庫的表

從pypi網站Duckcp頁面下載duckcp-0.1.1-py3-none-any.whl 一開始用的Python 3.11.2環境。 繼續沿用上文打補丁的方法&#xff0c;得到一個支持python3.11.1的安裝包。 因為缺少zip壓縮工具&#xff0c;使用python程序來完成對修改后文件的重新壓縮。 import os import zipfile…

基于深度學習的圖像分類:使用EfficientNet實現高效分類

前言 圖像分類是計算機視覺領域中的一個基礎任務&#xff0c;其目標是將輸入的圖像分配到預定義的類別中。近年來&#xff0c;深度學習技術&#xff0c;尤其是卷積神經網絡&#xff08;CNN&#xff09;&#xff0c;在圖像分類任務中取得了顯著的進展。EfficientNet是一種新型的…

Java基礎-綜合案例

1、設計一個可以執行基本數學運算&#xff08;加減乘除&#xff09;的計算器程序功能描述&#xff1a;用戶輸入兩個數字、一個運算符&#xff08;、-、*、/&#xff09;。根據所選運算符執行相應的數學運算&#xff0c;顯示運算結果。import java.util.Scanner;public class Te…

四、計算機組成原理——第3章:存儲系統

目錄 3.1存儲器概述 3.1.1存儲器的分類 1.按在計算機中的作用(層次)分類 2.按存儲介質分類 3.按存取方式分類 4.按信息的可保存性分類 3.1.2存儲器的性能指標 3.2主存儲器 3.2.1SRAM芯片和DRAM芯片 1.SRAM的工作原理 2.DRAM的工作原理 3.SRAM和DRAM的比較 4.存儲器芯片的內部結…

3D Semantic Occupancy Prediction

3D 語義占用預測&#xff08;3D Semantic Occupancy Prediction&#xff09;旨在將真實世界環境劃分為規則的三維體素&#xff08;voxel&#xff09;網格&#xff0c;并對每個體素同時預測&#xff1a; 占用狀態&#xff08;Occupancy&#xff09;&#xff1a;該體素是否被物體…

在Word和WPS文字中添加的拼音放到文字右邊

在Word和WPS文字中&#xff0c;可以方便地為中文漢字添加拼音。默認的是拼音在漢字的上方&#xff0c;而且不方便直接編輯。可以簡單操作后把拼音放在漢字的右邊&#xff0c;并且可以方便地編輯。一、Word&#xff1a;先為漢字添加拼音&#xff0c;然后選擇性粘貼為純文本即可1…

Torchv Unstrustured 文檔解析庫

一個強大且開發者友好的文檔解析庫&#xff0c;專為RAG&#xff08;檢索增強生成&#xff09;應用優化。基于Apache Tika、Apache POI和PDFBox等業界標準Java庫構建&#xff0c;TorchV Unstructured提供了增強的解析能力&#xff0c;具備智能表格結構識別和內容提取功能。 &am…