摘要:
今天主要给大家介绍一下数据库的锁和事务。数据库的事务主要解决的问题是并发控制,试想一下,如何多个用户并发的去修改同一记录,会发生什么?
1、锁
锁的类型:
- 共享锁:也叫读锁,多个客户可以同时读取同一个资源,互不干扰。
- 排它锁:也叫写锁,写锁的优先级高于读锁,当写锁被占用的时候,这个时候读也是会被等待的,其他客户写也会等待。
锁的粒度:
- 表锁:粒度比较粗,锁定整张表。
特点:开销小,加锁快;不会出现死锁;锁定粒度大,发出锁冲突的概率最高,并发度最低。 - 行锁:行级锁可以最大程度的支撑并发处理,同时也带来了最大的锁开销。
特点:开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。 - 页锁:介于两者之间。
锁的实现:
- 悲观锁:悲观锁指对数据被意外修改持保守态度,依赖数据库原生支持的锁机制来保证当前事务处理的安全性,防止其他并发事务对目标数据的破坏或破坏其他并发事务数据,将在事务开始执行前或执行中申请锁定,执行完后再释放锁定。这对于长事务来讲,可能会严重影响系统的并发处理能力。 自带的数据库事务就是典型的悲观锁。
- 乐观锁:乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。乐观锁适用于读多写少的应用场景,这样可以提高吞吐量。一般是加一个版本号字段 每次更新时候比较版本号。
2、事务
1.什么是事务?
事务是指一个单元的一系列SQL集合,要么都执行,要么都不执行。如果不对数据库进行并发控制,可能会产生 脏读、非重复读、幻像读、丢失修改的异常情况。
事务的特性(ACID):
- 原子性(automicity):事务中的所有SQL,要么全执行,要么全部不执行。这就是事务的原子性。
- 一致性(consistency):转账前,A和B的账户中共有500+500=1000元钱。转账后,A和B的账户中共有400+600=1000元。
- 隔离性(isolation):在A向B转账的整个过程中,只要事务还没有提交(commit),查询A账户和B账户的时候,两个账户里面的钱的数量都不会有变化。
- 持久性(durability):一旦转账成功(事务提交),两个账户的里面的钱就会真的发生变化。
2.什么是脏读?幻读?不可重复读?什么是事务的隔离级别?Mysql的默认隔离级别是?
- 脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据。
- 不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。
- 幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。也就是可以插入新的行记录。
事务的隔离级别:
- Read uncommitted(读未提交):就是一个事务可以读取另一个未提交事务的数据。会造成脏读,不可重复读。
- Read committed(读已提交):就是一个事务要等另一个事务提交后才能读取数据。但还是不可重复读。
- Repeatable read(可重复读):就是在开始读取数据(事务开启)时,不再允许修改操作。
- Serializable (串行化):Serializable 是最高的事务隔离级别,在该级别下,事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。
Mysql的默认隔离级别是:Repeatable read!!
3.什么是死锁?怎么解决?
死锁:两个或多个事务相互占用了对方的锁,就会一直处于等待锁的状态。
常见的解决死锁的方法:
1、如果不同程序会并发存取多个表,尽量约定以相同的顺序访问表,可以大大降低死锁机会。
2、在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁产生概率;
3、对于非常容易产生死锁的业务部分,可以尝试使用升级锁定颗粒度,通过表级锁定来减少死锁产生的概率;
如果业务处理不好可以用分布式事务锁或者使用乐观锁
4.多版本并发控制
MySQL的大多数事务型存储引擎实现的都不是简单的行级锁。基于并发性能的考虑,他们一般都同时实现了多版本并发控制(MVCC),不仅MySQL,包括Oracale、PostgreSQL等其他数据库都各自实现了MVCC,但基于实现的机制不尽相同,因为MVCC没有一个统一的标准。
InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的,一个是创建时间,一个是系统版本号。每开始一个新的事物,系统版本号会自动递增。