Please enable Javascript to view the contents

MySQL事务与SQL优化

 ·  ☕ 4 分钟

一、事务的语法

非常简单

set autocommit = 0; -- 关闭自动提交
start tansaction; -- 事务开始,也可以换成begin;
... -- 一系列SQL语句完成一个操作
-- 当执行commit或rollback,事务就结束了
commit; -- 提交当前的修改
rollback;
...
set autocommit = 1; -- 恢复自动提交
-- 可以在事务过程,设置保留点,方便回滚到灵活的位置
savepoint p1;
rollback to p1;

二、事务四大特性

事务重要的特性(ACID),原子性,一致性,隔离性,持久性。

  • 原子性(Atomicity)
    事务的进行不可分割,是一个原子性的操作序列单元。事务中的操作要么全部执行成功要么全部执行失败。如果事务中间环节出错,回滚事务开始的状态。
  • 一致性(Consistency)
    事务的执行前后不能破坏数据库数据的完整性和一致性。
    一个事务执行前后,数据库必须处于一致性的状态,也就是数据在操作无误、逻辑无误的情况下,期望结果却与实际结果不一致,也就是数据错乱了。比如:把一个数据原为1000,减掉500,提交后查询数据不是500,说明事务受到了干涉,使事务不一致性。事务应该保持一致性。
  • 隔离性(Isolation)
    在并发环境下,并发的事务互相隔离。不同事务处理相同的数据都有自己完整的数据空间。
  • 持久性(Duration)
    事务一旦提交,就数据库的数据就永久保存下来。即使发生服务器宕机或崩溃等故障。数据库重启,就能恢复事务结束后的状态。

三、事务的三大并发问题

事务面临的三大并发问题,脏读,不可重复读,幻读。

  • 脏读

事务A读到了事务B未提交的数据。比如事务A和事务B,A读取B更新的数据,B回滚撤销更新,A读到了脏数据。

  • 不可重复读

同一个事务中读取某表某行的数据,多次查询得到的结果不一样。比如事务A和事务B,A多次读取数据,B在A读取过程中,更新数据并提交,导致A多次读取同一处数据,结果不一致。(不一定是错误,只是场合不对)

  • 幻读(虚读)

事务A查询过程中,读取到了事务B插入后的数据或删除后的数据,导致前后读取不一致。(影响的是行,多了一行,少了一行)

四、事务隔离级别

数据库的事务存在一些隔离级别来解决一些并发问题,不同问题应该选择合适的隔离级别。

隔离级别 脏读 不可重复度 幻读
读未提交(READ_UNCOMMITTED)
读已提交(READ_COMMITTED)
可重复读(REPEATABLE_READ) ?
顺序读(SERIALIZABLE)

从上往下,隔离级别越高,并发安全越高,但并发性越差。一般数据默认级别为读已提交或可重复读。

查询当前会话的事务隔离级别

select @@tx_isolation;
select @@transaction_isolation; -- mysql8的命令

设置当前会话的事务隔离级别

set session transaction isolation level read uncommited;
set session transaction isolation level read commited;
set session transaction isolation level repeatable read;
set session transaction isolation level serializable;

一一介绍隔离级别

  • 读未提交

事务A正处理某一数据,对其更新但未提交,事务未完成;事务B能够访问到未提交新的数据,也就是会出现脏读。这个隔离级别适合只读的事务。

  • 读已提交

不同事务只能读到已提交的数据。不会出现脏读,事务重复读取数据的过程中,其他事务提交了数据修改,那么重复读会出现结果前后不一致。无法解决不可重复读的问题。

  • 可重复读

保证事务处理,多次读取同一处数据,同一数据的值与事务开始一致。解决脏读和不可重复读,但可能出现幻读。

  • 顺序读

事务排队处理,不能并发,就不会出现并发问题,安全但效率低。

五、不同隔离级别锁

行级锁:把一个表的行上锁,只能有一个事务处理这个行。

间隙锁:一个在索引记录之间的间隙上的锁。保证某个间隙内的数据在锁定情况下不会发生任何变化。

  • 读未提交

有行级锁,无间隙锁,可读未提交数据

  • 读提交

有行级锁,无间隙锁,读不到未提交的数据

  • 可重复读

有行级锁,有间隙锁,每次读取的数据一致,可能有幻读

  • 序列化

有行级锁,也有间隙锁,读表的时候上锁。

六、隐式提交

DDL、DML、DCL、DQL都是隐式提交,在执行这些语句就相当于已经执行了commit,其实可以理解为数据库自动提交了。

七、数据库SQL优化

1.对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引

2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫

描,如:

select 属性2 from 表名 where 属性1 is null;

最好不要给数据库留NULL,尽可能的使用 NOT NULL填充数据库.

备注、描述、评论之类的可以设置为 NULL,其他的,最好不要使用NULL。

3.应尽量避免在 where 子句中使用 !=<=> 操作符,否则引擎将放弃使用索引而进行全表扫描。

4.应尽量避免在 where 子句中使用 or 来连接条件,如果一个字段有索引,一个字段没有索引,将导致引擎放弃使用索引而进行全表扫描,如:

可以这样查询:

select id from t where num = 10 
union all 
select id from t where Name = 'admin';

5.in 和 not in 也要慎用,否则会导致全表扫描,如:

select id from t where num in(1,2,3);

对于连续的数值,能用 between 就不要用 in 了:

select id from t where num between 1 and 3;

很多时候用 exists 代替 in 是一个好的选择。

6.select语句避免使用星号(*)。