我们经常会遇到这样子的问题,我给朋友赚钱100,分为两步:
1)我的账户-100
2)朋友账户 +100
看似需求很简单,但是如果在上面的步骤1)结束后,系统崩溃了怎么办?
数据库中有事物管理,也就是对于一系列的数据库操作,要么全部成功,要不全部失败,这样就保证了数据的一致性。这就是事务管理的原子性。
以mysql的innodb的事务管理为例,事务管理执行过程如下:
1)开启事务管理:start transaction
2)回退操作:rollback,回退到开启事物管理之前,如果在事务管理中设置了savepoint saveplace,执行rollback to saveplace的时候,会会退到设置savepoint之前。
3)事物提交:commit。将数据结果永久写入数据库中,没办法再执行rollback回退了。
如果出现上面说的异常情况,我们就需要用到数据库的事务管理。
如下图:
我们正常的向数据库插入两条数据:
执行后,会在数据库中多出两条数据,
现在我们将这两条执行语句改一改,将下面一条执行语句改错,在看执行结果:
这时候,我们查看数据库,发现上面一条数据被正常执行并插入数据库了
很明显,上面这个情况,不是我们希望看到的,我们希望的是,在第二条执行语句报错的情况下,我们整个执行数据库的事物都不执行。
我们再将代码修改一下,加上python里面的事务管理 :
from django.db import transaction
我们再去查看数据库,就会发现,数据库没有数据,这就达到我们的需求了。
上面这种情况是在单线程下的示例,是没有问题的,但是如果是多线程时,会出现下面这种情况:
A给B转账100, 这是,程序会先读取B账户,给B账户加100,这个过程中,C也给B转账100,新线程也会操作B账户,给B账户加100,在A转账完成后,发现实际上B的账户只增加了100元。
这种情况下,为了保证数据的一致性,我们需要引入线程的隔离性,即保证每个线程看到的数据是一致的。这个需要借助【锁】来实现。
处理这种情况,我们可以用【同步锁】:
如下,我们创建五个线程同时去操作一个同账户,每次给该账户增加100元,按照我们的预期,最后结果应该是600,但是,从下面的打印,我们可以看出来,最终的结果是300,并不是我们要的600.
这就是上面说的多线程情况下的数据不一致问题。
下面,我们将代码修改一下:
上面的代码中,我们就加入了同步锁,threading.Lock()
在数据可能会被同时修改的地方,加上l.accquire() 获得同步锁,然后在资源操作完成后,再将锁释放掉l.release(),给下一个线程使用,这样就避免了数据同时被多个线程修改的问题。