mysql从入门到精通

基础SQL语句

DDL (Data Definition Language,数据定义语言)

数据库操作

-- 查询所有数据库
show DATABASES;

-- 查询当前选择的数据库
select DATABASE();

-- 创建数据库
create database itcast;
create database if not exists itcast default charset utf8mb4 collate utf8mb4_general_ci;  

-- 删除数据库
drop database if exists itcast;

-- 使用数据库
use itcast;

表操作

-- 查询当前数据库所有表
show tables;

-- 创建表
create table user (
	id int comment 'id',
	name varchar(128) comment '姓名',
	age int comment '年龄'
) comment '用户表';

-- 删除表
drop table if exists user;

-- 删除表并重新创建
truncate table user;

-- 查看表结构
desc emp;

-- 查看表创建语句
show create table emp;

-- 添加表字段
alter table user add column sex char(1) comment '性别';

-- 删除表字段
alter table user drop sex;

-- 修改表字段类型
alter table user modify sex varchar(1);

-- 替换表字段
alter table user change sex user_sex char(1) comment '性别';
数据类型

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

DML(Data Manipulation Language,数据操作语言)

-- 插入数据1( 指定字段)
insert into user (id, name, age, sex) values(1, 'm', 18, '1');

-- 插入数据2(全字段插入可以不写指定字段)
insert into user values (2, 'q', 19, '0');

-- 插入数据3(批量插入数据)
insert into user values (3, 'a', 20, '1'), (4, 'b', 21, '0'), (5, 'c', 22, '1');

-- 修改数据
update user set name = 'code' where id = 1;  -- 如果没有where条件,则修改整张表数据,慎重

-- 一条数据同时修改多个值
update user set name = 'horse', age = 17, sex = '1' where id = 1;

-- 删除数据
delete from user where id = 5; -- 没有where条件,则删除整张表数据

DQL (Data Query Language,数据查询语言)

select 基本查询

--
select * from user; -- 查询所有数据
select id, name, age, sex from user; 	-- 查询指定字段

-- 查询到的数据起别名:as 或 空格
select id as user_id, name user_name from user;

-- 去除重复数据
select distinct age from user;

where 条件查询

-- > 大于
select * from user where age > 18

-- >= 大于等于
select * from user where age >= 18;

-- < 小于
select * from user where age < 18;

-- <= 小于等于
select * from user where age <= 18;

-- = 等于
select * from user where age = 18;

-- <> 或 != 不等于
select * from user where age <> 18;
select * from user where age != 18;

-- BETWEEN ... AND ... 在某个范围之内(含最小、最大值)
select * from user where age between 18 and 26;

-- IN(...) 在in之后的列表中的值,多选一
select * from user where age in (18, 20, 22);

-- LIKE 占位符 模糊匹配( _:匹配单个字符,  %: 匹配任意个字符)
select * from user where name like '_';	-- 名字为任意一个字母
select * from user where name like '%a%' -- 名字含有a的;

-- 判断是否为null:is null
select * from user where name is null;

-- 判断不是null:is not null
select * from user where name is not null;

 -- 多个条件使用 and 和 or 隔开
 select * from user where age > 18 and age < 25;	--  18 < age < 25
 select * from user where age = 18 or age = 25;		-- age只能取到 18 或 25

聚合函数

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

-- count 统计数量
select count(*) from user;
 
-- max 最大值
select max(age) from user;

-- min 最小值
select min(age) from user;

-- avg 平均值
select avg(age) from user;

-- sum 求和
select sum(age) from user;

group by 分组查询

-- 根据性别分组 , 统计男性用户 和 女性用户的数量
select sex, count(sex) from user group by sex;

-- 根据性别分组 , 统计男性用户 和 女性用户的平均年龄
select sex, avg(age) from user group by sex;

-- 查询年龄小于30的用户 , 并根据性别分组 , 要求分组后数据量大于等于1才展示出来
select sex, count(*) '数据量' from user where id <= 30 group by sex having count('数据量') >= 1;

order by 排序查询:asc(升序,默认)、desc(降序)

-- 根据年龄升序排序
select * from user order by age asc;	-- asc可以省略
select * from user order by age;

-- 根据年龄降序排序
select * from user order by age desc;

-- 根据年龄降序排序,如果相同则按照id升序排序
select * from user order by age desc, id asc;

limit 分页查询

分页查询索引从0开始
起始索引 = (当前页码 - 1)* 每页展示数

-- 查询第1页用户数据, 每页展示10条记录
select * from user limit 0, 10;
select * from user limit 10;	-- 第一页的起始索引(0)开始省略

-- 查询第2页用户数据
-- 起始索引 = (当前页码 - 1)*  每页展示数 = (2 - 1) * 10 = 10	
select * from user limit 10, 10;	

DQL的执行顺序

执行顺序:from -> where -> group by -> having -> select -> order by -> limit
理解如下:
1. 先找到表 (from)
2. 数据条件限定 (where)
3. 将数据进行分组 (group by)
4. 分组后的数据条件限定 (having)
5. 挑选展示字段 (select)
6. 数据排序 (order by)
7. 跳过条数和展示条数 (limit)

-- DQL的基础语法
select
		字段列表
from
		表名列表	
where
		条件列表
group by
		分组字段列表
having
		分组后条件列表
order by
		排序字段列表
limit
		分页参数

DCL (Data Control Language,数据控制语言)

管理用户

-- 查询用户
select * from mysql.user;	-- 当前库的所有用户

-- 创建用户
create user 'myuser'@'localhost' identified by '123456'; -- localhost本地用户登录
create user 'myuser'@'%' identified by '123456';	-- % 远程用户登录

-- 修改用户密码
alter user 'myuser'@'localhost' identified with mysql_native_password by '123';

-- 删除用户
drop user 'myuser'@'localhost';

权限控制

在这里插入图片描述

-- 查询用户权限
show grants for 'myuser'@'localhost';

-- 授予用户权限
grant select, update on itcast.* to 'myuser'@'localhost'; -- 授予指定权限
grant all on *.* to 'myuser'@'localhost';	-- 授予用户 所有库和表的所有权限

-- 撤销用户权限
revoke update on itcast.* FROM 'myuser'@'localhost'; -- 撤销指定权限
revoke all on *.* from 'myuser'@'localhost';	-- 撤销所有权限

如报错:Access denied; you need (at least one of) the SYSTEM_USER privilege(s) for this operation。 解决方案:执行 grant system_user on *.* to 'root';

 


 

常用函数

字符串函数

-- concat(S1, S2, ..., Sn) 字符串拼接,将S1,S2,... Sn拼接成一个字符串
select concat('a', 'b', 'c');	-- abc

-- lower(str) 将字符串str全部转为小写
select lower('aBC');	-- abc

-- upper(str) 将字符串str全部转为大写
select upper('aBc');	-- ABC

-- lpad(str, n, pad)左填充,用字符串pad对str的左边进行填充,达到n个字符串长度
select lpad('89', 5, '0');	-- 00089

-- rpad(str,n,pad) 右填充,用字符串pad对str的右边进行填充,达到n个字符串长度
select rpad('89', 5, '0');	-- 89000

-- trim(str) 去掉字符串头部和尾部的空格
select trim('  xx  ');	-- xx;

-- substring(str, start, len) 返回从字符串str从start位置起的len个长度的字符串(索引从1开始)
select substring('abcdefg',3, 3);	-- cde

数值函数

-- ceil(x) 向上取整
select ceil(1.1);	-- 2

-- floor(x) 向下取整
select floor(1.7);	-- 1

-- mod(x,y) 返回x % y
select mod(9, 3);	-- 0

-- rand() 返回0~1内的随机数
select rand();	-- 随机数 0.16639879640787728

-- round(x,y) 求x的四舍五入,保留y位小数
select round(1.37, 1);	-- 1.4

日期函数

-- CURDATE() 返回当前日期
select curdate();	-- 2023-10-05

-- CURTIME() 返回当前时间
select curtime();	-- 03:45:23

-- NOW() 返回当前日期和时间
select now();	-- 2023-10-05 03:45:55

-- YEAR(date) 获取指定date的年份
select year('2023-10-05');	-- 2023
select year(now()); -- 2023

-- MONTH(date) 获取指定date的月份
select month(now());	-- 10;

-- DAY(date) 获取指定date的日期
select day(now());	-- 5
-- DATE_ADD(date, INTERVAL xx exprtype) 返回一个日期/时间值加上一个时间间隔xx expr后的时间值
-- exprtype = {year, month, day, hour, minute, second}
select date_add('2023-10-05 03:45:55', interval 1 year);	-- 2024-10-05 03:45:55
select date_add('2023-10-05 03:45:55', interval 1 minute);	-- 2023-10-05 03:46:55

-- DATEDIFF(date1,date2) 返回date1 - date2的天数
select datediff('2023-10-05', '2023-10-01');

流程函数

-- if(value , a , b) 如果value为true,则返回a,否则返回b
select if (true, 'a', 'b');	-- a
select if (false, 'a', 'b');	-- b

-- ifnull(value1 , value2) 如果value1不为空,返回value1,否则返回value2
select ifnull('value1', 'value2');	-- value1
select ifnull(null, 'value2');	-- value2

-- case when then 语句
-- 写法1:
select
	name, (case sex when '0' then '女' when '1' then '男' else '未知' end) as '性别'
from user;

-- 写法2:表达式判断
select
	name, (case when age < 18 then '未成年' when age > 18 then '成年' else '未知' end) as '是否成年'
from user;

 


 

约束

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

-- 创建表时添加约束
create table user_info(
	id int primary key auto_increment comment '主键(非空唯一)id,自增',	-- 主键约束
	username varchar(255) not null unique comment '用户名,非空唯一',	-- 非空约束、唯一约束
	status tinyint default 1 comment '用户状态,默认1激活,0禁用', -- 默认约束
	age int check (age >= 0 and age <= 200) comment '年龄,必须 >= 0',	-- 检查约束
	user_id int not null comment '用户外键',
	constraint fk_info_user_id foreign key (user_id) references user(id)	-- 外键约束
);

-- 删除约束
alter table user_info drop foreign key fk_info_user_id;

-- 添加约束
alter table user_info add constraint fk_info_user_id foreign key (user_id) references user(id); 

添加约束前需要注意数据是否已经满足约束,不然会添加失败。Check constraint 'stu_info_chk_2' is violated.

外键的删除/更新行为

在这里插入图片描述

-- 添加外键时设置更新和删除行为
alter table user_info 
add constraint fk_info_user_id foreign key (user_id) references user(id)
on update cascade
on delete cascade;

 


 

多表查询

笛卡尔积: 笛卡尔乘积是指在数学中,集合A和集合B的所有组合情况(A个数*B个数)。

多表关系

一对一:只需要在任意一方加入外键。
一对多:在多的一方加入外键。
多对多:通过中间表建立双方的外键。

多表查询的分类

分类:内连接、外连接、自连接。
在这里插入图片描述

内连接

内连接:两个表的交集部分

-- 隐式内连接
select e.name, d.name from emp e, dept d where e.dept_id = d.id;

-- 显式内连接
select e.name, d.name from emp e join dept d on e.dept_id = d.id;

外连接

左外连接:左表所有数据且包含两个表的交集部分。
右外连接:右表所有数据且包含两个表的交集部分。

-- 左外连接 left outer join
select e.name, d.name from emp e left outer join dept d on e.dept_id = d.id;

-- 右外连接 right outer join
select d.name, e.name from emp e right outer join dept d on e.dept_id = d.id;

自连接

-- 自连接(内连接)
select a.name, b.name from emp a, emp b where a.managerid = b.id;	-- a和b的交集

-- 自连接(外连接)
select a.name, b.name from emp a left outer join emp b on a.managerid = b.id;	-- a的所有数据且包含a和b的交集

联合查询 union,union all

union all:将两个查询结果直接合并
union:将两个查询结果去重合并

-- union all合并
select * from emp where salary < 5000		-- 注意这里别加分号;这是一条语句
union all
select * from emp where age > 50;

-- union合并去重
select * from emp where salary < 5000
union
select * from emp where age > 50; 	

注意对于联合查询的字段列数和类型必须保持一致。

子查询(嵌套查询)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

-- 标量子查询:子查询的数据为一个值
-- 查询销售部的所有员工信息
select * from emp e where e.dept_id = (select id from dept where name = '销售部'); 


-- 列子查询:子查询的数据为一列的
-- 查询销售部和财务部的所有员工信息
select * from emp e where e.dept_id in (select id from dept where name = '销售部' or name = '财务部');


-- 行子查询:子查询的数据为一行的
-- 查询与 "张无忌" 的薪资及直属领导相同的员工信息
select * from emp where (salary, managerid) = (select salary, managerid from emp where name = '张无忌');


-- 表子查询:子查询的数据为多行多列的
-- 查询与 "鹿杖客" , "宋远桥" 的职位和薪资相同的员工信息
select * from emp where (job, salary) in (select job, salary from emp where name = '鹿杖客' or name = '宋远桥');

exists / not exists 相关子查询

exists:查找主集和子集至少存在一个交集
not exists:主集和子集没有交集

-- exists
-- 查找所有至少有一个订单的客户
SELECT * FROM Customers  
WHERE EXISTS (SELECT * FROM Orders WHERE orders.customer_id = customers.customer_id);

-- 查询有选修成绩不及格的学生的的学号,姓名,性别,年龄及班级号。
SELECT s_id, s_name, sex, age, class_id
FROM stu_info
WHERE EXISTS (	
	SELECT *
	FROM stu_score
	WHERE stu_info.`s_id` = stu_score.`s_id`
	AND score < 60 	-- 返回当前学生选修课至少存在一门小于60分的集合
)
-- not exists
-- 查找没有下订单的所有客户
SELECT * FROM Customers  
WHERE NOT EXISTS (SELECT * FROM Orders WHERE orders.customer_id = customers.customer_id);

-- 查询选修了课程表中所有课程的学生信息
select *
from stu_info
where not exists (
	select *
	from course
	where not exists (	-- 不存在课程没有被选中的情况(空集) = 全选
		select *
		from stu_score
		where course.c_id = stu_score.c_id
		and stu_score.s_id = stu_info.s_id
	)
);

 


 

事务 transaction

事务操作

-- 方法1:查看/设置事务提交方式
select @@autocommit;			-- 1为自动提交,0为手动提交
set @@autocommit = 0;		-- 设置手动提交事务

-- 方法2:显式开启事务 begin、 start transaction
-- 以下两种显示写法
begin;	-- 手动开启事务
start transaction;	-- 手动开启事务

-- 提交事务,所有sql顺利完成可以提交事务
commit;

-- 回滚事务,中途如果遇到问题,就需要执行回滚
rollback;

注意:一旦开启了事务,除非提交事务,否则即使数据发生了改变也无法看到数据的变化。

事务的四大特性(ACID)

  1. 原子性(Atomicity):事务是不可分割的最小操作单位,要么全部成功,要么全部失败。
  2. 一致性(Consistency):事务完成时,必须使所有的数据都保持一致的状态(不管提交还是回滚,数据是恒定的,不能说执行完sql后,数据比执行完sql多了或者少了值)。
  3. 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行(事务A和事务B互不干扰,都是独立的)。
  4. 持久性(Durability):事务一旦提交事务或回滚,它对数据库中的数据的改变是永久的。

事务的并发问题

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

事务的隔离级别

Read uncommitted(读未提交)Read committed(读已提交)Repeatable Read(可重复读)Serializable(串行化)
在这里插入图片描述

-- 查看事务隔离级别
select @@transaction_isolation;

-- 设置事务隔离级别
set session transaction isolation level read uncommitted;	-- 设置当前会话隔离级别
set global transaction isolation level read uncommitted;	-- 设置全局隔离级别

隔离级别越高,数据越安全,但是性能就越差。

 


 

存储引擎

存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式 。
存储引擎是基于表的,而不是基于库的,所以存储引擎也可被称为表类型。
我们可以在创建表的时候,来指定选择的存储引擎,如果没有指定将自动选择默认的存储引擎。

-- 显示当前数据库(表)所支持的存储引擎
show engines;

-- 创建表时指定存储引擎
create table xx (
)engine=InnoDb;

InnoDB

介绍:InnoDB是一种兼顾高可靠性和高性能的通用存储引擎,在MySQL5.5之后,InnoDB是默认MySQL存储引擎。
特点:(事务,行级锁,外键)

  1. DML操作遵循ACID模型,支持事务。
  2. 行级锁,提高并发访问性能。
  3. 支持外键foreign key约束,保证数据的完整性和正确性。

文件:
每个表基本对应一个 表空间文件.ibd文件(innodb data),存储该表的表结构(frm-早期的 、sdi-新版的)、数据和索引。

-- 查看是否每一张表对应一个表空间文件
show variables like 'innodb_file_per_table';	-- 默认ON开启

可以使用终端执行:ibd2sdi xx.ibd查看存储结构

InnoDB的存储结构: 表空间文件 > 段 > 区 > 页 > 行
在这里插入图片描述
在这里插入图片描述

MyISAM

介绍:MyISAM是MySQL早期的默认引擎。
特点

  1. 不支持事务,不支持外键。
  2. 支持表锁,不支持行锁。
  3. 访问速度快。

文件
xxx.sdi:存储表结构信息(早期是.frm)
xxx.myd (myisam data):存储数据
xxx.myi (myisam index):存储索引

MEMORY

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

  1. 内存存放
  2. hash索引(默认)

文件
xxx.sdi;

区别及特点

在这里插入图片描述

存储引擎选择

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

  1. InnoDB:是MySQL的默认存储引擎,支持事务,外键。如果应用对事务的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询之外,还包含很多的更新、删除操作,那么InnoDB是比较合适的选择。(常用)

  2. MyISAM:如果应用是以读操作和插入操作为住,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不是很高,那么选择这个存储引擎是非常合适的。

  3. MEMORY:将所有数据保存在内存中,访问速度快,通常用于临时表及缓存。MEMORY的缺陷就是对表的大小有限制,太大的表无法缓存在内存中,而且无法保障数据的安全性。

MyISAM (对标MongoDB) 和 MEMORY (对标Redis) 将会被NoSql取代。

 


 

索引

索引概述

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

优点:

  1. 提高数据查询的效率,降低数据库的IO成本。
  2. 通过索引列对数据进行排序,降低数据排序的成本,降低cpu的消耗。

缺点:

  1. 索引列也是要占用空间的。
  2. 索引大大提高了查询效率,同时却也降低了更新表的速度,如对表进行insert,update,delete时,效率降低。

索引结构

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

  1. B+Tree索引:最常见的索引类型,大部分引擎都支持B+树索引。(支持的引擎:InnoDB, MyISAM, Memory)
    在这里插入图片描述

  2. Hash索引:底层数据结构是用哈希表实现的,只有精确匹配索引列的查询才有效,不支持范围查询。(支持的引擎:Memory)
    在这里插入图片描述
    在这里插入图片描述

  3. R-tree(空间索引):空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少。(支持的引擎:MyISAM)

  4. Full-test(全文索引):是一种通过建立倒排索引,快速匹配文档的方式。类似于Lucene,Solr,ES。(支持的引擎:5.6以后的InnoDB, MyISAM)

索引分类

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

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

  1. 聚集索引(Clustered Index)
    含义:将数据存与索引放到了一块,索引结构的叶子节点保存了行数据
    特点:必须有,而且只有一个
  2. 二级索引(Secondary Index)
    含义:将数据存与索引分开存储,索引结构的叶子节点关联的是对应的主键值
    特点:可以存多个

聚集索引选取的规则

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

二级索引的使用(回表查询):通过二级索引查找出的主键值,然后使用主键通过聚集索引拿到行数据;这个过程叫做回表查询。
在这里插入图片描述

索引语法

-- 查看索引
show index from tb_user;

-- 创建索引
create index idx_name on tb_user(name);	-- 常规索引
create unique index idx_phone on tb_user(phone)	-- 唯一索引
create index idx_pro_age_sta on tb_user(profession, age, status);	-- 联合索引

-- 删除索引
drop index idx_email on tb_user;

SQL性能分析

SQL的执行频率

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

-- 查看当前服务器增删改查的频率
show global status like 'Com_______';

慢查询日志

-- 查看是否开启了慢查询,还有存储的日志的路径
show variables like 'slow_query%';

在这里插入图片描述

开启慢查询需要配置my.cnf文件,一般路径在/etc/mysql/my.cnf,如找不到则百度

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

重启发现报错,可以看看my.cnf文件开头有没有 [mysqld]

profile 执行详情

-- 查看当前mysql是否支持profile操作, YES支持,NO不支持
select @@have_profiling;

-- 设置profile
set global profiling = 1;


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

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

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

explain 分析执行计划

-- explain + select语句
explain select * from user where id=  1;

在这里插入图片描述

索引的使用原则

最左前缀法则

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

-- 查到有联合索引idx_pro_age_sta, 顺序为:profession, age, status
show index from tb_user;	

-- 以下情况都经过索引
explain select * from tb_user where profession = '软件工程' and age = 31 and status = '0';	-- key_len = 54
explain select * from tb_user where profession = '软件工程' and age = 31;	-- key_len = 49
explain select * from tb_user where profession = '软件工程';	-- key_len = 47
-- 由上推出索引长度:profession = 47, age = 2, status = 5

-- 索引失效,由于跳过了profession,不满足最左前缀法则
explain select * from tb_user where age = 31 and status = '0';
explain select * from tb_user where status = '0'; 

-- 只走了profession的索引,跳过了age索引,status索引失效
explain select * from tb_user where profession = '软件工程' and status = '0';	-- key_len=47

-- 最左前缀法则和where顺序无关,顺序是建立索引时确定的,和条件判断时出现的字段有关。
explain select * from tb_user where age = 31 and status = '0' and profession = '软件工程';	-- 经过索引,key_len=54

范围查询

联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效。(>=和<=不会索引失效)

-- 只走了profession索引,age用了>导致索引失效,则status也失效
explain select * from tb_user where profession = '软件工程' and age > 30 and status = '0' -- key_len=49


-- age使用>=时,经过联合索引
explain select * from tb_user where profession = '软件工程' and age >= 30 and status = '0';	-- key_len=57

索引失效情况

-- 1. 索引列运算,导致索引失效
explain select * from tb_user where substring(phone,10,2) = '15';	-- 不走索引

-- 2. 字符串字段不加引号,导致索引失效
explain select * from tb_user where phone = '17799990000';	-- 走索引
explain select * from tb_user where status = 17799990000;	-- 不走索引

-- 3. like模糊查询:如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。
explain select * from tb_user where profession like '软件%';	-- 走索引
explain select * from tb_user where profession like '%软件';	-- 不走索引

-- 4. or连接条件:如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。
explain select * from tb_user where id = 10 or age = 23;	-- 由于age没有索引,则整个or都不会走索引
create index idx_age on tb_user(age);	-- 建立age索引
explain select * from tb_user where id = 10 or age = 23;	-- 走索引,因为age已经建立索引

-- 5. 数据分布情况决定走全表还是走索引:如果Mysql评估使用索引比全表更慢,则不适用索引。
explain select * from tb_user where phone >= '17799990005';		-- 数据占全表大部分,走了all
explain select * from tb_user where phone >= '17799990020';		-- 数据占全表的少量,走索引

SQL提示

-- use index 指定使用索引
explain select * from tb_user use index(idx_pro) where profession = '软件工程';		-- 建议mysql走idx_pro索引

-- ignore index 排除指定索引
explain select * from tb_user ignore index(idx_pro) where profession = '软件工程';	-- 排除idx_pro索引

-- force index 强制走指定索引
explain select * from tb_user force index(idx_pro) where profession = '软件工程';	-- 强制mysql走idx_pro索引

要盖索引 & 避免回表查询

尽量使用覆盖索引,减少select * 查询。
因为在查询使用了索引,并且返回需要的列,在索引叶子节点中或者叶子节点关联值已经全部能够找到。如果没有找到则会回表查询。
在这里插入图片描述

-- 字段值在索引叶子节点中或者叶子节点关联值中,无需回表查询
explain select id, profession,age, status from tb_user where profession = '软件工程' and age = 31 and status = '0' ;	-- Using where; Using index

-- 字段值不在索引叶子节点中或者叶子节点关联值中,需要回表查询
explain select id, name from tb_user where profession = '软件工程' and age = 31 and status = '0';	-- Using index condition

在这里插入图片描述

前缀索引

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

-- 前缀长度:可以根据索引的选择性来决定
-- 不重复的索引值(基数)/ 数据表的记录总数
select count(distinct substring(email,1,5)) / count(*) from tb_user;	-- 0.9583, 越高越好
select count(distinct substring(email,1,4)) / count(*) from tb_user;	-- 0.9167

-- 创建前缀长度
create index idx_email_5 on tb_user(email(5));

在这里插入图片描述

单列索引与联合索引

单列索引:即一个索引只包含单个列。
联合索引:即一个索引包含了多个列。
在这里插入图片描述

-- possible_keys:idx_phone,idx_name, key:idx_phone,mysql根据查询情况实际走了phone的索引
-- 索引信息不包括name值,所以需要回表查询
explain select id, name, phone from tb_user where phone = '17799990000' and name = '吕布';

-- 建立联合索引
create index idx_phone_name on tb_user(phone, name);

-- 使用联合索引
explain select id, name, phone from tb_user use index (idx_phone_name) where phone = '17799990000' and name = '吕布';

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

索引设计原则

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

 


 

SQL优化

insert优化

-- 1. 批量插入(推荐一次插入不超过1000条,如果超过可以分割成多条sql)
insert into tb_test values(...),(...),(...);

-- 2. 手动事务提交(不然每次插入后自动提交事务,费时间)
start transaction;
insert into tb_test values(...);
insert into tb_test values(...);
insert into tb_test values(...);
commit;

-- 3. 主键顺序插入:主键顺序插入会比乱序插入快
-- 主键乱序插入: 3 1 2 88 4
-- 主键顺序插入:1 2 3 4 88

-- 4. 大批量插入数据(如果一次性需要插入大批量数据,使用insert语句插入性能较低,此时可以使用mysql数据库提供的load data指令进行插入,[假设表结构为id,name,age])新建文件load_user_100w_sort.sql内容如下
/*
	1,codehorse,18
	2,mjq,19
	...此处省略2000w数据
*/
-- 客户端连接服务端时,加上参数 --local-infile。告诉服务端需要加载本地文件
mysql --local-infile -u root -p
-- 查看local_infile是否开启, ON开,OFF关
show variables like 'local_infile';
-- 设置全局参数local_infile为1,开启从本地加载文件导入数据的开关
set global local_infile = 1;
-- 执行load指令将准备好的数据,加载到表结构中(注意:windows下文本换行符为'\r\n')
load data local infile '/root/load_user_100w_sort.sql'
into table tb_user 
fields terminated by ',' 
lines terminated by '\n';

主键优化

页分裂

在这里插入图片描述
在这里插入图片描述

页合并

在这里插入图片描述
在这里插入图片描述
主键设计原则:

  1. 满足业务需求的情况下,尽量降低主键的长度。
  2. 插入数据时,尽量选择顺序插入,选择使用auto_increment自增主键。
  3. 尽量不用使用UUID做主键或者是其他自然主键,如身份证。
  4. 业务操作时,避免对主键的修改。

order by优化

MySQL的排序,有两种方式:
Using filesort :通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort
buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序。
Using index:通过有序索引顺序扫描直接返回有序数据,这种情况即为 using index,不需要
额外排序,操作效率高。
对于以上的两种排序方式,Using index的性能高,而Using filesort的性能低,我们在优化排序
操作时,尽量要优化为 Using index。

-- 按照age升序排序,如果age相同则按phone升序排序
explain select id, age, phone from tb_user order by age, phone;		-- Using filesort

-- 创建age、phone联合索引(默认asc升序索引)
create index idx_age_phone on tb_user(age, phone);
-- 建立联合索引后,再次按照age升序排序,如果age相同则按phone升序排序
explain select id, age, phone from tb_user order by age, phone;		-- Using index
-- 按照age降序排序,如果age相同则按phone降序排序。 Backward index scan:反向索引扫描
explain select id, age, phone from tb_user order by age desc, phone desc;		-- Backward index scan; Using index

-- 按照age升序排序,如果age相同则按phone降序排序
explain select id, age, phone from tb_user order by age asc, phone desc;	-- Using index; Using filesort
-- 创建age 升序排序、phone降序排序的联合索引
create index idx_age_phone_ad on tb_user(age asc ,phone desc); 
-- 建立联合索引后,再次按照age升序排序,如果age相同则按phone降序排序
explain select id, age, phone from tb_user order by age asc, phone desc;	-- Using index


-- 无论什么顺序的联合索引,都要注意最左前缀法则
explain select id, age, phone from tb_user order by phone, age;	  -- Using index; Using filesort
explain select id, age, phone from tb_user order by phone desc, age asc;  -- Using index; Using filesort

order by的使用原则

  1. 尽量根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则。
  2. 尽量使用覆盖索引。(因为出现回表查询,就会出现FileSort排序)。
  3. 多字段排序,一个升序一个降序的情况,此时需要注意联合索引在创建时指定排序规则(ASC/DESC)。
  4. 如果不可避免的出现filesort,大数据量排序时,可以适当增大排序缓冲区大小sort_buffer_size(默认256k)。
-- 查询排序缓冲区大小
show variables like 'sort_buffer_size';	-- 262144

-- 设置排序缓冲区大小
set global sort_buffer_size = 262144;

group by优化

在分组操作中,我们需要通过以下两点进行优化,以提升性能:

  1. 在分组操作时,可以通过索引来提高效率。
  2. 分组操作时,索引的使用也是满足最左前缀法则的。
-- group by字段如果没有索引会出现Using temporary(临时表)
explain select profession, count(*) from tb_user group by profession;	-- Using temporary
-- 建立profession、age、status的联合索引
create index idx_pro_age_sta on tb_user(profession, age, status);
-- 再次查询
explain select profession, count(*) from tb_user group by profession;	-- Using index

-- 不满足最左前缀法则也会出现Using temporary
explain select age, count(*) from tb_user group by age;	-- Using index; Using temporary
explain select age, count(*) from tb_user where profession = '软件工程' group by age;	-- Using index

limit 优化

对于limit分页,大数据量的情况下,越往后的页就越卡。
一个常见又非常头疼的问题就是limit 2000000,10,此时需要MySQL排序前200000010记录,仅仅返回2000000~2000010的记录,其他记录丢弃,查询排序的代价非常大。
==优化思路:==一般分页查询时,通过创建覆盖索引能给比较好地提高性能,可以通过覆盖索引 + 子查询形式进行优化。

-- 没优化写法
explain select * from tb_sku limit 20000000, 10;	-- 不要expalin执行时15.75 sec

-- 子查询 + 覆盖索引
explain select * from tb_sku s , (select id from tb_sku order by id limit 2000000,10) t where s.id = t.id; -- 2.17 sec

count 优化

效率:count(*) > count(数字) > count(主键) > count(字段)

-- count(*): InnoDB引擎并不会把全部字段取出来,而是专门做了优化,不取值,服务层直接按行进行累加。
select count(*) from tb_user;

-- count(数字):InnoDB 引擎遍历整张表,但不取值。服务层对于返回的每一行,放一个数字“1”进去,直接按行进行累加。
select count(1) from tb_user;

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

-- count(字段)
-- 没有not null约束 : InnoDB 引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,服务层判断是否为null,不为null,计数累加。
-- 有not null约束:InnoDB 引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,直接按行进行累加。
select count(profession) from tb_user; 

update 优化

当表开启事务后对没有索引条件的行进行修改,则触发表级锁。对有索引条件的行进行修改,则触发行级锁
InnoDB的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则会从行锁升级为表锁。
update优化:当开启事务修改某行值时,尽量命中索引,否则触发表级锁,影响效率。

-- 当id为索引,只触发行锁
update student set name = 'lisi' where id = 1;
-- 当name不为索引,触发表锁
update student set name = 'lisi' where name = 'zhangsan';
-- 建立name索引
create index idx_name on tb_user(name);
-- name为索引,触发行锁
update student set name = 'lisi' where name = 'zhangsan';  

 


 

视图 view

-- 创建视图
create view stu_v_1 as select id, name from student where id <= 10;
create or replace view stu_v_1 as select id, name from student where id <= 10; -- or replace:或替换

-- 查看视图创建语句
show create view stu_v_1;
show table status where comment='view';  --查看所有视图
-- 查询视图
select * from stu_v_1;

-- 修改视图
create or replace view stu_v_1 as select name from student where id <= 100; -- 暴力写法,创建或替换
alter view stu_v_1 as select name from student where id <= 100; -- 修改视图

-- 删除视图
drop view if exists stu_v_1;

视图的检查选项

-- cascaded check option 级联检查项:一层叠一层往上检查
create or replace view stu_v_1 as select id, name from student where id >= 10
create or replace view stu_v_2 as select id, name from stu_v_1 where id <= 20 with cascaded check option;

insert into stu_v_1 values(15, 'tom'); -- 满足stu_v_1视图检查项id >= 10
insert into stu_v_2 values(9, 'tom'); -- 满足stu_v_2.id <= 20 但是不满足 stu_v_1.id <= 10,插入失败

-- local check option:往上递归检查,如果没有检查项则不检查
create or replace view stu_v_3 as select id, name from student where id >= 10;
create or replace view stu_v_4 as select id, name from stu_v_3 where id <= 20 with local check option;

insert into stu_v_3 values(15, 'tom'); -- 没有条件,直接插入
insert into stu_v_4 values(9, 'tom'); -- 满足stu_v_2.id <= 20, 由于stu_v_3没有检查项,所以不会检查。 插入成功

 


 

存储过程 procedure

-- 创建存储过程
create procedure p1()
begin
	 select count(*) from student;
end;

-- 调用
call p1();

-- 删除
drop procedure p1;


-- 查看创建存储过程语句
show create procedure p1;
select * from information_schema.ROUTINES where ROUTINE_SCHEMA = '数据库名';

if判断

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

call p3();

存储过程参数

-- 参数: in: 输入   out:输出   inout:输入输出
create procedure p4(in score int, out res varchar(10), inout sum int)
begin
	if score >= 85 then
		set res := '优秀';
	elseif score >= 60 then
		set res := '及格';
	else
		set res := '不及格';
	end if;
	
	set sum := sum + sum;
end;

set @sum := 1;
call p4(85, @res, @sum);
select @res, @sum;

存储过程case

-- case用法
create procedure p6(in x int)
begin
	declare res varchar(10);
	case
		when x >= 1 and x <= 10 then
			set res := '1~10';
		when x >= 11 and x <= 20 then
			set res := '11~20';
		else 
			set res := '非法参数';
	end case;
	select res;
end; 

call p6(11);

while循环

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

call p7(10);

repeat重复执行

-- repeat: 一直重复直到满足条件才退出
create procedure p8(in n int)
begin
	declare sum int default 0;
	repeat
		set sum := sum + n;
		set n := n - 1;
	until n <= 0
	end repeat;
	
	select sum;
end;

call p8(10);

loop 循环

-- loop 循环
create procedure p9(in n int)
begin
	declare res varchar(100);
	
	even:loop
		if n <= 0 then
			leave even;
		end if;
		
		if n % 2 = 1 then
			set n := n - 1;
			iterate even;
		end if;
		if isnull(res) then   -- 一定要判断null,不然concat有一个null,最终结果就是null
			set res := cast(n as char);
		else
			set res := concat(res, ' ', cast(n as char));
		end if;
		set n := n - 1;
	end loop even;
	
	select res;
end;

call p9(10);
drop PROCEDURE p9;

游标 cursor

-- 游标的基本使用
create procedure p10(in uage int)
begin
	declare u_name varchar(100);
	declare u_age int;
	declare u_cursor cursor for select name, age from student where age <= uage; -- 1. 新建游标
	
	drop table if exists new_user;
	create table if not exists new_user(
		id int primary key auto_increment,
		name varchar(100),
		age int
	);
	
	open u_cursor;	-- 2. 开启游标
	while true do
		fetch u_cursor into u_name, u_age;	-- 3. 遍历游标
		insert into new_user values (null, u_name, u_age);
	end while;
	close u_cursor; -- 4. 关闭游标
end;

call p10(20);

由于while是死循环,导致游标不会停, 最后会报一个错:No data - zero rows fetched, selected, or processedNo data - zero rows fetched, selected, or processed
解决方案:配合条件处理一起使用

条件处理程序 handler

在这里插入图片描述

-- 游标 和 条件处理 一起使用
create procedure p10(in uage int)
begin
	declare u_name varchar(100);
	declare u_age int;
	declare u_cursor cursor for select name, age from student where age <= uage; -- 1. 新建游标
	
	declare exit handler for sqlstate '02000' close u_cursor; -- 当SQL执行状态码满足'02000'时exit
	-- declare exit handler for not found close u_cursor; -- sql执行状态满足02开头的代码
	
	drop table if exists new_user;
	create table if not exists new_user(
		id int primary key auto_increment,
		name varchar(100),
		age int
	);
	
	open u_cursor;	-- 2. 开启游标
	while true do
		fetch u_cursor into u_name, u_age;	-- 3. 遍历游标
		insert into new_user values (null, u_name, u_age);
	end while;
	close u_cursor; -- 4. 关闭游标
end;

call p10(20);

存储函数

在这里插入图片描述

-- 存储函数
create function p12(n int)	-- 定义存储函数
returns int deterministic
begin
	declare res int default 0;
	
	while n > 0 do
		set res := res + n;
		set n := n - 1;
	end while;
	
	return res;
end;

select p12(10);	-- 直接调用函数

变量

系统变量

-- 系统变量
show session variables; -- session,默认
show global variables; -- 全局
show global variables like 'auto%'; -- 模糊匹配查找变量
select @@global.autocommit;   -- 查看指定系统变量名

-- 设置系统变量
set global autocommit = 0;
set @@global.autocommit = 1;

用户自定义变量

-- 设置用户自定义变量
set @myName = 'codehorse'; -- 方法1:=
set @myName := 'codehorse2'; -- 方法2::=,推荐
select @myName := 'codehorse3';	-- 方法3:select
select count(*) into @MyCount from student; -- 方法4:select查表赋值

-- 查看用户自定义变量
select @myName, @MyCount;;

局部变量

-- declare 变量名 变量类型 [default ...]
create procedure p2()
begin
	declare stu_cnt int default 0;	-- declare定义局部变量
	declare myName varchar(255);   -- declare 变量名 变量类型
	
	select count(*) into stu_cnt from student;
	set myName := 'codehorse';
	
	select stu_cnt, myName;
end;

call p2()

触发器 trigger

-- 创建触发器日志表
create table stu_logs(
	id int primary key not null auto_increment comment 'id',
	op_type varchar(255) comment '操作类型 insert/update/delete', 
	op_time datetime comment '操作日期',
	info varchar(255) comment '日志信息'
)engine=innodb default charset=utf8;

insert插入触发器

-- 创建 插入触发器
create trigger stu_insert_trigger
	after insert on student for each row
begin
	insert into stu_logs(id, op_type, op_time, info) values(null, 'insert', now(), concat('插入的内容为:', 'id=',new.id, ', age=', new.age, ', name=', new.name));
end;

-- 当studetn插入数据时触发
insert into student(id, age, name) values(null, 18, 'horse');
-- 查看日志
select * from stu_logs; 

-- 查看创建的触发器
show triggers;

-- 删除触发器
drop trigger stu_update_trigger;

update更新触发器

-- 更新触发器
create trigger stu_update_trigger
	after update on student for each row
begin
	insert into stu_logs(id, op_type, op_time, info) values(null, 'update', now(), concat('更新前的数据为:id=', old.id, ', age=', old.age, ', name=', old.name, 
	' | 更新后的数据为:id=', new.id, ', age=', new.age, ', name=', new.name));
end;

-- 当student更新数据时触发
update student set name = 'horse' where id = 1;

delete删除触发器

-- 删除触发器
create trigger stu_delete_trigger
	before delete on student for each row
begin
	insert into stu_logs(id, op_type, op_time, info) values(null, 'delete', now(), concat('删除的数据id为:', old.id));
end;

-- 当student删除数据时触发
delete from student where id = 100;

 


 

全局锁

-- 开启全局锁
flush tables with read lock;	
-- 解除全局锁
unlock tables;

备份语句

-- 备份命令(这条需要加锁,不然备份过程中有可能会产生一致性问题)
mysqldump -h127.0.0.1 -uroot -proot company > G:/db01.sql  -- 备份命令不需要进入mysql客户端执行

-- 在InnoDB引擎中,我们可以在备份时加上参数 --single-transaction 参数来完成不加锁的一致性数据备份。
mysqldump --single-transaction -h127.0.0.1 -uroot -proot company > G:/db01.sql

表级锁

表锁 = 整张表的每一行数据都会自动加锁

表锁

-- 开启表共享读锁 (读锁)
lock tables student read;

select * from student;	-- 成功
update student set name = 'b' where id = 1; -- 失败

-- 开启表共享写锁(写锁)
lock tables student write;

select * from student;	-- 成功
update student set name = 'a' where id = 1; -- 成功,但只限当前客户端,别的客户端会锁住

-- 解除表级锁
unlock tables;

元数据锁 meta data lock (MDL)

在这里插入图片描述

-- 查看当前所有的元数据锁
select object_type, object_schema, object_name, lock_type, lock_duration from performance_schema.metadata_locks;

-- 客户端1
begin;
select id, name from student where id = 1; 	-- 执行成功,并产生shared_read锁。  可以使用查看元数据锁语句查看
commit;

-- 客户端2
begin;
update student set name = 'a' where id = 1; -- 执行成功(shared_read兼容shared_write锁),并产生shared_write锁
alter table student add column job varchar(255); -- 执行阻塞(exclusive和shared_write互斥,需要等read和write锁释放),并产生exclusive锁
commit;
意向锁

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

-- 查看当前所有的意向锁和行级锁
select object_schema, object_name, index_name, lock_type, lock_mode, lock_data from performance_schema.data_locks;

begin;
-- 意向共享锁
select id, name from student where id = 1 lock in share mode;  -- 给student表加上IS锁(意向共享锁)和id=1的行数据加上S锁(共享锁)
-- 意向排他锁
select id, name from student where id = 1 for update;	-- 给student表加上IX锁(意向共享锁)和id=1的行数据加上X锁(共享锁)
commit;

-- 客户端2
lock tables student read;	-- 表仅拥有IS锁时执行成功,表拥有IX锁时执行阻塞
unlock tables;

行级锁

注意:找数据的条件如果没有索引,行锁将会上升到表锁!!!

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

-- 查看当前所有的意向锁和行级锁
select object_schema, object_name, index_name, lock_type, lock_mode, lock_data from performance_schema.data_locks;

begin;
select id, name from student where name = 'a' lock in share mode;	-- 当前行数据加上共享锁(S)
select id, name from student where id = 1 for update; -- 当前行数据加上排他锁(X)
update student set name = 'horse' where name = 'a';	-- insert, update, delete 自动加排他锁
commit;

间隙锁(Gap Lock)& 临键锁(Next-Key Lock)

在这里插入图片描述

-- 查看当前所有的意向锁和行级锁
select object_schema, object_name, index_name, lock_type, lock_mode, lock_data from performance_schema.data_locks;

begin;
-- 假设id=5这条记录不能存在且a和b是最接近5的。 a(小于5的最大值) < 5 < b(大于5的最小值)
update student set name = 'horse' where id = 5;	-- 会给a和b之间的数据加上间隙锁(不包含a和b)

-- 假设现有记录id=[1, 2, 19, 25]
select id, name from student where id >= 15 lock in share mode;	-- 触发临键锁,锁住区间[15, 19],(19, 25],(25, +∞]
-- supremum pseudo-record: 相当于正无穷
commit;

 


 

InnoDB引擎

逻辑存储结构

在这里插入图片描述

架构

在这里插入图片描述

内存结构

磁盘结构

事务原理

MVCC

 


 

mysql管理

系统数据库

Mysql数据库安装完成后,一般自带四个数据库,具体作用如下:
information_schema:提供了访问数据库元数据的各种表和视图,包含了数据库、表、字段类型及访问权限等。 如表:engine、character_sets、views、triggers、routines 等
mysql:存储mysql运行过程中所需的各种信息。如表:time_zone(时区) 、user(用户)、salve*(主从相关)、(权限)、slow_log(慢日志) 等
performance_schema:mysql运行过程中的底层监控,用于收集数据库服务器性能参数。 如表:data_locks、error_log、metadata_locks
sys:包含了一系列方便DBA和开发人员利用 performance_schema 性能数据库进行性能调优和诊断的视图。

mysql常用工具命令

mysql 直接执行语句

-- 直接执行mysql语句,不进入客户端
mysql -uroot -proot -P3306 itcast -e "select * from student;"

mysqladmin

-- mysqladmin:执行管理操作的客户端程序。
mysqladmin --help   --mysqladmin使用文档
mysqladmin -uroot -proot version -- 查看mysql版本
mysqladmin -uroot -proot variables -- 查看mysql服务器系统变量

mysqlbinlog

-- mysqlbinlog: 用于查看mysql生成的二进制日志文件
mysqlbinlog --help
mysqlbinlog binlog.000020
mysqlbinlog binlog.000020 -v	-- 将行事件(数据变更)重构为SQL语句

在这里插入图片描述
注意:两个v的参数,不是w

mysqlshow

-- mysqlshow:可以快速便捷的查看MySQL数据库、表、列、索引等信息,
mysqlshow --help
mysqlshow -uroot -proot --count		-- 统计所有库中的表、数据的数量
mysqlshow -uroot -proot itcast student id --count
mysqlshow -uroot -proot itcast student-i	-- itcast库的student表的状态信息

mysqldump

-- mysqldump: 数据库备份、迁移。
mysqldump --help
mysqldump -uroot -proot itcast > G:/db01.sql	-- 备份itcast数据库
mysqldump -uroot -proot itcast > G:/db01.sql -d	-- 不包含数据
mysqldump -uroot -proot itcast > G:/db01.sql -t	-- 不包含数据表的创建语句
mysqldump -uroot -proot -T /var/lib/mysql-files/ itcast student -- 生成表备份和数据备份两个文件 

如果报错:mysqldump: Got error: 1290: The MySQL server is running with the --secure-file-priv option so it cannot execute this statement when executing ‘SELECT INTO OUTFILE’
方案1:查看信任目录

mysql -uroot -proot -e "show variables like '%secure_file_priv%'"

方案2:打开my.ini文件

# 在[mysqld]下添加:
secure_file_priv = '信任目录'

mysqlimport 导入

-- mysqlimport:用于导入mysqldump -T 生成的数据文件
mysqlimport --help
mysqlimport -uroot -proot itcast /var/lib/mysql-files/student.txt -- 向itcast库student表导入数据
```sql
-- source:加载sql脚本(需要在mysql客户端内执行)
source student.sql

 


 

日志

错误日志

-- 查看错误日志的存储路径
show variables like '%log_error%';	-- log_error:错误日志路径

二进制日志

作用:①. 灾难时的数据恢复; ②. MySQL的主从复制。
在MySQL8版本中,默认二进制日志是开启的。

-- 查看二进制日志的信息
show variables like '%log_bin%';

-- 查看二进制日志支持的格式
show variables like '%binlog_format%';

删除二进制文件

-- 删除 binlog.000030 编号之前的所有日志(不包括 binlog.000030)
purge master logs to 'binlog.000030';

-- 删除日志为 "2023-09-13 23:59:59" 之前产生的所有日志
purge master logs before '2023-09-13 23:59:59';

-- 删除全部 binlog 日志,删除之后,日志编号,将从 binlog.000001重新开始
reset master;

在这里插入图片描述
my.cnf配置文件中修改日志格式

# 将binlog_format设置为statement
binlog_format=statement

查询日志

-- 查询日志信息
show variables like '%general%';	-- 默认OFF

开启查询日志,可以修改MySQL的配置文件 my.cnf 文件,添加如下内容:

#该选项用来开启查询日志 , 0 代表关闭, 1 代表开启
general_log=1
#设置日志的文件名 , 如果没有指定, 在查询日志信息中有这条记录
general_log_file=mysql_query.log

慢查询日志

-- 慢查询日志信息
show variables like '%slow_query%';

-- 慢查询时间
show variables like 'long_query_time';

my.cnf中配置慢查询信息

#开启慢查询日志
slow_query_log=1

#执行时间参数
long_query_time=2

默认情况下,不会记录管理语句,也不会记录不使用索引进行查找的查询。可以修改log_slow_admin_statementslog_queries_not_using_indexes达到也记录慢查询记录。

#记录执行较慢的管理语句
log_slow_admin_statements=1

#记录执行较慢的未使用索引的语句
log_queries_not_using_indexes=1

 


 

主从复制

在这里插入图片描述

  1. Master 主库在事务提交时,会把数据变更记录在二进制日志文件 Binlog 中。
  2. 从库读取主库的二进制日志文件 Binlog ,写入到从库的中继日志 Relay Log 。(IO线程)
  3. slave重做中继日志中的事件,将改变反映它自己的数据。(SQL线程)

主库配置

  1. my.cnf中配置主库信息
#mysql 服务ID,保证整个集群环境中唯一,取值范围:1 – 232-1,默认为1
server-id=1

#是否只读,1 代表只读, 0 代表读写
read-only=0

#忽略的数据, 指不需要同步的数据库
#binlog-ignore-db=mysql

#指定同步的数据库
#binlog-do-db=db01
  1. 重启mysql服务
systemctl restart mysql
  1. 登录mysql,创建远程连接的账号,并授予主从复制权限
-- 创建myuser用户,并设置密码,该用户可在任意主机连接该MySQL服务
create user 'myuser'@'%' identified with mysql_native_password BY 'mypassword';

-- 为 'myuser'@'%' 用户分配主从复制权限
grant replication slave on *.* to 'myuser'@'%';
  1. 查看二进制日志文件和起始坐标
show master status;

从库配置

  1. my.cnf中配置从库信息
#mysql 服务ID,保证整个集群环境中唯一,取值范围:1 – 2^32-1,和主库不一样即可
server-id=2
#是否只读,1 代表只读, 0 代表读写
read-only=1
  1. 重启mysql服务
systemctl restart mysql
  1. 登录mysql,配置连接主库mysql二进制文件和坐标
-- 8.0.22之后的写法
change replication source to source_host='192.168.137.139', source_user='myuser', 
source_password='mypassword', source_log_file='binlog.000008',source_log_pos=585;

 -- 8.0.22之前的写法
change master to master_host='192.168.137.139', master_user='myuser',
master_password='mypassword', master_log_file='binlog.000008',master_log_pos=585;

在这里插入图片描述

  1. 开启同步操作
start replica; -- 8.0.22之后的写法

start slave; -- 8.0.22之前的写法
  1. 查看主从同步状态
show replica status;  -- 8.0.22之后的写法
show replica status\G;	-- 格式化显示

show slave status;	-- 8.0.22之前的写法

在这里插入图片描述
需要确保两个YES:io线程和sql线程都有在跑

停止主从复制

-- 停止从库复制
stop replica;	-- 8.0.22之后的写法
stop slave; 	-- 8.0.22之前的写法

-- 清除从库的同步复制信息
reset replica all;	-- 8.0.22之后的写法
reset salve all; 	-- 8.0.22之前的写法

 


 

分库分表

拆分策略

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

MyCat中间件

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值