python3 tinydb 工具类
前言
TinyDB 是一个轻量级的 NoSQL 数据库,适用于 Python 环境。它的设计目标是提供简单易用、灵活和零依赖的嵌入式数据库解决方案,非常适合小型应用程序、测试项目或其他对数据库没有复杂需求的场合。以下是 TinyDB 的一些关键特点:
-
轻量级: TinyDB 不依赖于任何外部数据库服务器,它将数据存储在 JSON 文件中,因此非常轻便。
-
易于使用: 提供简单直观的接口来进行数据的插入、查询、更新和删除等操作,可以方便地集成到 Python 项目中。
-
灵活的查询: 使用查询对象和链式语法,允许进行复杂的查询操作。
-
可扩展性: 支持自定义中间件和存储模块,用户可以根据需要扩展其功能。
-
事务支持: 提供对数据变更的事务性支持,确保数据的一致性。
tinydb 官网:https://tinydb.readthedocs.io/en/latest/intro.html
正文
在 Trae 的帮助下,生成了一个 tinydb_utils.py 文件
# 安装依赖(首次运行前执行)
# pip install tinydb filelock
from tinydb import TinyDB, Query
from json.decoder import JSONDecodeError
from filelock import FileLock
import json
import os
import shutil
from datetime import datetime
from tinydb.queries import QueryLike
class TinyDBUtils:
def __init__(self, db_path='tinydb_data.json', lock_suffix='.lock'):
self.db_path = db_path
self.lock_path = f'{db_path}{lock_suffix}'
self.lock = FileLock(self.lock_path)
self.db = self._init_db()
self.Query = Query()
def _init_db(self):
"""初始化数据库,处理文件损坏检测与修复"""
try:
# 尝试直接加载数据库
return TinyDB(self.db_path)
except JSONDecodeError:
# 检测到JSON格式错误,执行修复
self._repair_db()
return TinyDB(self.db_path)
except FileNotFoundError:
# 文件不存在时自动创建
return TinyDB(self.db_path)
def _repair_db(self):
"""修复损坏的数据库文件(备份原文件并初始化新库)"""
with self.lock:
# 创建备份目录(原数据库路径+"\.backups")
backup_dir = f'{self.db_path}.backups'
os.makedirs(backup_dir, exist_ok=True)
# 生成备份文件名(原文件名+时间戳\.bak,放入备份目录)
timestamp = datetime.now().strftime('%Y%m%d%H%M%S')
backup_filename = f'{os.path.basename(self.db_path)}.{timestamp}.bak'
backup_path = os.path.join(backup_dir, backup_filename)
# 备份损坏文件
if os.path.exists(self.db_path):
shutil.copyfile(self.db_path, backup_path)
print(f'检测到文件损坏,已备份至:{backup_path}')
# 清理旧备份(保留最多30个)
backup_files = [os.path.join(backup_dir, f) for f in os.listdir(backup_dir)
if f.endswith('.bak') and os.path.isfile(os.path.join(backup_dir, f))]
# 按修改时间升序排序(最旧的在前)
backup_files.sort(key=lambda x: os.path.getmtime(x))
# 删除超过30个的旧备份
if len(backup_files) > 30:
for old_backup in backup_files[:-30]:
os.remove(old_backup)
print(f'删除旧备份文件:{old_backup}')
# 初始化新数据库文件
with open(self.db_path, 'w', encoding='utf-8') as f:
json.dump({}, f)
print('已创建新的空数据库文件')
def insert(self, data):
"""插入新JSON对象(自动加锁,包含损坏修复逻辑)"""
with self.lock:
try:
return self.db.insert(data)
except JSONDecodeError:
# 检测到文件损坏,执行修复
self._repair_db()
# 重新初始化数据库实例
self.db = TinyDB(self.db_path)
# 重新尝试插入
return self.db.insert(data)
def insert_multiple(self, data_list):
"""插入新JSON对象(自动加锁,包含损坏修复逻辑)"""
with self.lock:
try:
return self.db.insert_multiple(data_list)
except JSONDecodeError:
# 检测到文件损坏,执行修复
self._repair_db()
# 重新初始化数据库实例
self.db = TinyDB(self.db_path)
# 重新尝试插入
return self.db.insert_multiple(data_list)
def get_all(self):
"""获取所有记录(自动加锁)"""
with self.lock:
return self.db.all()
def get_by_condition(self, condition:QueryLike):
"""根据条件查询记录(自动加锁)"""
with self.lock:
return self.db.search(condition)
def update(self, condition, new_data):
"""根据条件更新记录(自动加锁)"""
with self.lock:
return self.db.update(new_data, condition)
def delete(self, condition):
"""根据条件删除记录(自动加锁)"""
with self.lock:
return self.db.remove(condition)
def truncate(self):
"""清空数据库(自动加锁)"""
with self.lock:
self.db.truncate()
if __name__ == '__main__':
# 示例用法
db_utils = TinyDBUtils(db_path='demo_db.json')
# 插入测试数据
db_utils.insert({'name': 'Alice', 'age': 30, 'email': 'alice@example.com'})
db_utils.insert({'name': 'Bob', 'age': 25, 'email': 'bob@example.com'})
print('插入后数据:', db_utils.get_all())
# 更新数据
db_utils.update(db_utils.Query.name == 'Bob', {'age': 26, 'msgId':123})
print('更新后数据:', db_utils.get_all())
# 查询数据
print('查询Bob:', db_utils.get_by_condition(db_utils.Query.name == 'Bob'))
# 删除数据
db_utils.delete(db_utils.Query.name == 'Alice')
print('删除后数据:', db_utils.get_all())
# 清空数据库(测试修复功能可手动修改demo_db.json为非法JSON后运行)
db_utils.truncate()
性能测试脚本:tinydb_perf_test.py
# 性能测试脚本(需先安装依赖:pip install tinydb filelock)
import time
import random
from tinydb_utils import TinyDBUtils
from json import dumps
def generate_test_data(num=1000):
"""生成测试数据(姓名+随机年龄)"""
return [{"name": f"User_{i}", "age": random.randint(18, 60)} for i in range(num)]
def test_insert_performance(db_utils:TinyDBUtils, data):
"""测试插入性能"""
start = time.perf_counter()
# for item in data:
# db_utils.insert(item)
db_utils.insert_multiple(data)
end = time.perf_counter()
print(f"插入{len(data)}条数据耗时:{end - start:.4f}秒")
return end - start
def test_query_performance(db_utils):
"""测试查询性能"""
start = time.perf_counter()
all_data = db_utils.get_all()
end = time.perf_counter()
print(f"查询所有{len(all_data)}条数据耗时:{end - start:.4f}秒")
return end - start
def test_update_performance(db_utils, condition, new_data):
"""测试更新性能"""
start = time.perf_counter()
updated = db_utils.update(condition, new_data)
end = time.perf_counter()
print(f"更新{len(updated)}条数据耗时:{end - start:.4f}秒")
return end - start
def test_delete_performance(db_utils, condition):
"""测试删除性能"""
start = time.perf_counter()
deleted = db_utils.delete(condition)
end = time.perf_counter()
print(f"删除{len(deleted)}条数据耗时:{end - start:.4f}秒")
return end - start
def test_repair_performance(db_utils):
"""测试损坏修复性能"""
# 手动破坏数据库文件
with open(db_utils.db_path, 'w', encoding='utf-8') as f:
f.write('invalid json {')
start = time.perf_counter()
try:
# 触发修复逻辑
db_utils.insert({"name": "TestRepair"})
except Exception as e:
print(f"修复过程异常:{e}")
end = time.perf_counter()
print(f"数据库修复耗时:{end - start:.4f}秒")
return end - start
if __name__ == '__main__':
# 初始化测试数据库(独立于示例数据库)
test_db_path = 'test_perf_db.json'
db_utils = TinyDBUtils(db_path=test_db_path)
# 生成1000条测试数据
test_data = generate_test_data(10000)
# 性能测试流程
print("\n=== 插入性能测试 ===")
insert_time = test_insert_performance(db_utils, test_data)
print("\n=== 查询性能测试 ===")
query_time = test_query_performance(db_utils)
print("\n=== 更新性能测试(年龄+5)===")
update_time = test_update_performance(db_utils, db_utils.Query.age.exists(), {"age": 30})
print("\n=== 删除性能测试(年龄>30)===")
delete_time = test_delete_performance(db_utils, db_utils.Query.age > 30)
print("\n=== 损坏修复性能测试 ===")
repair_time = test_repair_performance(db_utils)
# 输出总耗时统计
print("\n=== 性能测试总结 ===")
print(f"插入耗时: {insert_time:.4f}s")
print(f"查询耗时: {query_time:.4f}s")
print(f"更新耗时: {update_time:.4f}s")
print(f"删除耗时: {delete_time:.4f}s")
print(f"修复耗时: {repair_time:.4f}s")
# 清理测试数据(可选)
# db_utils.truncate()
# import os
# os.remove(test_db_path)
# os.rmdir(f'{test_db_path}.backups')
运行 python .\tinydb_perf_test.py 输出如下
=== 插入性能测试 ===
插入10000条数据耗时:0.0637秒
=== 查询性能测试 ===
查询所有20001条数据耗时:0.0393秒
=== 更新性能测试(年龄+5)===
更新20000条数据耗时:0.0707秒
=== 删除性能测试(年龄>30)===
删除0条数据耗时:0.0558秒
=== 损坏修复性能测试 ===
检测到文件损坏,已备份至:test_perf_db.json.backups\test_perf_db.json.20250514152658.bak
已创建新的空数据库文件
数据库修复耗时:0.0064秒
=== 性能测试总结 ===
插入耗时: 0.0637s
查询耗时: 0.0393s
更新耗时: 0.0707s
删除耗时: 0.0558s
修复耗时: 0.0064s
不过 tinydb 还是有一定局限性的:
- 大量数据单次插入耗时较长,批量插入耗时较短;
- 运行时,会把所有的json加载到内存中,对于内存有限但是数据量大的情况并不是一个好的选择。

浙公网安备 33010602011771号