MySQL学习笔记

2023-11-12

自己学习MySQL时整理的笔记,包括实操中遇到的问题,不同版本之间的差异。后续也会继续完善,有PDF文档版。
学习视频:https://www.bilibili.com/video/BV1Kr4y1i7ru?t=1.9

怎么学,收获?

基础篇:(初级工程师)

  • MySQL概述
  • SQL
  • 函数
  • 约束
  • 多表查询
  • 事务

进阶篇:(中级工程师)

  • 存储引擎
  • 索引
  • SQL优化
  • 视图/存储过程/触发器
  • InnoDB核心
  • MySQL管理

运维篇:(高级工程师)

  • 日志
  • 主从复制
  • 分库分表
  • 读写分离

MySQL初级篇

MySQL概述

  1. 数据库(DB)
    • 数据存储的仓库
  2. 数据库管理系统(DBMS)
    • 操作和管理数据库的大型软件
  3. SQL
    • 操作关系型数据库的编程语言,定义了操作关系型数据库的统一标准

关系型数据库(RDBMS)

建立在关系模型基础上,由多张相互连接的二维表组成的数据库。

特点:

  1. 使用表存储数据,格式统一,便于维护
  2. 使用SQL语言操作,标准统一,使用方便

MySQL数据库 数据模型

客户端–连接–DBMS–使用sql语句通过DBMS创建数据库/表/存储数据

SQL

  • sql通用语法
  • sql分类
  • DDL
  • DML
  • DQL
  • DCL

SQL通用语法

  1. SQL语句可以单行或多行书写,以分号结尾
  2. SQL语句可以使用空格/缩进来增强可读性
  3. MySQL数据库的SQL语句不区分大小写,关键字建议使用大写
  4. 注释:
    • 单行注释:-- 注释内容 或 #注释内容(MySQL特有)
    • 多行注释:/*注释内容*/

SQL分类

分类 说明
DDL 数据库定义语言,用来定义数据库对象(数据库,表,字段)
DML 数据库操作语言,用来对数据库表中的数据进行增删改
DQL 数据库查询语言,用来查询数据库中表的记录
DCL 数据库控制语言,用来创建数据库用户、控制数据库的访问权限

DDL

DDL-数据库操作

查询、创建、删除、使用

# 查询
show databases;			-- 查询所有数据库
select database();		-- 查询当前数据库
# 创建
create database [if not exists] 数据库名 [default charset 字符集] [collate 排序规则];
# 删除 
drop databse [if exists] 数据库名;
# 使用
use 数据库名;

DDL-表操作

查询、创建、修改、删除

查询

# 查询
show tables;		-- 查询当前数据库所有表
desc 表名;		-- 查询表结构
show create table 表名;		-- 查询指定表的建表语句

创建

create table 表名(
	字段1	字段1类型 [comment 字段1注释],
    字段2	字段2类型 [comment 字段2注释],
    字段3	字段3类型 [comment 字段3注释],
    ......
    字段n	字段n类型 [comment 字段n注释]
)[comment 表注释];

注意:[…]为可选参数,最后一个字段后没有逗号

DDL-表操作-数据类型

MySQL中的数据类型有很多,主要分为三类:数值、字符串、日期时间

数值类型

  • age tinyint unsigned	-- 表示没负数
    

数值类型

字符串类型

二进制数据(视频/音频/安装包)开发当中一般不怎么做,性能不高,一般采用专门的文件服务器存储

定长字符串char(10) 和 变长字符串varchar(10)

相同点:10代表最多只能存储10个字符,一旦超过就报错

不同:

  • char(10)—>性能好:即使存储一个字符,也会占用10个字符空间,其它空间用空格补位
  • varchar(10)—>性能较差:存储一个字符就占用一个字符空间,根据存储内容去计算占用的空间是多少

在这里插入图片描述

日期时间类型

在这里插入图片描述

修改

添加字段

alter table 表名 add 字段名 类型(长度) [comment 注释] [约束];

alter table test add email varchar(20) comment '邮件';

desc test; -- 查看表信息,进行验证

修改数据类型

alter table 表名 modify 字段名 新数据类型(长度);

修改字段名和字段类型

alter table 表名 change 旧字段名 新字段名 类型(长度) [comment 注释][约束];

删除字段

alter table 表名 drop 字段名;

修改表名

alter table 表名 rename to 新表名;

删除

删除表

drop table [if exists] 表名;

删除指定表,并重新创建该表

truncate table 表名;	-- 原表的结构并不会变

DML

数据操作语言:对数据中表的数据进行增、删、改

  • 添加数据:insert
  • 修改数据:update
  • 删除数据:delete

DML-添加数据

  1. 给指定字段添加数据

    insert into 表名(字段1,字段2,...)values(值1,值2);
    
  2. 给全部字段添加数据

    insert into 表名 values(值1,值2,...);
    
  3. 批量添加数据

    insert into 表名(字段1,字段2,...) values(值1,值2,...),(值1,值2,...),(值1,值2,...);
    insert into 表名 values(值1,值2,...),(值1,值2,...),(值1,值2,...);
    

注意:

  • 插入数据时,指定的字段顺序需要与值的顺序是一一对应的
  • 字符串和日期型数据应该包含在引号中
  • 插入的数据大小,应该在字段的规定范围内

DML-修改数据

update 表名 set 字段1=值1, 字段2=值2, ... [where 条件];

注意:修改语句的条件可以有,也可以没有,没有条件则会修改整张表的所有数据

DML-删除数据


delete from 表名 [where 条件];

注意:

  • delete语句的条件可以有,也可以没有,没条件则表示删除整张表的所有数据
  • delete语句不能删除某一个字段的值(可以使用update)

DQL

数据库查询语言,查询数据库中表的记录

关键字:select

DQL-语法

-- 编写顺序
select 
	字段列表
from
	表名列表
where
	条件列表
group by
	分组字段列表
having
	分组后条件列表
order by
	排序字段列表
limit
	分页参数
  • 基本查询
  • 条件查询(where)
  • 聚合函数(count、max、min、avg、sum)
  • 分组查询(order by)
  • 分页查询(limit)

DQL-基本查询

  1. 查询多个字段

    select 字段1,字段2,字段3... from 表名;
    select * from 表名;
    
  2. 设置别名

    select 字段1[as 别名1], 字段2[as 别名2] ... from 表名;	# as可以省略不写
    
  3. 去除重复记录

    select distinct 字段列表 from 表名;
    

DQL-条件查询

  1. 语法

    select 字段列表 from 表名 where 条件列表;
    
  2. 条件

    比较运算符 功能 逻辑运算符 功能
    > 大于 and 或 && 并且(多个条件同时成立)
    >= 大于等于 or 或 || 或者(多个条件任意一个成立)
    < 小于 not 或 ! 非,不是
    <= 小于等于
    = 等于
    <> 或 != 不等于
    between…and … 在某个范围之内(含最小、最大值)
    in(…) 在in之后的列表中的值,多选一
    like 占位符 模糊匹配(_匹配单个字符,%匹配任意个字符)
    is NULL 为空

DQL-聚合函数

通常进行分组查询的时候,会配合着聚合函数来操作

  1. 介绍

    将一列数据作为一个整体,进行纵向计算。

  2. 常见的聚合函数

    函数 功能
    count 统计数量
    max 最大值
    min 最小值
    avg 平均值
    sum 求和
  3. 语法

    select 聚合函数(字段列表) from 表名;
    select avg(age) from user;
    

    注意:NULL值不参与聚合函数运算。

DQL-分组查询

  1. 语法

    select 字段列表 from 表名 [where 条件] group by 分组字段名 [having 分组后过滤条件];
    
  2. where与having区别

    • 执行时机不同:where是分组之前进行过滤,不满足where条件,不参与分组;而having是分组之后对结果进行过滤。
    • 判断条件不同:where不能对聚合函数进行判断,而having可以。
  3. 注意

    • 执行顺序:where > 聚合函数 > having

    • 分组之后,查询的字段一般为聚合函数和分组字段,查询其它字段无任何意义。

      -- 根据性别分组之后,统计人数【gender,count(*)】
      select name, gender, count(*) from emp group by gender;
      
      -- name字段查出来的是,每组第一个人的姓名,没有任何意义
      

DQL-排序查询

  1. 语法

    select 字段列表 from 表名 order by 字段1 排序方式1, 字段2 排序方式2;
    
  2. 排序方式

    • ASC:升序(默认值)
    • DESC:降序
  3. 注意:如果是多字段排序,当第一个字段值相同时,才会根据第二字段进行排序。

DQL-分页查询

  1. 语法

    select 字段列表 from 表名 limit 起始索引, 查询记录数;
    
  2. 注意

    • 起始索引从0开始,起始索引 =(查询页码 -1)* 每页显示记录数。
    • 分页查询是数据库的方言,不同的数据库有不同的实现,MySQL中的是limit。
    • 如果查询的是第一页数据,起始索引可以省略,直接简写为limit 10。

DQL-执行顺序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mOD84083-1687944484121)(D:\0-视频#-笔记\MySQL\笔记配图\DQL-执行顺序.png)]

DCL

数据库控制语言,用来管理数据库用户、控制数据库的访问权限。

DCL-管理用户

  1. 查询用户

    use mysql;
    select * from user;
    
  2. 创建用户

    create user '用户名'@'主机名' identified by '密码';
    

    主机名:localhost 只能本机访问,改为 % 表示任意主机访问

    用户名和主机地址才能定位一个MySQL用户

  3. 修改用户密码

    set password for '用户名'@'主机名' = password('新密码');
    # 新密码必须使用 PASSWORD() 函数来加密,若不使用 PASSWORD() 加密,也会执行成功,但是用户会无法登录。
    
    alter user '用户名'@'主机名' identified by '新密码';	# 好像只有5.7以上的版本支持alter修改
    
    alter user '用户名'@'主机名' identified with mysql_native_password by '新密码';	# 修改身份验证插件
    

    注意版本:

    • mysql5.7版本之前,修改用户密码使用set、update
    • mysql5.7版本之后,password属性已经取消(使用SELECT Password(‘123’) 会报错),修改用户密码的语句已经改变
    • 在MySQL 5.7中,默认的身份验证插件还是为 mysql_native_password
    • 在MySQL 8.0中,caching_sha2_password是默认的身份验证插件,此方式并不兼容常规的老的web服务接口。
      • ​ 像在使用MySQL的可视化工具Navicat时,又或者Django和MySQL的配合使用,由于Django不支持新版本MySQL的加密方式,会报错,所以需要将默认的连接方式恢复为旧的mysql_native_password方式。
  4. 删除用户

    drop user '用户名'@'主机名';
    

注意:

  • 主机名可以使用通配符 %
  • 这类SQL开发人员操作的比较少,主要是DBA(Database Administrator 数据库管理员)使用。

DCL-权限控制

注意:MySQL中 test 数据库比较特别,所有能连接到MySQL的用户,几乎都拥有test库的所有权限。

详情:mysql的test 库的相关特性-renwd-ChinaUnix博客

MySQL中定义了很多种权限,但是常用的就以下几种:

权限 说明
all, all privileges 所有权限
select 查询数据
insert 插入数据
update 修改数据
delete 删除数据
alter 修改表
drop 删除数据库/表/视图
create 创建数据库/表
  1. 查询权限

    show grants for '用户名'@'主机名';
    
  2. 授予权限

    grant 授权列表 on 数据库名.表名 to '用户名'@'主机名';
    
  3. 撤销权限

    revoke 权限列表 on 数据库名.表名 from '用户名'@'主机名';
    

注意:

  • 多个权限之间,使用逗号分隔
  • 授权时,数据库名和表名可以使用 * 进行通配,代表所有。

函数

函数是一段可以直接被另一段程序调用的程序或代码。

  • 字符串函数
  • 数值函数
  • 日期函数
  • 流程函数

字符串函数

MySQL中内置了很多字符串函数,常用的几个如下:

函数 功能
concat(S1, S2, …Sn) 字符串拼接,将S1, S2, …Sn拼接成一个字符串
lower(str) 将字符串 str 全部转为小写
upper(str) 将字符串 str 全部转为大写
lpad(str, n, pad) 左填充,用字符串 pad 对 str 的左边进行填充,达到n个字符串长度
rpad(str, n, pad) 又填充,用字符串 pad 对 str 的右边进行填充,达到n个字符串长度
trim(str) 去掉字符串头部和尾部的空格
substring(str, start, len) 返回从字符串 str 从 start 位置起的 len 个长度的字符串

数值函数

常见的数值函数如下:

函数 功能
cell(x) 向上取整
floor(x) 向下取整
mod(x,y) 返回x/y的模
rand() 返回0-1内随机数
round(x,y) 求参数x的四舍五入的值,保留y位小数

日期函数

常见日期函数如下:

函数 功能
curdate() 返回当前日期
curtime() 返回当前时间
now() 返回当前日期和时间
year(date) 获取指定date的年份
month(date) 获取指定date的月份
day(date) 获取指定date的日期
date_add(date, interval expr type) 返回一个日期/时间值加上一个时间间隔expr后的时间值
datediff(date1, date2) 返回起始时间date1和结束时间date2之间的天数

流程函数

流程函数也是常用的一类函数,可以在SQL语句中实现条件筛选,从而提高语句的效率。

函数 功能
if(value, t, f) 如果value为true,则返回t,否则返回f
ifnull(value1, value2) 如果value不为空,返回value1,否则返回value2
case when [val1] then [res1] … else [default] end 如果val1为true,返回res1,…否则返回default默认值
case [expr] when [val1] then [res1] … else [default] end 如果表达式expr的值等于val1,返回res1,…否则返回default默认值

约束

概述

  1. 概念:约束是作用于表中字段上的规则,用于限制存储在表中的数据。

  2. 目的:保证数据库中数据的正确、有效性和完整性、

  3. 分类

    约束 描述 关键字
    非空约束 限制该字段的数据不能为null not null
    唯一约束 保证该字段的所有数据都是唯一、不重复的 unique
    主键约束 主键是一行数据的唯一标识,要求非空且唯一 primary key
    默认约束 保存数据时,如果未指定该字段的值,则采用默认值 default
    检查约束(8.0.16版本之后) 保证字段值满足某一个条件 check
    外键约束 用来让两张表的数据之间建立连接,保证数据的一致性和完整性 foreign key

注意:约束是作用于表中字段上的,可以在创建表/修改表的时候添加约束。

约束演示

案例:根据需求,完成表结构的创建

字段名 字段含义 字段类型 约束条件 约束关键字
id ID唯一标识 int 主键,并且自动增长 primary key, auto_increment
name 姓名 varchar(10) 不为空,并且唯一 not null, unique
age 年龄 int 大于0,并且小于等于120 check
status 状态 char(1) 如果没有指定该值,默认为1 default
gender 性别 char(1)
-- 约束演示
create table user(
	id int primary key auto_increment comment '主键',
	name varchar(10) not null unique comment '姓名',
	-- 8.0.16版本之前不支持检查约束 age int check (age > 0 and age <= 120) comment '年龄',
	age tinyint unsigned comment '年龄',
	status char(1) default '1' comment '状态',
	gender char(1) comment '性别'
) comment '用户表';

外键约束

概念

外键用来让两张表的数据之间建立连接,从而保证数据的一致性和完整性

在这里插入图片描述

-- -----------------------------约束(外键)------------------------
-- 准备数据
create table dept(	-- dept:部;科(department)
    id int auto_increment comment 'ID' primary key,
       name varchar(10) not null comment '部门名称'
) comment '部门表';

insert into dept(id,name) values (1,'研发部'),(2,'市场部'),(3,'财务部'),(4,'销售部'),(5,'总经办');

create table emp(
	id int auto_increment comment 'ID' primary key,
       name varchar(50) not null comment '姓名',
       age tinyint unsigned comment '年龄',
       job varchar(20) comment '职位',
       salary int comment '薪资',
       entrydate date comment '入职日期',
       managerid int comment '直属领导ID',
       dept_id int comment '部门ID'
)comment '员工表';

insert into emp (id,name,age,job,salary,entrydate,managerid,dept_id) values
	(1,'金庸',66,'总裁',20000,'2000-01-01',null,5),
	(2,'张无忌',20,'项目经理',12500,'2005-12-05',1,1),
	(3,'杨逍',33,'开发',8400,'2000-11-03',2,1),
	(4,'韦一笑',48,'开发',11000,'2002-02-05',2,1),
	(5,'常遇春',43,'开发',10500,'2004-09-07',3,1),
   	(6,'小昭',19,'程序员鼓励师',6600,'2004-10-12',2,1);

语法

  • 添加外键

    -- 建表时添加
    create table 表名(
    	字段名 数据类型,
        ...
        [constraint] [外键名称] foreign key(外键字段名) references 主表(主表列名)
    );
    
    -- constraint:n. 限制,束缚;克制,拘束
    -- foreign:adj. 外国的;外交的,涉外的;陌生的,不熟悉的;外来的,异质的;外地的,不属于本地区的
    -- references:n. [图情]参考文献;参照;推荐信	v. 附……以供参考;把……引作参考
    
    -- 表创建好后 额外增加外键
    alter table 表名 add constraint 外键名称 foreign key(外键字段名) references 主表(主表列名);
    
  • 删除外键

    alter table 表名 drop foreign key 外键名称;
    
    
    
  • 删除/更新行为

    行为 说明
    no action 当在父表中删除/更新对于记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新。(与 restrict 一致)
    restrict 当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新。(与 no action 一致)
    cascade 当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有,则也删除/更新外键在子表中的记录。
    set null 当在父表中删除对应记录时,首先检查该记录是否有对应外键,如果有则设置子表中该外键值为null(这就要求该外键运行取null)。
    set default 父表有变更时,子表将外键列设置成一个默认值(Innodb不支持)
    alter table 表名 add constraint 外键名称 foreign key (外键字段) references 主表名(主表字段名) on update cascade on delete cascade;
    

外键设置不成功:原因有很多【比如引擎不对等】

多表查询

  • 多表关系
  • 多表查询概述
  • 内连接
  • 外连接
  • 自连接
  • 子查询
  • 多表查询案例

多表关系

概述:项目开发中,在进行数据库表结构设计时,会根据业务需求及业务模块之间的关系,分析并设计表结构,由于业务之间相互关联,所以各个表之间也存在各种联系,基本上分为三种:

  • 一对多(多对一)

  • 多对多

  • 一对一

一对多(多对一)

  • 案例:部门 与 员工的关系

  • 关系:一个部门对应多个员工,一个员工对应一个部门

  • 实现:在多的一方建立外键,指向一的一方的主键

在这里插入图片描述

多对多

  • 案例:学生 与 课程的关系

  • 关系:一个学生可以选修多门课程,一门课程也可以供多个学生选择

  • 实现:建立第三张中间表,中间表至少包含两个外键,分别关联两方主键

在这里插入图片描述

一对一

  • 案例:用户 与 用户详情表的关系

  • 关系:一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其它详情字段放在另一张表中,以提升操作效率

  • 实现:在任意一方加入外键,关联另一方的主键,并设置外键为唯一的(unique)

在这里插入图片描述

多表查询概述

  • 概述:指从多张表中查询数据

  • 笛卡尔积:笛卡尔乘积是指在数学中,两个集合A 和 B的所有组合情况。(在多表查询时,需要消除无效的笛卡尔积)

    在这里插入图片描述

  • 多表查询分类

    • 连接查询

      内连接:相当于查询A、B交集部分数据

      外连接:

      ​ 左外连接:查询左表所有数据,以及两张表交集部分数据

      ​ 右外连接:查询右表所有数据,以及两张表交集部分数据

      自连接:当前表与自身的连接查询,自连接必须使用表别名

    • 子查询

内连接

内连接查询的是两张表交集的部分

在这里插入图片描述

内连接查询语法:

条件不匹配的不会被查询出来

  • 隐式内连接

    select 字段列表 from 表1,表2 where 条件 ...;
    
  • 显式内连接

    select 字段列表 from 表1 [inner] join 表2 on 连接条件 ...;
    

外连接

在这里插入图片描述

外连接语法:

  • 左外连接

    select 字段列表 from 表1 left [outer] join 表2 on 条件 ...;
    

    相当于查询表1(左表)的所有数据 包含 表1和表2交集部分的数据

    这是多表查询,除了要显示左表的所有字段外,还要显示右表的字段,所以:左外连接 != 直接查询左表的所有数据

  • 右外连接

    select 字段列表 from 表1 right [outer] join 表2 on 条件 ...;
    

    相当于查询表2(右表)的所有数据 包含 表1和表2交集部分的数据

自连接

案例:查询员工 及其 所属领导的名字
在这里插入图片描述

自连接查询语法:

select 字段列表 from 表A 别名A join 表A 表名B on 条件 ...;

select 字段列表 from 表A 别名A,表A 表名B where 条件...;

自连接必须起别名。

自连接查询,可以是内连接查询,也可以是外连接查询。

联合查询-union,union all

对于union查询,就是把多次查询的结果合并起来,形成一个新的查询结果集。

select 字段列表 from 表A ...
union [all]
select 字段列表 from 表B ...;
  • 对于联合查询的多张表的列数必须保持一致,字段类型也需要保持一致
  • union all 会将全部的数据直接合并在一起,union 会对合并之后的数据去重。

子查询

  • 概念:SQL语句中嵌套select语句,称为嵌套查询,又称子查询

    select * from t1 where column1 = (select column1 from t2);
    

    子查询外部的语句可以是 insert / update / delete / select 的任何一个。

  • 根据子查询结果不同,分为:

    • 标量子查询(子查询结果为单个值)
    • 列子查询(子查询结果为一列)
    • 行子查询(子查询结果为一行)
    • 表子查询(子查询结果为多行多列)
  • 根据子查询位置,分为:where之后、from之后、select之后。

标量子查询

子查询返回的结果是单个值(数字、字符串、日期等),最简单的形式,这种子查询称为标量子查询

  • 常见的操作符号:= <> > >= < <=

列子查询

子查询返回的结果是一列(可以是多行),这种子查询称为列子查询

  • 常用的操作符:in、not in、any、some、all

    操作符 描述
    in 在指定的集合范围之内,多选一
    not in 不在指定的集合范围之内
    any 子查询返回列表中,有任意一个满足即可
    some 与any等同,使用some的地方都可以使用any
    all 子查询返回的列表的所有值都必须满足

行子查询

子查询返回的结果是一行(可以是多列),这种子查询称为行子查询

  • 常用的操作符:=、<>、in、not in

表子查询

子查询返回的结果是多行多列,这种子查询称为表子查询

  • 常用的操作符:in

多表查询练习

48. 基础-多表查询-练习1_哔哩哔哩_bilibili

多表查询写法不固定,很多场景下可以通过子查询来实现,也可以通过多表联查来实现

事务

  • 事务简介
  • 事务操作
  • 事务四大特性
  • 并发事务问题
  • 事务隔离级别

事务简介

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

在这里插入图片描述

默认MySQL的事务是自动提交的,也就是说,当执行一条DML语句,MySQL会立即隐式的提交事务。

事务操作

注意:MyISAM存储引擎是不支持事务处理的。

事务控制可以两种方式:

  1. 将事务改为手动提交,通过commit提交事务 或 rollback回滚事务。
  2. 通过start transaction 或 begin显式的来开启事务,事务执行操作完成之后,如果事务执行成功 执行commit,如果失败执行rollback指令。
  • 查看/设置事务提交方式

    select @@autocommit;		-- 查看事务的提交方式 1自动 0手动(会话参数,只针对当前窗口有效)
    set @@sutocommit = 0;
    
  • 提交事务

    commit;
    
  • 回滚事务

    rollback;
    
  • 开启事务

    start transaction 或 begin;
    

事务的四大特性

  • 原子性:事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
  • 一致性:事务完成时,必须使所有的数据都保持一致状态。
  • 隔离性:数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
  • 持久性:事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。

并发事务问题

多个并发事务在执行的过程当中,所出现的脏读、不可重复读、幻读的问题。

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

不可重复读两次查询结果不同,前提事务B提交了,没提交那叫脏读。

事务的隔离级别

用来解决并发事务所引发的问题的。

注意:事务隔离级别越高,数据越安全,但是性能越低。

隔离级别 脏读 不可重复读 幻读
read uncommitted
read committed ×
repeatable read(默认) × ×
serializable × × ×

√:表示会出现这种问题

×:表示已解决这种问题

repeatable read(MySQL的默认事务隔离级别)

  • 查看事务隔离级别

    -- MySQL8.0+
    select @@transaction_isolation;		-- 查看当前会话隔离级别
    select @@global.transaction_isolation;		-- 查看系统当前隔离级别
    
    -- MySQL5.0+
    select @@tx_isolation;		-- 查看当前会话隔离级别
    select @@global.tx_isolation;		-- 查看系统当前隔离级别
    
  • 设置事务隔离级别

    set [session|global] transaction isolation level {read uncommitted | read committed | repeatable read | serializable}
    

总结

  1. 事务简介

    事务是一组操作的集合,这组操作,要么全部执行成功,要么全部执行失败。

  2. 事务操作

    start transaction;		-- 开启事务
    commit / rollback;		-- 提交/回滚事务
    
  3. 事务四大特性(ACID)

    原子性(Atomicity)、一致性(Consistency)、隔离性 (Isolation)、持久性(Durability)

  4. 并发事务问题

    脏读、不可重复读、幻读

  5. 事务隔离级别

    read uncommitted | read committed | repeatable read | serializable
    

MySQL进阶篇

  • 存储引擎
  • 索引
  • SQL优化
  • 视图/存储过程/触发器
  • InnoDB引擎
  • MySQL管理

存储引擎

  • MySQL体系结构
  • 存储引擎简介
  • 存储引擎特点
  • 存储引擎选择

MySQL体系结构

在这里插入图片描述

MySQL5.5之后版本默认存储引擎为 InnoDB

  • 连接层

    最上层是一些客户端和链接服务,主要完成一些类似于连接处理、授权认证、及相关的安全方案。服务器也会为安全接入的每个客户端验证它所具有的操作权限。

  • 服务层

    第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现,如:过程、函数等。

  • 引擎层

    存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需求,来选择合适的存储引擎。

  • 存储层

    主要是将数据存储在文件系统之上,并完成与存储引擎的交互。

存储引擎简介

存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表的,而不是基于库的,所以存储引擎也可被称为表类型。

  1. 在创建表时,指定存储引擎

    create table 表名(
    	字段1 字段1类型 [comment 字段1注释],
        ...
        字段n 字段n类型 [comment 字段n注释]
    )engine = innodb [comment 表注释];
    
  2. 查看当前数据库支持的存储引擎

    show engines;
    

存储引擎特点

InnoDB

  • 介绍:

    InnoDB是一种兼顾高可靠性和高性能的通用存储引擎,在MySQL 5.5之后,InnoDB是默认的存储引擎。

  • 特点

    DML操作遵循ACID模型,支持 事务

    行级锁,提高并发访问性能;

    支持 外键 foreign key 约束,保证数据的完整性和正确性;

  • 文件

    xxx.ibd:xxx代表的是表名,innoDB引擎的每张表都会对应这样一个表空间文件,存储该表的表结构(frm、sdi)、数据和索引。

    早期表结构都存储在frm文件中,MySQL 8.0之后表结构都存储在sdi这个数据字典当中,sdi有融入了idb表空间文件当中。

    参数:innodb_file_per_table【该参数决定是多张表共用一个共享空间,还是说每一张表都对应一个表空间文件】

  • Innodb的逻辑存储结构

    在这里插入图片描述

MyISAM

  • 介绍

    MyISAM是MySQL早期的默认存储引擎。

  • 特点

    不支持事务,不支持外键

    支持表锁,不支持行锁

    访问速度快

  • 文件

    xxx.frm / xxx.sdi(表结构文件):存储表结构信息 【MySQL 8.0之后.frm文件变成了.sdi文件

    xxx.MYD(数据文件):存储数据

    xxx.MYI(索引文件):存储索引

    在这里插入图片描述

Memory

  • 介绍

    Memory引擎的表数据是存放在内存中的,由于受到硬件问题、或断电问题的影响,只能将这些表作为临时表或缓存使用。

  • 特点

    内存存放

    hash索引(默认)

  • 文件

    xxx.sdi:存储表结构信息

    因为数据都是存放在内存当中的,.sdi文件只存放表结构的数据

三种存储引擎的区别

特点 InnoDB MyISAM Memory
存储限制 64TB
事务安全 支持 - -
锁机制 行锁 表锁 表锁
B+tree索引 支持 支持 支持
Hash索引 - - 支持
全文索引 支持(5.6版本之后) 支持 -
空间使用 N/A
内存使用 中等
批量插入速度
支持外键 支持 - -

存储引擎选择

在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合。

  • InnoDB:支持事务、外键。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询之外,还包含很多的更新、删除操作,那么InnoDB存储引擎是比较合适的选择。
  • MyISAM:如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高,那么选择这个存储引擎是非常合适的。
  • Memory:将所有数据保存在内存中,访问速度快,通常用于临时表及缓存。Memory的缺陷就是对表的大小有限制,太大的表无法在内存中,而且无法保障数据的安全性。

总结

  1. 体系结构

    连接层、服务层、引擎层、存储层

  2. 存储引擎简介

    show engines;		-- 查看当前数据库支持的存储引擎
    create table xxx(...)engine=innodb;		-- 建表时指定存储引擎
    
  3. 存储引擎特点

    Innodb 与 MyISAM:事务、外键、行级锁

  4. 存储引擎应用

    INNODB:存储业务系统中对于事务、数据完整性要求较高的核心数据。

    MYISAM:存储业务系统的非核心事务。

索引

  • 索引概述
  • 索引结构
  • 索引分类
  • 索引语法
  • SQL性能分析
  • 索引使用
  • 索引设计原理

索引概述

  • 介绍

    索引(index)是帮助MySQL 高效获取数据数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法,这种数据结构就是索引。

  • 演示

    在这里插入图片描述

    上诉二叉树索引结构只是一个示意图,并不是真实的索引结构。

    无索引全从上至下全表扫描,有索引效率

  • 优缺点

    优点 缺点
    提高数据检索的效率,降低数据库的IO成本 索引列也是要占用空间的
    通过索引对数据进行排序,降低数据排序的成本,降低CPU的消耗。 索引大大提高了查询效率,同时却也降低了更新表的速度,如对表进行 insert、update、delete,效率降低。

索引结构

MySQL的索引是在存储引擎层实现的,不同的存储引擎有不同的结构,主要包含一下几种:

索引结构 描述
B+Tree索引 最常见的索引类型,大部分引擎都支持 B+树 索引
Hash索引 底层数据结构是用哈希表实现的,只有精确匹配索引列的查询才有效,不支持范围查询
R-tree(空间索引) 空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少
Full-text(全文索引) 是一种通过建立倒排索引,快速匹配文档的方式。类似于Lucene,Solr,ES

不同存储引擎,索引结构的支持情况:

索引 InnoDB MyISAM Memory
B+tree 索引 支持 支持 支持
Hash 索引 不支持 不支持 支持
R-tree 索引 不支持 支持 不支持
Full-text 5.6版本之后支持 支持 不支持

我们平常所说的索引,如果没有特别指明,都是指 B+树结构组织的索引。

索引结构-Btree

11. 进阶-索引-结构-Btree_哔哩哔哩_bilibili

  • 二叉树

    在这里插入图片描述

    二叉树缺点:顺序插入时,会形成一个链表,查询性能大大降低。大数据量的情况下,层级较深,检索速度慢。

    红黑树:大数据量情况下,层级越深,检索速度越慢。

  • B-tree(多路平衡查找树)

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

    在这里插入图片描述

    树的度数指的是一个节点的子节点个数。

    数据结构动态变化的过程可参考网站:https://www.cs.usfca.edu/~galles/visualization/Btree.html

索引结构-B+tree

以一一颗最大度数为4的 b+tree 为例:

在这里插入图片描述

相对于B-tree区别:

  1. 所有的数据都会出现在叶子节点
  2. 叶子节点形成一个单向链表

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

在这里插入图片描述

索引结构-Hash

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

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

在这里插入图片描述

Hash索引特点:

  1. hash索引只能用于对等比较(=,in),不支持范围查询(between,<,>,…)
  2. 无法利用索引完成排序操作
  3. 查询效率高,通常只需要一次检索就可以了,效率通常要高于B+tree索引

存储引擎支持:

  • 在MySQL,支持hash索引的是Memory引擎,而InnoDB中具有自适应hash功能,hash索引是存储引擎根据B+tree索引在指定条件下自动构建的。

索引结构-思考

为什么InnoDB存储引擎选择使用B+tree索引结构?

  • 相对于二叉树,层级更少,搜索效率更高;
  • 对于B-tree,无论是叶子节点还是非叶子节点,都会保存数据,这样导致一页中存储的键值减少,指针跟着减少,要保存同样大量数据,只能增加树的高度,导致性能降低;对于B+tree,不管查找哪个数据,都要到叶子节点中找,搜索效率稳定,而且叶子节点形成一个双向链表,便于范围搜索和排序;
  • 相对于Hash索引,B+tree支持范围匹配及排序操作;

索引分类

分类 含义 特点 关键字
主键索引 针对于表中主键创建的索引 默认自动创建,只能有一个 primary
唯一索引 避免同一个表中某数据列中的值重复 可以有多个 unique
常规索引 快速定位特点数据 可以有多个
全文索引 全文索引查找的是文本中的关键词,而不是比较索引中的值 可以有多个 fulltext

在InnoDB存储引擎中,根据索引的存储形式,又可以分为以下两种:

分类 含义 特点
聚集索引 将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据 必须有,而且只有一个
二级索引 将数据与索引分开存储,索引结构的叶子节点关联的是对于的主键 可以存在多个

聚集索引选取规则:

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

在这里插入图片描述

在这里插入图片描述

思考

  1. 一下SQL语句,哪个执行效率高,为什么?

    select * from  user where id=10;
    select * from user where name='Aem';
    
    -- id为主键,name字段创建的有索引
    

    id查询效率高;根据id只需要到聚集索引中对比,根据name查,首先要到name字段对应的二级索引来查,查找到对应的id,再根据id到聚集索引中去查询整行数据。

  2. InnoDB主键索引的B+tree高度为多少?

    • 假设:

      一行数据大小为1k,一页中可以存储16行这样的数据。InnoDB的指针占用6个字节的空间,主键即使为bigint,占用字节数8。

    • 高度为2:

      n*8+(n+1)*6=16+1024,算出来n约为1170

      n:当前节点存储key的数量;16*1024:一页的大小16k,能存多少字节

      1171*16=18736

      1171个指针对应1171个子节点,一个节点存储16行数据。

    • 高度3:

      1171*1171*16=2193985

      InnoDB即使你存储两千多万的记录,树的结构才三层,所以所检索效率很高的

索引语法

  • 创建索引

    create [unique | fulltext] index index_name on table_name(index_col_name,...);
    
  • 查看索引

    show index from table_name;
    

    注意:什么索引类型;哪个字段;什么数据结构

  • 删除索引

    drop index index_name on table_name
    

案例展示:https://www.bilibili.com/video/BV1Kr4y1i7ru?t=213.0&p=74

SQL性能分析

SQL执行频率

MySQL客户端连接成功之后,通过show [session|global] status命令可以提供服务器状态信息。通过如下指令,可以查看当前数据库的 insert、update、delete、select的访问频次:

show global status like 'com_______';		# 7个下划线 代表7个字符

在这里插入图片描述

慢查询日志

慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志。

MySQL的慢查询日志默认没有开启,需要在MySQL配置文件(/etc/my.cnf)中配置如下信息:

# 开启MySQL慢查询日志查询开关
show_query_log=1
# 设置慢查询日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志
long_query_time=2

配置完毕之后,通过以下指令重启MySQL服务进行测试,查看慢查询日志文件中的记录信息 /var/lib/mysql/localhost-show.log

profile详情

show profiles 能够在做SQL优化时帮助我们了解时间都耗费到哪里了。通过have_profiling参数,能够看到当前MySQL是否支持profile操作:

select @@have_profiling;

默认profiling是关闭的,可以通过set语句在session/global级别开启profiling:

set profiling=1;

执行一系列的业务SQL的操作,然后通过如下指令查看指令的执行耗时:

-- 查看每一条SQL的耗时基本情况
show profiles;

-- 查看指定query_id的SQL语句各个阶段的耗时情况
show profile for query query_id;

-- 查看指定query_id的SQL语句CPU的使用情况
show profile cpu for query query_id;

explain执行计划

explain 或者 desc命令获取MySQL如何执行select语句的信息,包括在select语句执行过程中表如何连接和连接的顺序。

语法:

-- 直接在select语句之前加上关键字explain/desc
explain select 字段列表 from 表名 where 条件;

在这里插入图片描述

explain执行计划各字段含义:

  • id

    select 查询的序列号,表示查询中执行select子句或者是操作表的顺序(id相同,执行顺序从上至下;id不同,值越大,越先执行)

    多表查询有多条记录,多个id

  • select_type

    表示select的类型,常见的取值有simple(简单表,即不使用表连接或者子查询)、primary(主查询,即外层的查询)、union(union中的第二个或者后面的查询语句)、subquery(select/where之后包含了子查询)等

  • type

    表示连接类型,性能由好到差的连接类型为null、system、const、eq_ref、ref、range、index、all。

    不访问任何表为null;system:访问系统表;const:根据主键,唯一索引访问;ref:根据非唯一性索引访问

  • possible_key

    显示可能应用在这张表上的索引,一个或多个。

  • key

    实际使用的索引,如果为null,则没有使用索引

  • key_len

    表示索引中使用的字节数,该值为索引字段最大可能长度,并非实际使用长度,在不损失精度的前提下,长度越短越好。

  • rows

    MySQL认为必须要执行查询的行数,在innodb引擎的表中,是一个估量值,可能并不准确。

  • filtered

    表示返回结果的行数占需读取行数的百分比,filtered的值越大越好。

索引使用

验证索引效率

22. 进阶-索引-使用规则-验证索引效率_哔哩哔哩_bilibili

最左前缀法则

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

23. 进阶-索引-使用规则-最左前缀法则_哔哩哔哩_bilibili

索引失效情况

SQL提示

SQL提示,是优化数据库的一个主要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的。

  • use index 告诉数据库使用哪个索引

    explain select * from tb_user use index(id_user_pro) where profession='软件工程';
    

    只是建议MySQL使用指定的索引,具体还得看这个索引快不快,MySQL接不接受

  • ignore index 告诉数据库忽略哪个索引

    explain select * from tb_user ignore index(id_user_pro) where profession='软件工程';
    
  • force index 强制数据库必须使用哪个索引

    explain select * from tb_user force index(id_user_pro) where profession='软件工程';
    

覆盖索引

尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到),减少select *

27. 进阶-索引-使用规则-覆盖索引&回表查询_哔哩哔哩_bilibili

using index condition:查找使用了索引,但是需要回表查询数据

using where;using index:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询数据

思考:

  • 一张表,有四个字段(id、username、password、status),由于数据量大,需要对以下SQL语句进行优化,该如何进行才是最佳方案:

    select id,username,password from tb_user where username='Tom';
    
    -- 针对username、password这两个字段建立联合索引【联合索引就是二级索引,叶子节点下挂的就是id】
    -- 这个时候再执行这条语句时直接覆盖索引不用回标查询
    

前缀索引

当字段类型为字符串(varchar、text等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO,影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。

  • 语法

    create index idx_xxx on table_name(column(n));	-- n表示提取这个字符串的前面n个字符建立索引
    
  • 前缀长度

    可以根据索引字段的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高查询效率越高,唯一索引的选择性是1,这是最好的索引选择性,性能也最好。

    -- 计算选择性公式:不重复数/数据表记录总数
    select count(distinct email)/count(*) from tb_user;		-- distinct 去重
    select count(distinct substring(email,1,5))/count(*) from tb_user; 	-- substring 截取字符串
    
  • 前缀索引查询流程

    28. 进阶-索引-使用规则-前缀索引_哔哩哔哩_bilibili

单列索引与联合索引

单列索引:即一个索引只包含单个列。

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

在业务场景中,如果存在多个查询条件,考虑针对于查询字段建立索引时,建议建立联合索引,而非单列索引。

索引设计原则

  1. 针对于数据量较大,且查询比较频繁的表建立索引。

    上百万数据,几千条不用索引效率也高

  2. 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引。

  3. 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度高,使用索引的效率越高。

  4. 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。

  5. 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。

    最左前缀法则

  6. 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价就越大,会影响增删改的效率。

  7. 如果索引列不能存储NULL值,请在创建表时用not null约束它。当优化器知道每列是否包含null值时,它可以更好的确定哪个索引最有效地用于查询。

索引总结

  1. 索引概述

    索引是高效获取数据的数据结构;

  2. 索引结构

    B+tree

    所有数据都会出现在叶子节点,叶子节点形成一个双向链表。

    Hash

    Memory存储引擎支持,一个哈希表,检索性能很高,只需计算出字段值的哈希值,然后定位到对应数据,如果存在对应的哈希碰撞,需要沿着链表再找到对应数据。缺点:只支持精确匹配,不支持范围查询,以及索引的排序。

  3. 索引分类

    两个层面的分类

    第一个:主键索引、唯一索引、常规索引、全文索引

    第二个:innodb存储引擎中,根据索引的存储结构分为:聚集索引、二级索引

    聚集索引B+tree叶子节点挂的是这一行的数据,而二级索引叶子节点挂的是对应的主键

    • 聚集索引

      innodb存储引擎的表当中必须存在,而且只有一个

      默认主键就是聚集索引,如果没有主键,选择第一个唯一索引作为聚集索引;如果既没有主键,又没有唯一索引,MySQL会自动生成一个隐藏的rowID作为聚集索引。

    • 二级索引

  4. 索引语法

    -- 如何创建索引、删除索引、查看索引
    create [unique] index xxx on xxx(xxx);
    show index from xxx;
    drop index xxx on xxx;
    
  5. SQL性能分析

    执行频次、慢查询日志、、profile、explain

  6. 索引使用

    联合索引

    使用联合索引时,遵循最左前缀法则

    索引失效

    1. 在索引列上进行函数运算,索引失效
    2. 字符串不加引号,造成隐式类型转换,索引失效
    3. like模糊匹配,前面加了占位符,索引失效
    4. or连接的条件,一侧有索引,另一侧没有,索引失效
    5. 如果MySQL评估,走全表扫描比走索引更快,索引失效

    SQL提示

    指的是执行select语句的时候,如果有很多索引,MySQL会根据内部的策略来选择使用哪个索引,此时也可以给MySQL提示,告诉它用哪个索引、忽略哪个索引、强制使用哪个索引。

    覆盖前缀

    查询返回的列在索引当中都包含了,不需要回表查询;回表查询指的是在查询的时候先走二级索引检索到这行数据的id,再根据id到聚集索引查询到这行的数据。

    前缀索引

    适用场景:当遇到一些字符串长度较长,或大文本字段时,针对它建立联合索引,缩小索引体积,提高检索效率

    单列/联合索引

    推荐使用联合索引,因为:联合索引性能较高,如果联合索引使用得当,可以避免回表查询

  7. 索引设计原则

    针对哪些表建立索引?

    数据量大、查询频次高

    针对这些表的哪些字段建立索引?

    经常在 where、order by、group by 之后出现的字段建立索引

    针对于这些字段建立什么样的索引?

    如果这列是唯一的,建立唯一索引,区分度高;如果能建立联合索引,尽量使用联合索引;如果涉及到字符串长度较长或者大文本字段,尽量使用前缀索引。

SQL优化

  • 插入数据
  • 主键优化
  • order by 优化
  • group by 优化
  • limit 优化
  • count 优化
  • update 优化

插入数据

insert 优化

  • 批量插入

    insert into 表名 values(1,'Tom'),(2,'Cat'),(3,'GC');
    

    如果批量插入,一次性插入的数据,不建议超过一千条;如果插入几万条数据,可以将其分割为多条insert语句。

  • 手动提交事务

    start transaction;
    insert into 表名 values(1,'A'),(2,'B'),(3,'C');
    insert into 表名 values(4,'D'),(5,'E'),(6,'F');
    insert into 表名 values(7,'G'),(8,'H'),(9,'I');
    commit;
    
  • 主键顺序插入

    主键乱序插入:3,8,2,77,62,10

    主键顺序插入:1,2,3,10,23,77

    顺序插入的性能高于乱序插入

大批量插入数据

如果一次性需要插入大批量数据,使用insert语句插入性能较低,此时可以使用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';		-- 字段值之间使用,分割,每一行数据之间使用\n分割

主键顺序插入性能高于乱序插入

主键优化

33. 进阶-SQL优化-主键优化_哔哩哔哩_bilibili

数据组织方式

在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表(IOT)

页分裂

页可以为空,也可以填充一半,也可以填充100%。每个页包含了2~n行数据(如果一行数据过大,会行溢出),根据主键排列。

主键顺序插入:

在这里插入图片描述

主键乱序插入:

在这里插入图片描述

50该插入页1,但已满。然后页1分裂,后面数据移到页3,50接上,再重新对链表指针进行设置

在这里插入图片描述

页合并

当删除一行记录时,实际上记录并没有被物理删除,只是记录被标记(flaged)为删除并且它的空间变得允许被其它记录声明使用。

当页中删除的记录达到 MERGE_THRESHOLD(默认为页的50%),InnoDB会开始寻找最靠近的页(前或后),看看是否可以将两个页合并以优化空间使用。

在这里插入图片描述

在这里插入图片描述

MERGE_THRESHOLD:合并页的阈值,可以自己设置,在创建表或者创建索引时指定。

主键设计原则

  • 满足业务需求的情况下,尽量降低主键的长度。

    聚集索引只有一个,二级索引可以有多个;二级索引叶子节点挂的就是数据的主键,如果主键长度较长,二级索引较多,将会占用大量的磁盘空间,在搜索的时候耗费大量的磁盘IO。

  • 插入数据时,尽量选择顺序插入,选择使用auto_increment自增主键。

    如果是主键顺序插入,第一个数据页写满了,再写下一个数据页;如果是主键乱序插入,可能会存在页分裂现象。

  • 尽量不要使用UUID做主键或者是其它自然主键,如身份证号。

    每次生成的UUID是无序的,每次插入就可能会存在页分裂;而且长度相对较长,检索时耗费大量磁盘IO

  • 业务操作时,避免对主键的修改。

    修改主键,还需要动对应的索引结构。

order by 优化

34. 进阶-SQL优化-order by优化_哔哩哔哩_bilibili

MySQL中的排序方式有两种: 优化尽量往using index优化

  1. using filesort:通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。
  2. using index:通过有序索引顺序扫描直接返回有序数据,这种情况叫 using index,不需要额外排序,操作效率高。

总结:

  • 根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则。

  • 尽量使用覆盖索引。

    覆盖索引,不需要额外的查询;没有用到覆盖索引,需要回表查询数据,然后在排序缓冲区中对数据进行排序,排序方式为using filesort,效率低。

  • 多字段排序,一个升序一个降序,此时需要注意联合索引在创建时的规则(ASC/DESC)

  • 如果不可避免地出现了filesort,大数据量排序时,可以适当增大排序缓冲区的大小 sort_buffer_size(默认256k)

group by 优化

35. 进阶-SQL优化-group by优化_哔哩哔哩_bilibili

  • 在分组操作时,可以通过索引来提高效率。
  • 分组操作时,索引的使用也是满足最左前缀法则的。

limit 优化

36. 进阶-SQL优化-limit优化_哔哩哔哩_bilibili

一个常见又非常头痛的问题就是 limit 2000000,10,此时需要MySQL排序前2000010记录,仅仅返回2000000~2000010的记录,其它记录丢弃,查询排序的代价非常大。

优化思路:一般分页查询时,通过创建 覆盖索引 能够比较好地提高性能,可以通过覆盖索引加子查询形式进行优化。

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

将查询结果id当作一张表,与tb_sku表进行连接查询,取出表中对应id地数据。

count 优化

聚合函数count():统计数量

优化思路:自己计数

explain select count(*) from 表名;
  • MyISAM 引擎把一个表的总行数存在了磁盘上,因此执行count(*)的时候会直接返回这个数,效率很高。

    前提是查询时后面没有where条件,直接查询这张表的总数据量;后面有条件的话MyISAM也是比较慢的

  • InnoDB 引擎就麻烦了,它执行count(*)的时候,需要把数据一行一行的从引擎里读出来,然后累积计数。


count的几种用法

  • count() 是一个聚合函数,对于返回的结果集,一行行地判断,如果 count 函数的参数不是null,累计值就加1,否则不加,最后返回累计值。

  • 用法:count(*)、count(主键)、count(字段)、count(3)【count某一个数】

    • count(主键)

      InnoDB引擎会遍历整张表,把每一行的主键id值都取出来,返回给服务层。服务层拿到主键后,直接按行进行累加计数(主键不可能为null)。

    • count(字段)

      没有not null约束:InnoDB引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,服务层判断是否为null,不为null,计数累加。

      有not null约束:InnoDB引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,直接按行进行累加计数。

    • count(1)

      InnoDB引擎遍历整张表,但不取值。服务层对与返回的每一行,放一个数字 1 进去,直接按行进行累加。

      放什么数字不重要,0也行、-1也行

    • count(*)

      InnoDB引擎并不会把全部字段取出来,而是专门做了优化,不取值,服务层直接按行进行累加。

  • 按照效率排序:count(字段) < count(主键) < count(1) ≈ count(*),所以尽量使用count(*)

update优化(避免行锁升级为表锁)

38. 进阶-SQL优化-update优化(避免行锁升级为表锁)_哔哩哔哩_bilibili

**InnoDB的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则会从行锁升级为表锁。**一旦升级为表锁,并发性能降低。

SQL优化总结

对于SQL优化,绝大部分情况下都是在考虑对索引进行优化

  1. 插入数据

    insert:批量插入、手动控制事务、主键顺序插入

    大批量插入:load date local infile

  2. 主键优化

    主键长度尽可能地短、顺序插入

    主键策略:auto_increment(主键自增)、UUID【推荐使用主键自增】

    主键自增长度相对较短,而且是顺序插入;UUID生成的每个UUID是无序的(乱序插入可能导致也分裂现象,效率比顺序插入低)、而且长度较长

  3. order by 优化

    尽量使用覆盖索引,涉及到的排序字段尽量建立对应索引;排序时同为升序或降序,是可以用到索引,但如果一升一降可以用索引,但是创建索引时,需指定排序方式。

    using index:直接通过索引返回数据,性能高

    using filesort:需要将返回的结果在排序缓冲区排序

  4. group by 优化

    索引,多字段分组满足最左前缀法则

  5. limit 优化

    覆盖索引 + 子查询

  6. count 优化

    性能:count(字段) < count(主键) < count(1) ≈ count(*),所以尽量使用count(*)

  7. update 优化

    尽量根据主键/索引字段进行数据更新

    根据索引字段更新,避免行锁升级为表锁,降低数据库的并发访问性能

视图/存储过程/触发器

  • 视图
  • 存储过程
  • 存储函数
  • 触发器

视图

  • 介绍

    视图(View)是一种虚拟存在的表。这张表在数据库中并不实际存在,行和列的数据来自于定义视图时使用的表(基表),并且是在使用视图时动态生成的。

    通俗的讲,视图只保存了查询的SQL逻辑,不保存查询结果。所以在创建视图时,主要工作就落在创建这条SQL查询语句上。

  • 创建

    create [or replace] view 视图名称[(列表名称)] as select语句 [with [cascaded | local] check option]
    

    [or replace]:替换视图

  • 查询

    -- 查看创建视图的语句
    show create view 视图名称;
    -- 查看视图数据
    select * from 视图名称 ...;
    
  • 修改

    方式一:

    create or replace view 视图名称[(列名列表)] as select语句 [with [cascaded | local] check option] 
    

    方式二:

    alter view 视图名称[(列名列表)] as select语句 [with [cascaded | local] check option]
    
  • 删除

    drop view [if exists] 视图名称 [,视图名称] ...;
    

视图-检查选项

视图检查选项指的是:当我们在对视图当中的数据进行更新、插入、删除时,检查选项会去检查我们所操作的数据是否符合视图定义时的条件。

!!!不同MySQL版本之间的检查选项有不同 !!!

41. 进阶-视图-检查选项(cascaded)_哔哩哔哩_bilibili

  • 视图的检查选项

    当使用with check option子句创建视图时,MySQL会通过视图检查正在更改的每个行,例如 插入、更新、删除,以使其符合视图的定义。MySQL允许基于另一个视图创建视图,它还会检查依赖视图中的规则以保持一致性。为了确定检查范围,MySQL提供两个选项:cascaded 和 local,默认值为 cascaded。

    • cascaded:

      操作检查选项为cascade的视图时,还会检查这个视图依赖的所有视图的条件是否满足

      create view v1 as select id,name from student where id < 20;
      
      create view v2 as select id,name from v1 where id > 10 with cascaded check option;
      
    • local:

      当我们操作视图时,会递归的去找当前视图所依赖的视图,如果依赖的视图有检查选项,将会判断我们所操作的数据是否满足这个视图的条件,如果某一个视图没有检查选项,将忽略这个条件。

视图-更新及作用

  • 视图的更新

    要使视图可更新,视图中的行与基础表中的行之间必须存在一对一的关系。如果视图包含以下任何一项,则该视图不可更新:

    比如执行了聚合函数,导致基表中多行数据对应视图中的一行数据,此时不可更新

    1. 聚合函数或窗口函数
    2. distinct【该关键字作用:对数据表中一个或多个字段去重,只返回其中的一条数据给用户】
    3. group by【分组操作】
    4. having【分组之后的过滤】
    5. union 或者 union all
  • 作用

    • 简单

      视图不仅可以简化用户对数据的理解,也可以简化他们的操作。那些经常被使用的查询可以被定义为视图,从而使得用户不必为以后的操作每次指定全部条件。

    • 安全

      数据库可以授权,但不能授权到数据库特定行和特定的列上。通过视图用户只能查询和修改他们所能见到的数据。

    • 数据独立

      视图可以帮助用户屏蔽真实表结构变化带来的影响。

      比如基表某字段name变为了studentname,只需要对创建视图的语句进行更新,针对studentname起一个别名name,这样就屏蔽了基表的变化对业务的影响。

视图-案例

44. 进阶-视图-案例_哔哩哔哩_bilibili

存储过程

  • 介绍

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

    存储过程思想上很简单,就是数据库SQL语言层面的代码封装与重用。

  • 特点

    • 封装、复用
    • 可以接收参数,也可以返回数据
    • 减少网络交互,提升效率

基本语法

  • 创建

    create procedure 存储过程名称([参数列表])
    begin
    	-- SQL语句
    end;
    
  • 调用

    call 名称([参数]);
    
  • 查看

    select * from information_schama.routines where rountine_schema='xxx';		-- 查询指定数据库的存储过程及状态信息
    show create procedure 存储过程名称;		-- 查询某个存储过程的定义
    
  • 删除

    drop procedure [if exists] 存储过程名称;
    

注意:在命令行中,执行创建存储过程的SQL时,需要通过关键字 delimiter 指定SQL语句的结束符(默认分号)。

变量

系统变量

是MySQL服务器提供,不是用户定义的,属于服务器层面。分为全局变量(global)、会话变量(session)。

  • 查看系统变量

    show [session | global] variables;		-- 查看所有系统变量
    show [session | global] variables like '...';	-- 可以通过模糊匹配方式查找变量
    select @@[session | global] 系统变量名;		-- 查看指定变量的值
    
  • 设置系统变量

    set [session | global] 系统变量名 = 值;
    set @@[session | global] 系统变量名 = 值;
    
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MySQL学习笔记 的相关文章

  • 多个数据库连接

    我有三张桌子 categories content info and content The categories表包含类别的id及其 IDparent类别 The content info包含两列 entry id帖子的 ID 和cat
  • MySQL:记录之间的平均间隔

    假设这张表 id date 1 2010 12 12 2 2010 12 13 3 2010 12 18 4 2010 12 22 5 2010 12 23 如何仅使用 MySQL 查询找到这些日期之间的平均间隔 例如 此表上的计算将是 2
  • Python Twisted 和数据库连接

    我们的工作项目包括同步应用程序 短期 和异步 Twisted 应用程序 长期 我们正在重构我们的数据库 并将构建一个 API 模块来解耦该模块中的所有 SQL 我想创建该 API 以便同步和异步应用程序都可以使用它 对于同步应用程序 我希望
  • ER_NOT_SUPPORTED_AUTH_MODE:客户端不支持服务器请求的身份验证协议;考虑升级MySQL客户端

    Nodejs中使用mysql的问题 const mysql require mysql var connection mysql createConnection host localhost user root password 1234
  • PHP 数据库显示在具有不同锚标记的相同字段中

    我四处寻找 看看这是否可行 但却空手而归 首先 这是我的代码 div style display none div ul li li li li li li ul
  • 捕获动态表中 HTML 元素的值

    我有从数据库生成的以下动态表
  • 在关系数据库中存储树结构的已知方法有哪些? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • SQLAlchemy+pymysql 错误:sqlalchemy.util.queue.Empty

    尝试使用 Eclispse 在 Ubuntu 上运行 Python 2 SQLAlchemy 0 8 和 MySQL5 2 但我不断收到以下错误 我使用 pymysql 实际上是 pymysql3 引擎 模块监视器 from sqlalch
  • 将程序存储在 phpMyAdmin 中

    我必须将存储过程添加到 MySQL 数据库 问题是托管提供php我的管理员来管理数据库 我在网上搜索了一下 想法是运行创建程序的MySQL本机语句 但由于程序的代码通常可能有 我们必须更改 MySQL 中的分隔符 php我的管理员没有这个选
  • 从 datagridview 选定的行更新 mysql 数据库

    我有一个 datagridview 它在表单加载事件上加载 mysql 数据库表 t pi clients 并且我有另一个选项卡 其中包含 t pi client 相应列的文本框 它能够从 fullrowselect 模式获取数据到这些文本
  • 使用 RMySQL 会干扰 RPostgreSQL

    我有一个 R 脚本 我想从 MySQL 数据库中提取一些数据 然后从 PostgreSQL 数据库中提取一些数据 但是 从 RMySQL 加载 MySQL 驱动程序会阻止我从以下位置加载 PostgreSQL 驱动程序 PostgreSQL
  • Java MYSQL/JDBC 查询从缓存的连接返回过时的数据

    我一直在 Stackoverflow 中寻找答案 但似乎找不到不涉及 Hibernate 或其他数据库包装器的答案 我直接通过 Tomcat 6 Java EE 应用程序中的 MYSQL 5 18 JDBC 驱动程序使用 JDBC 我正在缓
  • 优化mysql中日期类型字段的查询

    我目前准备了以下查询 select sum amount as total from incomes where YEAR date 2019 and MONTH date 07 and incomes deleted at is null
  • mysql中的按位移位

    如何在 MySQL 中进行按位移位 有没有具体的指令或者操作符 如果不是 如何最佳地模拟它 看一下按位运算符MySQL first http dev mysql com doc refman 5 0 en bit functions htm
  • 删除 mysql 数据库中超过 3 个月的行的作业

    我们使用 mysql 服务器作为集中式日志系统 我希望有一项工作来定期删除 清理超过 3 个月的表条目 做这个的最好方式是什么 提前致谢 hinling 您是否在字段中存储项目的创建日期 If so DELETE FROM myTable
  • MySQL 获取时间优化

    o我有一个包含 200 万个寄存器的表 但它很快就会增长得更多 基本上 该表包含具有相应描述符的图像的兴趣点 当我尝试执行选择在空间上靠近查询点的点的查询时 总执行时间花费太长 更准确地说 持续时间 获取 0 484 秒 27 441 秒
  • grails/mysql 时区更改

    完成更改应用程序时区的最佳方法是什么 在我看来 必须发生以下情况 服务器 TZ 已被系统管理员更改 mysql必须重新启动 数据库中每个基于时间的列都必须使用convert tz 或等效方法更新所有值 因此 要么必须编写一个 mysql 脚
  • 将错误保存到 MySQL 数据库

    我有一个 php 查询来更新 MySQL 数据库 请参见下文 sql update hr payroll set payroll number payroll number tax code tax bacs ref bacs ref pa
  • MySQL 将 ÅäÖ 视为 AAO?

    这两个查询给了我完全相同的结果 select from topics where name Harligt select from topics where name H rligt 这怎么可能 看起来mysql在搜索时会将 翻译成aao
  • 数据库中的持久日期不等于检索日期

    我有一个具有 Date 属性的简单实体类 此属性对应于 MySQL 日期时间列 Entity public class Entity Column name start date Temporal TemporalType TIMESTAM

随机推荐

  • 接口测试用例怎么写?一文1600字教你写一个优秀的接口测试的测试用例

    一 用例设计1 1 接口测试概念 接口测试 测试系统间接口的一种测试 测试的对象主要是接口 主要是测试外部系统与所测系统之间以及内部系统之间的交互点 2 接口测试方法 a 可以通过开发脚本代码进行测试 b 可以通过开源免费的接口调用调试工具
  • 虚拟文件系统 (VFS)-基于linux3.10

    引言 虚拟文件系统 VFS VirtualFileSystem 介于具体的文件系统和C库之间 其用提供一个统一的方法来操作文件 目录以及其它对象 其能够很好的抽象具体的文件系统 在linux上具体的文件系统主要分为三类 l 基于非易失性的存
  • 【CV with Pytorch】第 4 章 :构建图像分割模型

    我们周围的图像有不同的纹理 图案 形状和大小 它们携带着大量的信息 这些信息很容易被人眼和大脑理解 但计算机却不太容易理解 图像分割是一个问题集 我们试图训练计算机理解图像 以便它们可以分离不同的对象并将相似的对象分组 这可以是类似像素强度
  • 在远程服务器上执行本地的shell脚本

    1 使用ssh实现 ssh user hostname C bin bash lt test sh 2 使用expect实现 采用的策略就是先在本地通过expect把shell脚本推送到远程服务器上 之后再用expect模拟登录之后 先给远
  • Join中on条件是null的问题讨论

    MySQL dbs gt select from test1 id stu id stu age 1 1 25 2 1 NULL 2 rows in set 0 00 sec MySQL dbs gt select from test2 i
  • 【Vue + Koa 前后端分离项目实战9】使用开源框架==>快速搭建后台管理系统 -- part9 项目总结

    去读书 去学一门手艺 去做任何自己喜欢的事 永远不会晚 才不会辜负这份人生 本博客教学视频来源于imoom 0到1快速构建自己的后台管理系统 课程 官方演示地址 https talelin com 目录 一 项目介绍 1 技术准备 2 学到
  • doris tips

    1 schema表格式字段长度 如果是数字 字母这种的长度等于hive sql里面 length variable 的长度 如果是中文要占3 4个Char 2 表增加分区 可以通过脚本自己构造多个sql 语句 类似 ALTER TABLE
  • 【C语言】rand()函数(如何生成指定范围随机数)

    一 rand 函数简介 我们先来看一下cplusplus com The C Resources Network网站上rand函数的基本信息 系统生成随机数时需要使用rand函数 rand 会返回一个范围在0到RAND MAX 32767
  • groovy语言单元测试(spock)

    一 spock groovy单元测试的五种情况 单元测试 given mock单测中指定mock数据 模拟入参 when 触发行为 比如调用指定方法或函数 then 做出断言表达式 expect 期望的行为 when then的精简版 si
  • Linux的IO端口和IO内存

    Linux的IO端口和IO内存 分类 linux编程 2011 01 14 13 22 866人阅读 评论 1 收藏 举报 io linux linux内核 struct 平台 x86 CPU对外设端口物理地址的编址方式有两种 一种是IO映
  • 外界访问ubuntu端口bug解决

    跟着视频教程在ubuntu上部署了nacos 但是主机无法访问8848端口 虚拟机可以 检查防火墙以及8848端口 防火墙已关闭 端口也是开放的 virtual machine practice nacos logs sudo ufw st
  • iframe和form表单实现ajax请求上传数据

    form的target属性设置为iframe的name值时 表示提交到url后返回的数据显示到iframe区域
  • DeepSORT(工作流程)

    关于多目标跟踪 DeepSORT是针对多目标跟踪的跟踪算法 有人可能会想 将传统的单目标跟踪算法直接用于多目标跟踪 一起对每一个目标进行单目标跟踪不可以吗 理论上似乎可行 但是实际应用中会发现 单纯的套用单目标跟踪算法用于多个目标进行跟踪的
  • vue2在element UI的table中给指定列添加圆点标志,鼠标悬浮出提示信息

    项目场景 要求在列表数据给指定数据添加一些标志 鼠标悬浮提示指定数据 左侧为标志截图 右侧为悬浮提示截图 HTML布局 在template中想要添加标志的那一列添加圆点和悬浮提示信息两个节点 并添加单元格进入 退出事件两个事件 div cl
  • 想转行做软件测试?快来看看你适不适合

    我们来讨论这样一个问题 软件测试适合什么样的人干 每个人有每个人的看法 A 男生 B 女生 C 有耐性不足的人 沟通能力不好 抗打击能力不强的 逻辑太混乱 没有思路的人 比较懒的人 学习能力不强而又不积极主动的人 D 认真 负责 仔细 有恒
  • 【自然语言处理】BERT 讲解

    有任何的书写错误 排版错误 概念错误等 希望大家包含指正 在阅读本篇之前建议先学习 自然语言处理 Seq2Seq 讲解 自然语言处理 Attention 讲解 自然语言处理 ELMo 讲解 自然语言处理 Transformer 讲解 BER
  • vb中的三目运算

    IIf 函数 根据表达式的值 来返回两部分中的其中一个 语法 IIf expr truepart falsepart IIf 函数的语法含有下面这些命名参数 部分 描述 expr 必要参数 用来判断真伪的表达式 truepart 必要参数
  • 用爱思助手自签名ipa文件成功后安装失败

    设备 iPad pro 2021 系统版本 ios15 6 1 ipa文件已签名成功
  • python 将数组中取某一值的元素全部替换为其他元素的方法

    这里的问题是在做House Price Prediction的时候遇到的 尝试对GarageArea做log转化 但是由于有些房子没有车库 所以GarageArea 0 再通过log转化变成 inf了 所以我想把所有 inf的数据全部再转化
  • MySQL学习笔记

    自己学习MySQL时整理的笔记 包括实操中遇到的问题 不同版本之间的差异 后续也会继续完善 有PDF文档版 学习视频 https www bilibili com video BV1Kr4y1i7ru t 1 9 怎么学 收获 基础篇 初级