锁是计算机协调多个 进程 / 线程 并发访问某一资源的机制。加锁在保证了数据的安全操作的同时,增加了额外开销,我们应尽可能不加锁以减少开销。
事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。
① A:原子性(atomicity)
一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。
① C:一致性(consistency)
事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
① I:隔离性(isolation)
一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
① D:持久性(durability)/ 永久性(permanence)
一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作 / 故障不应该对其有任何影响。
举个例子,事务A和事务B操纵的是同一个资源,事务A有若干个子事务,事务B也有若干个子事务,事务A和事务B在高并发的情况下,会出现以下几种问题。
1、脏读
脏读,指事务A读到了事务B还没有提交的数据。
2、不可重复读
不可重复读,指在一个事务里面读取了两次某个数据,读出来的数据不一致。
3、幻读
幻读,就是指在一个事务里面的操作中发现了未被操作的数据。
事务隔离级别,为了解决以上几种问题而诞生。事务隔离级别越高,在并发下会产生的问题就越少,但同时带来较大开销,因此应权衡并发性与性能。
① 未提交读(READ_UNCOMMITTED)
读未提交,即能够读取到没有被提交的数据,该级别隔离机制无法解决脏读、不可重复读、幻读中的任何一种,故很少使用。
② 已提交读(READ_COMMITED (RC))
读已提交,即能够读到那些已经提交的数据,解决了脏读问题,无法解决不可重复读、幻读问题。
③ 可重复读(REPEATABLE_READ (RR))
在数据读出来之后加锁,解决了脏读、不可重复读的问题,无法解决幻读问题。
④ 可串行化(SERLALIZABLE)
最高事务隔离级别,所有事务排队串行执行,完全解决了脏读、不可重复读和幻读的问题了。
我们较常使用的是RC和RR。
各隔离级别 无法解决的问题:
隔离级 | 脏读可能性 | 不可重复读可能性 | 幻读可能性 | 加锁读 |
---|---|---|---|---|
未提交读 READ_UNCOMMITTED | √ | √ | √ | |
已提交读 READ_COMMITED (RC) | √ | √ | ||
可重复读 REPEATABLE_READ (RR) | √ | |||
可串行化 SERLALIZABLE | √ |
查看事务隔离级别:SELECT @@TX_ISOLATION
修改当前会话事务隔离级别使用SET session TRANSACTION ISOLATION LEVEL [隔离级别]
;)
修改全局事务隔离级别使用SET global TRANSACTION ISOLATION LEVEL [隔离级别]
;
所谓乐观锁、悲观锁是一种概念,数据库通常不会提供乐观锁,需要我们自己实现。
乐观锁
指在操作数据时,乐观地认为操作不会导致冲突,不做任何处理,在更新后判断是否有冲突。
悲观锁
指在操作数据时,悲观地认为操作会导致冲突,每次操作都必须加锁,悲观锁所需的开销较大,较为耗时。
悲观锁在Mysql中自带的具体实现有两种:共享锁
、排他锁
。
① 共享锁(读锁、S锁)
共享锁指的就是对于多个不同的事务,对同一个资源共享加锁。其他事务只可读,不可写。
共享锁允许多个不同事物对同一个资源加锁,其他事务可以读取数据,检查数据是否已被修改,又称读锁。
SELECT
语句,InnoDB引擎不会加任何锁。LOCK IN SHARE MODE
② 排他锁(写锁、X锁)
排它锁与共享锁相对应,就是指对于多个不同的事务,对同一个资源只能有一把锁。即其他事务不可读也不可写。
排他锁具有一对一的性质,所以一般在写数据(UPDATE
、INSERT
、DELETE
操作)的时候使用,不得被其他事务干扰,故又称写锁。
UPDATE
、INSERT
、DELETE
操作的数据集加排他锁。FOR UPDATE
Mysql常见引擎对页锁、行锁、表锁的支持:
页锁 | 行锁 | 表锁 | |
---|---|---|---|
MyISAM | √ | ||
BDB | √ | √ | |
InnoDB | √ | √ |
① 页锁
开销和加锁速度介于表锁和行锁之间;会出现死锁;锁定粒度介于表锁和行锁之间,并发度一般。
② 行锁
开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率低,并发度高。
③ 表锁
开销小,加锁快;不会出现死锁;锁定力度大,发生锁冲突概率高,并发度最低。
当程序中可能出现并发的情况时,就需要保证在并发情况下数据的准确性,以此确保当前用户和其他用户一起操作时,所得到的结果和他单独操作时的结果是一样的。这就叫做并发控制。
多版本并发控制 (MultiVersionConcucrrencyControl, MVCC)
解决问题:
两段锁协议:每个事务的执行可以分为两个阶段:生长阶段(加锁阶段)和衰退阶段(解锁阶段)。
加锁阶段:
在该阶段可以进行加锁操作。在对任何数据进行读操作之前要申请并获得S锁,在进行写操作之前要申请并获得X锁。加锁不成功,则事务进入等待状态,直到加锁成功才继续执行。
解锁阶段:
当事务释放了一个封锁以后,事务进入解锁阶段,在该阶段只能进行解锁操作不能再进行加锁操作。