多进程操作数据库–锁
问题情景:
A进程与B进程对同一个数据库进行操作,A进程不停插入数据,B进程不停删除数据
当B在删除数据的同时A去插入数据,此时A的操作会失败,执行结果返回值为5
问题原因:
操作数据库时,数据库会自动上锁(共享锁、排他锁等),insert语句与delete语句都会上排他锁,delete正在操作时insert无法获取锁,不能进行,该语句执行失败,然后停止。
以下是A进程的demo (A.lua):
#!/usr/bin/lua
--A进程的demo
local sqlite3 = require"lsqlite3" --require 数据库模块
local sn = "123"
local sqlCreatApCfgTable = "CREATE TABLE apCfg(sn text,data text);" --创建数据库表语句
local Sqlinsert = string.format("insert into apCfg(sn,data) values('123','data123');",sn) -- 插入数据语句
local db = sqlite3.open("/test.db") --打开数据库
db:exec(sqlCreatApCfgTable) --执行建表语句
while true do
ret = db:exec(Sqlinsert) --执行插入语句
print(Sqlinsert)
print(ret) --0为sqlite_ok,执行成功
if ret ~= 0 then --执行失败即停止
break
end
end
demo中循环插入数据并打印返回值,ret为sql语句执行的返回值,执行成功时ret为0(SQLITE_OK),执行失败时停止。
以下是B进程的demo (B.lua):
#!/usr/bin/lua
--B进程的demo
local sqlite3 = require("lsqlite3")
local db = sqlite3.open("/test.db")
local Sqldelete = string.format("DELETE FROM apCfg WHERE sn = '%s';","123")
while true do
ret = db:exec(Sqldelete)
print(Sqldelete)
print(ret)
end
先运行A
lua A.lua &
后运行B
lua B.lua &
A打印结果:
insert into apCfg (sn,data) values ('123','data123');
0
insert into apCfg (sn,data) values ('123','data123');
0
insert into apCfg (sn,data) values ('123','data123');
5
#
经查询,5表示数据库正忙,lua的抛出异常提示信息:database is locked (注- exec() 不会抛出异常,nrows()会)
B此时执行delete,并不停止
DELETE FROM apCfg WHERE sn = '123';
0
DELETE FROM apCfg WHERE sn = '123';
0
DELETE FROM apCfg WHERE sn = '123';
0
此时多进程操作数据库,会导致有时操作失败,后续程序会出现错误,因此我们需要添加措施保障sql语句执行正常
解决方法:
使用sqlite3数据库提供的接口sqlite3_busy_handler()
使用sqlite3数据库提供的接口sqlite3_busy_timeout()
这两个函数是C语言中的函数,注册到lua中分别为 busy_handler 与 busy_timeout ,若想在C语言中使用可百度一下。在这里我使用的是busy_handler,方法简单
使用busy_handler 分两步
1.定义回调函数
2.调用busy_handler
busy_handler设置好,启动后就可以生效了,实际代码如下:
#!/usr/bin/lua
--A进程的demo
local sqlite3 = require"lsqlite3" --require 数据库模块
local sn = "123"
local sqlCreatApCfgTable = "CREATE TABLE apCfg(sn text,data text);" --创建数据库表语句
local Sqlinsert = string.format("insert into apCfg(sn,data) values('123','data123');",sn) -- 插入数据语句
local db = sqlite3.open("/test.db") --打开数据库
db:exec(sqlCreatApCfgTable) --执行建表语句
function callback(...) --定义回调函数
print("callback ")
return 1
end
db:busy_handler(callback,1) --调用busy_handler
while true do
ret = db:exec(Sqlinsert) --执行插入语句
print(Sqlinsert)
print(ret) --0为sqlite_ok,执行成功
if ret ~= 0 then --执行失败即停止
break
end
end