Mysql学习记录补充

news2024/11/29 8:36:57

索引

在无索引情况下,就需要从第一行开始扫描,一直扫描到最后一行,我们称之为 全表扫描,性能很低。
在这里插入图片描述
如果我们针对于这张表建立了索引,假设索引结构就是二叉树,那么也就意味着,会对age这个字段建立一个二叉树的索引结构。
在这里插入图片描述
优势:
1.提高数据查询的效率,降低数据库的IO成本。(数据库的数据是存在磁盘的,你要查询就要操作磁盘就会有IO)
2.通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗。

劣势:
1.索引列也是要占用空间的。
2.降低更新表的速度,对表进行DML时,效率降低。

以下为MYSQL支持的所有索引数据结构和相应的支持引擎。
在这里插入图片描述
在这里插入图片描述

索引结构

B-tree

B树的定义

B树,又称多路平衡查找树,B树中所有结点的孩子个数的最大值称为B树的阶,通常用m表示。一棵m阶B树或为空树,或为满足如下特性的m叉树:

  • 1.树中每个结点至多有m棵子树,即至多含有m−1个关键字。

  • 2.若根结点不是终端结点,则至少有两棵子树。

  • 3.除根结点外的所有非叶结点至少有⌈m/2⌉棵子树,即至少含有⌈m / 2 ⌉−1个关键字。

  • 4.所有非叶结点的结构如下:
    在这里插入图片描述
    其中,n为元素个数,K代表节点的关键字,P代表指针。满足K1<K2<…<Kn;Pi指针指向子树的根节点,Pi-1所指的子树中所有节点关键字小于Ki,Pi所指子树中所有节点关键字大于Ki。
    节点中关键字个数有限制为:n(⌈m/2⌉-1<= n <= m-1)

  • 5.所有的叶结点都出现在同一层次上,并且不带信息(可以视为外部结点或类似于折半查找判定树的查找失败结点,实际上这些结点不存在,指向这些结点的指针为空)。

以一颗最大度数(max-degree)为5(5阶)的b-tree为例,那这个B树每个节点最多存储4个key,5
个指针:

在这里插入图片描述

B树的插入
  • 1.定位。找出插入该关键字的最低层中的某个非叶结点(在B树中查找key时,会找到表示查找失败的叶结点,这样就确定了最底层非叶结点的插入位置。注意:插入位置一定是最低层中的某个非叶结点)。

  • 2.插入。每个非失败结点的关键字个数都在区间[ [⌈m/2⌉−1,m−1]内。插入后的结点关键字个数小于m,可以直接插入;插入后检查被插入结点内关键字的个数,当插入后的结点关键字个数大于m−1时,必须对结点进行分裂。

  • 3.分裂:取一个新结点,在插入key后的原结点,从中间位置⌈m/2⌉将其中的关键字分为三部分,左部分包含的关键字放在原结点中,右部分包含的关键字放到新结点中,中间位置⌈m/2⌉的结点插入原结点的父结点。若此时导致其父结点的关键字个数也超过了上限,则继续进行这种分裂操作,直至这个过程传到根结点为止,进而导致B树高度+1

B树的查找

在B树上查找到某个结点后,先在有序表中进行查找,若找到则查找成功,否则按照对应的指针信息到所指的子树中去查找。

B树的查找包含两个基本操作:①在B树中找结点;②在结点内找关键字。

由于B树常存储在磁盘上,因此前一个查找操作是在磁盘上进行的,而后一个查找操作是在内存中进行的,即在找到目标结点后,先将结点信息读入内存,然后在结点内采用顺序查找法或折半查找法。
在这里插入图片描述
例如,在上图中查找关键字42,首先从根结点开始,根结点只有一个关键字,且42>22,若存在,必在关键字22的右边子树上,右孩子结点有两个关键字,而36<42<45,则若存在,必在36和45中间的子树上,在该子结点中查到关键字42,查找成功。若查找到叶结点时(对应指针为空指针),则说明树中没有对应的关键字,查找失败。

B+ 树

B+树是应文件系统(比如数据库)所需而出现的一种B树的变形树。
m阶的B+树与m阶的B树的主要差异如下:

  • 1.有n棵子树的结点中包含有n个关键字;
  • 2.所有的数据都在叶子节点。叶子结点本身依关键字的大小自小而大顺序链接;
  • 3.所有分支(非叶子)结点可以看成是索引,不含具体数据,结点中仅含有其子树中的最大(或最小)关键字。
  • 4.在B+树中,每个结点(非根内部结点)的关键字个数n的范围是⌈m/2⌉≤n≤m(根结点:1≤n≤m);在B树中,每个结点(非根内部结点)的关键字个数n范围是⌈m/2⌉−1≤n≤m−1 (根结点: 1≤n≤m−1)。
    在这里插入图片描述
    在mysql中对B+tree进行了一定优化。:在原B+Tree的基础上,增加一个指向相邻叶子节点的链表指针,形成了带有顺序指针的B+Tree,提高区间访问的性能,利于排序。

在这里插入图片描述

Hash

哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。

如果两个(或多个)键值,映射到一个相同的槽位上,他们就产生了hash冲突(也称为hash碰撞),可以通过链表来解决。

在这里插入图片描述

特点:

  • A. Hash索引只能用于对等比较(=,in),不支持范围查询(between,>,< ,…)
  • B. 无法利用索引完成排序操作
  • C. 查询效率高,通常(不存在hash冲突的情况)只需要一次检索就可以了,数据较少时效率通常要高于B+tree索引。

索引分类

在MySQL数据库,将索引的具体类型主要分为以下几类:主键索引、唯一索引、常规索引、全文索引。
在这里插入图片描述
而在Innodb引擎中,又可以分为:聚集索引和二级索引。

聚集索引(Clustered Index):将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据。必须有,而且只有一个。

  • 如果存在主键,主键索引就是聚集索引。
  • 如果不存在主键,将使用第一个唯一(UNIQUE)索引作为聚集索引。
  • 如果表没有主键,或没有合适的唯一索引,则InnoDB会自动生成一个rowid作为隐藏的聚集索引。

二级索引:将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键。可以存在多个。

在这里插入图片描述

  • 聚集索引的叶子节点下挂的是这一行的数据 。
  • 二级索引的叶子节点下挂的是该字段值对应的主键值。

查找举例:
在这里插入图片描述
1.根据name字段进行查询,根据name='Arm’到name字段的二级索引中进行匹配查找。但是在二级索引中只能查找到 Arm 对应的主键值 10。
2.由于查询返回的数据是*,所以此时,还需要根据主键值10,到聚集索引中查找10对应的记录,最终找到10对应的行row。
3.最终拿到这一行的数据,直接返回即可。

索引语法

创建索引

CREATE [ UNIQUE | FULLTEXT ] INDEX index_name ON table_name ( index_col_name,… ) ;

name字段为姓名字段,该字段的值可能会重复,为该字段创建索引。

CREATE INDEX idx_user_name ON tb_user(name);

为profession、age、status创建联合索引。

CREATE INDEX idx_user_pro_age_sta ON tb_user(profession,age,status);

phone手机号字段的值,是非空,且唯一的,为该字段创建唯一索引。

CREATE UNIQUE INDEX idx_user_phone ON tb_user(phone);

查看索引

SHOW INDEX FROM table_name ;

删除索引

DROP INDEX index_name ON table_name ;

索引使用

索引失效

最左前缀法则

如果索引了多列(联合索引),要遵守最左前缀法则。最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。如果跳跃某一列,索引将会部分失效(后面的字段索引失效)。

在这里插入图片描述
在 tb_user 表中,有一个联合索引,这个联合索引涉及到三个字段,顺序分别为:profession,age,status。

对于最左前缀法则指的是,查询时,最左的列,也就是profession必须生效,否则索引全部失效。

PS:必须存在,但是顺序无所谓。索引无效不代表无法查询,只是用于辅助查询的索引失效了。

范围查询

联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效。
例如

select * from tb_user where profession = ‘软件工程’ and age > 30 and status= ‘0’;

这里的status字段的索引无效。

运算引起失效

不要在索引列上进行运算操作, 索引将失效。

select * from tb_user where substring(phone,10,2) = ‘15’;

这样会使得phone失效。

字符串不加引号失效

字符串类型字段使用时,不加引号,索引将失效。

这样写没问题

explain select * from tb_user where profession = ‘软件工程’ and age = 31 and status = ‘0’;

但是由于数据库中status是字符串类型,这样不加引号会导致数据库做一次隐式转换,从而又导致了运算,引起失效。

explain select * from tb_user where profession = ‘软件工程’ and age = 31 and status = 0;

模糊查询

在左侧添加模糊查询会使得索引失效。

生效

explain select * from tb_user where profession like ‘软件%’;

失效

explain select * from tb_user where profession like ‘%工程’; explain select * from tb_user where profession like ‘%工%’;

or连接

当or连接的条件,左右两侧字段都有索引时,索引才会生效。

如果age没有索引,但是id和phone有索引那么。

explain select * from tb_user where id = 10 or age = 23;
explain select * from tb_user where age = 23 or phone = ‘17799990017’;

数据分布影响

MySQL在查询时,会评估使用索引的效率与走全表扫描的效率,如果走全表扫描更快,则放弃索引,走全表扫描。

因为索引是用来索引少量数据的,如果通过索引查询返回大批量的数据,则还不如走全表扫描来的快,此时索引就会失效。

SQL提示

use index : 建议MySQL使用哪一个索引完成此次查询

例如

explain select * from tb_user use index(idx_user_pro) where profession = ‘软件工程’;

ignore index : 忽略指定的索引。

例如

explain select * from tb_user ignore index(idx_user_pro) where
profession = ‘软件工程’;

force index : 强制使用索引。

例如

explain select * from tb_user force index(idx_user_pro) where
profession = ‘软件工程’;

索引使用性能优化

覆盖索引

尽量使用覆盖索引,减少select *。

我们在创建索引的时候就已经把表中的id和很多字段给放到索引中了,但是如果你查找的数据不再索引中,就还是会造成回表查询,增加io次数。我们就是要尽量避免这种情况。

先到二级索引中查找数据,找到主键值,然后再到聚集索引中根据主键值,获取数据的方式,就称之为回表查询。

explain中会有extra字段。
在这里插入图片描述

前缀索引

有些字段的长度很长,查询的时候进行比对就会造成大量的IO,影响查询的效率,我们可以对这种字符串建立索引,从而提高索引效率。

例如:

create index idx_email_5 on tb_user(email(5));

在这里插入图片描述

联合索引

针对于查询字段建立索引时,建议建立联合索引,而非单列索引。

单列索引:即一个索引只包含单个列。
联合索引:即一个索引包含了多个列。

假如我们频繁查询的数据是phone和name两个字段,我们可以选择建立两个单列索引和一个联合索引。

但是如果我们的select语句中同时用phone和name作为判断变量,那么如果选择单列索引,还是会造成回表查询。

联合索引的索引示意。
在这里插入图片描述

索引的简历原则

    1. 针对于数据量较大,且查询比较频繁的表建立索引。
    1. 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索
      引。
    1. 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。
    1. 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。
    1. 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。
    1. 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。
    1. 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询。

SQL优化

insert优化

如果我们需要一次性往数据库表中插入多条记录,可以从以下三个方面进行优化。
批量插入数据

Insert into tb_test values(1,‘Tom’),(2,‘Cat’),(3,‘Jerry’);

手动控制事务

start transaction; insert into tb_test
values(1,‘Tom’),(2,‘Cat’),(3,‘Jerry’); insert into tb_test
values(4,‘Tom’),(5,‘Cat’),(6,‘Jerry’); insert into tb_test
values(7,‘Tom’),(8,‘Cat’),(9,‘Jerry’);
commit;

插入大批量数据,用MySQL数据库提供的load指令进行插入

-- 客户端连接服务端时,加上参数 -–local-infile
mysql –-local-infile -u root -p

-- 设置全局参数local_infile为1,开启从本地加载文件导入数据的开关
set global local_infile = 1;


-- 执行load指令将准备好的数据,加载到表结构中
load data local infile '/root/sql1.log' into table tb_user fields terminated by ',' lines terminated by '\n' ;

主键优化

在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的。

页分裂

B+树将聚集索引存储在叶子节点,一个叶子节点可以看做一个页面,如果插入的数据不断增加,会进行“页分裂”。

顺序插入主键
我们顺序插入主键时,当一个页的空间不够了会进行页分裂然后插入到下一个页中。
在这里插入图片描述
乱序插入主键

但是当我们乱序插入时,就会造成频繁的页分裂,还会造成频繁的存储修改和指针修改。
在这里插入图片描述
所以对于主键的设计有如下原则。
1.尽量降低主键长度。
2.尽量选择自增主键,循序插入。
3.尽量不要用无规则主键。
4.尽量避免对主键的修改。

页合并

如果数据删除,则会在页面空间小于一定阈值时进行“页合并”。
在这里插入图片描述

order by优化

B+树自身有一定顺序,所以在排序时尽量使用覆盖索引。

比如:

create index idx_user_age_phone_aa on tb_user(age,phone);

创建索引后,根据age, phone进行升序排序

select id,age,phone from tb_user order by age;

需要注意的是,order by的多个字段的顺序,必须和索引相应字段的顺序都一致或都相反,会无法完成覆盖索引。

例如

如下sql语句的两个字段age和phone和索引中的是相反顺序,但是由于都是相反的,就仍然可以完成覆盖索引。

select id,age,phone from tb_user order by age desc , phone desc ;

但是一个升序,一个降序,就无法完成覆盖索引。

explain select id,age,phone from tb_user order by age asc , phone desc ;

group by优化

分组操作优化也是尽量使用覆盖索引,避免回表查询。

注意:gropu by后跟的关键字也是要符合最左原则的。

比如,如下sql可以完成覆盖索引。

select profession ,count(*) from tb_user group by profession , age;

但是这样就不行了。

select profession ,count(*) from tb_user group by age;

limit优化

通过创建 覆盖索引 能够比较好地提高性能,可以通过覆盖索引加子查询形式进行优化。

select * from tb_sku t , (select id from tb_sku order by id limit 2000000,10) a where t.id = a.id;

count优化

  • MyISAM 引擎把一个表的总行数存在了磁盘上,因此执行 count(*) 的时候会直接返回这个数,效率很高; 但是如果是带条件的count,MyISAM也慢。
  • InnoDB 引擎就麻烦了,它执行 count(*) 的时候,需要把数据一行一行地从引擎里面读出来,然后累积计数。

效率方面:
count(字段) < count(主键 id) < count(1) ≈ count(*)。

所以尽量使用 count(*)。

update优化

当我们在执行删除的SQL语句时,会锁定id为1这一行的数据,然后事务提交之后,行锁释放。

update course set name = ‘javaEE’ where id = 1 ;

但是当我们在执行如下SQL时。

update course set name = ‘SpringBoot’ where name = ‘PHP’ ;

当我们开启多个事务,在执行上述的SQL时,我们发现行锁升级为了表锁。 导致该update语句的性能大大降低。

存储过程

存储过程是事先经过编译并存储在数据库中的一段 SQL 语句的集合,调用存储过程可以简化应用开发人员的很多工作,减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的。

从思想来看就是sql语句的封装和复用。

但是,,个人觉得这个东西其实都是可以在jvm里实现的。

基本语法

创建

CREATE PROCEDURE 存储过程名称 ([ 参数列表 ])
BEGIN
	-- SQL语句
END ;

调用

CALL 名称 ([ 参数 ]);

查看

SELECT * FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA = 'xxx'; -- 查询指定数据库的存储过程及状态信息

HOW CREATE PROCEDURE 存储过程名称 ; -- 查询某个存储过程的定义

删除

DROP PROCEDURE [ IF EXISTS ] 存储过程名称 ;

例如

create procedure p1()
begin
	select count(*) from student;
end;

-- 调用
call p1();

-- 查看
select * from information_schema.ROUTINES where ROUTINE_SCHEMA ='itcast';

show create procedure p1;

-- 删除
drop procedure if exists p1;

参数

  • IN 该类参数作为输入,也就是需要调用时传入值 。是默认参数类型
  • OUT 该类参数作为输出,也就是该参数可以作为返回值。
  • INOUT 既可以作为输入参数,也可以作为输出参数。
CREATE PROCEDURE 存储过程名称 ([ IN/OUT/INOUT 参数名 参数类型 ])
BEGIN
	-- SQL语句
END ;

变量

系统变量

查看

SHOW [ SESSION | GLOBAL ] VARIABLES ; – 查看所有系统变量
SHOW [ SESSION | GLOBAL ] VARIABLES LIKE ‘…’; – 可以通过LIKE模糊匹配方式查找变量
SELECT @@[SESSION | GLOBAL] 系统变量名;

赋值

SET [ SESSION | GLOBAL ] 系统变量名 = 值 ;

用户变量

创建与赋值

SET @变量名 = 值 [, @变量名 = 值] ;
SELECT 字段名 INTO @变量名 FROM 表名;

使用

SELECT @var_name ;

局部变量

创建

DECLARE 变量名 变量类型 [DEFAULT 值] ;

赋值

SET 变量名 = 值 ;
SELECT 字段名 INTO 变量名 FROM 表名;

例如

create procedure p2()
begin
		declare stu_count int default 0;
		select count(*) into stu_count from student;
		select stu_count;
end;

call p2();

if-Then

IF 条件1 THEN
.....
ELSEIF 条件2 THEN -- 可选
.....
ELSE -- 可选
.....
END IF;

例如

create procedure p3()
begin
	declare score int default 58;
	declare result varchar(10);
	
	if score >= 85 then
		set result := '优秀';
	elseif score >= 60 then
		set result := '及格';
	else
		set result := '不及格';
	end if;
	
	select result;
end;

call p3();

case

创建
语法1

CASE case_value
	WHEN when_value1 THEN statement_list1
	[ WHEN when_value2 THEN statement_list2] ...
	[ ELSE statement_list ]
END CASE;

语法2

CASE
	WHEN search_condition1 THEN statement_list1
	[WHEN search_condition2 THEN statement_list2] ...
	[ELSE statement_list]
END CASE;

例如

create procedure p6(in month int)
begin
	declare result varchar(10);
	case
		when month >= 1 and month <= 3 then
			set result := '第一季度';
		when month >= 4 and month <= 6 then
			set result := '第二季度';
		when month >= 7 and month <= 9 then
			set result := '第三季度';
		when month >= 10 and month <= 12 then
			set result := '第四季度';
		else
			set result := '非法参数';
	end case ;

	select concat('您输入的月份为: ',month, ', 所属的季度为: ',result);
end;
call p6(16);

while

与while类似的还有repeat和loop,功能上来讲都可以用while替代,感兴趣可以自己去看一下。

WHILE 条件 DO
	SQL逻辑...
END WHILE;

例如

create procedure p7(in n int)
begin
	declare total int default 0;
	while n>0 do
		set total := total + n;
		set n := n - 1;
	end while;
	
	select total;
end;

call p7(100);

游标

用来存储查询结果集的数据类型 , 在存储过程和函数中可以使用游标对结果集进
行循环的处理。游标的使用包括游标的声明、OPEN、FETCH 和 CLOSE,其语法分别如下。

通俗来讲,就是一个存放SQL——结果集的变量,打开就读了sql,然后通过fetch就可以读结果集。
创建游标

DECLARE 游标名称 CURSOR FOR 查询语句 ;

打开游标

OPEN 游标名称 ;

获取游标记录

FETCH 游标名称 INTO 变量 [, 变量 ] ;

关闭游标

CLOSE 游标名称 ;

例如

create procedure p11(in uage int)
begin
	declare uname varchar(100);
	declare upro varchar(100);
	declare u_cursor cursor for select name,profession from tb_user where age <=uage;


	drop table if exists tb_user_pro;
	create table if not exists tb_user_pro(
		id int primary key auto_increment,
		name varchar(100),
		profession varchar(100)
	);


	open u_cursor;
	while true do
		fetch u_cursor into uname,upro;
		insert into tb_user_pro values (null, uname, upro);
	end while;
	close u_cursor;

end;
call p11(30);

Handler

用来定义在流程控制结构执行过程中遇到问题时相应的处理步骤。

基本语法:

DECLARE handler_action HANDLER FOR condition_value [, condition_value]... statement ;
handler_action 的取值:

	CONTINUE: 继续执行当前程序
	EXIT: 终止执行当前程序
condition_value 的取值:
	SQLSTATE sqlstate_value: 状态码,如 02000
	SQLWARNING: 所有以01开头的SQLSTATE代码的简写
	NOT FOUND: 所有以02开头的SQLSTATE代码的简写
	SQLEXCEPTION: 所有没有被SQLWARNING 或 NOT FOUND捕获的SQLSTATE代码的简写

例如

create procedure p11(in uage int)
begin
	declare uname varchar(100);
	declare upro varchar(100);
	declare u_cursor cursor for select name,profession from tb_user where age <=uage;
	
	declare exit handler for SQLSTATE '02000' close u_cursor;


	drop table if exists tb_user_pro;
	create table if not exists tb_user_pro(
		id int primary key auto_increment,
		name varchar(100),
		profession varchar(100)
	);


	open u_cursor;
	while true do
		fetch u_cursor into uname,upro;
		insert into tb_user_pro values (null, uname, upro);
	end while;
	close u_cursor;

end;

call p11(30);

上面的

declare exit handler for SQLSTATE ‘02000’ close u_cursor;

可以改为

declare exit handler for not found close u_cursor;

函数

这个我觉的就更没必要在sql里写了

存储函数是有返回值的存储过程,存储函数的参数只能是IN类型的。具体语法如下:

CREATE FUNCTION 存储函数名称 ([ 参数列表 ])
RETURNS type [characteristic ...]
BEGIN
	-- SQL语句
	RETURN ...;
END ;

对于characteristic

  • DETERMINISTIC:相同的输入参数总是产生相同的结
  • NO SQL :不包含 SQL 语句。
  • READS SQL DATA:包含读取数据的语句,但不包含写入数据的语句。
create function fun1(n int)
returns int deterministic
begin
	declare total int default 0;
	
	while n>0 do
		set total := total + n;
		set n := n - 1;
	end while;

	return total;
end;
select fun1(50);

在多线程并发访问数据库时,锁用于确保数据的一致性和有效性。

按照锁的粒度讲MySQL的锁分为三类

  • 全局锁:锁定数据库中的所有表。
  • 表级锁:每次操作锁住整张表。
  • 行级锁:每次操作锁住对应的行数据。

全局锁

全局锁就是对整个数据库实例加锁,加锁后整个实例就处于只读状态。
其典型的使用场景是做全库的逻辑备份,对所有的表进行锁定,从而获取一致性视图,保证数据的完整性。

基本语法

加全局锁

flush tables with read lock ;

数据备份

mysqldump -uroot –p1234 itcast > itcast.sql

释放锁

unlock tables ;

表级锁

锁定粒度大,发生锁冲突的概率高,并发度低。应用在MyISAM、InnoDB、BDB等存储引擎中。

表锁

加锁

lock tables 表名… read/write。

释放锁
客户端断开连接默认释放锁

unlock tables

共享锁(read lock):对于所有线程写操作全部拒绝,但是允许所有线程的读操作。
排他锁(write lock):允许持锁线程读写,拒绝其他线程一切读写

元数据锁

meta data lock , 元数据锁,简写MDL。

MDL加锁过程由系统自动控制,无需显式使用,访问表时自动获取。

MDL作用是维护表元数据(表明,字段名,字段类型)的数据一致性,表上有活动事务时,不可以对表元数据进行写入操作。避免DML与DDL和DQL冲突,保证读写的正确性。

也就是说,会给DML和DDL语句执行时加MDL。DQL会获取SHARED_READ,DML会获取SHARED_WRITE,而DDL会获取EXCLUSIVE。EXCLUSIVE和另外两个都是互斥的。

意向锁

在InnoDB中使用意向锁来减少表锁的检查,使表锁无需检查每行数据是否加锁。

意向锁的作用是避免DML或DQL在执行时,行锁与表锁的冲突。

原理:对涉及到的行加锁同时也对整个表加意向锁,其他线程想要加表锁时不必逐行判断行锁,而是通过判断想要添加的表锁和意向锁是否冲突,从而判断能否成功添加表锁。

意向共享锁:与 表锁共享锁(read)兼容,与表锁排他锁(write)互斥。

select … lock in share mode

意向排他锁:与表锁共享锁(read) 及 排他锁(write)都互斥。

由DML自动添加。

行级锁

每次操作锁住对应的行数据。锁定粒度最小,发生锁冲突的概率最低,并发度最高。应用在InnoDB存储引擎中。

  • InnoDB的数据是基于索引组织的,行锁是通过对索引上的索引项加锁来实现的,而不是对记录加的锁。
  • 对于无索引字段不会添加行锁,会直接添加表锁。

行锁(Record Lock)

锁定单个行记录的锁,防止其他事务对此行进行update和delete。在RC、RR隔离级别下都支持。
在这里插入图片描述
共享锁:允许一个事务去读一行,对于此行,阻塞想要获取排他锁的进程。不同线程对对同一行数据获取共享锁是允许的。

添加和意向锁的操作一致。

select … lock in share mode

排他锁:允许获取排他锁的事物对数据更新,阻塞想获取改行任何锁的进程。

由DML自动添加

间隙锁(gap lock)

锁定索引记录间隙(不含该记录),确保索引记录间隙不变,防止其他事务在这个间隙进行insert,产生幻读。在RR隔离级别支持。

在这里插入图片描述

临键锁(Next-Key Lock)

行锁和间隙锁组合,同时锁住数据,并锁住数据前面的间隙Gap。在RR隔离级别下支持。

间隙锁唯一目的是防止其他事务插入间隙。间隙锁可以共存,一个事务采用的间隙锁不会阻止另一个事务在同一间隙上采用间隙锁。
在这里插入图片描述
默认情况下,InnoDB在 REPEATABLE READ事务隔离级别运行,InnoDB使用 临键锁进行搜索和索引扫描,以防止幻读。

  • 索引上的等值查询(唯一索引),给不存在的记录加锁时, 优化为间隙锁 。
  • 索引上的等值查询(非唯一普通索引),向右遍历到最后一个不满足查询需求的值时,临键锁退化为间隙锁。
  • 索引上的范围查询(唯一索引)–会访问到不满足条件的第一个值为止。

Innodb

InnoDB: 是Mysql的默认存储引擎,支持事务、外键。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询之外,还包含很多的更新、删除操作,那么InnoDB存储引擎是比较合适的选择。
在这里插入图片描述
表空间
表空间是InnoDB存储引擎逻辑结构的最高层, 如果用户启用了参数 innodb_file_per_table(在8.0版本中默认开启) ,则每张表都会有一个表空间(xxx.ibd),一个mysql实例可以对应多个表空间,用于存储记录、索引等数据。


段,分为数据段(Leaf node segment)、索引段(Non-leaf node segment)、回滚段(Rollback segment),InnoDB是索引组织表,数据段就是B+树的叶子节点, 索引段即为B+树的非叶子节点。段用来管理多个Extent(区)。


区,表空间的单元结构,每个区的大小为1M。 默认情况下, InnoDB存储引擎页大小为16K, 即一个区中一共有64个连续的页。


页,是InnoDB 存储引擎磁盘管理的最小单元,每个页的大小默认为 16KB。为了保证页的连续性,InnoDB 存储引擎每次从磁盘申请 4-5 个区。


InnoDB 存储引擎数据是按行进行存放的。

基本架构

内存架构

在这里插入图片描述
Buffer Pool

缓冲池。是主存中的一个区域,用于缓冲磁盘和内存之前的访问速率差值,将经常访问的数据加载到缓冲池,在执行DML和DQL时先操作缓冲池中的数据,之后以一定频率刷新到磁盘,减少了磁盘IO。在专用服务器上通常将80%内存分配给缓冲池。

缓冲池中包含:索引页,数据页,undo页,插入缓存,自适应哈希索引,锁信息。

缓冲池中的页分为三种。

  • free page:空闲page,未被使用。
  • clean page:被使用page,数据没有被修改过。
  • dirty page:脏页,被使用page,数据被修改过,也中数据与磁盘的数据产生了不一致。

Change Buffer

更改缓冲区。当DML操作的数据不在缓冲池时,先将数据变更存入更改缓冲区,直到数据被读取时,将该数据合并入缓冲池中。避免了DML操作对索引的影响从而产生大量磁盘IO。

Adaptive Hash Index
自适应哈希索引。Innodb会在某种特定条件下使用hash索引,这种索引无需人工调控。

Log Buffer

日志缓冲区。默认大小16MB,存储需要存入磁盘的log文件,定期刷新到磁盘。

磁盘结构

在这里插入图片描述
System Tablespace
系统表空间。是上述内存架构的更改缓冲区存储区域。也可能会有表的数据和索引。
File-Per-Table Tablespaces
文件表空间。每创建一个表都产生一个对应存储表数据和索引的表空间。

General Tablespaces
通用表空间。用户主动创建的表空间。可在创建表时指定该空间。

创建表空间

CREATE TABLESPACE ts_name ADD DATAFILE ‘file_name’ ENGINE =engine_name;

指定该空间

CREATE TABLE xxx … TABLESPACE ts_name;

Undo Tablespaces
撤销表空间。用于存储undo log日志。
Temporary Tablespaces
临时表空间。用于存储用户创建的会话临时表或全局临时表。
Doublewrite Buffer Files
双写缓冲区。缓冲池将数据刷新到磁盘前,先将数据写入双写缓冲区,便于恢复数据。
Redo Log
重做日志。分为重做日志缓冲和重做日志文件。前者存于内存后者存于磁盘。事物提交后将修改信息存入重做日志文件,用于数据恢复。

后台线程

Master Thread
核心后台线程。负责调度其他线程以及各种操作。

IO Thread
要负责IO请求的回调。
有四种:
Read thread 4个 负责读操作。
Write thread 4个 负责写操作。
Log thread 1个 负责将日志缓冲区刷新到磁盘。
Insert buffer thread 1个 负责将写缓冲区内容刷新到磁盘。

Purge Thread
用于回收事务提交后不可用的undo log。

Page Cleaner Thread
协助 Master Thread 刷新脏页到磁盘的线程

事物支持

谈到事物支持,就是谈如何满足事物的四个特性:原子,一致,持久,隔离。
Innodb有两份日志,锁和mvcc模式来保证事物实现这些特性。
在这里插入图片描述

MVCC(弱一致性,多版本并发控制)

MVCC(Multi-Version Concurrency Control),多版本并发控制。维护一个数据的多个版本,实现读写无冲突,通过快照读实现非阻塞读功能。具体的实现依赖于隐藏字段,undo log链,ReadView

当前读:读取记录的最新版本,读取时需要加锁。可避免脏读。

快照读:读取当前可见版本有可能是历史数据,无需加锁。通过undo log版本链实现。

  • Read Committed:每次select,都生成一个快照读。
  • Repeatable Read:开启事务后第一个select语句才是快照读的地方。

普通的select是快照读,而在默认的RR隔离级别下,开启事务后第一个select语句才是快照读的地方,后面执行相同的select语句都是从快照中获取数据,可能不是当前的最新数据,这样也就保证了可重复读。

  • Serializable:快照读会退化为当前读。
隐藏字段和undo log链
  • DB_TRX_ID 最近修改事务ID,记录插入这条记录或最后一次修改该记录的事务ID。
  • DB_ROLL_PTR 回滚指针,指向这条记录的上一个版本,用于配合undo log,指向上一个版本。
  • DB_ROW_ID 隐藏主键,如果表结构没有指定主键,将会生成该隐藏字段。

回滚指针和最近修改事物ID
如下是一条insert的原始数据。可见最近修改事物ID是1,因为新插入所以回滚指针是null
在这里插入图片描述
而后有如下四个事物并发访问。
在这里插入图片描述
当执行完事物2之后。
在这里插入图片描述
当执行完事物3之后
在这里插入图片描述

readview

读视图。作为MVCC为快照读SQL提供数据时的依据,记录当前系统活跃事物的id。

  • m_ids 当前活跃的事务ID集合
  • min_trx_id 最小活跃事务ID
  • max_trx_id 预分配事务ID,当前最大事务ID+1(因为事务ID是自增的)
  • creator_trx_id ReadView创建者的事务ID

trx_id 代表当前undo log版本链对应事务ID。
在这里插入图片描述

  • READ COMMITTED :在事务每一次执行快照读都生成ReadView。
  • REPEATABLE READ:在事务第一次快照读时生成ReadView,后续复用该ReadView。
举例分析

上述事物5在读取时就会分两次产生两个readview。
在这里插入图片描述
以第一个readview为例。在进行匹配时,会从undo log的版本链,从上到下进行挨个匹配:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

其实看到这里也就明白了MVCC实际上是就是通过“维护”数据版本来实现的非阻塞读,提高了并发性,这也是弱一致性的体现。

由此可见,高并发和高一致是互斥的概念,并非一致性弱就不好,也并非高一致性就好,具体业务需要具体权衡。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1432418.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【FX110网】日交所发布1月交易数据:衍生品交易额达历年1月最高!

日本交易所集团&#xff08;日交所&#xff0c;JPX&#xff09;发布了其2024年1月的交易数据概览。数据显示&#xff0c;该交易所当月衍生品交易额创新历年来的1月交易数据最高纪录。2024年1月共有19个交易日。 2024年1月交易概览现货股票市场 2024年1月&#xff0c;该交易所主…

ArrayList在添加元素时报错java.lang.ArrayIndexOutOfBoundException

一、添加单个元素数组越界分析 add源码如下 public boolean add(E e) {ensureCapacityInternal(size 1); // Increments modCount!!elementData[size] e;return true; } size字段的定义 The size of the ArrayList (the number of elements it contains). ArrayList的大…

三层交换组网实验(华为)

思科设备参考&#xff1a;三层交换组网实验&#xff08;思科&#xff09; 一&#xff0c;技术简介 三层交换技术的出现&#xff0c;解决子网必须依赖路由器进行管理的问题&#xff0c;解决传统路由器低速、复杂所造成的网络瓶颈问题。一个具有三层交换功能的设备可简单理解为…

Unity引擎学习笔记之【角色按键器操作】

角色按键Character Controls 一、脚本操作 设置脚本 设置基本键盘操作 //水平轴float horizontal Input.GetAxis("Horizontal");//垂直轴float vertical Input.GetAxis("Vertical");//创建方向向量Vector3 dir new Vector3(horizontal,0,vertical);/…

《Python 网络爬虫简易速速上手小册》第5章:Python 数据存储与管理(2024 最新版)

文章目录 5.1 选择数据存储方案5.1.1 重点基础知识讲解5.1.2 重点案例&#xff1a;使用 SQLite 存储博客文章数据5.1.3 拓展案例 1&#xff1a;使用 MongoDB 存储社交媒体动态5.1.4 拓展案例 2&#xff1a;使用 Elasticsearch 存储和检索日志数据 5.2 数据清洗与预处理5.2.1 重…

鱼和熊掌如何兼得?一文解析RDS数据库存储架构升级

在2023年云栖大会上&#xff0c;阿里云数据库产品事业部负责人李飞飞在主题演讲中提到&#xff0c;瑶池数据库推出“DB存储”一体化能力&#xff0c;结合人工智能、机器学习、存储等方法和创新能力&#xff0c;实现Buffer Pool Extension能力和智能冷温热数据分层能力。在大会的…

Linux 高并发服务器

多进程并发服务器 使用多进程并发服务器时要考虑以下几点&#xff1a; 父进程最大文件描述个数(父进程中需要close关闭accept返回的新文件描述符)系统内创建进程个数(与内存大小相关)进程创建过多是否降低整体服务性能(进程调度) server /* server.c */ #include <stdio…

【刷题题解】最长回文子序列

给你一个字符串 s &#xff0c;找出其中最长的回文子序列&#xff0c;并返回该序列的长度。 子序列定义为&#xff1a;不改变剩余字符顺序的情况下&#xff0c;删除某些字符或者不删除任何字符形成的一个序列 这道题&#xff0c;一眼动态规划&#xff0c;但是即使动起来也规划…

TrinityCore安装记录

TrinityCore模拟魔兽世界&#xff08;World of Warcraft&#xff09;的开源项目&#xff0c;并且该项目代码广泛的优化、改善和清理代码。 前期按照官方手册按部就班的安装即可。 注意几点&#xff1a; 1 需要配置Ubuntu22.04版本的服务器或者Debian11 服务器。2 需要使用gi…

python_ACM模式《剑指offer刷题》链表4

题目&#xff1a; 面试tips&#xff1a; 询问是否需要判断环&#xff0c;可微调下方代码。 思路&#xff1a; 思路一&#xff1a; 判断环是否存在&#xff1a;设定一快一慢指针&#xff0c;均从头节点出发&#xff0c;快指针一次走两步&#xff0c;慢指针一次走一步。若无环…

docker安装-centos

Docker CE 支持 64 位版本 CentOS 7&#xff0c;并且要求内核版本不低于 3.10 卸载旧版本Docker sudo yum remove docker \ docker-common \ docker-selinux \ docker-engine使用yum安装 yum 更新到最新版本: sudo yum update执行以下命令安装依赖包&#xff1a; sudo yum…

[设计模式Java实现附plantuml源码~结构型]处理多维度变化——桥接模式

前言&#xff1a; 为什么之前写过Golang 版的设计模式&#xff0c;还在重新写Java 版&#xff1f; 答&#xff1a;因为对于我而言&#xff0c;当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言&#xff0c;更适合用于学习设计模式。 为什么类图要附上uml 因为很…

SpringBoot第二天

Bean管理 Bean扫描 在Spring中&#xff0c;Bean的扫描有两种写法 第一种是在xml配置文件中用标签扫描 <context:component-scan basepackage"com.cacb"/> 第二种是是注解扫描 ComponentScan(basePackages"com.cacb") 而在SpringBoot中&#xf…

张维迎《博弈与社会》威胁与承诺(4)宪政与民主

有限政府 动态博弈理论对我们理解民主与法治具有重要的意义。 自人类进入文明时代以来&#xff0c;政府就是社会博弈重要的参与人。任何社会要有效运行&#xff0c;都需要赋予政府一些自由裁量权。但如果政府的自由裁量权太大&#xff0c;政府官员为所欲为&#xff0c;不仅老百…

Jetson AGX Orin安装Anaconda,Cuda,Cudnn,pytorch,Tensorrt,ROS

Anaconda&#xff1a;https://repo.anaconda.com/archive/ Cuda&#xff1a;https://forums.developer.nvidia.com/t/pytorch-for-jetson/72048 1&#xff1a;安装Anaconda3 下载&#xff1a;Anaconda3-2021.11-Linux-aarch64.sh chmod x Anaconda3-2021.11-Linux-aarch64.s…

瑞_23种设计模式_工厂模式

文章目录 1 什么是工厂模式案例案例代码 2 简单工厂模式&#xff08;Simple Factory&#xff09;2.1 简单工厂模式的结构2.2 案例改进——简单工厂模式2.3 案例改进代码实现2.4 简单工厂模式优缺点2.5 拓展——静态工厂 3 工厂方法模式&#xff08;Factory Method&#xff09;★…

OJ_整数奇偶排序

题干 c实现 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<algorithm> using namespace std;//compare函数不交换返回true bool compare(int a, int b) {//1.a奇数&#xff0c;b偶数&#xff0c;不交换//2.a奇数&#xff0c;b奇数&#xff0c;a比b…

openGauss学习笔记-213 openGauss 性能调优-总体调优思路

文章目录 openGauss学习笔记-213 openGauss 性能调优-总体调优思路213.1 调优思路概述213.2 调优流程 openGauss学习笔记-213 openGauss 性能调优-总体调优思路 213.1 调优思路概述 openGauss的总体性能调优思路为性能瓶颈点分析、关键参数调整以及SQL调优。在调优过程中&…

【Redis】整理

对于现代大型系统而言&#xff0c;缓存是一个绕不开的技术话题&#xff0c;一提到缓存我们很容易想到Redis。 Redis整理&#xff0c;供回顾参考

TiDB架构设计和实践:高性能分布式数据库解决方案

摘要&#xff1a;TiDB是一个开源的分布式NewSQL数据库&#xff0c;具备强大的水平扩展能力和高性能查询能力。本文将介绍TiDB的架构设计和实践经验&#xff0c;帮助读者了解如何利用TiDB构建可靠、高性能的分布式数据库系统。 正文&#xff1a; ### 1. 引言 随着互联网规模的…