Sam*_*Sam 5 python mysql concurrency process
我探索了一些解决方案,但没有取得太大成功。我有两个 python 进程(使用子进程中的 Popen 创建)和一个 mysql 表(称为persone
),其中包含一行两列:Age:0, id:1
。一个进程选择该行,获取它的年龄并将其加一。这样做了 1000 次。第二个做同样的事情,但是减少它。我并行运行每个进程。
理论上,我希望最后年龄保持为零。问题是我在 mymain.py 末尾不断获得 100 到 -100 之间的随机值,我猜这意味着同时进行了访问,从而损坏了数据库。我想知道我错过了什么?
这是我用来测试这个的代码:
ajoutAge.py
import MySQLdb as lite
num=1000
connexion = lite.connect(host="localhost", user="root", passwd="123", db="test")
with connexion:
for i in range(num):
cur = connexion.cursor()
cur.execute('start transaction')
cur.execute('select Age from persone where id=1')
age = cur.fetchone()[0]
cur.execute('update persone set Age='+str(age+1)+' where id=1')
# cur.execute('rollback')
cur.execute('commit')
print "ajout Done"
Run Code Online (Sandbox Code Playgroud)
RetraitAge.py
import MySQLdb as lite
num=1000
connexion = lite.connect(host="localhost", user="root", passwd="123", db="test")
with connexion:
for i in range(num):
cur = connexion.cursor()
cur.execute('start transaction')
cur.execute('select Age from persone where id=1')
age = cur.fetchone()[0]
cur.execute('update persone set Age='+str(age-1)+' where id=1')
cur.execute('commit')
print "retrait Done"
Run Code Online (Sandbox Code Playgroud)
mymain.py
from subprocess import Popen
a=Popen("python ajoutAge.py", shell=True)
aa=Popen("python ajoutAge.py", shell=True)
b=Popen("python retraitAge.py", shell=True)
bb=Popen("python retraitAge.py", shell=True)
a.communicate()
aa.communicate()
b.communicate()
bb.communicate()
print "Main done"
Run Code Online (Sandbox Code Playgroud)
我的表使用 InnoDB 作为存储引擎。
你正在创造一个竞争条件。
每次您选择当前年龄时,该年龄与您的更新之间都会有一个瞬间。在那一瞬间,另一个进程可能正在(而且显然有时正在)更新该值。
因此,在第一个过程中,当您将值更新为 Age+1 时,它会根据稍微过时的值递增。
要解决此问题,您有几个选择:
SELECT ... FOR UPDATE
,这会锁定该行,防止其他进程在您完成事务之前对其进行修改。您需要确保不提交 SELECT 事务,这将释放锁,从而允许另一个竞争条件。
UPDATE persone SET age = age+1 WHERE id=1
(或者age=age-1
当然)。换句话说,在更改值的同一表达式中读取值,因此它是原子的,并且没有并发进程可以“潜入”并在其间更改它。
归档时间: |
|
查看次数: |
2549 次 |
最近记录: |