数据库中的锁与事务

原文地址: CoderGO : 数据库中的锁与事务

引言

什么事事务?事务是一组原子性的SQL语句,这组语句要么全部成功,要么发生失败时全部不执行。一个良好的支持事务数据库系统必须支持如下4个特性:

  • 原子性(atomicity)
  • 一致性(consistency)
  • 隔离性(isolation)
  • 持久性(durability)

事务中的隔离性在数据库系统中一般采用锁来实现,针对不同的隔离级别有不同的隔离策略,要想在性能和隔离性中作出均衡,在数据库系统种是一个难点。

一、事务的特征

一个好的事务数据库,必须具备ACID特征:

  • 原子性:
    一个事务必须是不可分割的最小工作单元,整个事务中所有操作要么全部提交成功,或者全部失败回滚。
  • 一致性:
    数据库总是从一个一致性的状态转换到另一个一致性的状态,事务不会破坏数据库的完整性和业务上的一致性。
  • 隔离性:
    一个事务所做的修改在最终提交前,对其他事务是不可见的。在并发时,每个事务都拥有自己的工作和数据空间,不会相互干扰。
  • 持久性:
    一旦事务提交,所做的修改会永久保存到数据库系统中,及时系统崩溃,修改的数据也不会丢失。

数据库系统采用日志来保证事物的原子性,一致性和持久性。日志保存了用户对数据库所做的更改,如果事务在执行过程中发生错误,则数据库可以根据日志所记录的更改过程,回退到事务开始执行前的状态。

隔离性有多个级别(未提交读,提交读,可重复读,可串行化),一般数据库采用锁机制实现事务的隔离性,并达到某个级别,此时就可以保证事务的隔离性。当多个事务同时更新数据库中的相同数据时,只允许持有锁的事务才能更改数据,其他事务必须等待获得锁后才能更改数据。

二、隔离级别

SQL标准中定义了四种隔离级别,每种级别规定了一个事务中所做的修改,哪些时事务间可见,那些事事务内可见,哪些不可见,低级别的隔离往往具有更高的并发性能,开销也低。

  • 未提交读:
    事务中的修改,没有提交,其他事务也可以读取未提交的数据,也叫脏读。实际中一般很少用。
  • 提交读:
    大多数数据库系统的默认隔离级别是提交读,但MySQL不是。提交读满足隔离性的简单定义:一个事务开始时,只能看见已经提交的事务所做的修改。这个级别可能会在事务开启前后两次读到的数据不一致,又称为不可重复读
  • 可重复读:
    可重复读解决了脏读和不可重复读的问题,保证在同一个事务中多次读取 同样的数据结果是一致的(即使有其他事务在此期间对该记录进行了修改)。仍存在幻读的问题:当某个事务在读取某范围 内的记录时,另一个事务在该范围内插入了新的记录,之前的食物重新读取该范围时,会产生新的记录(幻行)。
    在MySQL中默认隔离级别为可重复读,并且InnoDB使用MVCC(多版本并发控制)解决了幻读的问题。
  • 可串行化:
    最高的隔离级别。强制事务串行化,避免了幻读的问题。会在读取的每一行数据上加锁,会导致锁争用问题,实际很少使用。

ANSI SQL隔离级别

隔离级别 脏读可能性 不可重复读可能性 幻读可能性 加锁读
read uncommitted Yes Yes Yes No
cead committed No Yes Yes No
repedtabel read NO No Yes No
serializable No No No Yes

三、数据库中的锁

在数据库中多个SQL语句在同一时刻修改数据,会产生并发控制的问题,如果不加以控制,就会造成事务中的隔离性被破坏,引起不可预知的错误。

1.锁的类型

实现并发访问可以采用两种类型的锁,读锁/共享锁和写锁/排它锁:

  • 读锁/共享锁:共享的锁,多个客户端可以同时读取一个资源,互补干扰。
  • 写锁/排它锁:一个锁会阻塞其他的写锁和读锁,确保一个时刻只有一个客户端对通过一个数据进行修改写入。

在数据库系统中,锁对于用户时透明的,一般情况下,用户不用关心什么时候使用锁。

2.锁的粒度

锁定的数据量越少,系统的并发性能越好,因此数据库的锁只对需要修改的数据进行锁定。但加锁也需要消耗资源,锁的各种操作,包括获得,检查锁状态和释放锁都会增加系统开销。因此有必要在锁的开销和数据安全性之间平衡。一般情况下,数据库系统通常采用行级锁,即锁定修改的数据行。

在MySQL中提供了多种选择,每种引擎均会实现自己的锁策略和粒度。下面是两个最重要的锁策略。

  • 表锁
    是开销比较小的策略,会锁定整张表。用户对表进行写操作,需要先获得锁,并且会阻塞用户对该表的所有读操作。
    数据库中对表进行修改,如alter table会使用到表锁,会锁定整张表,因此此类操作在数据库中应该谨慎使用。
  • 行锁
    行锁可以高效的支持并发,当然锁开销也是最大。MySQL的InnoDB引擎中实现了行锁,在用户写数据时,只锁定需要操作的数据行,相比于表锁并发度更好。

  1. 《高性能MySQL》
  2. 《精通Hibernate:Java对象持久化技术详解》

你可能感兴趣的:(数据库)