Skip to content

Latest commit

 

History

History
112 lines (66 loc) · 6 KB

MySQL数据库-基础篇(2)事务与锁.md

File metadata and controls

112 lines (66 loc) · 6 KB

事务

ACID特性

总得来说,事务是为了保证数据的安全性,一致性,正确性。必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性

  • 原子性(Atomic),事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行
  • 一致性(Consistent),事务的执行结果,必须是从一个一致状态,变成另一个新的一致状态。事务的原子性保证其一致性
  • 隔离性(Isolation),主要在并发时,各个事务之间互不影响。并发事务所作的修改必须与任何其它并发事务所作的修改隔离。
  • 持久性(Duration),事务一旦提交,数据就永久的保存在数据库,它对于系统的影响是永久性的。

事务问题

并发操作通常会带来一些事务问题,如下

  • 更新丢失:两个不同的事务(或者Java程序线程)在某一时刻对同一数据进行读取后,先后进行修改。导致第一次操作数据丢失。
  • 脏读:指一个事务读取了另外一个事务未提交的数据。
  • 不可重复读:指在一个事务内读取表中的某一行数据,多次读取结果不同。该问题重点是修改,同样的条件,你读取过的数据,再次读取出来发现值不一样了
  • 幻读:一个事务的两次不同时间的相同查询返回了不同的的结果集。该问题重点在于新增或者删除,同样的条件,第 1 次和第 2 次读出来的记录数不一样

SQL中对数据的修改分为更新、插入和删除,分别对应三种不可重复读的问题,后两种问题也被称为“幻读”。

事务隔离级别

事务隔离级别 脏读 不可重复读 幻读
读未提交(read-uncommitted)
不可重复读(read-committed)
可重复读(repeatable-read)
串行化(serializable)

事务控制语句

  • BEGIN 或 START TRANSACTION 显式地开启一个事务;
  • COMMIT 也可以使用 COMMIT WORK,不过二者是等价的。COMMIT 会提交事务,并使已对数据库进行的所有修改成为永久性的;
  • ROLLBACK 也可以使用 ROLLBACK WORK,不过二者是等价的。回滚会结束用户的事务,并撤销正在进行的所有未提交的修改;
  • SAVEPOINT identifier,SAVEPOINT 允许在事务中创建一个保存点,一个事务中可以有多个 SAVEPOINT;
  • RELEASE SAVEPOINT identifier 删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常;
  • ROLLBACK TO identifier 把事务回滚到标记点;
  • SET TRANSACTION 用来设置事务的隔离级别。InnoDB 存储引擎提供事务的隔离级别有READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ 和 SERIALIZABLE。

MYSQL 事务处理主要有两种方法:

1、用 BEGIN, ROLLBACK, COMMIT来实现

  • BEGIN 开始一个事务
  • ROLLBACK 事务回滚
  • COMMIT 事务确认

2、直接用 SET 来改变 MySQL 的自动提交模式:

  • SET AUTOCOMMIT=0 禁止自动提交
  • SET AUTOCOMMIT=1 开启自动提交

在《深入浅出MySQL++数据库开发、优化与管理维护+第2版+唐汉明》14中注明

MySQL 支持对MyISAM 和 MEMORY 存储引擎的表进行表级锁定,对BDB存储引擎的表进行页级锁定,对InnoDB存储引擎进行行级锁定。默认情况下,表锁和行锁都是自动获得的, 不需要额外的命令,但是在有点情况下,用户需要明确地进行锁表或进行事务的控制,以变确保整个事务的完整性,这样就需要使用事务控制和锁定语句来完成

在MySQL 中有存在 3 种锁:行级锁、表级锁和页级锁;MyISAM 和 MEMORY 存储引擎 采用表级锁; BDB 采用页面锁,也支持表级锁;InnoDB 默认支持行级锁,也支持表级锁

MySQL的 3 种锁特性如下:

  • 表级锁:开销小,加锁快,不会出现死锁,颗粒度大,发生锁冲突概率高,并发度最低
  • 行级锁:开销大,加锁慢,会出现死锁,颗粒度最小,发生锁冲突概率最低,并发度最高
  • 页级锁,开销、加锁时间、颗粒度介于表锁和行锁之间 ,会出现死锁,并发一般

表锁

MySQL 表锁有两种模式,共享读锁和独占写锁

对MyISAM来说,读锁,不会阻塞其他用户对同一表的读请求;写锁,会阻塞其他用户对同一表的读和写操作。

加锁方式如下:

-- 加锁:
lock tables table_name { read | write } [local]
-- 解锁:
unlock tables

加锁时可以在 LOCK TABLES 时加了“local”选项,作用是在满足MyISAM 表并发插入条件的情况下,允许其他用户在表尾并发插入记录;

**注意:**在用lock tables 加锁时,必须先获取得所涉及表的锁,且加锁后,只能访问这些表,不能访问未加锁的表。如果加的读锁,那么只能支持查询操作,不能更新

行锁

行级锁是一种排他锁,防止其他事务修改此行,在使用以下语句时,Oracle 会自动应用行级锁。

InnoDB行锁是通过给索引上的索引项加锁来实现的,只有通过索引条件检索数据,InnoDB才使用行级锁,否则,InnoDB将使用表锁;如如果没有索引。InnoDB将通过隐藏的聚簇索引来对记录加锁,且在没有索引的情况下,InnoDB会对所有的记录都加锁;由于MySQL的行锁是针对索引加的锁,而不是记录,所以当访问不同记录时,使用相同的索引键,是会出现锁冲突的。换句话说,只要不是使用相同的索引键,是不会导致锁冲突的。

共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE
排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE
使用 COMMITROLLBACK 语句释放锁。

使用 LOCK IN SHARE MODE 获取共享锁很容易造成死锁问题。