SQLite3在NFS下会不会导致文件写乱?

一、搭建NFS实验环境:

以centos8系统为例:

Linux 4.19.90-23.8.v2101.ky10.x86_64 #1 SMP Mon May 17 17:08:34 CST 2021 x86_64 x86_64 x86_64 GNU/Linux

nfs服务端:

bash 复制代码
yum install -y nfs-utils rpcbind
mkdir -p /usr/share/nfs/share
echo "/usr/share/nfs/share 10.10.10.123/24(rw,sync,no_root_squash)" >> /etc/exports
exportfs -a
systemctl enable rpcbind nfs-server
systemctl start rpcbind nfs-server
exportfs -v
firewall-cmd --permanent --zone=public --add-service=nfs --add-service=mountd --add-service=rpc-bind --add-service=nscd --add-service=nfs-lock --add-service=nfs-idmap
firewall-cmd --reload

nfs客户端:

bash 复制代码
yum install -y nfs-utils
mkdir -p /mnt/nfs_share
mount -t nfs 10.10.10.123:/usr/share/nfs/share /mnt/nfs_share

二、基于NFS压测SQLite3的WAL模式:

压测脚本 wal_performance_test.py:

python 复制代码
import time
import sqlite3
import subprocess
import re
from datetime import datetime

def wal_performance_test():
    # WAL模式
    dev, ip = get_default_interface_ip()
    start = time.perf_counter()
    for i in range(100):
        try:
            conn = sqlite3.connect('/mnt/nfs_share/wal_test.db')
            # 设置WAL模式必须将journal_mode设为WAL
            conn.execute("PRAGMA journal_mode=WAL;")  
            # 调整同步策略为NORMAL提升性能
            conn.execute("PRAGMA synchronous=NORMAL;") 
            # 设置WAL文件自动清理的检查点阈值
            conn.execute("PRAGMA wal_autocheckpoint=1000;") 
            conn.execute('CREATE TABLE IF NOT EXISTS data (id INT, host VARCHAR(15))')
            conn.execute("BEGIN TRANSACTION")
            for ii in range(100):
                conn.execute("INSERT INTO data VALUES (?,?)", (i*100+ii,ip,))
            conn.commit()
            conn.close()
            time.sleep(1)
        except Exception as e:
            now = datetime.now()
            print(now, e)
            time.sleep(1)
    wal_time = time.perf_counter() - start
    print(f"WAL模式: {wal_time:.6f}s")

def sqlite_performance_test():
    # 常规模式
    dev, ip = get_default_interface_ip()
    start = time.perf_counter()
    for i in range(100):
        try:
            conn = sqlite3.connect('/mnt/nfs_share/wal_test.db')
            conn.execute('CREATE TABLE IF NOT EXISTS data (id INT, host VARCHAR(15))')
            conn.execute("BEGIN TRANSACTION")
            for ii in range(100):
                conn.execute("INSERT INTO data VALUES (?,?)", (i*100+ii,ip,))
            conn.commit()
            conn.close()
            time.sleep(1)
        except Exception as e:
            now = datetime.now()
            print(now, e)
            time.sleep(1)
    normal_time = time.perf_counter() - start
    print(f"常规模式: {normal_time:.6f}s")

def get_default_interface_ip():
    """通过路由表获取默认网卡和IP"""
    try:
        # 获取默认路由接口 - 使用 stdout=subprocess.PIPE
        result = subprocess.run(
            ['ip', 'route', 'show', 'default'],
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            universal_newlines=True,
            check=True
        )
        # 解析默认路由行
        match = re.search(r'dev\s+(\w+)', result.stdout)
        if match:
            interface = match.group(1)
            # 获取该接口的IP地址
            ip_result = subprocess.run(
                ['ip', '-4', 'addr', 'show', interface],
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                universal_newlines=True,
                check=True
            )
            # 解析IP地址
            ip_match = re.search(r'inet\s+(\d+\.\d+\.\d+\.\d+)', ip_result.stdout)
            if ip_match:
                return interface, ip_match.group(1)
    except subprocess.CalledProcessError as e:
        print(f"命令执行失败: {e}")
        print(f"错误输出: {e.stderr}")
    except Exception as e:
        print(f"错误: {e}")
    return None, None


wal_performance_test()

分别在客户机124和125执行:

bash 复制代码
python3 wal_performance_test.py

两个客户端交替出现错误 disk I/O error,总计出现6条:

test@124 wal$ python3 wal_performance_test.py

2025-12-09 14:33:21.776389 disk I/O error

2025-12-09 14:33:47.146377 disk I/O error

2025-12-09 14:34:39.921037 disk I/O error

WAL模式: 105.810731s

test@125 wal$ python3 wal_performance_test.py

2025-12-09 14:33:20.745065 disk I/O error

2025-12-09 14:34:13.513216 disk I/O error

2025-12-09 14:34:38.881051 disk I/O error

WAL模式: 101.554718s

三、验证数据:

在123机器执行:

bash 复制代码
yum install -y sqlite
sqlite3 wal_test.db
sqlite> select count(*) from data;
19400
sqlite> select count(*) from data where host='10.10.10.124';
9700
sqlite> select count(*) from data where host='10.10.10.125';
9700

总的执行数据量2*100*100=20000,减去6次失败 20000-600=19400,数据量正好。

也就是不会导致数据写乱,但是加锁失败会报错,需要客户端自己重试,如果不重试数据会丢失。

四、基于NFS压测SQLite的三种事务类型:

压测脚本 nfs_transaction_test.py:

python 复制代码
import time
import sqlite3
import sys
from datetime import datetime

def nfs_deferred_transaction_test():
    start = time.perf_counter()
    for i in range(10000):
        try:
            conn = sqlite3.connect('/mnt/nfs_share/nfs_test.db')
            conn.execute("BEGIN TRANSACTION")
            row = conn.execute('SELECT * FROM data LIMIT 1').fetchone()
            counter = row[0] + 1
            conn.execute("UPDATE data SET counter=?", (counter,))
            conn.commit()
            conn.close()
            time.sleep(0.01)
        except Exception as e:
            now = datetime.now()
            print(now, e)
            time.sleep(0.01)
    cost_time = time.perf_counter() - start
    print(f"deferred: {cost_time:.6f}s")

def nfs_immediate_transaction_test():
    start = time.perf_counter()
    for i in range(10000):
        try:
            conn = sqlite3.connect('/mnt/nfs_share/nfs_test.db')
            conn.execute("BEGIN IMMEDIATE TRANSACTION")
            row = conn.execute('SELECT * FROM data LIMIT 1').fetchone()
            counter = row[0] + 1
            conn.execute("UPDATE data SET counter=?", (counter,))
            conn.commit()
            conn.close()
            time.sleep(0.01)
        except Exception as e:
            now = datetime.now()
            print(now, e)
            time.sleep(0.01)
    cost_time = time.perf_counter() - start
    print(f"immediate: {cost_time:.6f}s")

def nfs_exclusive_transaction_test():
    start = time.perf_counter()
    for i in range(10000):
        try:
            conn = sqlite3.connect('/mnt/nfs_share/nfs_test.db')
            conn.execute("BEGIN EXCLUSIVE TRANSACTION")
            row = conn.execute('SELECT * FROM data LIMIT 1').fetchone()
            counter = row[0] + 1
            conn.execute("UPDATE data SET counter=?", (counter,))
            conn.commit()
            conn.close()
            time.sleep(0.01)
        except Exception as e:
            now = datetime.now()
            print(now, e)
            time.sleep(0.01)
    cost_time = time.perf_counter() - start
    print(f"exclusive: {cost_time:.6f}s")

def main():
    conn = sqlite3.connect('/mnt/nfs_share/nfs_test.db')
    conn.execute('CREATE TABLE IF NOT EXISTS data (counter INT)')
    row = conn.execute('SELECT * FROM data LIMIT 1').fetchone()
    if row is None:
        conn.execute("INSERT INTO data (counter) VALUES (?)", (0,))
    conn.commit()
    conn.close()
    command = sys.argv[1] if len(sys.argv)>1 else ""
    if command == "" or command == "1":
        nfs_deferred_transaction_test()
    elif command == "2":
        nfs_immediate_transaction_test()
    elif command == "3":
        nfs_exclusive_transaction_test()
    else:
        print("错误:参数不足或命令无效")

if __name__ == "__main__":
    main()

1、deferred事务:

两台客户机都会出现大量的报错:

test@124 wal$ python3 nfs_transaction_test.py 1

。。。。。。

2025-12-10 01:06:16.459700 database is locked

2025-12-10 01:06:16.695814 database is locked

2025-12-10 01:06:16.777952 database is locked

deferred: 287.597685s

test@125 wal$ python3 nfs_transaction_test.py 1

。。。。。。

2025-12-10 01:06:16.591695 database is locked

2025-12-10 01:06:16.657538 database is locked

2025-12-10 01:06:16.848156 database is locked

deferred: 385.892006s

数据库中的计数也少了:

sqlite> select * from data;

12948

sqlite> select * from data;

Error: database is locked

sqlite> select * from data;

13018

sqlite> select * from data;

Error: database is locked

sqlite> select * from data;

13284

sqlite> select * from data;

15139

sqlite> select * from data;

15139

2、immediate事务:

两台客户机仅有一条报错产生:

test@124 wal$ python3 nfs_transaction_test.py 2

immediate: 347.385314s

test@125 wal$ python3 nfs_transaction_test.py 2

2025-12-10 00:42:48.581516 disk I/O error

immediate: 353.346707s

从123使用linux工具查询(deferred事务)会偶尔出现报错,但最终的数据是正确的。

root@localhost share# sqlite3 nfs_test.db

SQLite version 3.32.3 2020-06-18 14:00:33

Enter ".help" for usage hints.

sqlite> select * from data;

865

sqlite> select * from data;

Error: database is locked

sqlite> select * from data;

Error: database is locked

sqlite> select * from data;

1275

sqlite> select * from data;

Error: database is locked

sqlite> select * from data;

9723

sqlite> select * from data;

20000

sqlite>

3、exclusive事务:

两台客户机都没有报错:

test@124 wal$ python3 nfs_transaction_test.py 3

exclusive: 329.862279s

test@125 wal$ python3 nfs_transaction_test.py 3

exclusive: 342.372154s

从123使用linux工具查询(deferred事务)会偶尔出现报错,但最终的数据是正确的。

root@localhost share# sqlite3 nfs_test.db

SQLite version 3.32.3 2020-06-18 14:00:33

Enter ".help" for usage hints.

sqlite> select * from data;

Error: database is locked

sqlite> select * from data;

427

sqlite> select * from data;

Error: database is locked

sqlite> select * from data;

685

sqlite> select * from data;

20000

sqlite>

--end--

相关推荐
兵慌码乱7 天前
面向桌面端的资产管理系统分层架构设计与核心模块实现
python·系统架构·sqlite·pyqt5·数据库设计·桌面应用开发·mvc架构
兵慌码乱8 天前
基于Python+PyQt5+SQLite的药房管理系统实现:事务一致性与界面解耦全流程解析
python·sqlite·信号与槽·pyqt5·数据库设计·桌面应用开发·事务处理
伶俜6614 天前
鸿蒙原生应用实战(十八)ArkUI 记账本:SQLite 账单 + 图表统计 + 分类管理
jvm·sqlite·harmonyos
伶俜6614 天前
鸿蒙原生应用实战(二十)ArkUI 课程表 App:Grid 网格 + SQLite 存储 + 周次切换 + 上课提醒
华为·sqlite·harmonyos
Lanceli_van15 天前
SQLite 3.45.2(sqlite-autoconf-3450200)ARM 交叉编译完整步骤
arm开发·sqlite
王小王-12315 天前
基于电脑硬件市场数据分析与可视化系统
数据库·数据分析·django·sqlite·电脑·电脑硬件数据·电脑硬件市场分析
winfredzhang15 天前
给本地图库的“人“加上属性:DeepFace + MediaPipe Pose 联合分析(含 GitHub 镜像踩坑)
python·sqlite·mediapipe·打标签·场景和属性
zh_xuan16 天前
PC端操作SQLite数据库
数据库·c++·sqlite
winfredzhang16 天前
用 wxPython + 通义千问 VL 打造一款“批量人物图像识别“桌面应用
python·sqlite·wxpython·qwen 3.7max·分析照片
爱和冰阔落16 天前
【MCP实战】从0写一个本地工具服务器:文件搜索、SQLite查询与安全边界
服务器·安全·sqlite