MySQL-day7-索引与事务
文章目录
索引
一、为什么需要索引?(索引的优缺点)
1、索引产生的意义
索引是表的目录,在查找内容之前可以先在目录中查找索引位置,以此快速定位查询数据。对于索引,会保存在额外的文件中。
索引,是数据库中专门用于帮助用户快速查询数据的一种数据结构。类似于字典中的目录,查找字典内容时可以根据目录查找到数据的存放位置,然后直接获取即可。
没有索引行不行?答案是肯定的,可以不使用索引,在数据库中将数据整齐的排列在磁盘阵列中,当获取数据的时候只需要逐个搜索,并返回结果,但是 如果开发的应用有几百上千万甚至亿级别的数据,那么不深入了解索引的原理, 写出来程序就根本跑不动,光查一个数据库就得好几天,因此就需要索引,能够快速的查找的需要的数据。
2、索引的优缺点
优点
1、极大地加速了索引过程,减少IO次数
2、创建唯一索引,保证了数据库表中的唯一性
3、加速了表与表之间的连接
4、针对分组和排序检索时,能够显著减少查询查询中的分组和排序时间
缺点
1、索引表占据物理空间
2、数据表中的数据增加、修改、删除的同时需要去动态维护索引表,降低了数据的维护速度
二、索引的数据结构
任何一种数据结构都不是凭空产生的,一定会有它的背景和使用场景,我们现在总结一下,我们需要这种数据结构能够做些什么,其实很简单,那就是:每次查找数据时把磁盘IO次数控制在一个很小的数量级,最好是常数数量级。那么我们就想到如果一个高度可控的多路搜索树是否能满足需求呢?就这样,b+树应运而生。
如上图,是一颗b树,关于b树的定义可以参见B树,这里只说一些重点,浅蓝色的块我们称之为一个磁盘块,可以看到每个磁盘块包含几个数据项(深蓝色所示)和指针(黄色所示),如磁盘块1包含数据项17和35,包含指针P1、P2、P3,P1表示小于17的磁盘块,P2表示在17和35之间的磁盘块,P3表示大于35的磁盘块。真实的数据存在于叶子节点即3、5、9、10、13、15、28、29、36、60、75、79、90、99。非叶子节点只不存储真实的数据,只存储指引搜索方向的数据项,如17、35并不真实存在于数据表中。
1、b树的查找过程
如图所示,如果要查找数据项29,那么首先会把磁盘块1由磁盘加载到内存,此时发生一次IO,在内存中用二分查找确定29在17和35之间,锁定磁盘块1的P2指针,内存时间因为非常短(相比磁盘的IO)可以忽略不计,通过磁盘块1的P2指针的磁盘地址把磁盘块3由磁盘加载到内存,发生第二次IO,29在26和30之间,锁定磁盘块3的P2指针,通过指针加载磁盘块8到内存,发生第三次IO,同时内存中做二分查找找到29,结束查询,总计三次IO。真实的情况是,3层的b+树可以表示上百万的数据,如果上百万的数据查找只需要三次IO,性能提高将是巨大的,如果没有索引,每个数据项都要发生一次IO,那么总共需要百万次的IO,显然成本非常非常高。
动图演示其查找过程:
2、b树性质
1)索引字段要尽量的小:通过上面的分析,我们知道IO次数取决于b+数的高度h,假设当前数据表的数据为N,每个磁盘块的数据项的数量是m,则有h=㏒(m+1)N,当数据量N一定的情况下,m越大,h越小;而m = 磁盘块的大小 / 数据项的大小,磁盘块的大小也就是一个数据页的大小,是固定的,如果数据项占的空间越小,数据项的数量越多,树的高度越低。这就是为什么每个数据项,即索引字段要尽量的小,比如int占4字节,要比bigint8字节少一半。这也是为什么b+树要求把真实的数据放到叶子节点而不是内层节点,一旦放到内层节点,磁盘块的数据项会大幅度下降,导致树增高。当数据项等于1时将会退化成线性表。
2)索引的最左匹配特性(即从左往右匹配):当b+树的数据项是复合的数据结构,比如(name,age,sex)的时候,b+数是按照从左到右的顺序来建立搜索树的,比如当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来确定下一步的所搜方向,如果name相同再依次比较age和sex,最后得到检索的数据;但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪个节点,因为建立搜索树的时候name就是第一个比较因子,必须要先根据name来搜索才能知道下一步去哪里查询。比如当(张三,F)这样的数据来检索时,b+树可以用name来指定搜索方向,但下一个字段age的缺失,所以只能把名字等于张三的数据都找到,然后再匹配性别是F的数据了, 这个是非常重要的性质,即索引的最左匹配特性。
三、Mysql索引分类以及使用场景
1、功能
#1. 索引的功能就是加速查找
#2. mysql中的primary key,unique,联合唯一也都是索引,这些索引除了加速查找以外,还有约束的功能
2、MySQL的索引分类以及使用场景
1)分类
索引分类
1.普通索引index :加速查找
2.唯一索引
主键索引:primary key :加速查找+约束(不为空且唯一)
唯一索引:unique:加速查找+约束 (唯一)
3.联合索引
-primary key(id,name):联合主键索引
-unique(id,name):联合唯一索引
-index(id,name):联合普通索引
4.全文索引fulltext :用于搜索很长一篇文章的时候,效果最好。
5.空间索引spatial :了解就好,几乎不用
2)场景
举个例子来说,比如你在为某商场做一个会员卡的系统。
这个系统有一个会员表
有下列字段:
会员编号 INT
会员姓名 VARCHAR(10)
会员身份证号码 VARCHAR(18)
会员电话 VARCHAR(11)
会员住址 VARCHAR(50)
会员备注信息 TEXT
那么这个 会员编号,作为主键,使用 PRIMARY
会员姓名 如果要建索引的话,那么就是普通的 INDEX
会员身份证号码 如果要建索引的话,那么可以选择 UNIQUE (唯一的,不允许重复)
#除此之外还有全文索引,即FULLTEXT
会员备注信息 , 如果需要建索引的话,可以选择全文搜索。
用于搜索很长一篇文章的时候,效果最好。
用在比较短的文本,如果就一两行字的,普通的 INDEX 也可以。
但其实对于全文搜索,我们并不会使用MySQL自带的该索引,而是会选择第三方软件如Sphinx,专门来做全文搜索。
#其他的如空间索引SPATIAL,了解即可,几乎不用
四、 索引的两大类型hash与btree
我们可以在创建上述索引的时候,为其指定索引类型
1、索引类型分两类:
- hash类型的索引:查询单条快,范围查询慢
- btree类型的索引:b+树,层数越多,数据量指数级增长(通常都使用btree,因为innodb默认支持它)
2、不同的存储引擎支持的索引类型也不一样:
- InnoDB 支持事务,支持行级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
- MyISAM 不支持事务,支持表级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
- Memory 不支持事务,支持表级别锁定,支持 B-tree、Hash 等索引,不支持 Full-text 索引;
- NDB 支持事务,支持行级别锁定,支持 Hash 索引,不支持 B-tree、Full-text 等索引;
- Archive 不支持事务,支持表级别锁定,不支持 B-tree、Hash、Full-text 等索引;
五、创建/删除索引语法
方法一:创建表时:
create table 表名 (
字段名1 数据类型 [完整性约束条件…],
字段名2 数据类型 [完整性约束条件…],
[ unique | fulltext | spatial ] index | key
[索引名] (字段名[(长度)] [ASC |DESC])
);
方法二:CREATE在已存在的表上创建索引
CREATE [UNIQUE | FULLTEXT | SPATIAL ] INDEX 索引名
ON 表名 (字段名[(长度)] [ASC |DESC]) ;
方法三:ALTER TABLE在已存在的表上创建索引
ALTER TABLE 表名 ADD [UNIQUE | FULLTEXT | SPATIAL ] INDEX
索引名 (字段名[(长度)] [ASC |DESC]) ;
···善用帮助文档···
help create
help create index
·················
1.创建索引
-在创建表时就创建(需要注意的几点)
create table s1(
id int ,#可以在这加primary key
#id int index #不可以这样加索引,因为index只是索引,没有约束一说,
#不能像主键,还有唯一约束一样,在定义字段的时候加索引
name char(20),
age int,
email varchar(30)
#primary key(id) #也可以在这加
index(id) #可以这样加
);
-在创建表后在创建
create index name on s1(name); #添加普通索引
create unique age on s1(age);添加唯一索引
alter table s1 add primary key(id); #添加住建索引,也就是给id字段增加一个主键约束
create index name on s1(id,name); #添加普通联合索引
2.删除索引
drop index id on s1;
drop index name on s1; #删除普通索引
drop index age on s1; #删除唯一索引,就和普通索引一样,不用在index前加unique来删,直接就可以删了
alter table s1 drop primary key; #删除主键(因为它添加的时候是按照alter来增加的,那么我们也用alter来删)
3.查看索引
show index from s1;
六、索引测试
1、准备测试数据
1. 准备表
create table s1(
id int,
name varchar(20),
gender char(6),
email varchar(50)
);
2. 创建存储过程,实现批量插入记录
mysql> delimiter // #声明存储过程的结束符号为$$
mysql> create procedure auto_insert1()
BEGIN
declare i int default 1;
while(i<3000000)do
insert into s1 values(i,concat('cm',i),'f',concat('cm',i,'@oldboy'));
set i=i+1;
end while;
END // #$$结束
mysql> delimiter ; #重新声明分号为结束符号
3. 查看存储过程
mysql> show create procedure auto_insert1\G
4. 调用存储过程
mysql> call auto_insert1(); # 过程会很慢,一分钟后可强制结束,也已将创建了一些
2、未创建索引前的查询速度缓慢
#无索引:从头到尾扫描一遍,所以查询速度很慢
mysql> select * from s1 where id=333;
+------+---------+--------+----------------+
| id | name | gender | email |
+------+---------+--------+----------------+
| 333 | cm333 | male | cm333@oldboy |
+------+---------+--------+----------------+
1 row in set (1.33 sec)
mysql> explain select * from s1 where id=333;
mysql> select * from s1 where email='cm333@oldboy';
+------+---------+--------+----------------+
| id | name | gender | email |
+------+---------+--------+----------------+
| 333 | cm333 | male | cm333@oldboy |
+------+---------+--------+----------------+
1 row in set (1.50 sec)
3、加上索引后查询速度极快
1. 一定是为搜索条件的字段创建索引,比如select * from s1 where id > 5;就需要为id加上索引
2. 在表中已经有大量数据的情况下,建索引会很慢,且占用硬盘空间,插入删除更新都很慢,只有查询快
比如create index idx on s1(id);会扫描表中所有的数据,然后以id为数据项,创建索引结构,存放于硬盘的表中。
建完以后,再查询就会很快了
mysql> create index idx on s1(id);
3. 需要注意的是:innodb表的索引会存放于s1.ibd文件中,而myisam表的索引则会有单独的索引文件table1.MYI
mysql> select * from s1 where id=333;
+------+---------+--------+----------------+
| id | name | gender | email |
+------+---------+--------+----------------+
| 333 | cm333 | male | cm333@oldboy |
+------+---------+--------+----------------+
1 row in set (0.01 sec)
七、innodb核心特性事务
1.什么是事务
一组数据操作执行步骤,这些步骤被视为一个工作单元:
1)用于对多个语句进行分组
2)可以在多个客户机并发访问同一个表中的数据时使用
所有步骤都成功或都失败
1)如果所有步骤正常,则执行
2)如果步骤出现错误或不完整,则取消
2.事务演示
#1.创建一个表
mysql> create table jiaoyi(id int primary key auto_increment,name varchar(10),money int);
#2.插入两条数据
mysql> insert into jiaoyi values(1,'qiudao',100);
Query OK, 1 row affected (0.01 sec)
mysql> insert into jiaoyi values(2,'oldboy',200);
Query OK, 1 row affected (0.00 sec)
#3.再打开一个窗口
mysql> select * from jiaoyi;
数据与原窗口查看结果一致
#4.开启一个事务(符合逻辑)
mysql> start transaction;
mysql> update jiaoyi set money=0 where name='qiudao';
mysql> select * from jiaoyi;
mysql> update jiaoyi set money=400 where name='oldboy';
mysql> select * from jiaoyi;
#在执行commit之前,另一个窗口是看不到数据变化的
mysql> commit;
#执行commit之后其他人都能看到数据的变化
#5.开启一个事务(不符合逻辑)
mysql> start transaction;
mysql> update jiaoyi set money=-100 where name='qiudao';
mysql> select * from jiaoyi;
+------+--------+-------+
| id | name | money |
+------+--------+-------+
| 1 | qiudao | -100 |
| 2 | oldboy | 400 |
+------+--------+-------+
2 rows in set (0.00 sec)
#由于钱的值不能为负数,由逻辑判断,操作失败,回滚
mysql> rollback;
mysql> select * from jiaoyi;
+------+--------+-------+
| id | name | money |
+------+--------+-------+
| 1 | qiudao | 0 |
| 2 | oldboy | 400 |
+------+--------+-------+
2 rows in set (0.00 sec)
3.事务通俗理解
#伴随着“交易”出现的数据库概念。
我们理解的“交易”是什么?
1)物与物的交换(古代)
2)货币现金与实物的交换(现代1)
3)虚拟货币与实物的交换(现代2)
4)虚拟货币与虚拟实物交换(现代3)
数据库中的“交易”是什么?
1)事务又是如何保证“交易”的“和谐”?
2)ACID
4.一个成功事务的生命周期
start transaction;
sql1
sql2
sql3
...
commit;
# 若用了begin手动开始编辑事务,编辑完后只要commit手动提交,另一端则能查询到数据
5.一个失败事务的生命周期
start transaction;
sql1
sql2
sql3
...
rollback;
# 若用了begin手动开始编辑事务,编辑完后且只要不commit手动提交,另一端则查询不到数据
6.事务的特性 ACID
Atomic(原子性)
所有语句作为一个单元全部成功执行或全部取消。
Consistent(一致性)
如果数据库在事务开始时处于一致状态,则在执行该事务期间将保留一致状态。
Isolated(隔离性)
事务之间不相互影响。
Durable(持久性)
事务成功完成后,所做的所有更改都会准确地记录在数据库中。所做的更改不会丢失。
我们可以分析一下,事务的四大特征中,所有的操作都会走向磁盘,所以持久性是事务操作的目的,而原子性是实现事务的基础,隔离性是实现数据安全的一种策略、手段,而最终维护的,就是数据的一致性,一致性才是事务中最重要的。四大特征之间,隔离性是为了达到一致性的手段。
ACID四大特征中,最难理解的不是一致性,而是事务的隔离性,数据库权威专家针对事务的隔离性研究出来了事务的隔离四种级别,四种事务隔离级别就是为了解决数据在高并发下产生的问题(脏读、不可重复读、幻读)。
7.事务的控制语句
1)自动提交
#1.查看自动提交
mysql> show variables like 'autocommit';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.01 sec)
#2.临时关闭
set autocommit =0;
#3.永久关闭
[root@db01 ~]# vim /etc/my.cnf
[mysqld]
autocommit=0
2)事务的隐式提交
1)现在版本在开启事务时,不需要手工start transaction;,只要你输入的是DML语句,就会自动开启事务。
2)有些情况下事务会被隐式提交
#隐式提交触发条件
1.执行事务没有commit时,如果使用了DDL或者DCL会自动提交上一条事务
2.执行事务没有commit时,如果你手动执行begin,会自动提交上一条事务
3.执行事务没有commit时,如果执行锁表(lock tables)或者解锁(unlock tables),会自动提交上一条事务
4.load data infile(导数据)会自动提交上一条事务
5.select for update
6.在autocommit=1的时候,会自动提交上一条事务
update ....
commit;
insert into ....
begin;
update ....
create table ....
delete ....
3)控制语句
begin(或 start transaction):显式开始一个新事务,推荐begin #开启事务
savepoint:分配事务过程中的一个位置,以供将来引用 #临时存档
commit:永久记录当前事务所做的更改 #提交事务
rollback:取消当前事务所做的更改 #回滚
roolback to savepoint:取消在 savepoint 之后执行的更改 #回到存档点
release savepoint:删除 savepoint 标识符 #删除临时存档
set autocommit:为当前连接禁用或启用默认 autocommit 模式 #临时开关自动提交
PS:永久开启或关闭autocommit,则在配置文件(my.cnf)插入一行:
autocommit=1 # 开启状态
autocommit=0 # 关闭状态
八、事务的隔离级别
事务并发带来的问题
前面讲到了事务的隔离性,如果要提升系统的吞吐量,当有多个任务需要处理时,应当让多个事务同时执行,这就是事务的并发。既然事务存在并发执行,那必然产生同一个数据操作时的冲突问题,来看一下都会出现什么问题。
1.更新丢失
Lost Update,当两个事务更新同一行数据时,双方都不知道对方的存在,就有可能覆盖对方的修改。比如两个人同时编辑一个文档,最后一个改完的人总会覆盖掉前面那个人的改动。
2.脏读
Dirty Reads,一个事务在执行时修改了某条数据,另一个事务正好也读取了这条数据,并基于这条数据做了其他操作,因为前一个事务还没提交,如果基于修改后的数据进一步处理,就会产生无法挽回的损失。
3.不可重复读
Non-Repeatable Reads,同样是两个事务在操作同一数据,如果在事务开始时读了某数据,这时候另一个事务修改了这条数据,等事务再去读这条数据的时候发现已经变了,这就是没办法重复读一条数据。
4.幻读
Phantom Read,与上方场景相同,事务一开始按某个查询条件没查出任何数据,结果因为另一个事务的影响,再去查时却查到了数据,这种就像产生幻觉了一样,被称作幻读。
1、四种隔离级别
首先,更新丢失这种问题应该是由应用层来解决的,因为数据库没有办法控制用户不去更新某条数据。但是另外三个问题是可以得到解决的,既然有方案解决解决它不就好了,干嘛还要设置这么多隔离级别呢?
刚才说了,如果我们要性能好、吞吐量提升,那就不得不付出一些代价,如果要做到完全没有副作用,那么就只需要让事务排队执行就好了,一个一个执行绝对不会出现脏读幻读的问题,但是这样会导致数据库处理的非常慢。那怎么办呢?官方唯一能做的就是给你提供各种级别的处理方式,由你根据具体业务场景选择,于是就有了隔离级别。
读未提交 Read uncommitted
读未提交其实就是事务没提交就可以读,很显然这种隔离级别会导致读到别的还没提交的数据,一旦基于读到的数据做了进一步处理,而另一个事务最终回滚了操作,那么数据就会错乱,而且很难追踪。总的来说说,读未提交级别会导致脏读。
读提交 Read committed
顾名思义就是事务提交后才能读,假设你拿着银行卡去消费,付钱之前你看到卡里有2000元,这个时候你老婆在淘宝购物,赶在你前面完成了支付,这个时候你再支付的时候就提示余额不足,但是分明你看到卡里的钱是够的啊。
这就是两个事务在执行时,事务A一开始读取了卡里有2000元,这个时候事务B把卡里的钱花完了,事务A最终再确认余额的时候发现卡里已经没有钱了。很显然,读提交能解决脏读问题,但是解决不了不可重复读。
Sql Server,Oracle的默认隔离级别是Read committed。
可重复读 Repeatable read
看名字就看出来了,它的出现就是为了解决不可重复读问题,事务A一旦开始执行,无论事务B怎么改数据,事务A永远读到的就是它刚开始读的值。那么问题就来了,假设事务B把id为1的数据改成了2,事务A并不知道id发生了变化,当事务A新增数据的时候却发现为2的id已经存在了,这就是幻读。
MySQL的默认隔离级别就是Repeatable read。
串行化 serializable
这个就是最无敌的存在了,所有的事务串起来一个个执行,因为没有并发的场景出现了,什么幻读、脏读、不可重复读统统都不存在的。但是同样的,基本并发能力会非常差。最终,到底什么隔离级别完全要根据自己的业务场景选择,没有最好的,只有最适合的。
事务隔离级别越严格,越消耗计算机性能,效率也越低,通常情况下,设置为允许不可重复读就可以解决大多数的问题了。
Mysql默认使用的数据隔离级别是REPEATABLE READ ,可重复读,允许幻读。
2、事务并发常见问题实例演示
2.1脏读
脏读:比如有两个事务并行执行操作同一条数据库记录,A事务能读取到B事务未提交的数据。
1)修改自动提交事务开关
临时有效
mysql数据库事务开关
开启自动提交事务:set autocommit = 1;
关闭自动提交事务:set autocommit = 0 ;
查看事务开关:show variables like '%autocommit%';
2)修改数据库的事务隔离级别
//全局的 ---此处演示我们设置全局的
set global transaction isolation level read uncommitted;
//当前会话
set session transaction isolation level read uncommitted;
3)终端1查询用户指定信息(此处不提交事务)
-- 关闭自动提交事务
set autocommit = 0;
-- 查询指定用户
select * from jiaoyi where id=1;
-- 修改指定用户余额
update jiaoyi set money = 500;
4)终端2查询用户信息
---------- 窗口B ------------------------------------------
-- 关闭自动提交事务
set autocommit = 0;
-- 查询指定用户
select * from jiaoyi where id=1;
+------+--------+-------+
| id | name | money |
+------+--------+-------+
| 1 | qiudao | 500 |
+------+--------+-------+
1 row in set (0.00 sec)
很显然,事务A修改了数据后还没有提交事务,此时事务B可以读取到事务A没有提交的事务的数据。这就是脏读,脏读情况下的数据是不可取的,所以一般没有数据库事务级别设置为允许脏读。
一句话总结:脏读就是指事务A读取到事务B修改但未提交事务的数据。
2.2不可重复读
学习完脏读后,我们再来看看什么是不可重复读。比如事务A在同一事务中多次读取同一记录,此时事务B修改了事务A正在读的数据并且提交了事务,但是事务A读取到了事务B所提交的数据,导致两次读取数据不一致。
1)修改事务隔离级别
//全局的 ---此处演示我们设置全局的
set global transaction isolation level read committed;
//当前会话
set session transaction isolation level read committed;
2)窗口1开启事务,查询指定用户数据
---------- 窗口A ------------------------------------------
-- 关闭自动提交事务
set autocommit = 0;
-- 查询指定用户
select * from jiaoyi where id = 1;
此时窗口2执行事务
-- 查询指定用户
select * from jiaoyi where id = 1;
-- 提交事务
commit;
3)窗口2同时执行事务
窗口1先是开启事务,查询指定用户信息,然后窗口2开启事务,查询数据指定用户,修改数据,提交事务,然后再回到窗口1,查询指定用户信息;窗口2操作内容如下
-- 关闭自动提交事务
set autocommit = 0;
-- 查询指定用户
select * from jiaoyi where id = 1;
-- 修改指定用户余额
update money set money = 300 where id=1;
-- 提交事务
commit;
事务A在两次查询中,查询的数据不一样,这就是不可重复读。Mysql默认采用的就是不可重复读的隔离级别,用一句话总结,不可重复读就是事务A读取到事务B已提交事务的数据,导致两次读取数据信息不一致。
2.3 幻读
上面我我们学习了一下什么不可重复读,在mysql数据库中,不可重复读是不被允许的。
//全局的 ---此处演示我们设置全局的
set global transaction isolation level repeatable read;
//当前会话
set session transaction isolation level repeatable read;
2)窗口1
-- 关闭自动提交事务
set autocommit = 0;
select * from jiaoyi where id = 5;
###此时窗口1未查询到id为5的数据,正准备进行插入时,窗口2插入了一条id为5的数据。
-- 没有查询到结果,进行插入
insert into jiaoyi values (5, 'cm',200);
-- 再查询id为5的
select * from money where id = 5;
commit;
3)窗口2:此处,事务A中查询指定id为5的记录,没有查询到结果后,事务B进行插入了一条id为5的记录并且提交事务;
-- 关闭自动提交事务
set autocommit = 0;
-- 插入一条记录
insert into jiaoyi values (5,'cm',200);
-- 提交事务
commit;
在上述事务A中,不提交事务的情况下,插入id为5的记录会一直报错主键冲突,但是再怎么查询id为5的记录都查询不到;这是因为在MySql的设计中,事务中查询的是被修改前的日志。即Undo log。
3、修改隔离级别
#查看隔离级别
mysql> show variables like '%iso%';
+---------------+------------------+
| Variable_name | Value |
+---------------+------------------+
| tx_isolation | READ-UNCOMMITTED |
+---------------+------------------+
1 row in set (0.00 sec)
#配置隔离级别
[root@db01 ~]# vim /etc/my.cnf
[mysqld]
transaction_isolation=read-uncommit
八、事务的日志
1.redo log
redo,顾名思义“重做日志”,是事务日志的一种。
1)作用
在事务ACID过程中,实现的是“ D ”持久化的作用。
REDO:记录的是,内存数据页的变化过程
特性:WAL(Write Ahead Log)日志优先写
2)REDO工作过程
执行步骤:
update t1 set num=2 where num=1;
1)首先将t1表中num=1的行所在数据页加载到内存中buffer page
2)MySQL实例在内存中将num=1的数据页改成num=2
3)num=1变成num=2的变化过程会记录到,redo内存区域,也就是redo buffer page中
提交事务执行步骤:
commit;
1)当敲下commit命令的瞬间,MySQL会将redo buffer page写入磁盘区域redo log
2)当写入成功之后,commit返回ok
2.undo log
1)作用
undo,顾名思义“回滚日志”,是事务日志的一种。
在事务ACID过程中,实现的是“A”原子性的作用。当然CI的特性也和undo有关
# PS:
undo buffer cache 会实时的将数据写入磁盘,也就是 是否执行了 commit
事务id:txid
日志版本号:lsn 物理备份时可以看到
八、事务中的锁
1.什么是锁?锁的作用
- 锁,顾名思义就是锁定的意思,修改数据时锁住数据
- 在事务ACID特性过程中,“锁”和“隔离级别”一起来实现“I”隔离性的作用。
2.示例
数据 id=1
事务1 set id=2
事务2 set id=3
#1.创建一个表
create table test(id int);
#2.插入数据
insert into test values(1);
#3.开启两个窗口开启事务
begin;
update test set id=2 where id=1;
begin;
update test set id=3 where id=1;
3.锁的类别
排他锁:保证在多事务操作时,数据的一致性。(在我修改数据时,其他人不得修改)
共享锁:保证在多事务工作期间,数据查询时不会被阻塞。
乐观锁:多事务操作时,数据可以被同时修改,谁先提交,谁修改成功。
悲观锁:多事务操作时,数据只有一个人可以修改。
3.多版本并发控制(MVCC)
1)只阻塞修改类操作(排它锁),不阻塞查询类操作(共享锁)
2)乐观锁的机制(谁先提交谁为准)
4.锁得粒度
MyISAM:表级锁
InnoDB:行级锁
5.事务的隔离级别
1)四种隔离级别:
1.rc: read committed 允许事务查看其他事务所进行的已提交更改
2.ru: read uncommitted(独立提交),未提交读,允许事务查看其他事务所进行的未提交更改;
3.rr: repeatable read 可重复读 InnoDB 的默认级别 #commit提交以后可能看不到数据变化,必须重新连接
4.serializable:串行化:,将一个事务的结果与其他事务完全隔离 #如果一个事务没有提交,查询也不能查了
# 我改微信头像时你不能看我的信息,我看你朋友圈的时候你不能发朋友圈、不能看朋友圈
2)修改隔离级别
#查看隔离级别
mysql> show variables like '%iso%';
#修改隔离级别为RU
[mysqld]
transaction_isolation=read-uncommit
mysql> use oldboy
mysql> select * from stu;
mysql> insert into stu(id,name,sex,money) values(2,'li4','f',123);
#修改隔离级别为RC
[mysqld]
transaction_isolation=read-commit
6.脏读,幻读,不可重复读
1.脏读:(RU级别会发生)
一个事务执行了,去没有提交,被其他人读取到了值,可是事务回滚了,那刚被独到的数据被称为脏数据
2.幻读:(RR级别可以解决这个问题) # 可理解为:操作端的当前状态
事务将数据库中所有数据都删除的时候,但是事务B就在这个时候新插入了一条记录,当事务A删除结束后发现还有一条数据,就好像发生了幻觉一样。这就叫幻读。
# 说白了,就是当前用户的当前数据还没刷新到最新,或者刷新了一部分,且有残留没刷新完全,导致数据的不一致
3.不可重复读: # 被操作端的当前状态
执行一个事务读取两次数据,在第一次读取某一条数据后,有一个事务修改了读到的数据并提交,第一个事务再次读取该数据,两次读取便得到了不同的结果。
# 说白了,就是当前用户的当前数据还没刷新到最新,导致数据的不一致