sql高级

数据库的范式

为了建立冗余较小、结构合理的数据库,设计数据库时必须遵循一定的规则。在关系型数据库中这种规则就称为范式。

范式是符合某一种设计要求的总结。要想设计一个结构合理的关系型数据库,必须满足一定的范式。实际上,数据库范式就是我们在数据库中创建表的规则

数据库范式一共有6种范式:第一范式,第二范式,第三范式,BC范式,第四范式,第五范式

第一范式(1NF)

  • 定义:每个列都不可再分
  • 要求:
    • 每一列都是原子性的,不可再分
    • 每一行必须唯一,可以通过主键来实现

第二范式(2NF):

  • 定义:在1NF的基础上,非主键列完全依赖于主键,而不是依赖于主键的一部分
  • 要求:
    • 表中必须有一个主键
    • 其他列必须完全依赖于主键

第三范式(3NF):

  • 定义:在2NF的基础上,非主键列只依赖于主键,不依赖于其他非主键列
  • 要求:
    • 消除传递依赖

第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关;如下表通过订单编号知道项目编号,负责人和客户编号;通过客户编号知道客户名称和客户联系电话(而不是由订单编号得到客户名称和客户联系电话,他们没有直接关系),满足第三范式。

|------|------|-----|------|
| 订单编号 | 项目编号 | 负责人 | 客户编号 |

|------|------|--------|
| 客户编号 | 客户名称 | 客户联系电话 |

BC范式

所有的非主属性对每一个码都是完全函数依赖 (暗含 主关键字里面可能有多个码可以将实体区分)

所有的主属性对每一个不包含它的码也是完全函数依赖(即所选码与未选择的码之间也是完全函数依赖的)

没有任何属性完全函数依赖于非码的任何一组属性(即非主属性之间不能函数依赖)

解释:

例如关系模式 S(Sno,Sname,Sdept,Sage) 假设 Sname具有唯一性

1:非主属性 (Sdept,Sage) 不仅依赖于Sno,而且依赖于Sname,因为不仅可以通过学号知道学生的信息,还可以通过姓名知道学生的信息。

2:Sno 与Sname之间也是完全函数依赖关系

3:没有任何一个属性函数依赖于Sdept和Sage

第四范式

多值依赖的概念:表中一对多关系,要求只有一个主键,对应表中其他非主属性

第四范式即在满足巴斯-科德范式(BCNF)的基础上,消除非平凡且非函数依赖的多值依赖(即把同一表内的多对多关系删除)。

第五范式

满足第四范式(4NF)的基础上,消除不是由候选码所蕴含的连接依赖。如果关系模式R中的每一个连接依赖均由R的候选码所隐含,则称此关系模式符合第五范式。


存储引擎

MyISAM

特点

1.不支持事务

2.不支持外键

3.支持表锁(表的读锁和写锁)


准备工作

复制代码
create table if not exists `user`
(
  `id` int primary key auto_increment,
  `username` varchar(255) not null,
  `password` varchar(255) not null
) engine = myisam;

-- 查看表的详细信息
show table status like 'user';

插入一条数据


案例

表的写锁
复制代码
-- 开启事务
START TRANSACTION;

-- 锁定user表进行写操作(写锁),锁上后别人既不能读也不能写
lock table `user` write;

UPDATE `user` SET username = 'admin1' WHERE id = 1;

-- 提交事务
COMMIT;

-- 解锁
unlock tables;

选中1~10行执行,然后打开user表

发现user表被锁住了,无法进行读操作,说明MyISAM存储引擎是支持写锁的

执行第13行代码,解锁

发现马上可以读了


表的读锁
复制代码
-- 测试表的读锁
-- 开启事务
START TRANSACTION;

-- 锁定user表进行读操作(读锁),锁上后别人只能读,不能写
lock table `user` read;

select * from user;

-- 提交事务
COMMIT;

-- 解锁
unlock tables;

选中1~11行执行,打开user表

发现是可以读的

现在对user表进行写操作

发现无法进行写,符合预期(上了读锁后别人不能写,只能读)

现在执行第14行代码

写操作立刻执行完成了,符合预期


InnoDB(MySql5.5版本后默认的存储引擎)

特点

1.支持事务

2.支持外键

3.支持表锁

4.支持行锁


准备工作

复制代码
create table if not exists `employee`
(
    `id`       int ,
    `username` varchar(255) not null,
    `password` varchar(255) not null
) engine=innodb;

-- 查看表的详细信息
show table status like 'employee';
复制代码
-- 定义存储过程
DELIMITER $$

CREATE PROCEDURE insert_data()
BEGIN
    DECLARE i INT DEFAULT 1;
    WHILE i <= 10000000 DO
            INSERT INTO employee (id,username, password)
            VALUES (i,CONCAT('user', i), CONCAT('pass', i));
            SET i = i + 1;
        END WHILE;
END$$

DELIMITER ;

-- 调用存储过程,插入1000万条数据
CALL insert_data();

前置知识

事务隔离级别

MySQL 支持四种事务隔离级别:

  • 未提交读(READ UNCOMMITTED):允许读取其他事务未提交的数据,这可能导致脏读。
  • 提交读(READ COMMITTED):只能读取其他事务已提交的数据,可以避免脏读,但可能出现不可重复读。
  • 可重复读(REPEATABLE READ):在同一个事务中多次读取同一数据的结果是一致的,可以避免脏读和不可重复读,但可能出现幻读(可以通过加锁来避免)。这是 MySQL InnoDB 存储引擎的默认隔离级别。
  • 串行读(SERIALIZABLE):所有事务按顺序执行,避免了脏读、不可重复读和幻读,但性能最差。

查看事务隔离级别

复制代码
-- 查看事务隔离级别
SHOW VARIABLES LIKE 'transaction_isolation';

共享锁和排他锁
  1. 排他锁(Exclusive Locks,简称X锁)
    • 别名:写锁、独占锁。
    • 功能:当某个事务对数据库中的某一数据对象加上排他锁时,其他事务既不能对该数据对象加共享锁,也不能加排他锁,直到该排他锁被释放。这种锁机制主要用于保护数据在修改过程中不被其他事务干扰。
  1. 共享锁(Share Locks,简称S锁)
    • 别名:读锁。
    • 功能:允许多个事务同时读取同一数据对象,而不影响其他事务对该数据对象的读取。在数据被共享锁锁定的期间,其他事务可以并发地获取该数据的共享锁进行读取操作,但无法对其进行修改,因为修改操作需要排他锁。
  • FOR UPDATE将对选定的行应用排他锁,防止其他事务修改这些行,直到当前事务提交或回滚。(排他锁)
  • LOCK IN SHARE MODE将对选定的行应用共享锁,允许其他事务读取这些行,但阻止它们进行修改。(共享锁)

间隙锁

间隙锁(Gap Lock)是数据库中一种用于控制并发访问的锁定机制,特别是在InnoDB存储引擎的可重复读(Repeatable Read)隔离级别下。以下是关于间隙锁的详细解释及其作用:

一、间隙锁的定义

间隙锁是一种锁定索引范围而不是具体数据行的锁。当事务执行范围查询并请求共享或排他锁时,InnoDB会给符合条件的已有数据记录的索引项加锁,同时对键值在条件范围内但并不存在的记录(即"间隙")也加锁。这种锁定的范围是开区间,即不包括边界值。

二、间隙锁的作用

  1. 防止幻读
    • 幻读是指在同一个事务中,两次相同的查询返回了不同数量的行,这通常是因为其他事务在此期间插入了新的数据。
    • 间隙锁通过锁定查询范围内的间隙,防止其他事务在该范围内插入新的数据行,从而确保查询结果的一致性,避免幻读现象的发生。
  1. 维护数据一致性
    • 当事务需要对索引范围内的数据进行更新或删除操作时,间隙锁可以确保在事务执行期间,其他事务无法在该范围内插入新的数据行,从而维护数据的一致性。
  1. 减少锁冲突和死锁
    • 在高并发环境下,多个事务可能试图同时对相同的数据范围进行操作。使用间隙锁可以减少锁冲突和死锁的发生,因为间隙锁锁定了索引范围而不是具体的数据行,从而降低了锁竞争的可能性。

案例

原本的employee表的内容如下


例1
复制代码
-- 开启事务
start transaction;

-- 添加排他锁
select * from employee where id=5 for update;

update employee set username='tom' where id =5;

-- 提交事务
commit;

选定1~7行执行


现在另起一个终端,执行如下代码(写)

复制代码
update employee set username='joe' where id =5;

结果如下

由于对id为5的这行上了排他锁,其他业务无法进行写操作,符合预期

现在执行如下代码

复制代码
update employee set username='joe' where id =6;
update employee set username='joe' where id =7;
update employee set username='joe' where id =8;
update employee set username='joe' where id =9;
update employee set username='joe' where id =10;

发现无一例外,都超时了

这是为什么呢?我们只是对id为5的那行上了锁,为什么对其他行的更改也无法执行呢?

这是因为:

在innodb中,为了防止发生幻读,采取了间隙锁策略。比如我们锁定了id=1的记录时,实际会把临近几个记录也锁上。

在另一个终端,执行如下代码(读)

复制代码
SELECT * FROM `employee` WHERE id =5;

得到如下结果

发现居然可以读出数据,但是数据是修改之前的数据,为什么会这样呢?

原因如下:

在可重复读(REPEATABLE READ)隔离级别下,InnoDB 使用多版本并发控制(MVCC)来提供快照读。这意味着,在没有显式加锁的情况下,读取操作会看到一个一致性的快照,而不是最新的数据。因此,即使一个事务对某行数据执行了 SELECT ... FOR UPDATE其他事务仍然可以通过快照读来读取该行数据的旧版本,而不会被阻塞

执行第10行代码后(提交事务),重新执行 SELECT * FROM `employee` WHERE id =5 ,结果如下

这是由于提交事务释放了排他锁,因而得到了最新的结果


例2
复制代码
start transaction;

-- 添加共享锁(读锁)其他事务能读,但是不能写
select * from employee where id=5 lock in share mode;

commit;

执行1~6行代码

另起一个终端,执行如下代码

复制代码
SELECT * FROM `employee` WHERE id =5;

update employee set username='joe' where id =5;

结果如下

共享锁允许其他事务读,但不允许写,符合预期

执行第6行代码后,重新执行 update employee set username='zoe' where id =5; 结果如下

符合预期


慢查询

-- 查看所有环境变量

复制代码
show variables;

-- 开启慢查询日志

复制代码
set global slow_query_log ='on'; 
show variables  like 'slow_query_log';

-- 设置慢查询的阈值为1s 默认10s

复制代码
set global long_query_time=1;
show variables like 'long_query_time';

-- 查看 MySQL 数据目录的绝对路径

复制代码
SHOW VARIABLES LIKE 'datadir';

-- 查看慢查询日志的路径(拼上前面查到的datadir就是完整路径了)

复制代码
SHOW VARIABLES LIKE 'slow_query_log_file';

查询优化

employee表中有1000万条数据

复制代码
select count(*) '总条数' from employee;

select * ===> select 具体需要的字段

可以看到,优化了1秒多


创建索引

索引的分类

(1) 普通索引:最基本的索引,它没有任何限制。

(2) 唯一索引:与普通索引类似,不同的就是索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。

(3) 主键索引:它是一种特殊的唯一索引,用于唯一标识数据表中的某一条记录,不允许有空值,一般用

primary key 来约束。

(4) 联合索引(又叫复合索引):多个字段上建立的索引,能够加速复合查询条件的检索。

(5) 全文索引:老版本 MySQL 自带的全文索引只能用于数据库引擎为MyISAM 的数据表,新版本 MySQL 5.6 的 InnoDB 支持全文索引。默认 MySQL不支持中文全文检索,可以通过扩展 MySQL,添加中文全文检索或为中文内容表提供一个对应的英文索引表的方式来支持中文。

什么样的数据列需要创建索引

1.经常被当作查询条件的列

2.数据区分度很大的(如身份证id)

3.数据量很大的情况下

什么样的数据列不适合加索引

1.不会用来做查询的列

2.区分度小的

3.数据量小的

聚簇索引和非聚簇索引(二级索引)

一、聚簇索引(Clustered Index)

  1. 定义 :聚簇索引是一种数据存储方式,它将数据行和索引键一起存储在同一个数据页中,使得数据的物理存储顺序与索引的逻辑顺序一致。
  2. 特点
    • 每张表只能有一个聚簇索引,因为数据行本身只能按照一种方式物理存储。
    • 聚簇索引的叶子节点存储的是整行记录,这意味着找到索引即找到了数据。
    • 由于数据行和索引键一起存储,因此聚簇索引能够显著提高范围查询的性能,因为相邻的索引行通常也对应相邻的数据。
    • 建立聚簇索引需要额外的存储空间,通常至少需要相当于表大小的120%的附加空间,以存放表的副本和索引中间页。
  1. 应用场景:聚簇索引适用于需要频繁进行范围查询或排序操作的场景,如按照日期、时间等字段进行查询。

二、非聚簇索引(Non-Clustered Index)

  1. 定义 :非聚簇索引是一种索引存储方式,它将索引键与数据行分开存储。 索引的叶子节点存储的是指向数据行的指针(通常是主键值或行标识符)。
  2. 特点
    • 一个表中可以有多个非聚簇索引,因为数据行的物理存储顺序与索引的逻辑顺序可以不一致。
    • 非聚簇索引的叶子节点存储的是主键值或行指针,而不是整行记录。因此,通过非聚簇索引查询数据时,通常需要回表操作,即先通过索引找到主键值或行指针,然后再根据这些指针回到表中查找完整的数据行。
    • 非聚簇索引不改变表中数据的物理存储顺序,而是单独创建一张索引表来存储索引键和指针。
  1. 应用场景:非聚簇索引适用于需要频繁进行单值查询的场景,如按照某个特定字段(如用户ID)进行查询。此外,当表中存在多个查询条件且这些条件不是主键或聚簇索引时,可以考虑创建非聚簇索引以提高查询性能。

External Player - 哔哩哔哩嵌入式外链播放器


一文搞懂聚簇索引和非聚簇索引

非聚簇索引不存储数据,它的叶子结点存的是聚簇索引的key。第二种情况下要查name需要命中叶子结点的key,也就是主键id,根据主键id查询一次,也就是回表查询。

第三种情况查的就是no,也就是叶子结点,因此不需要回表查询。


案例

例1

添加索引前

复制代码
select * from employee where id=5000000;

explain select * from employee where id=5000000;

添加索引后

创建employee表的时候,并没有指定id为主键,而主键是默认带索引的,因此我们现在指定id为主键

复制代码
-- 添加主键索引
alter table employee add primary key(id);

重新执行

复制代码
select * from employee where id=5000000;

explain select * from employee where id=5000000;

可以看到,查询时间由17s优化到0.03s


例2

复制代码
-- 添加索引
alter table employee add index idx_username (username);

那么现在employee表中有一个聚簇索引(id)和一个非聚簇索引(username)

运行如下sql语句,查看结果

复制代码
explain select * from employee where id=5000000;

查询条件是通过id查询,id是聚簇索引,直接通过聚簇索引找到数据,不存在回表查询。(聚簇索引==>data)


复制代码
explain select username `password` from employee where username='user5000000';

查询条件是通过username查询,username上有非聚簇索引,结果集中需要有username,password字段,因此需要通过二级索引------idx_username 找到符合要求的叶子结点,叶子结点中记录了聚簇索引(主键)的key,在通过主键查表获得响应的数据,这里出现了回表查询。


复制代码
explain select username from employee where username='user5000000';

查询条件是通过username查询,username上有非聚簇索引,结果集中只需要有username字段,因此需要通过二级索引------idx_username 找到符合要求的叶子结点,叶子结点中记录了username,也就不需要再进行回表查询了。


通过explain+sql 分析sql语句的执行效率

Explain命令在解决数据库性能上是第一推荐使用命令,大部分的性能问题可以通过此命令来简单的解决,Explain可以用来查看SQL语句的执行效 果,可以帮助选择更好的索引和优化查询语句,写出更好的优化语句。

Explain语法:explain + sql

例如:explain select * from news;

输出:

+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+-------------------+---------+---------+-------+------+-------+

输出解释

id

标识查询的执行顺序。如果查询涉及到子查询或多个表的连接,每个操作会有一个唯一的 id

select_type

表示查询的类型,常见的有:

  • SIMPLE:简单的 SELECT 查询(不包含子查询或 UNION)。
  • PRIMARY:最外层的查询。
  • SUBQUERY:子查询中的第一个 SELECT
  • DERIVED:派生表(通常是 FROM 子句中的子查询)。
  • UNION:在 UNION 中的第二个或之后的 SELECT 语句。

table

表示查询涉及的表名。

partitions(MySQL 5.7 及以上):

如果查询涉及分区表,这里是分区信息。

type(重要)

表示连接类型,常见的类型有:

  • system:表中只有一行记录(系统表)。
  • const:最多匹配一行记录(常量查询)。
  • eq_ref:唯一索引匹配(如主键或唯一索引)。
  • ref:非唯一索引匹配。
  • fulltext:使用fulltext索引进行全文搜索。
  • ref_or_null:类似于ref,但MySQL还会搜索包含NULL值的行。
  • index_merge:这表示使用了索引合并优化技术,即MySQL使用了多个索引来查找行,并将结果合并。这通常比使用单个索引扫描更有效,但比使用单个索引的查询要慢。
  • unique_subquery:在子查询中,返回唯一候选行的子查询,子查询的索引是唯一的。
  • index_subquery:在子查询中,返回匹配行的索引查找,子查询可能返回多个匹配行。
  • range:索引范围扫描。
  • index:全索引扫描。
  • ALL:全表扫描(未使用索引)。

结果值从好到坏依次是:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

一般来说,得保证查询至少达到range级别,最好能达到ref,否则就可能会出现性能问题。

possible_keys

MySQL 可能使用的索引。

key

MySQL 实际选择的索引。

key_len

使用的索引长度。(越小越好)

ref

表示哪些列或常量与 key 一起使用来选择行。

rows

估计需要扫描的行数。

filtered

表示过滤后的行数占原始行数的百分比。(越大越好)

Extra

包含额外的信息,可能的值包括:

  • Using where:表示使用 WHERE 子句进行过滤。
  • Using index:表示使用了覆盖索引(不需要回表查询)。
  • Using temporary:表示需要创建临时表。
  • Using filesort:表示数据排序操作无法使用索引。
  • NULL:表示没有额外的信息。

MYSQL事务

事务的四大特性

事务具有四个基本特征,分别是原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),简称ACID。

  1. 原子性(Atomicity)
    • 事务是一个不可分割的工作单位,作为一个整体要么全部执行成功,要么全部不执行。
    • 如果事务中的任何一项操作失败,那么整个事务将失败,并且所有已经执行的操作都将被撤销并回滚到事务开始之前的状态。
  1. 一致性(Consistency)
    • 事务的执行不能破坏数据库数据的完整性和一致性。
    • 一个事务在执行之前和执行之后,数据库都必须处于一致性状态。
    • 这意味着事务必须确保数据的完整性约束(如主键约束、外键约束等)不被破坏。
  1. 隔离性(Isolation)
    • 在并发环境中,并发的事务是互相隔离的,一个事务的执行不能被其他事务所干扰。
    • 不同的事务并发操作相同的数据时,每个事务都有各自完整的数据空间。
    • 一个事务内部的操作及使用的数据对其他并发事务是隔离的,并发执行的各个事务不能互相干扰。
  1. 持久性(Durability)
    • 事务一旦提交后,数据库中的数据必须被永久地保存下来。
    • 即使服务器系统崩溃或服务器宕机等故障发生,已经提交的事务对数据库的改变仍然是永久的。

并发事务可能带来的问题

  • 脏读:一个事务读取了另一个事务未提交的数据,若另一个事务回滚,则所读数据为"脏"数据。
  • 不可重复读:一个事务中多次读取同一数据,若其他事务在读取期间对数据进行了更新并提交,则多次读取的结果可能不一致。
  • 幻读:一个事务在读取某个范围内的数据时,另一个事务又在此范围内插入了新的记录,前一个事务再次读取时会出现"幻行"。

事务隔离级别

MySQL支持四种事务隔离级别,分别是:

读未提交(Read Uncommitted)、

读已提交(Read Committed)、

可重复读(Repeatable Read)、

串行化(Serializable)。


修改事务隔离级别

复制代码
-- 查看当前会话的事务隔离级别
select @@transaction_isolation;

-- 查看全局的事务隔离级别
select @@global.transaction_isolation;

-- 设置当前会话的事务隔离级别为REPEATABLE READ
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- 设置全局的事务隔离级别为REPEATABLE READ
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ;

数据库demo下有一张accounts表,下面的案例都基于这张表


1. 读未提交(Read Uncommitted)

读未提交是最低的事务隔离级别,允许一个事务读取另一个事务尚未提交的数据 。这种隔离级别可能导致脏读、不可重复读和幻读。

案例(脏读)

复制代码
-- 设置该会话的事务隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

use demo;

start transaction;

select * from accounts where account_id=2;

- 根据第8行查出来的数据做些什么,但是这个数据是"脏数据"

commit;

-- 设置该会话的事务隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

use demo;

start transaction;

update accounts set balance=balance-500 where account_id=2; 

-- 回滚事务
rollback;

执行步骤:

session1执行1~6行

session2执行1~8行

session1执行8~9行,得到的结果如下

这时候session2执行11行,回滚事务,此时数据库中的情况如下

session1执行10~12行,这时候1500是"脏数据",session1带着"脏数据"去干别的事了,这就是"脏读"


2. 读已提交(Read Committed)

读已提交确保一个事务只能读取另一个事务已经提交的数据。 这种隔离级别可以避免脏读,但可能导致不可重复读和幻读。

案例(不可重复读)

复制代码
-- 设置该会话的事务隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

use demo;

start transaction;

-- 第一次读
select * from accounts where account_id=2;

-- 第二次读
select * from accounts where account_id=2;

commit;

-- 设置该会话的事务隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

use demo;

start transaction;

update accounts set balance=balance-500 where account_id=2; 

commit;

执行步骤

session1执行1~9行,得到如下结果

session2执行1~10行,提交事务,此时accounts表的情况如下

session1执行10~12行,得到如下结果

在同一个事务中,第一次读和第二次读的结果不一样,也就是所谓的"不可重复读"问题。


3. 可重复读(Repeatable Read)

可重复读**确保一个事务在同一个事务中多次读取同一数据时,结果始终一致。这种隔离级别可以避免脏读和不可重复读,但可能导致幻读。**MySQL的InnoDB存储引擎通过next-key locking机制来避免幻读,但其他存储引擎可能不支持此机制。

案例(幻读)

复制代码
-- 设置该会话的事务隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

use demho;

start transaction;

-- 第一次读
select * from accounts where balance>1500;

-- 第二次读
select * from accounts where balance>1500;

commit;

-- 设置该会话的事务隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

use demo;

start transaction;

insert into accounts(balance) values (3000);

insert into accounts(balance) values (4000);

commit;

执行步骤:

session1执行1~9行,得到如下结果

session2执行1~12行,提交事务,这时accouts表中的数据如下

session1执行10~12行,得到的结果如下

按照我们所期望的,应该得到id为2,3,4的三条记录,也就是所谓的"幻读",为什么不是这样呢?

这是因为:

MySQL的InnoDB存储引擎通过next-key locking机制来避免幻读 ,但其他存储引擎可能不支持此机制。

那么现在我们把accounts表删了,重新创建,这次指定存储引擎为myisam

复制代码
create table `accounts`
(
    `account_id` int(11) not null auto_increment primary key,
    `balance`    int(11) not null
)ENGINE myisam;

insert into accounts
values (1, 1000),
       (2, 2000);

重新按上面的执行顺序执行一遍

第一次读:

第二次读:

出现了"幻读"现象,符合我们的预期。


4. 串行化(Serializable)

串行化是最高的事务隔离级别,它确保事务完全串行化执行,不能并发执行。从而避免脏读、不可重复读和幻读。然而,这种隔离级别会显著降低并发性能,因为每个事务都需要等待其他事务完成后才能执行。

案例(注意这个案例的accounts表还是用的InnoDB存储引擎)

复制代码
-- 设置该会话的事务隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

use demo;

start transaction;

-- 第一次读
select * from accounts where balance>1500;

-- 第二次读
select * from accounts where balance>1500;

commit;

-- 设置该会话的事务隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

use demo;

start transaction;

insert into accounts(balance) values (5000);

commit;

执行步骤:

session1执行1~13行

session2执行1~7行,此时一切正常

session2执行第8行,超时,这是因为session1还没提交事务

session1执行第14行,提交事务,然后重新执行session2的第8行

结果符合我们的预期,成功执行了


总结

|-----------------------|------------------------------------------------|-------------------------------------------------------------------------|--------------------------------------------------------------|
| 事务隔离级别 | 特点 | 优点 | 缺点 |
| 读未提交(read uncommited) | 允许一个事务读取另一个事务尚未提交的数据 | * 最低的隔离级别,具有最高的并发性能。 * 读取未提交的数据可以获得最新的数据状态。 | 可能出现脏读,可重复读,幻读的问题 |
| 读已提交(read commited) | 确保一个事务只能读取另一个事务已经提交的数据 | * 避免了脏读问题,只能读取到已提交的数据,较好地保证了数据的一致性。 | 可能出现可重复读,幻读的问题 并发性能受到一定影响,因为每次读取都需要等待其他事务的提交。 |
| 可重复读(repeatable read) | 确保一个事务在同一个事务中多次读取同一数据时,结果始终一致。 | * 避免了脏读和不可重复读问题,保证了事务在执行期间读取的数据保持一致。 * 这是MySQL默认的隔离级别,适用于大多数需要数据一致性的场景。 | 可能出现幻读的问题 并发性能受到一定影响,因为需要维护数据的一致性。 |
| 串行化(serializable) | 串行化是最高的事务隔离级别,它确保事务的执行效果是串行的,即使这些事务是并发提交的。 | * 最高的隔离级别,确保事务之间完全隔离,避免了脏读、不可重复读和幻读问题。 * 提供了最高级别的数据一致性保证。 | 并发性能最低,会对并发访问造成较大的影响,可能导致事务的等待和延迟。 由于事务需要串行执行,因此系统的吞吐量会受到限制。 |

事务的隔离级别由低到高,数据的一致性升高。

事务的隔离级别由低到高,事务的并发性降低。

在实际应用中,应根据具体需求选择合适的事务隔离级别,以平衡数据一致性和并发性能。例如,在需要高并发性能的场景中,可以选择较低的隔离级别(如读已提交);而在需要强数据一致性的场景中,可以选择较高的隔离级别(如串行化)。


sql连接中断问题

开启mysql服务:

cmd中输入services.msc

Services.msc 是电脑上的服务管理策略文件,支持启动、终止并设置Windows服务的管理策略

随便启动一个,后面的80只是版本号

相关推荐
憧憬成为web高手8 小时前
皮卡丘靶场速通--sql 2
数据库·sql·mybatis
段一凡-华北理工大学9 小时前
向量数据库实战:选型、调优与落地~系列文章12:文本分块策略实战:chunk_size 怎么选?重叠多少?
开发语言·数据库·后端·oracle·rust·工业智能体·高炉智能化
oradh11 小时前
Oracle XTTS实现跨版本迁移和升级(Oracle 11g单库升级至19C RAC集群)
数据库·oracle·11g升级19c·xtts跨版本迁移和升级
z1234567898612 小时前
2026最新两款AI编程工具深度对比实测
java·数据库·ai编程
程序猿DD12 小时前
一个 API Key,统一调用大模型、生图和联网搜索
数据库·网关
TlSfoward13 小时前
抓包代理链路下的 TLS 指纹变化分析 TLSFOWARD抓包工具
数据库·爬虫·网络协议·搜索引擎·php
数智化管理手记13 小时前
账龄分析手工统计易遗漏?自动账龄分析工具怎么搭建
大数据·网络·数据库·人工智能·数据挖掘
LabVIEW开发13 小时前
NI Package Manager安装LabVIEW时InternalServerError错误
网络·数据库·labview·labview知识·labview功能·labview程序
SelectDB13 小时前
PostgreSQL 实时同步到 Apache Doris:一站式 CDC 解决方案
数据库
欲速不达882013 小时前
Mysql并发DML锁等待的优化
数据库·mysql