MySQL高级(上)

本文详细介绍了MySQL的高级特性,包括视图的定义、使用和删除,存储过程的创建、调用与参数类型,以及函数的使用。进一步讨论了触发器的概念和作用,解析了MySQL的架构层次,特别是连接层、服务层、引擎层和物理文件存储层。此外,文章还探讨了存储引擎如InnoDB和MyISAM的特点,强调了索引的重要性,包括索引的原理、优势、劣势、类型以及创建原则。事务的概念、特性、设置和隔离级别也被详细阐述,分析了并发事务处理可能遇到的问题和解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

MySQL高级(上)

视图

什么是视图?
视图是基于查询的虚拟表。通俗的理解,视图就是一条 SELECT 语句执行后 返回的结果集。
SELECT 语句所查询的表称为视图的基表,而查询的结果集称为虚拟表,视 图本身并不存储具体的数据,视图的数据存在于视图的基表中,基本表数据发生了改变,视图的数据也会跟着改变、
为什么使用视图?
使用视图是为了方便复杂的查询语句。基本思路是将复杂的查询语句定义在 视图内部,然后对视图进行查询,从而简化复杂的查询语句。

定义视图

CREATE VIEW 视图名 AS SELECT 列 1,列 2… FROM 表(查询语句);

-- 定义视图
-- CREATE VIEW 视图名 AS SELECT 列 1,列 2... FROM 表(查询语句);
CREATE VIEW sel_news AS 
		SELECT * FROM admin

使用视图

SELECT * FROM 视图名

-- 使用视图  SELECT * FROM 视图名
SELECT * FROM sel_news

删除视图

drop view 视图名

-- 删除视图  drop view 视图名
DROP VIEW sel_news

存储过程

概述
如果实现用户的某些需求时,需要编写一组复杂的 SQL 语句才能实现,那么 可以将这组复杂的 SQL 语句集编写在数据库中,由 JDBC 调用来执行这组 SQL 语句。把编写在数据库中的 SQL 语句集称为存储过程。
存储过程(PROCEDURE)是事先经过编译并存储在数据库中的一段 SQL 语 句的集合。调用存储过程可以简化应用开发人员的很多工作,减少数据在数据库 和应用服务器之间的传输,对于提高数据处理的效率是有好处的。
存储过程类似于 JAVA 语言中的方法,需要先定义,使用时需要调用。存储 过程可以定义参数,参数分为 IN、OUT、INOUT 类型三种类型。IN 类型的参 数表示接收调用者传入的数据,OUT 类型的参数表示向调用者返回数据,INOUT 类型的参数既可以接收调用者传入的参数,也可以向调用者返回数据。

MySQL 存储过程的定义

创建存储过程的语法格式

create procedure 存储过程名([in 变量名 类型,out 参数 2,…]) 
	begin
		[declare 变量名 类型 [DEFAULT 值];] 
			存储过程语句块; 
end;

语法解析:

  1. 存储过程的参数分为 in,out,inout 三种类型。
  2. in 代表输入参数(默认情况下为 in 参数),表示该参数的值必须由调用程序 指定。
  3. out 代表输出参数,表示该参数的值经存储过程计算后,将 out 参数的计算结 果返回给调用程序。
  4. inout 代表即是输入参数,又是输出参数,表示该参数的值即可以由调用程序 指定,又可以将 inout 参数的计算结果返回给调用程序。
  5. 存储过程中的语句必须包含在 begin 和 end 之间。
  6. declare 中用来声明变量,变量默认赋值使用 default,语句块中改变变量值,使 用 set 变量=值;

存储过程使用

定义第一个存储过程

-- 存储过程 基本语法
DELIMITER $$

CREATE
    PROCEDURE `journalism_db`.`demo1`()
	BEGIN
	-- DECLARE 用来声明变量
	DECLARE v_name VARCHAR(15) DEFAULT '';
	SET v_name = 'jim';
	SELECT v_name;-- 测试输出语句
	END$$

DELIMITER ;

-- 调用存储过程
CALL demo1();

定义一个有参数的存储过程

-- 查询新闻类型为1的有几条  演示入参和出参

DELIMITER $$

CREATE
    PROCEDURE type_count(IN p_jourtypeId INT,OUT p_count INT)
	-- 存储过程体
	BEGIN
	 SELECT COUNT(*) INTO p_count FROM journalism WHERE jourtypeId=p_jourtypeId;
	 SELECT p_count;
	END$$
DELIMITER ;

-- 在一个存储过程中调用另一个存储过程
CALL type_count(1,@p_count);

流程控制语句 if else

--  演示存储过程中逻辑判断
DELIMITER $$
CREATE   PROCEDURE  demo2(IN p_day INT,OUT p_name VARCHAR(10))
 
  BEGIN
         IF p_day = 1  THEN 
           SET p_name  = "星期一";
            SELECT p_name;     
         ELSEIF p_day = 2 THEN 
           SET p_name  = "星期二";
            SELECT p_name;
         ELSE   
           SET p_name = "无效日期";
           SELECT  p_name;  
       
         END IF; 
  END$$
DELIMITER ;
-- 测试
CALL demo2(3,@p_name);

使用存储过程插入信息

-- 存储过程保存管理员,在存储过程中判断账号是否已经存储,不存在存储,否则返回账号已存在
DELIMITER $$
CREATE   PROCEDURE  save_admin(IN p_account VARCHAR(10),IN p_password VARCHAR(10),OUT p_result VARCHAR(10))
  BEGIN
       -- 声明一个变量,接收查询到的结果
       DECLARE v_count INT DEFAULT 0;
       
       SELECT COUNT(*) INTO v_count FROM admin WHERE account = p_account;
          IF v_count = 0 THEN
             INSERT INTO admin(account,PASSWORD)VALUES(p_account,p_password);
              SET p_result = "保存成功";
              SELECT p_result; 
          ELSE 
             SET p_result = "账号已存在";
             SELECT p_result;   
          END IF; 
  END$$
DELIMITER ;


CALL save_admin('t01','111',@p_result);

mybatis 调用存储过程

<parameterMap type="map" id=“usermap"> 
	<parameter property="addend1" jdbcType="VARCHAR" mode="IN"/> 
	<parameter property="result" jdbcType="VARCHAR" mode="OUT"/>
</parameterMap>

jdbcType 必须指定

<insert id="saveUserDemo" parameterMap="usermap" 
	statementType="CALLABLE"> 
		{call saveuser(?, ?)} 
</ insert > 
	Map<String, Object> parms = new HashMap<String, Object>(); 
						parms.put("addend1", 3); 
						userDao.saveUserDemo(parms); 
						parms.get(“result”);//获得输出参数

函数

create function 函数名([参数列表]) returns 数据类型 
begin 
	DECLARE 变量; 
		sql 语句; 
	return 值;
end;

注意:

  1. 参数列表包含两部分:参数名 参数类型
  2. 函数体:肯定会有 return 语句,如果没有会报错
  3. 函数体中仅有一句话,则可以省略 begin end
  4. 使用 delimter 语句设置结束标记
    全局设置函数可以没有参数
    SET GLOBAL log_bin_trust_function_creators=TRUE;
    删除函数
    DROP FUNCTION 函数名;

不带参数

-- 全局设置,设置函数可以没有参数
SET GLOBAL log_bin_trust_function_creators=TRUE;

-- 函数
DELIMITER $$
CREATE  FUNCTION  demo()  RETURNS INT
   
    BEGIN
       DECLARE v_count INT DEFAULT 0;
       SELECT COUNT(*) INTO v_count FROM admin;
       RETURN v_count;      
    END$$

DELIMITER ;

SELECT demo();

带参数函数

-- 带参数函数

DELIMITER $$
CREATE  FUNCTION  findType(p_type INT)  RETURNS VARCHAR(10)
   
    BEGIN
       DECLARE v_type VARCHAR(10) DEFAULT '';
       IF p_type = 0 THEN
          SET v_type = '超级管理员';
       ELSE
         SET v_type = '管理员';   
       END IF;  
       RETURN v_type;      
    END$$

DELIMITER ;

SELECT account,id,findType(TYPE) FROM admin

DELIMITER $$
CREATE  FUNCTION  find_news_type(p_typeid INT)  RETURNS VARCHAR(10)
   
    BEGIN
       DECLARE v_type VARCHAR(10) DEFAULT '';
        SELECT journame INTO v_type FROM jourtype WHERE  id = p_typeid;
       RETURN v_type;      
    END$$

DELIMITER ;

SELECT id,title,find_news_type(jourtypeId) FROM journalism;

触发器

触发器(trigger)是一种特殊的存储过程,其特殊性在于它并不需要用户直接 调用,而是在对表添加、修改、删除之前或者之后自动执行的存储过程。
触发器具有以下特点:
1、与表相关联
触发器定义在特定的表上,这个表称为触发器表
2、自动激活触发器
当对表中的数据执行 INSERT、UPDATE 或 DELETE 操作时,如果对表上的这 个特定操作定义了触发器,该触发器自动执行,这是不可撤销的。
3、不能直接调用
与存储过程不同,触发器不能被直接调用,也不能传递或接受参数。
4、作为事务的一部分
触发器与激活触发器的语句一起做为对一个单一的事务来对待,可以从触发器中 的任何位置回滚。
定义触发器的语法规则

CREATE TRIGGER 触发器名称 触发时机 触发事件 
ON 表名称 
FOR EACH ROW -- 行级触发 
BEGIN
		语句 
END;

语法解析:

  1. 触发器名称:是用来标识触发器的,由用户自定义。
  2. 触发时机:其值是 before 或 after。
  3. 触发事件:其值是 insert,update 和 delete
  4. 表名称:标识建立触发器的表名,即在哪张表上建立触发器
  5. 语句:是触发器程序体,触发器程序可以使用 begin 和 end 作为开始和结束, 中间包含多条语句;
    案例删除用户时,自动触发删除用户菜单关系
-- 触发器
DELIMITER $$

CREATE
    TRIGGER delete_admin_role 
    BEFORE 
    DELETE
    ON admin
    FOR EACH ROW -- 行级触发器  操作多行时,每行都会触发触发器
    BEGIN
           DELETE FROM admin_role WHERE adminid = old.id;
    END$$

DELIMITER ;
 

DELETE FROM admin WHERE id > 5



DELIMITER $$

CREATE
    TRIGGER insert_admin_log 
    AFTER 
    INSERT
    ON admin
    FOR EACH ROW -- 行级触发器  操作多行时,每行都会触发触发器
    BEGIN
           INSERT INTO admin_log(id,account,oper_time)VALUES(new.id,new.account,NOW());
    END$$

DELIMITER ;


INSERT INTO admin(account,PASSWORD)VALUES('kkk','123');

在行级触发器代码中,可以使用 old 和 new 访问到该行的旧数据和新数据,old 和 new 是对应表的行记录类型变量。

MySQL 架构

进入到 mysql 高级内容学习之前,先整体认识一下 MySQL 的完整架构图.
在这里插入图片描述

连接层

最上层是一些客户端和连接服务,包含本地 sock 通信和大多数基于客户端 /服务端工具实现的类似于 tcp/ip 的通信。主要完成一些类似于连接处理、授权 认证、及相关的安全方案。

服务层

第二层架构主要完成大多数的核心服务功能,如 SQL 接口,并完成缓存的 查询,SQL 的分析和优化及部分内置函数的执行。所有跨存储引擎的功能也在 这一层实现,如过程、函数等。在该层,服务器会解析查询并创建相应的内部解 析树,并对其完成相应的优化,如确定查询表的顺序,是否利用素引等,最后生 成相应的执行操作。如果是 select 语句,服务器还会查询内部的缓存。如果缓 存空间足够大,这样在解决大量读操作的环境中能够很好的提升系统的性能。

引擎层

存储引擎层,存储引擎真正的负责了 MysQL 中数据的存储和提取,服务器 通过 API 与存储引擎进行通信。不同的存储引擎具有的功能不同,这样我们可以 根据自己的实际需要进行选取。

物理文件存储层

数据存储层,主要是将数据存储在运行于裸设备的文件系统之上,并完成与 存储引擎的交互。

MySQL 引擎

概述
MySQL 中的数据用各种不同的技术存储在文件中。这些技术中的每一种 技术都使用不同的存储机制、索引技巧、锁定水平、并且最终提供广泛的不同的 功能和能力。通过选择不同的技术,你能够获得额外的速度或者功能,从而改善 你的应用的整体功能。
这些不同的技术以及配套的相关功能在 MySQL 中被称作存储引擎(也称作 表类型)。MySQL 默认配置了许多不同的存储引擎,可以预先设置或者在 MySQL 服务器中启用。你可以选择适用于服务器、数据库和表格的存储引擎,以便在选 择如何存储你的信息、如何检索这些信息以及你需要你的数据结合什么性能和功 能的时候为你提供最大的灵活性。
数据库引擎是用于存储、处理和保护数据的核心服务。利用数据库引擎可控 制访问权限并快速处理事务,从而满足企业内大多数需要处理大量数据的应用程 序的要求。
查看支持的引擎

SHOW ENGINES;

在这里插入图片描述
查看表引擎
SHOW TABLE STATUS LIKE ‘表名’
修改引擎
方式 1:将 mysql.ini 中 default-storage-engine=InnoDB,重启服务.
方式 2:建表时指定 CREATE TABLE 表名(…)ENGINE=MYISAM;
方式 3:建表后修改 ALTER TABLE 表名 ENGINE = INNODB;
存储引擎主要有: 1. MyIsam , 2. InnoDB, 3. Memory, 4. Blackhole, 5. CSV,6. Performance_Schema, 7. Archive, 8. Federated , 9 Mrg_Myisam

我们主要分析使用 MyIsam 和 InnoDB。
在这里插入图片描述
InnoDB
InnoDB:默认的存储引擎
InnoDB 是一个事务型的存储引擎,有行级锁和外键约束,支持全文检索(全文索 引),它的设计目标是处理大容量数据库系统,MySQL 运行时 Innodb 会在内存 中建立缓冲池,用于缓冲数据和索引;支持主键自增.不存储表的总行数.
MyISAM
MyISAM 也是 MySQL 的引擎,但是它没有提供对数据库事务的支持,也 不支持行级锁和外键,因此当 INSERT(插入)或 UPDATE(更新)数据时即写操作 需要锁定整个表,效率便会低一些;支持全文检索;存储表的总行数.
在这里插入图片描述

索引

为什么要有索引呢?

假设有一张表,表中有 100 万条数据,这 100 万条数据在硬盘上是存储在 数据页上的,一页数据大小为 16K,存储 100 万条数据需要很多数据页,假设 其中有一条数据是 id=‘7900’,如果要查询这条数据,其 SQL 是 SELECT * FROM 表名称 WHERE id = 7900。mysql 需要扫描全表来查找 id=7900 的记 录。全表扫描就是从“数据页 1”开始,向后逐页查询。对于少量的数据,查询 的速度会很快,但是,当随着数据量的增加,性能会急剧下降。100 万条数据逐 页查询的时间是无法被用户接受的
什么是索引?
索引是帮助 MySQL 高效获取数据的数据结构
排好序的快速查找的数据结构.
数据库在存储数据本身之外,还维护着一个满足特定查找算法的数据结构, 这些数据结构以某种方式指向数据,这样就可以在这些数据结构的基础上实现高 级查找算法,这种数据结构就是索引。

在这里插入图片描述
左边是数据表,一共有两列七条记录,最左边的是数据记录的物理地址. 为了加快 Col2 的查找,可以维护一个右边所示的二叉查找树,每个节点分别包含索引键值和一个指向对应数据记录物理地址的指针,这样就可以运用二叉 查找在一定的复杂度内获取到相应数据,从而快速的检索出符合条件的记录。

索引原理

索引的目的在于提高查询效率,与我们查阅图书所用的目录是一个道理:先 定位到章,然后定位到该章下的一个小节,然后找到页数。相似的例子还有:查 字典,查火车车次,飞机航班等.
本质都是:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果, 同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总 是用同一种查找方式来锁定数据。
索引类似于书的目录,在一本书前面加上目录,查找内容时不必逐页翻阅就 能够快速地找到所需的内容。借助索引,执行查询时不必扫描整个表就能够快速 地找到所需要的数据。

索引优势

  • 提高数据检索的效率,降低数据库的 IO 成本;
  • 通过索引列对数据进行排序,降低数据排序的成本,降低了 CPU 的消耗;

索引劣势

实际上,索引也是一张表,该表保存了主键与索引字段,并指向实体表的记录, 所以索引列也是要占用磁盘空间的。
虽然索引大大提高了查询速度,同时却会降低更新表的速度,例如对表进行 INSERT,UPDATE 和 DELETE,因为更新表时,MySQL 不仅要保存数据,还要保存一下索引文件,每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。

索引分类

主键索引: 设定为主键后数据库会自动建立索引
ALTER TABLE 表名 add PRIMARY KEY 表名(列名);
删除建主键索引:
ALTER TABLE 表名 drop PRIMARY KEY ;
单值索引: 即一个索引只包含单个列,一个表可以有多个单列索引
创建单值索引
CREATE INDEX 索引名 ON 表名(列名);
删除索引:
DROP INDEX 索引名;
唯一索引: 索引列的值必须唯一,允许为 null
CREATE UNIQUE INDEX 索引名 ON 表名(列名);
删除索引
DROP INDEX 索引名 ON 表名;
组合索引(复合索引):
即一个索引包含多个列,在数据库操作期间,复合索引比单值索引所需要的 开销更小(对于相同的多个列建索引),当表的行数远大于索引列的数目时可 以使用复合索引.
创建复合索引
CREATE INDEX 索引名 ON 表名(列 1,列 2…);
删除索引:
DROP INDEX 索引名 ON 表名;
组合索引最左前缀原则
列如表中有 a,b,c 3 列,为 a,b 两列创建组合索引,那么在使用时需要满足最左 侧索引原则.在使用组合索引的列作为条件时,必须要出现最左侧列为条件,否则 组合索引不生效.
例如 :

select * from table where a=’’and b=’’索引生效 
select * from table where b=’’and a=’’索引生效 
select * from table where a=’’and c=’’索引生效 
select * from table where b=’’and c=’’索引不生效

全文索引
需要模糊查询时,一般索引无效,这时候就可以使用全文索引了。

CREATE FULLTEXT INDEX 索引名 ON 表名(字段名) WITH PARSER ngram; 
SELECT 结构 FROM 表名 WHERE MATCH(列名) AGAINST(搜索词')

查看索引:
SHOW INDEX FROM 表名;

索引创建原则

哪些情况需要创建索引

  • 主键自动建立唯一索引
  • 频繁作为查询条件的字段应该创建索引(where 后面的语句)
  • 查询中与其它表关联的字段,外键关系建立索引
  • 查询中排序的字段,排序字段若通过索引去访问将大大提高排序速度分组中的字段
    哪些情况不要创建索引
  • 表记录太少
  • 经常增删改的表:提高了查询速度,同时却会降低更新表的速度,如对表进 行 INSERT、UPDATE 和 DELETE,因为更新表时,MySQL 不仅要保存数据, 还要保存一下索引文件
  • Where 条件里用不到的字段不创建索引
  • 数据重复且分布平均的表字段,因此应该只为最经常查询和最经常排序的数 据列建立索引,某个数据列包含许多重复的内容,建立索引没有太大实际效果。

索引数据结构

B+树之前必须先了解二叉查找树、平衡二叉树(AVLTree)和平衡多路查找树 (B-Tree),B+树即由这些树逐步优化而来。使其更适合实现外存储索引结构, InnoDB 存储引擎就是用 B+Tree 实现其索引结构。

  • 排好序的,一个节点可以存储多个数据.
  • 非叶子节点不存储数据,只存储索引,可以放更多的索引.
  • 数据记录都存放在叶子节点中.
  • 所有叶子节点之间都有一个链指针.
    在这里插入图片描述
    Mysql 索引使用的是 B+树,因为索引是用来加快查询的,而 B+树通过对 数据进行排序所以是可以提高查询速度的,然后通过一个节点中可以存储多个元 素,从而可以使得 B+树的高度不会太高.并且叶子节点之间有指针,可以很好的 支持全表扫描,范围查找等 SQL 语句。

聚簇索引和非聚簇索引

聚簇索引
找到了索引就找到了需要的数据,那么这个索引就是聚簇索引,所以主键就 是聚簇索引。
非聚簇索引
索引的存储和数据的存储是分离的,也就是说找到了索引但没找到数据,需 要根据索引上的值(主键)再次回表查询,非聚簇索引也叫做辅助索引。
一个例子
下面我们创建了一个学生表,做三种查询,来说明什么情况下是聚簇索引,什 么情况下不是

CREATE TABLE student (
id BIGINT, 
NO VARCHAR (20), 
NAME VARCHAR (20), 
PRIMARY KEY (`id`), 
UNIQUE KEY `idx_no` (`no`) 
)

第一种,直接根据主键查询获取所有字段数据,此时主键是聚簇索引,因为主 键对应的索引叶子节点存储了 id=1 的所有字段的值。
SELECT * FROM student WHERE id = 1
第二种,根据编号查询编号和名称,编号本身是一个唯一索引,但查询的列包 含了学生编号和学生名称,当命中编号索引时,该索引的节点的数据存储的是主 键 ID,需要根据主键 ID 重新查询一次,所以这种查询下 no 不是聚簇索引.
SELECT NO,NAME FROM student WHERE NO = 123
第三种,我们根据编号查询编号(有人会问知道编号了还要查询?要,你可能 需要验证该编号在数据库中是否存在),这种查询命中编号索引时,直接返回编 号,因为所需要的数据就是该索引,不需要回表查询,这种场景下 no 是聚簇索 引。
SELECT NO FROM student WHERE NO = 123
在这里插入图片描述
MySQL 中 InnoDB 引擎的索引和文件是存放在一起的,找到索引就可以找到数据,是聚簇 式设计.
在这里插入图片描述
而 MyISAM 引擎采用的是非聚簇式设计,索引文件和数据文件不在同一个文件中.
在这里插入图片描述

事务

概述
数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作 序列,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。
在 MySQL 中只有使用了 Innodb 数据库引擎的数据库或表才支持事务。 事务处理可以用来维护数据库的完整性,保证成批的 SQL 语句要么全部执行, 要么全部不执行。
事务用来管理 insert,update,delete 语句。

事务特性

一般来说,事务是必须满足 4 个条件(ACID):原子性(Atomicity,或称不可 分割性)、一致性(Consistency)、隔离性(Isolation,又称独立性)、持久 性(Durability)

  • 原子性: 一个事务(transaction)中的所有操作,要么全部完成,要么全部不 完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚 (Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
  • 持久性: 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
  • 隔离性: 数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性 可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为 不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、 可重复读(repeatable read)和串行化(Serializable)。
  • 一致性: 在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示 写入的数据必须完全符合所有的预设规则。比如不能出现转了 100,对方收到了 50 的情况。前面提到的原子性、持久性和隔离性,都是为了保证数据库状态的 一致性。

事务设置

默认情况下, MySQL 启用自动提交模式(变量 autocommit 为 ON)。这 意味着, 只要你执行 DML 操作的语句,MySQL 会立即隐式提交事务。
由于变量 autocommit 分会话系统变量与全局系统变量,所以查询的时候, 最好区别是会话系统变量还是全局系统变量。
MYSQL 事务处理主要有两种方法:

  1. 用 BEGIN, ROLLBACK, COMMIT 来实现
    BEGIN; / START TRANSACTION; 开始一个事务
    ROLLBACK 事务回滚
    COMMIT 事务确认
  2. 直接用 SET 来改变 MySQL 的自动提交模式:
    SET SESSION / GLOBAL autocommit=0; 禁止自动提交
    SET SESSION / GLOBAL autocommit=1;开启自动提交
    查看 autocommit 模式
    SHOW SESSION / GLOBAL VARIABLES LIKE ‘autocommit’;

并发事务处理带来的问题:

当两个或者多个事务选择同一行,然后基于最初选定的值进行更新操作时, 由于每个事务都不知道其他事务的存在,则会发生丢失更新问题,即最后的更新 并覆盖了前一个程序员所做的更改。
假设有两个事务 A 和 B,同时并发。
(1)脏读

  1. 事务 B 更新年龄 18
  2. 事务 A 读取数据库信息,年龄是 18
  3. 事务 B 回滚
    在这里插入图片描述
    这时候如果事务 A 读取的了 18,这种情况下合理吗?
    这种情况就叫做脏读:读取到其他事务未提交的数据。
    (2)不可重复读::在事务 A 中先后两次读取同一个数据,两次读取的结果不一 样,这种现象称为不可重复读。脏读与不可重复读的区别在于:前者读到的是其 他事务未提交的数据,后者读到的是其他事务已提交的数据。
    在这里插入图片描述
    事务 A 连续两次读取的数据竟然不一样,这种情况你能接受吗?
    这种情况就叫做**不可重复读:**在同一个事务中两次读取的数据不一致。
    (3)幻读:在事务 A 中按照某个条件先后两次查询数据库,两次查询结果的条 数不同,这种现象称为幻读。不可重复读与幻读的区别可以通俗的理解为:前者 是数据变了,后者是数据的行数变了。
  4. 事务 A 读取年龄大于 15 的数据,发现有 1 条记录
  5. 事务 B 插入一条记录,并提交
  6. 事务 A 再读取年龄大于 15 的数据,发现有 2 条记录
    在这里插入图片描述
    事务 A 就好像出现了幻觉一样,这种情况合理吗?
    这就叫做幻读:在同一个事务中两次读取到的数据量不一致。一般幻读出现在范 围查询。

事务隔离级别

只有 InnoDB 支持事务,所以这里说的事务隔离级别是指 InnoDB 下的事务隔离 级别。

查看隔离级别

SELECT @@global.transaction_isolation,@@transaction_isolation;

设置隔离级别

SET SESSION/GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
读未提交(read uncommitted):一个事务可以读取到另一个事务未提交的修 改。这会带来脏读,幻读,不可重复读问题
读已提交(read committed): 一个事务只能读取另一个事务已经提交的修改。 其避免了脏读,仍然存在不可以重复读和幻读问题
可重复读(repeatable read MySQL 默认隔离级别): 同一个事务中多次读取相 同的数据返回的结果是一样的。其避免了脏读和不可重复读问题,但是幻读依然存在。
串行化(serializable): 事务串行执行,避免了以上所有问题。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值