perf(db): tune sqlite pool and add maintenance scheduler
This commit is contained in:
62
db_pool.py
62
db_pool.py
@@ -7,12 +7,28 @@
|
||||
|
||||
import sqlite3
|
||||
import threading
|
||||
import time
|
||||
from queue import Empty, Full, Queue
|
||||
|
||||
from app_config import get_config
|
||||
from app_logger import get_logger
|
||||
|
||||
|
||||
logger = get_logger("database")
|
||||
config = get_config()
|
||||
|
||||
DB_CONNECT_TIMEOUT_SECONDS = max(1, int(getattr(config, "DB_CONNECT_TIMEOUT_SECONDS", 10)))
|
||||
DB_BUSY_TIMEOUT_MS = max(1000, int(getattr(config, "DB_BUSY_TIMEOUT_MS", 10000)))
|
||||
DB_CACHE_SIZE_KB = max(1024, int(getattr(config, "DB_CACHE_SIZE_KB", 8192)))
|
||||
DB_WAL_AUTOCHECKPOINT_PAGES = max(100, int(getattr(config, "DB_WAL_AUTOCHECKPOINT_PAGES", 1000)))
|
||||
DB_MMAP_SIZE_MB = max(0, int(getattr(config, "DB_MMAP_SIZE_MB", 256)))
|
||||
DB_LOCK_RETRY_COUNT = max(0, int(getattr(config, "DB_LOCK_RETRY_COUNT", 3)))
|
||||
DB_LOCK_RETRY_BASE_MS = max(10, int(getattr(config, "DB_LOCK_RETRY_BASE_MS", 50)))
|
||||
|
||||
|
||||
def _is_lock_conflict_error(error: sqlite3.OperationalError) -> bool:
|
||||
message = str(error or "").lower()
|
||||
return ("locked" in message) or ("busy" in message)
|
||||
|
||||
|
||||
class ConnectionPool:
|
||||
@@ -46,16 +62,29 @@ class ConnectionPool:
|
||||
|
||||
def _create_connection(self):
|
||||
"""创建新的数据库连接"""
|
||||
conn = sqlite3.connect(self.database, check_same_thread=False)
|
||||
conn = sqlite3.connect(
|
||||
self.database,
|
||||
check_same_thread=False,
|
||||
timeout=DB_CONNECT_TIMEOUT_SECONDS,
|
||||
)
|
||||
conn.row_factory = sqlite3.Row
|
||||
# 启用外键约束,确保 ON DELETE CASCADE 等约束生效
|
||||
conn.execute("PRAGMA foreign_keys=ON")
|
||||
# 设置WAL模式提高并发性能
|
||||
conn.execute("PRAGMA journal_mode=WAL")
|
||||
# 在WAL模式下使用NORMAL同步,兼顾性能与可靠性
|
||||
conn.execute("PRAGMA synchronous=NORMAL")
|
||||
# 设置合理的超时时间
|
||||
conn.execute("PRAGMA busy_timeout=5000")
|
||||
pragma_statements = [
|
||||
"PRAGMA foreign_keys=ON",
|
||||
"PRAGMA journal_mode=WAL",
|
||||
"PRAGMA synchronous=NORMAL",
|
||||
f"PRAGMA busy_timeout={DB_BUSY_TIMEOUT_MS}",
|
||||
"PRAGMA temp_store=MEMORY",
|
||||
f"PRAGMA cache_size={-DB_CACHE_SIZE_KB}",
|
||||
f"PRAGMA wal_autocheckpoint={DB_WAL_AUTOCHECKPOINT_PAGES}",
|
||||
]
|
||||
if DB_MMAP_SIZE_MB > 0:
|
||||
pragma_statements.append(f"PRAGMA mmap_size={DB_MMAP_SIZE_MB * 1024 * 1024}")
|
||||
|
||||
for statement in pragma_statements:
|
||||
try:
|
||||
conn.execute(statement)
|
||||
except sqlite3.DatabaseError as e:
|
||||
logger.warning(f"设置数据库参数失败 ({statement}): {e}")
|
||||
return conn
|
||||
|
||||
def _close_connection(self, conn) -> None:
|
||||
@@ -198,7 +227,20 @@ class PooledConnection:
|
||||
|
||||
def commit(self):
|
||||
"""提交事务"""
|
||||
self._conn.commit()
|
||||
for attempt in range(DB_LOCK_RETRY_COUNT + 1):
|
||||
try:
|
||||
self._conn.commit()
|
||||
return
|
||||
except sqlite3.OperationalError as e:
|
||||
if (not _is_lock_conflict_error(e)) or attempt >= DB_LOCK_RETRY_COUNT:
|
||||
raise
|
||||
|
||||
sleep_seconds = (DB_LOCK_RETRY_BASE_MS * (2**attempt)) / 1000.0
|
||||
logger.warning(
|
||||
f"数据库提交遇到锁冲突,{sleep_seconds:.3f}s 后重试 "
|
||||
f"({attempt + 1}/{DB_LOCK_RETRY_COUNT})"
|
||||
)
|
||||
time.sleep(sleep_seconds)
|
||||
|
||||
def rollback(self):
|
||||
"""回滚事务"""
|
||||
|
||||
Reference in New Issue
Block a user