mysql事务的四原则_mysql数据库 事务的四大规则ACID 事务的隔离级别

ACID:原子性,一致性,隔离性,持久性。

1.原子性。如果理解java的原子类就非常好理解这个了,即这部分的操作不可分割,要么全部执行完毕,要么都不执行,不存在中间过程被影响的现象。然后事务执行过程出错就会回滚到事务没有提交之前的状态。

2.一致性。这个我一直没搞得很明白,似乎是网上的一大疑难杂症。我目前的理解就是:操作完后该是啥就是啥。比如三个账户里的金额1w,然后相互转来转去,最后还是1w。就是一种结果符合预期的规则吧。不是非常理解,也不知道它有啥用,以后遇到问题再说吧。

3.隔离性。数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。

4.持久性。即事务一旦提交成功,则永久对数据库做出修改。

这里要注意:mysql的事务默认开始,也就是说你每执行一次sql就提交了一次事务。

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

1、用 BEGIN, ROLLBACK, COMMIT来实现BEGIN 开始一个事务

ROLLBACK 事务回滚

COMMIT 事务确认

2、直接用 SET 来改变 MySQL 的自动提交模式:SET AUTOCOMMIT=0 禁止自动提交

SET AUTOCOMMIT=1 开启自动提交

具体可以参考前面链接的菜鸟教程的案例自己去操作数据库感觉一下,我由于写过redis,mysql,写得多了就不再写了。但是操作过后才会有更深刻的印象。

然后来对事务的隔离级别进行具体讲解:下图铭记。此图截于上面的链接,侵权立删

读未提交:read uncommitted 也就是会把其它事务还没提交的sql语句的结果从数据库中读出。这种现象也叫脏读现象。

比如:你在一个连接里:开启一个事务A,事务A的事务级别一直为Repeated read 。然后往表里插入一个记录,此时没有提交事务,然后你在事务A里进行查询是可以查询得到这条记录的。然后你再另一个连接里:开启一个事务B,先直接去查询这条记录,此时默认的事务也为Repeated read,然后你会发现找不到这条记录,然后把事务B的事务隔离级别设置为读未提交,然后事务B去查询这个表,会发现可以查询到这条记录。这就是脏读现象,读到了没有提交的事务。这个和redis不一样啊,redis中事务B是读不到事务A的东西的,因为redis把命令都放在了命令缓存队列中,并没有去执行。

下面是我的实验过程:这个是事务B的那个连接。

select @@session.tx_isolation;是查询当前此连接采用的事务隔离级别。

set session transaction isolation level read uncommitted;是设置当前此连接的事务隔离级别

mysql> SELECT @@session.tx_isolation;

+------------------------+

| @@session.tx_isolation |

+------------------------+

| REPEATABLE-READ |

+------------------------+

1 row in set (0.00 sec)

mysql> select * from jd;

Empty set (0.00 sec)

mysql> select * from jd;

Empty set (0.28 sec)

mysql> set session transaction isolation level read uncommitted;

Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @@session.tx_isolation;

+------------------------+

| @@session.tx_isolation |

+------------------------+

| READ-UNCOMMITTED |

+------------------------+

1 row in set (0.00 sec)

mysql> select * from jd;

+------+

| id |

+------+

| 1 |

+------+

1 row in set (0.00 sec)

很明显,事务A的隔离级别一直没变,事务A也没提交,事务B的隔离级别由可重复读变成读未提交,然后事务B就由原来的不能查看事务A未提交的数据变为能查看事务A未提交的数据。

下面是事务A的:

mysql> select * from jd;

Empty set (0.00 sec)

mysql> start transaction;

Query OK, 0 rows affected (0.27 sec)

mysql> insert into jd values(1);

Query OK, 1 row affected (0.09 sec)

mysql> select * from jd;

+------+

| id |

+------+

| 1 |

+------+

1 row in set (0.00 sec)

未提交事务即可查看已操作的sql。

通过上面的操作大家应该对读未提交这个隔离级别和脏读这个现象有深刻的认识了吧。

然后我们再来看读已提交,即read committed.

即事务A处于read committed级别,开启事务查询表中的数据,然后事务B处于repeatable read级别,此时事务B修改表中的数据。此时可观察事务A是否可以看到事务B的操作,可以的话就是脏读了,然而事实是不会出现脏读。然后提交事务B的操作。然后事务A再在事务中查询表中的数据,就会读到事务B的操作。这就是不可重复读的现象。由于事务B的事务提交,事务A不能重复读出正确的数据。

这个(read committed)隔离级别不会出现脏读现象,但是会出现不可重复读现象。

下面是演示实例:事务A的:

mysql> set session transaction isolation level read committed;

Query OK, 0 rows affected (0.00 sec)

mysql> SELECT @@session.tx_isolation;

+------------------------+

| @@session.tx_isolation |

+------------------------+

| READ-COMMITTED |

+------------------------+

1 row in set (0.00 sec)

mysql> start transaction;

Query OK, 0 rows affected (0.00 sec)

mysql> select * from jd;

+------+

| id |

+------+

| 1 |

+------+

1 row in set (0.00 sec)

mysql> select * from jd;//这个我是用于看是否会出现脏读现象。

+------+

| id |

+------+

| 1 |

+------+

1 row in set (0.00 sec)

mysql> select * from jd;

+------+

| id |

+------+

| 1 |

| 2 |

+------+

2 rows in set (0.00 sec)

下面是事务B的:

mysql> set session transaction isolation level repeatable read;

Query OK, 0 rows affected (0.00 sec)

mysql> select @@session.tx_isolation;

+------------------------+

| @@session.tx_isolation |

+------------------------+

| REPEATABLE-READ |

+------------------------+

1 row in set (0.00 sec)

mysql> select * from jd;

+------+

| id |

+------+

| 1 |

+------+

1 row in set (0.00 sec)

mysql> start transaction;

Query OK, 0 rows affected (0.00 sec)

mysql> insert into jd values(2);

Query OK, 1 row affected (0.28 sec)

mysql> select * from jd;

+------+

| id |

+------+

| 1 |

| 2 |

+------+

2 rows in set (0.00 sec)

mysql> commit;

Query OK, 0 rows affected (0.00 sec)

大家可以参考这自己试试,试试更健康。

然后再来看默认的隔离级别 repeatable read.

感觉这个例子我是直接参考前面链接里的来写了一遍,重在实践。这里我就不贴我的代码了,大家可以直接去博主那里取代码尝试。我这里从博主那里截图:

这里两个事务的隔离级别都是可重复读。我们发现了它不会出现不可重复读的现象。如果出现了得话上面右边事务提交后,左边事务就会查询出来了。后面的插入数据时发生的error就是幻读(虚读)现象。也就是说我查询出里面并没有这条记录,可是你却告诉我已经有了这条记录,说明前面读出来的是幻象。

做上面这个实验时注意要设置某一列的值的唯一性,比如主键,不然是可以插入重复数据的,这样就不会包error了。

然后把事务的隔离级别改为:serializable

也就是:完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞

我们可以观察到右边的事务要插入数据时time out了,不能插入数据。查询没有问题。所以就严格地保证了安全性。也不会发生前面的脏读,不可重复读,幻读这三大问题。

建议大家把原文的全部实验都做完。特别是后面几个,那些涉及到了排他锁和共享锁,如果暂时不懂没关系,我后面会专门写篇文章进行讲解。

我挑几个着重点说说:

用id<=1加的锁,只锁住了id<=1的范围,可以成功添加id为2的记录,添加id为0的记录时就会等待锁的释放。

这里就是添加的行级锁。

如果使用普通的读,会得到一致性的结果,如果使用了加锁的读,就会读到“最新的”“提交”读的结果。

这个就很像java里的synchronized和volatile,保证了内存的可见性。保证了值的刷新。

当隔离级别是可重复读,且禁用innodb_locks_unsafe_for_binlog的情况下,在搜索和扫描index的时候使用的next-key locks可以避免幻读。

可以这么讲,InnoDB提供了这样的机制,在默认的可重复读的隔离级别里,可以使用加锁读去查询最新的数据(提交读)。

MySQL InnoDB的可重复读并不保证避免幻读,需要应用使用加锁读来保证。而这个加锁度使用到的机制就是

上面摘自:MySQL 四种事务隔离级的说明 这篇博文绝对是篇好文,推荐!

随着事务级别的逐渐增高,安全性越高,但是性能越差。根据需要的场景来选择合适的隔离级别,这才是我们需要关注积累经验的。

这里简单总结一下:

事务A读取到了其它事物还没有提交的操作,这就叫脏读。也就是读未提交。

事务A在多次读取的过程中,前几次没有读取到事务B的操作,后几次读取到了事务B提交后的操作,这叫不可重复读,也就是读已提交。

事务A在多次读取的过程中,一直都没有读取到这之中事务B提交的操作,这叫可重复读。

事务A在前面多次读取的过程中,没有读取到事务B提交的操作,但是事务A在插入数据的过程中,与事务B提交后的操作发生冲突,可是之前事务A并没有查询到记录的存在,这就是幻读。

事务A在查询过程中,事务B可以查询,但是一旦进行插入,就会发生time out的error。这就是序列化的隔离级别。

然后再重新看一次那图:

相信大家此时已经对上图了如执掌。

欢迎交流讨论。

你可能感兴趣的:(mysql事务的四原则)