随笔分类
并发控制
无论何时,只要有多个查询需要在同一时刻修改数据,都会产生并发控制的问题.
MySQL在服务器层和存储引擎层进行了相关的并发控制.
读写锁
在处理并发读和写的过程时,可以通过实现一个由两种类型组成的锁系统来进行并发的控制,即共享锁(shared lock)和排它锁(exclusive lock),也叫读锁(read lock)和写锁(write lock).
读锁是共享的,多个客户可同一时刻对数据进行读取
写锁是排他的,即一个写锁会阻塞其它的写锁和读锁,这也是处于安全的考虑,也只有这样,才能保证同一时刻只有一个线程在修改数据
实际的数据库系统中,时刻都在发生锁定,当有用户在修改某一部分数据时,MySQL会锁定这部分数据,以防止其它用户读取统一数据(脏读),大多数时候,MySQL的内部管理是透明的.
锁粒度
理想的方式是,只对修改的数据进行精确锁定,任何时候,在给定的资源上,锁定的数据量越少,则系统的并发程度越高,只要相互之间不发生冲突即可.
问题是加锁也需要消耗系统资源,锁的各种操作,包括获得锁,检查锁是否已经解除、释放锁等,这些都会增大系统的开销.
如果系统花费大量时间在锁的管理控制上面,而非数据的读取,那这便会对系统的性能造成影响
锁策略
:在锁的开销和数据的安全性之间寻求平衡,此平衡会影响到性能.
MySQL提供了多种选择,每种 MySQL存储引擎可以实现自己的锁策略和锁粒度.
表锁
table lock
表锁是 MySQL中最基本也是开销最小的锁策略.
锁定整张表
某些特定的场景下,表锁也可能有良好的性能.
READ LOCAL表锁支持某些类型的并发写操作
写锁也会比读锁有更高的优先级
尽管存储引擎可以管理自己的锁(即有锁机制),MySQL本身还是会使用各种有效的表锁来实现不同的目的.
行级锁
可以最大程度地支持并发处理
,因为行级锁实现了锁颗粒度的最小化,最大化地降低了同一资源发生争抢的概率,
同时也带来了最大的锁开销
,由于锁定资源的颗粒度最小,所以每次获取锁和释放锁要干的事情也就越多,带来的消耗也就越大.
行级锁只在存储引擎层实现
,而 MySQL服务器层没有实现,服务器层完全不了解存储引擎层的锁实现,所有的存储引擎都实现了自己的锁机制.
事务
事务就是一组原子性的 SQL查询,或者说一个独立的工作单元
事务内的语句,要么全部执行成功,要么全部执行失败.
数据库执行引擎会对数据库应用改组查询的全部语句
一个运行良好的事务处理系统,应当具备 ACID
标准特征:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability).
-
原子性:一个事务必须视为不可分割的工作单元. 对于一个事务而言,不可能只执行其中的一部分操作.
-
一致性:数据库总是从一个一致的状态转换到另一个一致的状态.
一致性是
事务执行的最终目的
,一致性即满足因果关系的守恒定律,原子性、隔离性、持久性都是为了实现一致性而存在的. -
隔离性:一个事务所做的修改在最终提交之前,对其它的事务都是不可见的
-
持久性:一旦事务提交,所做的修改便会永久的保存到数据库中
这里的持久化其实是个模糊的概念,实际上持久化也是分很多不同的等级的,有的持久化策略提供非常强的安全保障,而有些则未必.
且不可能做到 100%的持久性保证的策略存在(否则又何来备份能够增加持久性之说.)
就像锁粒度的升级会增加系统开销一样,这种事务处理过程中额外的安全性,也会需要数据库系统做更多的额外工作.
一个实现了 ACID的数据库,通常会需要更强的 CPU处理能力、更大的内存和更多的存储空间
.
隔离级别
在 SQL标准中定义了 4种
隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的.
较低级别的隔离通常可以执行更高的并发
,系统的开销也更低.
每种存储引擎实现的隔离级别不进相同.
-
READ UNCOMMITTED(未提交读)
在 READ UNCOMMITTED级别,事务中所做的修改即便没有提交,对其它事务也是可见的
事务可以读取未提交的数据,这也叫脏读(Dirty Read)
脏读会导致许多问题,且未提交读实际上并没有比其它级别好太多,却少了其它级别的很多好处,除非真的有非常必要的理由,在实际应用中一般很少使用. -
READ COMMITTED(提交读)
大多数数据库系统的默认隔离级别都是提交读(MySQL不是). 即一个事务从开始直到提交之前,所作的任何修改对其它事务是不可读. 这也就
避免了脏读
这个级别有时也叫
不可重复读(nonrepeatable read)
,因为两次同样的查询,可能会得到不一样的结果(因为在提交之前,所作的修改对其它事务实际上是透明的). -
REPEATABLE READ 可重复读
可重复读
解决了脏读和不可重复读
的问题. 这个级别保证了同一个事务中多次读取同样的记录
的结果是一致的.但可重复读并没有解决幻读的问题,所谓幻读,指的是事务多次进行
同一范围内的数据查询
时,查出的数据记录数是不一样的,即出现了幻行(Phantom Row)InnoDB 和 XtraDB通过多版本并发控制(MVCC,Multiversion Concurrrncy Control)来解决了幻读的问题
可重复读是 MySQL 的默认事务隔离级别
. -
SERIALIZABLE READ可串行化读
事务的最高级别隔离,通过强制事务串行执行,来避免了前面所说的幻读的问题.
可串行化读会在
读取的每一行数据上都加锁
,所以可能导致大量的超时和锁争用的问题.只有在非常需要确保数据的一致性并且可以接受没有并发的情况下,才考虑去采用该级别.
隔离级别 | 脏读 | 不可重复读 | 幻读 | 加锁表 |
---|---|---|---|---|
未提交读 | √ | √ | √ | × |
提交读 | × | √ | √ | × |
可重复读 | × | × | √ | × |
串行化读 | × | × | × | √ |