数据库事务的四大特性(ACID)
- 原子性: 事务包含的所有操作要么全部成功, 要么全部失败回滚.
- 一致性: 简单的来说A和B一共有5000元, 不管他们两个之间怎么转账, 他们两的钱相加起来还是5000元
- 隔离性: 并发的情况下, 每个用户访问同一张, 数据库为每个用户开启的事务, 不能被其它的事务所干扰, 多个事务之间要相互隔离
- 事务一旦被提交了, 那么数据库中的数据的改变就是永久的, 即使在数据库系统遇到故障的情况下也不会丢失提交的事务
数据库的隔离级别
如果数据库不考虑隔离性的的话会发生什么?
- 脏读: 事务在处理过程中读取了另一个未提交的事务中的数据
- 不可重复读: 一个事务多次查询相同数据, 结果却不相同
- 虚读(幻读): 事务A把表中的所有1改为了2, 此时B又插入了1进表中, 这时A查看修改结果, 发现还有一条没有被修改
- 不可重复读和脏读的区别是, 脏读是读取了另一个未提交的数据, 而不可重复读是读取了前一个事务提交的数据
- 幻读和不可重复读都是读取了前一条已经提交的数据, 不同的是不可重复读查询的都是同一项数据, 而幻读针对的是一批数据
mysql的四种隔离级别
- 串行化: 避免脏读 不可重复读 幻读发生, 一个事务一个事务的执行
- 可重复读: 避免脏读 不可重复读的发生, 无论其他的事务是否修改并提交了数据, 在这个事务中不受其它事务影响(mysql的默认隔离级别)
- 读取已提交: 可避免脏读的发生
- 读未提交: 最低级别, 什么都无法保证
django中ORM使用事务(使用事务有多种方式, 说两种最常用的)
- 装饰器: 就django而言, 把整个视图中的所有Mysql数据库的操作都看成一个事务, 范围太大, 不够灵活, 而且无法直接作用于类视图
from django.db import transaction
@transaction.atomic
def viewfunc(request):
......
- 上下文管理器: 可以灵活的有选择的把某些Mysql数据库的操作看做一个事务, 而不用关心视图的类型, 推荐使用
from django.db import transaction
def viewfunc(request):
......
with transaction.atomic():
......
- 事务中的保存点: 可以在事务中创建保存点来记录数据的特定状态, 数据库出现错误时, 可以回滚到保存点的状态
from django.db import transaction
save_id = transaction.savepoint()
transaction.savepoint_rollback(save_id)
transaction.savepoint_commit(save_id)
乐观锁和悲观锁以及任务队列(用户并发下单的解决办法)
- 悲观锁: 和线程中的互斥锁差不多, 就是当一个事务查询某条记录时, 加锁, 不给别人访问
- 乐观锁: 乐观锁并不是真实存在的锁, 而是在即将跟新的数据的时候, 再去数据库中查询该条数据还是不是原来查询的值, 如果没变, 则执行跟新操作, 反之不在执行
- 任务对列: 将下单的逻辑放到任务队列中, 并将并行转为串行, 所有人排队下单, 比如开启只有一个进程的Celery, 一个订单一个订单的处理.
- 上面说了数据库的隔离级别, 当要使用乐观锁的时候, 就不能使用可重复读这个默认隔离级别了, 要改为读取已提交, 这样才能保证正常的使用乐观锁.