【MySQL】事务及索引

事务及索引

  • 事务
    • 事务操作
    • 事务四大特性(ACID)
    • 并发事务问题
    • 事务隔离级别
    • 事务隔离效果演示
  • 索引
    • 索引操作
    • 索引结构

事务

事务 是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。

最常见的一个例子就是 用户A 给 用户B 转 1000元,那么 用户A 的余额就要少 1000,而 用户B 的余额就要多1000,这完整的一件事必须都执行成功,即用户A 的钱确实少了1000,用户B 的钱确实多了1000,不能因为其它异常情况而导致 A 的少了1000,而 B 却没有多1000,要么 A 的就不少 1000,B的也不多 1000。

我们先创建一张表:

create table account (
id int auto_increment primary key comment ' 主键ID',
name varchar(10) comment '姓名',
money int comment '余额'
) comment '账户表';

-- 插入数据
insert into account(id, name, money) VALUES (null, '张三', 2000), (null, '李四', 2000);

表的数据如下图所示:

【MySQL】事务及索引_第1张图片

现在我们开始模拟转账的过程(张三给李四转账1000):

【MySQL】事务及索引_第2张图片

转账后两人余额情况:

【MySQL】事务及索引_第3张图片

那现在假设语句书写错误:
在这里插入图片描述
张三和李四两个人的总金额就发生了变化。

事务操作

在 MySQL 命令行的默认设置下,事务都是自动提交的,即执行 SQL 语句后就会马上执行 COMMIT 操作。

为了避免这种情况,我们可以设置事务提交方式,把自动提交改成手动提交。

  • 查看/设置 事务提交方式
    SELECT @@autocommit;
    SET @@autocommit = 0;
    【MySQL】事务及索引_第4张图片

  • 提交事务
    COMMIT;

  • 回滚事务
    ROLLBACK;

现在我们在尝试操作转账这个过程:

【MySQL】事务及索引_第5张图片

这里看似转账成功了,但是当我们重新打开一个 mysql 客户端就可以发现并没有转账成功:

【MySQL】事务及索引_第6张图片

现在我们在最开始的客户端提交事务:

【MySQL】事务及索引_第7张图片

手动提交后,在重新开启的客户端就可以看到表里的金额发生了变化:

【MySQL】事务及索引_第8张图片

如果我们在执行 SQL语句发生错误时,我们就不再提交事务,而是选择 回滚 rollback;,这样就可以恢复到上次提交时状态,这里就不做演示了。


上面的事务操作是修改默认的事务提交方式,使用手动提交事务完成的。

现在我们不修改事务的提交方式,来完成上面的效果:

  • 开启事务
    START TRANSACTION;BEGIN;
    【MySQL】事务及索引_第9张图片
    【MySQL】事务及索引_第10张图片

  • 提交事务
    COMMIT;
    【MySQL】事务及索引_第11张图片
    【MySQL】事务及索引_第12张图片

  • 回滚事务
    ROLLBACK;
    注意:commit 提交后(假设语句都是正确的情况下),回滚 rollback 并不会回到事务提交前,回滚是发生错误时候使用的,发生了错误肯定就不要 commit 了
    【MySQL】事务及索引_第13张图片

事务四大特性(ACID)

  • 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。就像刚刚的转账,转成功就是张三减1000,李四加1000,失败就还是原来样子,没有谁多谁少。
  • 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。无论转账是否成功,两人总金额都是4000。
  • 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。事务A的操作并不会影响事务B。
  • 持久性(Durability):事务一旦提交或回滚,对数据库中的数据大改变就是永久的。

并发事务问题

  • 脏读
    一个事务读取到另外一个事务还没有提交的数据。
  • 不可重复读
    一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。
  • 幻读
    一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现 “幻影”。

事务隔离级别

针对以上的事务并发问题,我们可以设置具体的隔离来解决:

隔离级别 脏读 不可重复读 幻读
Read uncommitted
Read committed ×
Repeatable Read(默认) × ×
Serializable × × ×

(注意:√ 表示会出现,× 表示不会出现,例如,在使用 Read committed 隔离后,会出现不可重复读和幻读)
隔离级别越高,性能就越低,性能越高,数据安全性就越低。

查看事务隔离级别
SELECT @@TRANSACTION_ISOLATION;
【MySQL】事务及索引_第14张图片
设置事务隔离级别
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}
【MySQL】事务及索引_第15张图片

事务隔离效果演示

使用 Read uncommitted 就会出现脏读的情况:
【MySQL】事务及索引_第16张图片
A 读取到了 B 还没有提交后的数据就是脏读。


现在我们将隔离级别修改成 Read committed:
【MySQL】事务及索引_第17张图片
【MySQL】事务及索引_第18张图片

Read committed 虽然解决了脏读,但是并不能解决 不可重复读这个问题:
【MySQL】事务及索引_第19张图片


将隔离级别修改成 默认的 Repeatable Read,就可以解决不可重复读这个问题:

【MySQL】事务及索引_第20张图片
【MySQL】事务及索引_第21张图片

但是会出现幻读这个情况:
【MySQL】事务及索引_第22张图片


使用 Serializable 隔离可以解决幻读这个现象:
【MySQL】事务及索引_第23张图片
只有事务A 提交后,事务B 才会执行刚刚一直在等待执行的语句:
【MySQL】事务及索引_第24张图片

索引

索引 (index) 是帮助 MysQL 高效获取数据的数据结构(有序)。说直白点,索引就是为了提高查询的效率。但索引的存在也占用了额外的空间,也降低了更新表 (insert,update等) 的速度。

索引操作

  • 查看索引 — show index from 表名;
    【MySQL】事务及索引_第25张图片
  • 创建索引 — create index 索引名 on 表名 (列名);
    【MySQL】事务及索引_第26张图片
  • 删除索引 — drop index 索引名 on 表名;
    【MySQL】事务及索引_第27张图片

索引结构

索引的存在是为了提高查询速度,那么我们可以想到哈希表和二叉搜索树都是查询速度比较快的结构,但是在 mysql 中,我们也常使用 范围查询(between …),而哈希表只适用等值查询,对于二叉搜索树结构,那树的深度会随着表中的数据增多而变深,每次查找或插入也并不理想。

所以针对以上情况,mysql 使用的是 B+ 树,在了解 B+ 树之前,先看一下 B 树:

B 树,也叫多路平衡查找树,以一棵最大度为 5 的 B 树为例(每个节点最多存储4个key,5个指针):
【MySQL】事务及索引_第28张图片
将一段数据分为多个区间存储,比二叉树的深度减少了很多。


B+ 树: 以一棵最大度为 5 的 B+ 树为例:
【MySQL】事务及索引_第29张图片

MySQL 索引数据结构对经典的 B+Tree 进行了优化。在原 B+Tree 的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的 B+Tree,提高区间访问的性能。


综上可以总结出使用 B+ 树的原因:

  • 相对于二叉树,层级更少,搜索效率高。
  • 在 B 树中,叶子结点和非叶子结点都会保存数据,导致一定空间内的键值就会减少,指针跟着减少,若同样要保存大量数据,只能增加树的高度,从而导致性能降低。
  • 对于 hash 索引,B+ 树支持范围查询和排序操作,而 hash 只支持等值查询。

你可能感兴趣的:(数据库,mysql,数据库,java)