总得来说,事务是为了保证数据的安全性,一致性,正确性。必须满足所谓的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
使用 COMMIT 或 ROLLBACK 语句释放锁。
使用 LOCK IN SHARE MODE 获取共享锁很容易造成死锁问题。