SQLite的入门级项目学习记录(二)
再补充一些基础知识:
并行操作的问题
1、可以多游标同时运行
SQLite,对于同一个连接sqlite3.connect(db_file),可以同时创建多个游标,每个游标都是独立的,可以执行各自的SQL命令序列。
import sqlite3
# 创建数据库连接
conn = sqlite3.connect('example.db')
# 创建第一个游标
cursor1 = conn.cursor()
cursor1.execute("SELECT * FROM table_1")
# 创建第二个游标
cursor2 = conn.cursor()
cursor2.execute("SELECT * FROM table_2")
# 获取第一个游标的结果
results1 = cursor1.fetchall()
for row in results1:
print(row)
# 获取第二个游标的结果
results2 = cursor2.fetchall()
for row in results2:
print(row)
# 关闭游标和连接
cursor1.close()
cursor2.close()
conn.close()
在这个例子中,cursor1和cursor2是两个独立的游标,它们可以同时执行不同的查询任务。但是,所有的游标操作都会通过同一个数据库连接conn来与数据库进行交互。
需要注意的是,尽管可以创建多个游标,但SQLite数据库是单线程的,这意味着在任何给定的时间点上,只有一个游标的操作可以被执行。如果需要并发执行多个数据库操作,可能需要考虑使用其他的数据库系统或者使用多线程/多进程的方式来处理。
2、关于多游标多线程访问同一个数据库
SQLite 支持多线程访问数据库,但是它并不是为高并发设计的。
SQLite 使用文件锁来同步对数据库文件的访问,这意味着多个线程可以打开数据库连接并对数据库进行读操作,但是在任何给定时间点上,只允许一个线程进行写操作。
在多线程环境中使用 SQLite 时,应该注意以下几点:
读写锁:SQLite 使用读写锁(Read-Write Lock)来控制对数据库的并发访问。多个线程可以同时获得读锁,但是当有线程持有写锁时,其他线程无法获得读锁或写锁。
事务:在多线程环境中,每个线程应该使用自己的数据库连接,并且在进行写操作时应该使用事务来确保数据的一致性。
连接池:由于 SQLite 的并发性能有限,使用连接池可以提高性能,因为连接可以被重用,而不是为每个线程创建一个新的连接。
线程安全:确保你的应用程序代码是线程安全的,特别是在多线程环境下共享资源时。
性能考虑:由于 SQLite 的设计限制,它在高并发写操作的场景下可能表现不佳。如果你的应用程序需要处理大量的并发写操作,可能需要考虑使用更适合高并发的数据库系统,如 PostgreSQL 或 MySQL。
import sqlite3
import threading
# 定义工作函数
def worker(conn):
cursor = conn.cursor()
cursor.execute("INSERT INTO some_table (column1, column2) VALUES (?, ?)", ('value1', 'value2'))
conn.commit()
cursor.close()
# 创建数据库连接
conn = sqlite3.connect('example.db', check_same_thread=False)
# 创建表
conn.execute("CREATE TABLE IF NOT EXISTS some_table (column1 TEXT, column2 TEXT)")
# 创建多个线程
threads = []
for i in range(10):
t = threading.Thread(target=worker, args=(conn,))
threads.append(t)
t.start()
# 等待所有线程完成
for t in threads:
t.join()
# 关闭连接
conn.close()
3、关于SQLite 的锁机制
SQLite 使用文件锁来处理多个线程同时对同一条记录执行写操作的情况。当一个线程尝试对一条记录执行写操作时,SQLite 会对这条记录加写锁。如果此时有其他线程已经持有该记录的写锁或者正在等待写锁,那么尝试获取写锁的线程将会被阻塞,直到前面的写操作完成并且锁被释放。
SQLite 的锁机制如下:
共享锁(Shared Locks):允许多个线程同时读取同一条记录,但不允许写入。
互斥锁(Reserved Locks):表示一个线程打算写入一条记录,但尚未开始写入。在这个状态下,其他线程仍然可以获得共享锁进行读取,但不能获得写锁。
未决锁(Pending Locks):当一个线程试图获取写锁,但发现已经有其他线程持有共享锁或保留锁时,它会进入未决状态。在这个状态下,线程会等待直到可以获取写锁。
独占锁(Exclusive Locks):当一个线程获得写锁时,其他线程无法获得任何类型的锁,直到写锁被释放。
如果多个线程同时尝试写入同一条记录,SQLite 会按照它们尝试获取锁的顺序来处理。一旦一个线程获得了写锁,其他线程就必须等待,直到写锁被释放。这确保了即使在多线程环境下,SQLite 也能保持数据的一致性和完整性。
然而,由于 SQLite 的这种锁机制,它在高并发写操作的场景下可能表现不佳。如果应用程序需要处理大量的并发写操作,可能需要考虑使用更适合高并发的数据库系统,如 PostgreSQL 或 MySQL。