数据库事务和锁

标签(空格分隔): 数据库


在查django transaction.atomic()配合 select_for_update使用的过程中,学到了很多东西,记录如下

事务

事务最基本的功能就是保证一批操作要么都成功,要么都失败.值得注意的是, 在一个事务里, 之前的操作对之后的操作是有影响的, 换句话说, 事务里的语句也是一条一条被执行, 而不是等待事务结束后全部执行!

也就是说

with transaction.atomic():
    s = Settlement.objects.first()
    print s.id  # 这条会执行数据库查询操作
    sleep(10)

read uncommit 未提交读

问题: 会读到其他事务还没有提交的改动

优点: Read Uncommitted这种级别,数据库一般都不会用,而且任何操作都不会加锁,这里就不讨论了。 优点就是不用讨论,哈哈哈~

read commit 提交读

优点: 不会读到其他事务还没有commit的改动

问题: 其他事务commit之后会对其造成影响, 即不可重复读, 第一次读和第二次读中间如果有另外一个事务commit了,第二次的结果可能会与第一次不一样

repeat read 重复读

优点: 第一次查到的数据都会被锁上,其他事务不能更改,所以该事务每次读到的都一样.

问题: 会出现"幻读", 这个名字起得不好, 其实也是两次读到的东西不一样,只不过是因为其他事务insert了新行.只有类似count(*)这样操作的时候才会和之前不一样.

很多人容易搞混不可重复读和幻读,确实这两者有些相似。但不可重复读重点在于update和delete,而幻读的重点在于insert。

sql 标准里重复读是不能解决幻读问题的, 但是mysql innode引擎通过MVVP解决了这个问题.mysql 提供了Next-Key锁: 一种行锁和GAP(间隙锁)的合并,行锁上文已经介绍了,接下来说下GAP间隙锁。 GAP锁能够按照区间锁定, 比如你count(*)那就把整个表的间隙都锁住, 其他事务就不能insert到这个区间,从而解决了幻读问题.

Serializable

这个级别很简单,读加共享锁,写加排他锁,读写互斥。使用的悲观锁的理论,实现简单,数据更加安全,但是并发能力非常差。如果你的业务并发的特别少或者没有并发,同时又要求数据及时可靠的话,可以使用这种模式。

这里要吐槽一句,不要看到select就说不会加锁了,在Serializable这个级别,还是会加锁的!

悲观锁和乐观锁

悲观锁: 它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。

乐观锁: 通过版本号(创建出若干个快照)解决多事务,并发修改访问问题.

update on transaction 更新操作对事务的影响

为了保证并发操作, UPDATE 操作都会加上一个读锁, 其他事务不能修改该条记录.

select for update

在查询的时候就加一个悲观锁(x锁), 直到本事务结束 这样就不会有人来修改这条数据了.(在少量并发情况下,完美解决问题)

mysql innode下的三种select

因为innode实现了版本控制的并发机制, 所以能够提供一种任意时刻都可以读的方法(SERIALIZABLE下,所有无格式SELECT语句被 隐式转换成SELECT ... LOCK IN SHARE MODE。):

select . from .;

两种加锁读:

SELECT * FROM parent WHERE NAME = 'Jones' LOCK IN SHARE MODE;

在共享模式执行一个读意味着我们读最新的可用数据,并在我们读的行设置一个共享锁定。共享模式锁防止其它人更新或删除我们已读的行。同时,如果最新的数据属于其它客户端尚未提交的事务,我们等着知道那个事务被提交。这种模式用途是用来检测一条语句存不存在, 如果存在则能保证不会被删掉.不存在保证别人插不进去(Next-Key).

select . from . for update;

读最新的可见数据,在每个它读取的行设置独占锁定。因此,它设置与搜索的SQL UPDATE可能会在行上设置的锁定同样的锁定。

能解决自己问题的文章难找啊!!!

MySQL 5.1 中文手册

Innodb中的事务隔离级别和锁的关系