mysql的事务以及隔离级别

 

文章目录

 

事务定义

事务是最小的不可分割的工作单元,事务能保证一个业务的完整性

执行一个事务时,事务中只要有一个操作失败,整个事务就会失败

事务例子

银行转账系统,先扣费再转账,如果扣费成功了,但是转账失败了,假如扣费是一个事务,转账为一个事务,就会导致程序扣费成功了,但是没有转账成功,这样会造成要取钱的李明(客户)发现资产不翼而飞了。所以我们应该把扣钱和转账看成一个事务来处理。当成一个事务的话,事务中其中一步失败了会导致整个事务失败。

-- 扣费操作
UPDATE user SET money=money-100 WHERE name='张华';

-- 转账操作
UPDATE user SET monry=monry+100 WHERE name='李明';
事务四大特征
  • A(automicity 原子性)

    事务所有操作在数据库中反映出来

    即全部成功或全部失败

  • C(consistency 一致性)

    事务中的操作必须保证数据库中数据的一致性

    即执行之后不能让一方少了 2000 块,另一方多了 1000 块,要保证数据的一致

  • I(isolation 隔离性)

    两个事务之间具有隔离性

    mysql 事务的四大隔离级别

  • D(durability 耐久性)

    事务一旦结束,比如 commit 后就无法再回滚了

    事务一旦提交就会持久性存储在磁盘中,提交前存储在内存中

事务自动提交设置

autocommit

mysql 默认是有事务的,autocommit=1 表示自动提交,我们来查看一下 mysql 默认提交事务与否

SELECT @@autocommit;

mysql 设置默认事务不自动提交如下

SET autocommit=0;

只有设置为不自动提交的时候,可以用 rollback 回滚

ROLLBACK;

不设置为默认提交,可以通过 commit 来提交数据

commit;

BEGIN

使用begin;开启事务,如下语句一起去执行之后,是把下面看成一个事务,并且可以通过 roolback 回滚,实际中这种比较常用

BEGIN;
UPDATE user SET money=money-100 WHERE name='张华';
UPDATE user SET monry=monry+100 WHERE name='李明';

START TRANSACTION

使用start transaction;下面语句一起去执行后也一样起到事务的效果,rollback 一块回滚,实际中这种比较常用

START TRANSACTION;
UPDATE user SET money=money-100 WHERE name='张华';
UPDATE user SET monry=monry+100 WHERE name='李明';

我们最后一旦 commit 了事务就无法回滚了

事务隔离级别的设置

查询事务隔离级别,设置全局后当前事务会话级不会影响,设置会话级后全局事务不会影响

-- 全局事务查询
SELECT @@global.tx_isolation;

-- 会话级事务查询
SELECT @@tx_isolation;
SELECT @@SESSION.tx_isolation;

修改事务隔离级别,下面以读提交举例,其他的隔离级别设置在下面标题内容中有写

-- 设置全局的事物隔离级别为读提交
SET global transaction isolation level READ UNCOMMITTED;

-- 设置会话级
SET session transaction isolation level READ UNCOMMITTED;
事务四种隔离级别

查看数据库事务的隔离级别

-- MYSQL5.6
-- 系统级别
SELECT @@global.tx_isolation;
-- 会话级别
SELECT @@tx_isolation;

-- MYSQL8.0
-- 系统级别
SELECT @@GLOBAL.TRANSACTION_ISOLATION;
-- 事务级别
SELECT @@transaction_isolation;

mysql 事务的默认隔离级别为 pepeatable read(重复读,可能产生幻读的问题)

读未提交 READ UNCOMMITTED(产生脏读的问题)

并发事务提交前可读,且可写

此隔离级别表示允许一个事务允许读到另一个事务未提交的数据

脏读,两边事务开启,一个事务中读到了另一个事务未提交的数据,实际开发中是不允许脏读出现的!

修改 MYSQL8.0 的隔离级别为 read uncommitted

SET global transaction isolation level READ UNCOMMITTED;

读提交 READ COMMITTED(产生不可重复读的问题)

并发事务提交后可读,且可写

此隔离级别表示允许一个事务允许读到另一个事务已提交的数据

对于读未提交的隔离级别是在提交前就能读取数据, 那现在读提交的隔离级设置成了提交后另一个事务才可读,但是这样会产生不可重复读的问题,比如一个事务中两次读取,但是两次读之间其他事务操作同样的数据并提交,这样第一个事务两次读的数据不一致,操作就会出现问题

修改 MYSQL8.0 的隔离级别为 read committed

SET global transaction isolation level READ COMMITTED;

重复读 REPEATABLE READ(产生幻读的问题)

并发事务可写不可读

此隔离级别表示允许一个事务不会读到另一边的事物提交的数据,但是这边事务在操作数据库时候可能会产生冲突的异常

既然读提交会导致一个事务中两次读取数据不一致,那我们这样,在第一个事务中读取不到第二个事务提交的数据,这样第一个事务两次读取都是一样的,但是会产生幻读,就是第二次读取到的数据虽然是某个值,但是实际它已经改变了,这就是幻读

修改 MYSQL8.0 的隔离级别为 repeatable read,这个也是 mysql 默认的事物隔离级别

SET global transaction isolation level REPEATABLE READ;

串行化 SERIALIZABLE

并发事务不可读不可写

串行化,当表被一个事务操作的时候(提交之前),其他事务在里头的操作是不可进行的,会进入排队状态。串行化会出现一个问题就是性能很差

修改 MYSQL8.0 的隔离级别为 serializable

SET global transaction isolation level SERIALIZABLE;

性能比较

read uncommitted(脏读) 好于 read committed(不可重复读) 好于 repeatable read(幻读) 好于 serializable(串行化)

实际项目中的事务
  • mysql 默认是重复读,但实际项目中很多使用的确是读提交(oracle 默认事务就是读提交),而不会采用读未提交和串行化,这是为什么呢?

    答:读未提交肯定不行,读到了别人没有提交的数据。串行化的话每次事务操作操作就被锁住性能太低了

  • 为什么我们不采用 mysql 默认的重复读做项目隔离级别呢?

    答:①因为读提交的重复读问题我们本身是可以接收的,不需要解决,就是事务中读到了另一个事务修改后的数据,读出来本身没有大问题

  • 为什么 mysql 默认隔离级别是重复读?

    答:大致就是之前的版本数据库低的模式有 bug 存在

题外话

一般数据库会采用读提交作为默认的事物隔离级别,比如 oracle,sql server,但是 mysql 的隔离级别却是重复读,

 

上一篇:AJAX里调用AJAX,作定时进度刷新


下一篇:一段能让VisualStudio炸掉的代码