MySQL 基础 事务
什么是mysql的事务
MySQL 事务主要用于处理操作量大,复杂度高的数据。简单的说,事务就是一连串的DML的sql语句组合在一起,所以语句执行成功才算成功,如果有语句执行失败,执行就不成功 。比如说,在人员管理系统中,你删除一个人员,你即需要删除人员的基本资料,也要删除和该人员相关的信息,如信箱,文章等等,这样,这些数据库操作语句就构成一个事务!
注意:mysql只有Innodb存储引擎才支持事务。DML之的是数据库管理语句:insert, update, delete语句。
事务的特性
原子性(Atomicity):一个事务的所有操作,要么全部都完成,要么全部都不完成,不会再在中间的环节结束。如果其中一个地方发生了错误,会回滚到开始的地方,即该事务的语句一个都没有执行。
一致性(Consistency):在事务开始之前和事务结束之后,数据库的完整性没有被破坏,即修改的数据完全符合预期,例如从某个表减去一些数据,并将减去的数据加到另一表,这整个过程不会出现其他多余的数据,也不会突然减少数据。
隔离性(lsolation):数据库可以多个事务并行执行,相互之间不影响,隔离性可以放在多个事务并发执行时由于交叉执行而导致的数据不一致的问题。而隔离性的隔离效果是有等级的。在接下来回对隔离级别产生的影响和解决方法进行详述。
永久性(Durability):事务处理后,对数据的修改是永久的,即便系统故障也不会丢失。
事务的简单使用:
若要进行事务处理,必须先把MySQL的提交方式更改为手动提交,而不是自动提交。自动提交是指:MySQL的每一次语句执行就进行提交,而手动提交就是,我们可以同时执行多个语句然后一起提交。
查看提交方式:
设置提交方式:
然后我们就可以进行事务的实例训练了:
实例1:commit方式提交
mysql> select * from student; // 事务开始前的数据
+----+--------+------+
| id | name | age |
+----+--------+------+
| 1 | 老汪 | 25 |
| 2 | peilin | 20 |
| 3 | 微微 | 21 |
| 4 | 老晴 | 22 |
+----+--------+------+
4 rows in set (0.00 sec) mysql> start transaction; // 开始事务
Query OK, 0 rows affected (0.00 sec) mysql> insert into student values(5, '老个', 22); // 数据操作
Query OK, 1 row affected (0.00 sec) mysql> commit; // 事务接受,也就是提交
Query OK, 0 rows affected (0.60 sec) mysql> select * from student; // 事务介绍后的数据
+----+--------+------+
| id | name | age |
+----+--------+------+
| 1 | 老汪 | 25 |
| 2 | peilin | 20 |
| 3 | 微微 | 21 |
| 4 | 老晴 | 22 |
| 5 | 老个 | 22 |
+----+--------+------+
5 rows in set (0.00 sec)
从上面看来和普通的方式没有什么区别,接下来是rollback方式进行提交:
实例2:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec) mysql> insert into student values(6, '情感', 22);
Query OK, 1 row affected (0.00 sec) mysql> rollback; // 事务回滚
Query OK, 0 rows affected (0.00 sec) mysql> select * from student; // 查询发现数据不变,没有插入
+----+--------+------+
| id | name | age |
+----+--------+------+
| 1 | 老汪 | 25 |
| 2 | peilin | 20 |
| 3 | 微微 | 21 |
| 4 | 老晴 | 22 |
| 5 | 老个 | 22 |
+----+--------+------+
5 rows in set (0.00 sec)
从上面两个简单的事务可以看出:rollback提交是会将所有事务中的执行失效,回到事务开始前的状态。
注意:假如使用commit的方式提交,若事务中的sql语句有一句出错了,其他的没错,commit 提交后,除了出错的语句,其他的语句一样有效。
例如3:
mysql> select * from student;
+----+--------+------+
| id | name | age |
+----+--------+------+
| 1 | 老汪 | 25 |
| 2 | peilin | 20 |
| 3 | 微微 | 21 |
| 4 | 老晴 | 22 |
| 5 | 老个 | 22 |
+----+--------+------+
5 rows in set (0.00 sec) mysql>
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec) mysql> insert into student values(7,'老人', 22);
Query OK, 1 row affected (0.00 sec) mysql> insert into student values('情感', 22); // 插入错语句
ERROR 1136 (21S01): Column count doesn't match value count at row 1
mysql> commit;
Query OK, 0 rows affected (0.00 sec) mysql> select * from student; // 事务中的语句仍然成功
+----+--------+------+
| id | name | age |
+----+--------+------+
| 1 | 老汪 | 25 |
| 2 | peilin | 20 |
| 3 | 微微 | 21 |
| 4 | 老晴 | 22 |
| 5 | 老个 | 22 |
| 7 | 老人 | 22 |
+----+--------+------+
6 rows in set (0.00 sec)
注意:如果不在进行事务处理,需要把手动提交给关闭改为自动提交。
隔离性的隔离级别和可能出现的问题
隔离的级别:
读取未提交(read-uncommited):
事务A为提交的数据,事务B可以读取到,会导致脏数据
离级别最低:这一级别一般是理论上存在,数据库的一般级别都会高于该级别。
读取已提交:事务A和事务B,事务A提交的数据,事务B可以读取到。可以避免脏数据的产生。但是会导致'不可重复读取的问题'。
可重复读取:事务A和事务B,事务A提交之后的数据,事务B读取不到,事务B是可重复读取数据,这种隔离级别高于读已提交,换句话说
,对方提交之后的数据,我还是读取不到,这种隔离级别可以避免“不可重复读取”,达到可重复读取,比如1点和2点读到数据是同一个, MySQL默认级别,虽然可以达到可重复读取,但是会导致“幻像读”
串行化:事务A和事务B,事务A在操作数据库时,事务B只能排队等待,这种隔离级别很少使用,吞吐量太低,用户体验差,这种级别可以避免“幻像读”,每一次读取的都是数据库中真实存在数据,事务A与事务B串行,而不并发
导致的问题解释:
1、脏读:事务A读取了事务B更新的数据,然后B回滚操作,那么A读取到的数据是脏数据 2、不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果 不一致。 3、幻读:系统管理员A将数据库中所有学生的成绩从具体分数改为ABCDE等级,但是系统管理员B就在这个时候插入了一条具体分数的记录,当系统管理员A改结束后发现还有一条记录没有改过来,就好像发生了幻觉一样,这就叫幻读。 小结:不可重复读的和幻读很容易混淆,不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
读取未提交(read-uncommitted) | 是 | 是 | 是 |
读取已提交(不可重复读取)(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read)(默认) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
接下来就为大家演示一下什么是脏数据,不可重复读和幻读:
首先要对隔离级别进行设置,默认的是可重复读取: 这个数据可以在配置文件进行配置:
查看方式:
mysql> show variables like '%isolation%';
+-----------------------+-----------------+
| Variable_name | Value |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set, 1 warning (0.00 sec)
对隔离级别进行设置:
mysql> set session transaction isolation level read uncommitted; // 对当前的事务进行设置,而不是全局 set global transaction isolation level read uncommitted; 这是全局设置
Query OK, 0 rows affected (0.00 sec) mysql> show variables like '%isolation%';
+-----------------------+------------------+
| Variable_name | Value |
+-----------------------+------------------+
| transaction_isolation | READ-UNCOMMITTED |
+-----------------------+------------------+
1 row in set, 1 warning (0.00 sec)
出现问题一,脏数据:
第一个客户端进行事务操作,但不提交:事务A
mysql> show variables like '%isolation%';
+-----------------------+------------------+
| Variable_name | Value |
+-----------------------+------------------+
| transaction_isolation | READ-UNCOMMITTED |
+-----------------------+------------------+
1 row in set, 1 warning (0.00 sec) mysql>
mysql>
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 0 |
+--------------+
1 row in set (0.00 sec) mysql>
mysql>
mysql>
mysql> begin; // 事务开始
Query OK, 0 rows affected (0.00 sec) mysql> select * from student;
+----+--------+------+
| id | name | age |
+----+--------+------+
| 1 | 老汪 | 25 |
| 2 | peilin | 20 |
| 3 | 微微 | 21 |
| 4 | 老晴 | 22 |
| 5 | 老个 | 22 |
| 7 | 老人 | 22 |
| 8 | 老杨 | 22 |
+----+--------+------+
7 rows in set (0.00 sec) mysql> insert into student values(9, 'wallace', 33); // 插入数据,但未提交
Query OK, 1 row affected (0.00 sec)
第二个客户端进行数据查询:客户端B
mysql> use school;
Database changed
mysql> select * from student;
+----+---------+------+
| id | name | age |
+----+---------+------+
| 1 | 老汪 | 25 |
| 2 | peilin | 20 |
| 3 | 微微 | 21 |
| 4 | 老晴 | 22 |
| 5 | 老个 | 22 |
| 7 | 老人 | 22 |
| 8 | 老杨 | 22 |
| 9 | wallace | 33 | ===>这条数据是事务A的数据,但A并未提交,B还是查询出来了,这就是脏数据。
+----+---------+------+
8 rows in set (0.00 sec)
出现问题二,不可重复读取:
一,解决脏读问题:
客户端A,设置全局为read committed 查询数据
mysql> set global transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec) mysql> begin;
Query OK, 0 rows affected (0.00 sec) mysql> select * from student; // 客户端B在事务跟新前
+----+------------+------+
| id | name | age |
+----+------------+------+
| 1 | yangyang | 25 |
| 2 | peilin | 20 |
| 3 | 微微 | 21 |
| 4 | 老晴 | 22 |
| 5 | 老个 | 22 |
| 7 | 老人 | 22 |
| 8 | 老杨 | 22 |
| 9 | wallace | 33 |
| 10 | wace | 33 |
| 11 | 老五年 | 33 |
| 12 | 老年 | 33 |
| 13 | 老大法师年 | 33 |
| 14 | aaa | 333 |
+----+------------+------+
13 rows in set (0.00 sec) mysql> select * from student; // 客户端B在事务跟新为提交之前
+----+------------+------+
| id | name | age |
+----+------------+------+
| 1 | yangyang | 25 |
| 2 | peilin | 20 |
| 3 | 微微 | 21 |
| 4 | 老晴 | 22 |
| 5 | 老个 | 22 |
| 7 | 老人 | 22 |
| 8 | 老杨 | 22 |
| 9 | wallace | 33 |
| 10 | wace | 33 |
| 11 | 老五年 | 33 |
| 12 | 老年 | 33 |
| 13 | 老大法师年 | 33 |
| 14 | aaa | 333 |
+----+------------+------+
13 rows in set (0.00 sec)
客户端B:
mysql> begin;
Query OK, 0 rows affected (0.00 sec) mysql> update student set name='aixs' where id=1; // 更新B,但在A中未查询到脏数据。
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0 mysql> select * from student;
+----+------------+------+
| id | name | age |
+----+------------+------+
| 1 | aixs | 25 |
| 2 | peilin | 20 |
| 3 | 微微 | 21 |
| 4 | 老晴 | 22 |
| 5 | 老个 | 22 |
| 7 | 老人 | 22 |
| 8 | 老杨 | 22 |
| 9 | wallace | 33 |
| 10 | wace | 33 |
| 11 | 老五年 | 33 |
| 12 | 老年 | 33 |
| 13 | 老大法师年 | 33 |
| 14 | aaa | 333 |
+----+------------+------+
13 rows in set (0.00 sec)
二,不可重复读取:
对B中的事务进行提交:查询客户端A的数据,发现和之前的不一样这就是不可重复读取
mysql> select * from student;
+----+------------+------+
| id | name | age |
+----+------------+------+
| 1 | aixs | 25 | ====》变更的值
| 2 | peilin | 20 |
| 3 | 微微 | 21 |
| 4 | 老晴 | 22 |
| 5 | 老个 | 22 |
| 7 | 老人 | 22 |
| 8 | 老杨 | 22 |
| 9 | wallace | 33 |
| 10 | wace | 33 |
| 11 | 老五年 | 33 |
| 12 | 老年 | 33 |
| 13 | 老大法师年 | 33 |
| 14 | aaa | 333 |
+----+------------+------+
13 rows in set (0.00 sec)
出现问题三,幻读:
一,可重复读取: 客户端A对age更新,事务并提交
mysql> set global transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec) mysql> select * from student;
+----+--------+------+
| id | name | age |
+----+--------+------+
| 1 | quert | 20 |
| 2 | peilin | 20 |
| 3 | 微微 | 21 |
| 4 | 老晴 | 22 |
+----+--------+------+
4 rows in set (0.00 sec) mysql> update student set age = age -5 where id =1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0 mysql> commit;
Query OK, 0 rows affected (0.00 sec) mysql> select * from student;
+----+--------+------+
| id | name | age |
+----+--------+------+
| 1 | quert | 15 |
| 2 | peilin | 20 |
| 3 | 微微 | 21 |
| 4 | 老晴 | 22 |
+----+--------+------+
4 rows in set (0.00 sec)
客户端B,在事务是否提交时进行数据查询,数据都一致:
mysql> select * from student;
+----+--------+------+
| id | name | age |
+----+--------+------+
| 1 | quert | 20 |
| 2 | peilin | 20 |
| 3 | 微微 | 21 |
| 4 | 老晴 | 22 |
+----+--------+------+
4 rows in set (0.00 sec) mysql> select * from student;
+----+--------+------+
| id | name | age |
+----+--------+------+
| 1 | quert | 20 | ====》提交后数据仍然不变,可重复读取
| 2 | peilin | 20 |
| 3 | 微微 | 21 |
| 4 | 老晴 | 22 |
+----+--------+------+
4 rows in set (0.00 sec)
二,幻读
客户端A 进行事务,提交插入数据
mysql> begin;
Query OK, 0 rows affected (0.00 sec) mysql> insert into student values(18, 'aa', 33);
Query OK, 1 row affected (0.00 sec) mysql> commit;
Query OK, 0 rows affected (0.00 sec) mysql> select * from student;
+----+--------+------+
| id | name | age |
+----+--------+------+
| 1 | quert | 15 |
| 2 | peilin | 20 |
| 3 | 微微 | 21 |
| 4 | 老晴 | 22 |
| 18 | aa | 33 |
+----+--------+------+
客户端B:但是在事务B中无法查询出新增的数据,就好像什么都没有方式,这就是幻读
mysql> select * from student;
+----+--------+------+
| id | name | age |
+----+--------+------+
| 1 | quert | 15 |
| 2 | peilin | 20 |
| 3 | 微微 | 21 |
| 4 | 老晴 | 22 |
+----+--------+------+
4 rows in set (0.00 sec)
四,串行化
客户端A,进行事务,但不提交
mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec) mysql> start transaction;
Query OK, 0 rows affected (0.00 sec) mysql> select * from account;
+------+--------+---------+
| id | name | balance |
+------+--------+---------+
| 1 | lilei | 10000 |
| 2 | hanmei | 10000 |
| 3 | lucy | 10000 |
| 4 | lily | 10000 |
+------+--------+---------+
4 rows in set (0.00 sec)
客户端B,进行事务操作,不能执行,会报错,因为是串行化,A事务必须先结束。B才可以执行。
mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec) mysql> start transaction;
Query OK, 0 rows affected (0.00 sec) mysql> insert into account values(5,'tom',0);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
补充:
1、事务隔离级别为读提交时,写数据只会锁住相应的行
2、事务隔离级别为可重复读时,如果检索条件有索引(包括主键索引)的时候,默认加锁方式是next-key 锁;如果检索条件没有索引,更新数据时会锁住整张表。一个间隙被事务加了锁,其他事务是不能在这个间隙插入记录的,这样可以防止幻读。
3、事务隔离级别为串行化时,读写数据都会锁住整张表
4、隔离级别越高,越能保证数据的完整性和一致性,但是对并发性能的影响也越大。
5、MYSQL MVCC实现机制参考链接:https://blog.csdn.net/whoamiyang/article/details/51901888
6、关于next-key 锁可以参考链接:https://blog.csdn.net/bigtree_3721/article/details/73731377
MySQL 基础 事务的更多相关文章
- mysql基础 事务的认识和使用
事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit).事务是恢复和并发控制的基本单位. 在关系数据库中,一个事务可以是一条SQL语句,一组SQL语句或整个程序 ...
- MySQL基础之事务编程学习笔记
MySQL基础之事务编程学习笔记 在学习<MySQL技术内幕:SQL编程>一书,并做了笔记.本博客内容是自己学了<MySQL技术内幕:SQL编程>事务编程一章之后,根据自己的理 ...
- mysql基础之-mysql锁和事务(七)
0x01 MySQL锁: 执行操作时施加锁的模式 读锁:用户在读的时候施加的锁,为防止别人修改,但是用户可以读,还被称为共享锁 不会对其他用户进行阻塞 理解: ----->(这里的不阻塞,是可以 ...
- Mysql基础代码(不断完善中)
Mysql基础代码,不断完善中~ /* 启动MySQL */ net start mysql /* 连接与断开服务器 */ mysql -h 地址 -P 端口 -u 用户名 -p 密码 /* 跳过权限 ...
- MYSQL基础操作
MYSQL基础操作 [TOC] 1.基本定义 1.1.关系型数据库系统 关系型数据库系统是建立在关系模型上的数据库系统 什么是关系模型呢? 1.数据结构可以规定,同类数据结构一致,就是一个二维的表格 ...
- mysql 基础篇5(mysql语法---数据)
6 增删改数据 -- ********一.增删改数据********* --- -- 1.1 增加数据 -- 插入所有字段.一定依次按顺序插入 INSERT INTO student VALUES(1 ...
- MySQL基础学习总结
1.MySQL基础概念 mysql逻辑架构如下: 每个客户端连接都会在服务器中拥有一个线程,这个连接的查询只会在这个单独的线程中执行. MySQL是分层的架构.上层是服务器层的服务和查询执行引擎,下层 ...
- MYSQL 基础操作
1.MySQL基础操作 一:MySQL基础操作 1:MySQL表复制 复制表结构 + 复制表数据 create table t3 like t1; --创建一个和t1一样的表,用like(表结构也一样 ...
- mysql基础之存储引擎
原文:mysql基础之存储引擎 数据库对同样的数据,有着不同的存储方式和管理方式,在mysql中,称为存储引擎 常用的表的引擎 Myisam ,批量插入速度快, 不支持事务,锁表 Innodb, 批量 ...
随机推荐
- Asp.Net Identity cookie共享
n the Startup.Auth.cs file, add the CookieDomain parameter with your domain: var cookieAuthenticatio ...
- ftp如何使用命令上传文件
本地上传到服务器的步骤如下: 1."开始"-"运行"-输入"FTP" 2.open qint.ithot.net 这一步可以与第一步合并,在 ...
- java封装实现Excel建表读写操作
对 Excel 进行读写操作是生产环境下常见的业务,网上搜索的实现方式都是基于POI和JXL第三方框架,但都不是很全面.小编由于这两天刚好需要用到,于是就参考手写了一个封装操作工具,基本涵盖了Exce ...
- C++设计实现一个不能被继承的类
C++不同于Java,Java中被final关键字修饰的类不能被继承,C++能实现不被继承的类,但是需要自己实现. 为了使类不被继承,最好的办法是使子类不能构造父类的部分,此时子类就无法实例化整个子类 ...
- HDU - 6231 K-th Number (2017CCPC哈尔滨站 二分+尺取法)
Alice are given an array A[1..N] with N numbers. Now Alice want to build an array B by a parameter K ...
- 洛谷P1192 台阶问题【记忆化搜索】
题目:https://www.luogu.org/problemnew/show/P1192 题意: 给定n和k,一个人一次可以迈1~k步,问走n步有多少种方案. 思路: 本来傻乎乎上来就递归,显然会 ...
- 入手Docker容器注意事项:命令结束容器退出
在没有 docker 容器的时候,在终端(terminal)中运行 shell 命令,我们知道当终端退出时(比如关闭终端窗口或退出 ssh 会话),终端中执行的命令也会结束.所以,当我们在终端中执行持 ...
- Prometheus 函数
函数列表 一些函数有默认的参数,例如:year(v=vector(time()) instant-vector).v是参数值,instant-vector是参数类型.vector(time())是默认 ...
- SQL之分组排序取top n
转自:http://blog.csdn.net/wguangliang/article/details/50167283 要求:按照课程分组,查找每个课程最高的两个成绩. 数据文件如下: 第一列no为 ...
- 20个有趣的Linux命令
写在前面:本博客为本人原创,严禁任何形式的转载!本博客只允许放在博客园(.cnblogs.com),如果您在其他网站看到这篇博文,请通过下面这个唯一的合法链接转到原文! 本博客全网唯一合法URL:ht ...