MySQL
特点 :持久化储存、更加安全的权限管理访问机制
常见数据库 : MySQL、oracle、postgre SQL 、SQL Server、redis
下载完MySql 在终端连接数据库 ,前提是添加环境变量 进入终端输入 mysql -u root -p
注意 :utf-8的储存为三个字节,而mysql数据库中有些特殊字符占四个字符,所以mysql一般用utf-8mb4编码,因为它支持四个字节
1. 启动与停止
-
方式一: win + r,输入services.msc 进入window系统服务,因为安装mysql时,他是自动注册为系统服务,进入后找到mysql可以点启动或者停止。 mysql安装时是默认开机自动启动的
-
方式二: 打开命令指示符,输入 net stop mysql 80 -->停止mysql 其中80是指mysql的版本
输入 net start mysql80 -->启动mysql
该方式执行命令指示符时,必须是以管理员身份运行
2.客户端连接
要想操作mysql,必须先启动mysql,在连接mysql
- 方式一:mysql提供的客户端命令行工具----->Mysql 8.0 commend Line Client
- 方式二:系统自带的命令指示符 输入 mysql [-h 127.0.0.1] [-p 3306] -u root -p exit 断开连接
mysql 语法
- sql语句可以单行或者多行书写,以分行结尾
- sql语句可以使用空格或者缩进来增强语句的可读性
- mysql数据库的sql语句不区分大小写,关键字建议大写
- 单行注释:--或者#
- 多行注释:/*...*/
SQL分类:
分类 | 全称 | 说明 |
---|---|---|
DDL | Data Definition Language | 数据定义语言,用来定义数据库对象(数据库、表、字段) |
DML | Data Manipulation Language | 数据操作语言,用来对数据库表中的数据进行增删改 |
DQL | Data Query Language | 数据查询语言,用来查询数据库中表的记录 |
DCL | Data Control Language | 数据控制语言,用来创建数据库用户,控制数据库的访问权限 |
3.DDL
3.1 DDL-数据库操作
-
show databases; -->查询所有数据库
-
select database(); --->查询当前使用的数据库
-
create database [if not exits] 数据库名 [default charset 字符集] [collate 排序规则]; --->创建数据库
-
drop database [if exits] 数据库名; 删除数据库,如果存在则删除
-
use 数据库名; -->使用数据库
#查询所有数据库
show databases;#查询当前使用的数据库
select database();#创建数据库
create database [if not exits] 数据库名 [default charset 字符集] [collate 排序规则];删除数据库,如果存在则删除
drop database [if exits] 数据库名;
#使用数据库
use 数据库名;#例如
create database if not exit test default charset utf8mb4;
注意:若是创建库时没有指定字符集,则会默认数据库的编码是Latin1,无法插入汉字。
#查看数据库的编码
show variables like 'character%';
#查看course表的编码
show create table course;
#修改数据库的编码格式
alter database db1 character set utf8mb4;
#修改表的编码格式
alter table course default character set utf8mb4;
修改完之后,如果插入中文还是失败的话。
就删了旧数据库重新建一个数据库吧。建数据库的时候指定数据库的编码格式
3.2 DDL-表操作
注意 要操作表得先使用库
-
show tables; ---> 查询当前数据库里面有哪些表
-
desc 表名; ----->查询表结构,查看表中的字段和类型
-
show create table 表名 ----> 查询指定表的建表的语句,就是当时建表的sql语句
-
create table 表名(字段1 字段1类型 [comment 字段1注释],
字段2 字段2类型 [comment 字段2注释])
...
字段n 字段n类型 [comment 字段n注释])[comment 表注释]; ------>表创建
例如:create table tb_user(
id int comment '编号',
name varchar(10) comment '姓名',
gander char(1) comment '性别'
)comment '用户表';
3.3 DDL-表修改
-
添加字段
alter table 表名 add 字段名 类型(长度) [comment 注释];
-
修改数据类型
alter table 表名 modlfy 字段名 新数据类型(长度);
-
修改字段和类型
alter table 表名 change 旧字段名字 新字段名字 类型(长度);
-
删除字段
alter table 表名 drop 字段名;
-
修改表名
alter table 表名 rename to 新表名;
-
删除表
drop table [if exits] 表名;
-
添加主键
alter table 表名 modify column 主键字段 int aotu_increment;
4. DML
4.1 添加数据
-
给指定字段添加数据
insert to 表名 (字段1...字段n) values (value1,...valuesn);
-
给全部字段添加数据
insert to 表名 values (值1,值2,....值n);
-
批量添加数据
insert to 表名 values (值1,值2....值n),....(值1,值2,...值n);
-
字符串和日期类型数应该包含在引号中
4.2 字符串类型
char | 定长字符串 | 性能好 |
---|---|---|
varchar | 变长字符串 | 性能较差 |
4.3 日期类型
date | YYYY-MM-DD | 日期值 |
---|---|---|
time | HH:MM:DD | 时间值持续时间 |
year | YYYY | 年份值 |
---|---|---|
datetime | YYYY-MM-DD HH:MM:SS | 混合时间和时间值 |
4.4DML-修改数据
update 表名 set 字段名1=值1,字段名2=值2,... [where 条件];
4.5 DML-删除数据
delete from 表名 [where 条件];
注意 : 条件没有则会删除整张表的所有数据
5.DQL
5.1 基本查询
语法
-
查询多个字段
select 字段1,字段2,... from 表名;
-
查询所有字段
select * from 表名;
5.2 条件查询
语法
select 字段列表 from 表名 where 条件列表;
条件类型
- > < != =
- between...and... 在某个范围之内(含最大 最小值)
- in(...) 在in之后的列表中的值多选一
例如:选出年龄在18,20,40的
select * from emp where age in(18,20,40);
- like 占位符,模糊匹配( __ 匹配单个字符,**%**匹配任意字符)
例如: 查询名字为俩个字的
select * from emp where name like'_ _';
例如:查询身份证最后为x的
select * from emp where idcard like '%x';
- is null 是空
- and or
- not 或者!
5.3 聚合函数
将一列数据作为一个整体,进行纵向计算
语法
select 聚合函数(字段列表) from 表名;
注意 null是不参与计算的
例如:统计表有多少条数据
select count(*) from emp ;
例如:统计西安员工的年龄之和
select sum(age) from emp where workaddress = '西安' ;
5.4 分组查询
语法
select 字段列表 from 表名 [where 条件] group by 分组字段名 [having 分组后过滤的条件] ;
- having :指分组完再进行筛选的条件
例如
#按性别分组,统计男性和女性员工的平均年龄
select gander,avg(age) from emp group by gender ;
#查询年龄小于45员工,并根据工作地址分组,获取员工数量大于等于3的工作地址
select workaddress,count(*) from emp where age < 45 goup by workaddress having count(*) >= 3;
5.5 排序查询
语法
select 字段列表 from 表名 order by 字段1 排序方式1,字段2 排序方式2 ;
- asc:升序(默认值) desc : 降序
- 如果是多字段排序,当第一个字段值相同时,才会根据第二个字段进行排序
例如
select * from emp order by age asc ;
5.6 分页查询
语法
select 字段列表 from 表名 limit 起始索引,查询记录数 ;
注意 : 起始索引从0开始, 起始索引=(查询页码-1)* 每页显示记录数
例如
select * from emp limit 10,10;
6.DCL 权限管理
概念 :用来管理数据库用户、控制数据库的访问权限
6.1 DCl-管理用户
-
查询用户
use mysql;
#用户和权限信息都在user数据库里
select * from user -
创建用户
#语法 '%':表示任意主机
create user '用户名'@'主机名' identified by '密码';#创建用户itcast 只能在当前主机localhost访问,密码123456
create user 'itcast'@'localhost' identified by '123456';#创建用户itheima,可以在任意主机访问该数据库,密码:123456
create user 'itheima'@'%' identified by '123456'; -
修改用户密码
#语法
alter user '用户名'@'主机名' identified with mysql_native_password by '密码';#修改用户itheima的访问密码为1234
alter user 'itheima'@'%' identified with mysql_native_password by '1234'; -
删除用户
#语法
drop user '用户名'@'主机名'; -
要给用户配置简单密码
如果你想设置简单密码,需要降低Mysql的密码安全级别
set global validate_password_policy=LOW; # 密码安全级别低
set global validate_password_length=4; # 密码长度最低4位即可
6.2 DCL-权限控制
权限:
-
all 、all privileges : 所有权限
-
select : 查询数据权限
-
insert : 插入数据
-
update : 修改数据
-
delete : 删除数据
-
alter : 修改表
-
drop : 删除数据库或者表或者视图
-
create : 创建数据库或者表
#查询权限
show grants for '用户名'@'主机名' ;
#例如
show grants for 'heima'@'%' 表示只有连接与登录权限#授予权限
grant 权限列表 on 数据库名.表名 to '用户名'@'主机名';
#例如
grant all on . to 'heima'@'%'; 表示该用户在所有库中所有表中有所有权限#撤销权限
revoke 权限列表 on 数据库名.表名 from '用户名'@'主机名' ;
#例如
revoke all on itcast.* from 'heima'@'%'; 表示撤销hiema用户在itcast库中所有表的所有权限刷新权限,生效
flush privileges;
在windows上连接linux的mysql
-
创建用户:默认的root用户只能在当前节点localhost访问,是无法远程访问的,我们还需要创建一个root用户,用户远程访问@
#创建一个用户远程访问
create user 'root'@'%' identified with mysql_native_password by '密码';#并给root用户分配权限
grant all on . to 'root'@'%';刷新权限,生效
flush privileges;
这样之后,可在windows上远程连接到linux的mysql数据库,若连接超时,可把linux的防火墙关闭再尝试一下
7.mysql函数
7.1字符串函数
-
concat(s1,s2,...,s3) : 字符串拼接,将s1,s2...sn拼接成一个字符串
select concat('Hello','mysql');
Hellomysql
-
lower(str) : 将字符串str全部转为小写
-
upper(str) : 将字符串str全部转为大写
-
lpad(str,n,pad) : 左填充,用字符串pad对str的左边进行填充,达到n个字符串长度
例如:变更员工工号,统一为五位数,不足五位数的在前面补0
id | name | workno |
---|---|---|
1 | 小明 | 101 |
2 | 小红 | 102 |
3 | 小刚 | 103 |
4 | 小王 | 104 |
update emp set workno=lpad(workno,5,'0');
>>>
id | name | workno |
---|---|---|
1 | 小明 | 00101 |
2 | 小红 | 00102 |
3 | 小刚 | 00103 |
4 | 小王 | 00104 |
-
rpad(str,n,pad) : 右填充,用字符串pad对str的右边进行填充,达到n个字符串长度
-
trim(str) : 去掉字符串头部和尾部的空格
-
substring(str,start,len) : 返回从字符串str从strat起的len个长度的字符串,起始索引为1
select substring('Hello Mysql',1,5);
Hello
7.2数值函数
- ceil(x) : 向上取整
- floor(x) : 向下取整
- mod(x,y) : 返回x/y的取余
- rand() : 返回0到1内的随机数
- round(x,y) :求参数x的四舍五入的值,保留y位小数
例如:生成一个六位数的随机验证码
select lpad(round(rand()*1000000,0),6,'0');
7.3日期函数
-
curdate() : 返回当前日期
-
curtime() : 返回当前时间
-
now() : 返回当前日期和时间
-
year(date) :获取指定date的年份
-
month(date) : 获取指定date的月份
-
day(date) : 获取指定date的日期
-
date_add(date,interval expr type) : 返回一个日期/时间值加上一个时间间隔expr后的时间值
select date_add('2021-10-12 23:20:20',interval 50 year);
2071-10-12 23:20:20
-
datediff(date1,date2) : 返回起始时间date1和结束时间date2之间的天数
select datediff('2021-12-01','2021-10-01');
61
7.4流程函数
很常用,在sql语句中实现条件筛选,从而提高语句的效率
-
if(value, t, f) : 如果value为True,则返回t,否则返回f
select if(false,'ok','no')
no
-
ifnull(value1, value2) : 如果value1不为空,返回value1,否则返回f
select ifnull('ok','default') -------> ok
select ifnull(null,'default') -------> default -
case when [val1] then [res1]...else [default] end : 如果val1为True,返回res1,... 否则返回default默认值 when可写多个
例如:各科成绩大于等于85为优秀,大于等于60为及格,否则为不及格
select id,
name,
(case when math>=85 then '优秀' when math >=60 then '及格' else '不及格' end) as '数学',
(case when english>=85 then '优秀' when english >=60 then '及格' else '不及格' end) as '英语'
from emp;
- case [expr] when [val1] then [res1],...else [default] end : 如果expr的值等于val1,返回res1,... 否则返回default默认值 when可写多个
例如:查看员工姓名和工作地址(北京/上海-->一线城市,其他-->二线城市)
select name,
(case workaddress when '北京' then '一线城市' when '上海' then '一线城市' else '二线城市' end) as '工作地址'
from emp;
8.约束
8.1 概念
约束是作用于表中字段上的,可以在创建表或者修改表的时候添加约束,违法约束会报错
目的 : 保证数据库中数据的正确、有效性和完整性
注意 : 多个约束之间用空格分开即可
自动增长:auto_increment
8.2 非空约束
关键字 :not null
限制该字段的数据不能为空
8.3 唯一约束
关键字:unique
保证该字段的所有数据都是唯一,不重复的
8.4 主键约束
关键字: primary key
主键是一行数据的唯一标识,要求非空并且唯一
8.5 默认约束
关键字: default
保存数据时,如果未指定该字段的值,则采用默认值
8.6 检查约束
关键字 : check
保证字段值满足某一个条件
create table user(
in int primary key auto_increment comment '主键', #主键,并且自动增长
name varchar(10) not null unique comment '姓名', #不为空,并且唯一
age int check(age>0 and age<=120) comment '年龄', #大于0并且小于等于120
status char(2) default '1' comment '状态', #如果没有指定该值,默认为1
gender char(1) comment '性别' #无条件
)comment '用户表' ;
8.7 外键约束
关键字: foreign key
用来让俩张表的数据之间建立连接,保证数据一致性和完整性
具有外键的表称为子表,主键的表称为父表
-
建表时创建外键的语法
create table 表名(
字段名 数据类型,
....
[constraint] [外键名称] foreign key(外键字段名) references 主表(主表列名)
);
-
建表完后创建外键的语法
alter table 表名 add constraint 外键名称 foreign key(外键字段名) references 主表(主表列名);
-
删除外键
alter table 表名 drop foreign key 外键名称 ;
例:
alter table emp add constraint fk_emp_dept_id foreign key(dept_id) references dept(id); #创建外键
alter table emp drop foreign key fk_emp_dept_id ; #删除外键
-
添加主键
alter table 表名 modify column 主键字段 int aotu_increment;
8.7.1 外键约束:删除/更新行为
-
no action : 当在父表删除/更新对应记录时,首先检查该记录是否有对应外键,如果有,则不允许删除/更新
-
cascade : 当在父表删除/更新对应记录时,首先检查该记录是否有对应外键,如果有,则也删除/更新外键在子表中的记录
-
set null : 当在父表删除对应记录时,首先检查该记录是否有对应外键,如果有,则设置子表中该外键值为null(这要求外键允许取null值)
alter table 表名 add constraint 外键名称 foreign key(外键字段名) references 主表(主表字段名)
on update cascade on delete cascade ;
9. 多表关系
-
一对多(多对一) : 案例如部门与员工的关系,一个部门对应多个员工,一个员工对应一个部门,
实现 : 再多的一方表中建立外键,指向另一方的主键
-
多对多的关系 : 如学生与课程的关系,一个学生可以选修多门课程,一门课程可以供多个学生选择
实现 : 建立第三张中间表,中间表至少包含俩个外键,分别关联俩方主键
-
一对一关系 : 如用户与用户之详情的关系, 一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他详情字段放在另一张表中,以提升效率
实现 :在任意一方加入外键,关联另外一方的主键,并且设置外键为唯一的(unique)
10. 多表查询
从多张表中查询数据
多卡尔积 : 指俩个集合A与集合B的所有组合情况
emp 表:
id | name | dept_id |
---|---|---|
1 | 小明 | 1 |
2 | 香杠 | 2 |
3 | 小红 | 1 |
4 | 小王 | 3 |
5 | 小李 | 4 |
6 | 小鹏 | 3 |
7 | 小光 | 5 |
8 | 小康 | null |
dept 表:
id | name |
---|---|
1 | 项目部 |
2 | 学习部 |
3 | 管理部 |
4 | 人事部 |
5 | 宣传部 |
6 | null |
10.1 连接查询
10.1.1 内连接
相当于查询A、B交集部分的数据
-
隐式内连接 :
select 字段列表 from 表1,表2 where 条件 ;
-
显示内连接
select 字段列表 from 表1 [inner] join 表2 on 连接条件 ;
#查询每一个员工的姓名,以及关联部门的名称
select emp.name,dept.name from emp,dept where emp.dept_id = dept.id;
或者 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;
>>>
name | name |
---|---|
小明 | 项目部 |
小红 | 项目部 |
香杠 | 学习部 |
小王 | 管理部 |
小鹏 | 管理部 |
小李 | 人事部 |
小光 | 宣传部 |
10.1.2 外连接
-
左外连接: 查询左表所有数据和俩张表交集的部分数据
-
右外连接: 查询右表所有的数据和俩张表交集的部分数据
select 字段列表 from 表1 left [outer] join 表2 on 条件...;
select 字段列表 from 表1 right [outer] join 表2 on 条件...;#查询emp表所有数据,和相对应的部门信息
select e.*,d.name from emp e left join dept d on e.dept_id = d.id ;
>>>
id | name | dept_id | name |
---|---|---|---|
1 | 小明 | 1 | 项目部 |
2 | 香杠 | 2 | 学习部 |
3 | 小红 | 1 | 项目部 |
4 | 小王 | 3 | 管理部 |
5 | 小李 | 4 | 人事部 |
6 | 小鹏 | 3 | 管理部 |
7 | 小光 | 5 | 宣传部 |
8 | 小康 | null | null |
10.1.3 自连接
自连接查询可以是内连接,也可以是外连接
注意 : 必须给表取别名
select 字段列表 from 表1 别名1 join 表1 别名2 on 条件...;
10.2 联合查询---union,union all
把多次的查询结果合并起来形成一个新的查询结果集
select 字段列表 from 表1...
union [all]
select 字段列表 from 表2... ;
注意 : 对于联合查询的多张表的列数必须保持一致,字段类型也需要保持一致
- union all : 会将全部的数据直接合并在一起,
- union : 会对合并后的数据去重
10.3 子查询
-
概念:sql语句中嵌套select语句,称为嵌套语句,又称为子查询
#例如
select * from t1 where column1=(select column1 from t2);
子查询外部的语句可以是insert、update、delete、select的任何一个
根据子查询结构不同,分为:
- 标量子查询 :子查询结果为单个值
- 列子查询 :子查询结果为一列
- 行子查询 :子查询结果为一行
- 表子查询 :子查询结果为多行多列
根据子查询位置,分为:where 之后、from 之后、select 之后
10.3.1 标量子查询
概念 : 子查询返回结果为单个值(数字、字符串、日期等)
常用操作符 : = 、 <、>等
#查询销售部的所有员工信息 现在dept表查销售部id,再到emp表中筛选员工
select * from emp where dept_id=(select id from dept where name='销售部');
#查询'东方白'入职之后的员工信息
select * from emp where entrytime > (select entrytime from emp where name='东方白');
10.3.2 列子查询
概念 : 子查询结果返回一列(可以是多行)
常用操作符 :in、not in 、 any 、some 、all
-
any :子查询返回列中,有任意一个满足即可
-
some:与any相同
-
all:子查询返回列表中的所有值都必须满足
#查询销售部和市场部所有员工信息
select * from emp where dept_id in(select id from dept where name='销售部' or name='市场部');#查询比财务部所有人工资都高的员工信息
select * from emp where salary > all(select salary from emp where dept_id=(select id from dept where name='财务部'));
10.3.3 行子查询
概念: 子查询返回结果是一行(可以是多列)
常用操作符 : = < > in 、not in
#查询与张无忌的薪资及其直属领导相同信息的员工信息
select * from emp where (salary,managerid)=(select salary,managerid from emp where name='张无忌');
10.3.4 表子查询
概念 :子查询返回结果是多行多列,返回一张新表,经常放在from之后
常用操作符:in
#查询与'陆帐客','宋院桥' 的薪资和职位相同的员工信息
select * from emp where (job,salary) in (select job,salary from emp where name='陆帐客' or name='宋院桥');
#查询入职日期是'2006-01-01'之后的员工信息及其部门信息
select e.*,d.* from (select * from emp where entrydate >'2006-01-01') e left join dept d on e.dept_id=d.id;
#查询低于本部门平均工资的一个信息
select * from emp e2 where e2.salary < (select avg(e1.salary) from emp e1 where e1.dept_id=e2.dept_id);
#查询所有部门员工人数
select d.id,d.name,(select count(*) from emp e where e.dept_id=d.id) '人数' from dept d;
#查询学生逃课情况,展示出学生名称 学号 课名
select s.name,s.no,c.name from student s,student_courage sc,course c where s.id=sc.studentid and sc.courseid=c.id;
11 事务
概念 : 事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有操作作为一个整体,一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。
mysql的事务是默认自动提交的,当执行一条DML语句,mysql会立即隐式的提交事务
11.1 事务操作
#查看|设置事务提交方式
select @@autocommit; --结果为1:自动提交,结果为0:手动提交
#将事务设置为手动提交
select @@autocommit=0;
#提交事务
commit;
#回滚事务
rollback;
#开启事务
start transaction 或 begin;
- 方式一:将事务设置为手动提交,如果事务没报错,就执行commit;报错就执行rollback;
- 方式二:将开启事务与要执行的语句一起执行,如果没报错就执行commit;,报错就执行rollback;
11.2 事务四大特性
- 原子性 :事务是不可分割的最小单元,要么全部成功,要么全部失败
- 一致性 :事务完成时,必须使所有的数据都保持一致状态
- 隔离性 : 数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行
- 持久性 :事务一旦提交或者回滚,它对数据库中的数据该表是永久的
11.3 并发事务问题
- 脏读:一个事务读到另一个事务还没有提交的数据
- 不可重复读 :一个事务先后读取同一条记录,但俩次读取的数据不同
- 幻读 : 一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了幻影
11.4 事务隔离级别
- read uncommitted : 有脏读 有不可重复读 有幻读
- read committed: 没有脏读 有不可重复读 有幻读
- repeatable read(默认):没有脏读 没有不可重复读 有幻读
- serializable:没有脏读 没有不可重复读 没有幻读
事务隔离级别越高,数据越安全,但性能越低
#查看事务隔离级别
select @@transaction_isolation
#设置事务隔离级别 --session : 指当前会话 ,gloal:指所有会话
set [session | global] transaction isolation level {read uncommitted | read committed | repeatable read | serializable};
12 存储引擎
存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式,存储引擎是基于表的,而不是基于库,所以存储引擎也可被称为表类型。
mysql默认的存储引擎是InnoDB
#在创建表时,指定存储引擎:
create table 表名(
字段1,字段类型
字段2,字段类型
....
)engine=innoDB [comment 表注释];
#查看当前数据库支持的存储引擎
show engines;
12.1 InnoDB存储引擎
InnoDB :是一种兼顾高可靠性和高性能的通用存储引擎,在mysql5.5之后,innoDB是默认mysql存储引擎
innoDB特点
-
DML操作遵循ACID模型,支持事务
-
行级锁,提高并发访问性能
-
支持外键foreign key 约束,保证数据的完整性和正确性
-
文件:
xxx.ibd : xxx代表的是表名,innoDB引擎的每张表都会对立这样一个表空间文件,存储该表的表结构(frm、sdi)、数据和索引
ibd文件时二进制,查看文件可以到cmd中指到文件上一个路径,输入ibd2sdi+文件名
- 逻辑存储结构 :表空间、段、区、页、行
表空间包含若干段,段中包含若干区,区中包含若干页,页中包含若干行;一个区大小是1M,一个页大小为16k
12.2 MylSAM存储引擎
MylSAM是mysql早期的默认存储引擎
特点 :
- 不支持事务,不支持外键
- 支持表锁,不支持行锁
- 访问速度快
文件:
- xxx.sdi : 存储表结构信息
- xxx.MYD:存储数据
- xxx.MYL: 存储索引
12.3 Memory存储引擎
Memory引擎的表数据存储在内存中,由于受到硬件问题,或者断电问题的影响,只能将这些表作为临时或者缓存使用
特点:
- 内存存放
- Hash索引
文件:
- xxx.sdi: 存储表结构信息
索引
索引是帮助mysql高效获取数据的数据结构(有序)
优点 :
- 提高数据检索的效率,降低数据库的IO成本
- 通过索引列对数据进行排序,降低数据排序成本,降低cpu的消耗
缺点 :
- 索引列也是占空间的
- 索引大大提高了查询效率,同时却也降低更新表的速度,如对表进行insert、update、delete时,效率降低
1 索引结构
msyql的索引是在存储引擎实现的,不同的存储引擎有不同的结构
- B+tree索引 :最常见的索引类型,大部分引擎都支持B+tree索引
- Hash :底层数据结构是哈希表的实现,只有精通匹配索引列的查询才有效,不支持范围查询
B+tree索引、InnoDB、MyISAM、Memory存储引擎都支持,Hash索引,只有Memory存储引擎支持
- 二叉树 :
缺点:顺序插入时,会形成一个链表,查询性能大大降低,大数据量情况下,层级较深,检索速度慢 ,红黑树可以解决掉顺序插入时问题,但解决不了后面

- B-tree:以树的一个节点的子节点个数最大为5阶的B-tree为例(每个节点最多存储4个key,5个指针)

- B+tree :

所有元素都出现在叶子节点,所有叶子节点形成单向链表,上面分叶子节点只起到索引,不存储数据,数据存储在叶子当中,而B-tree索引分叶子节点与叶子节点都存数据
B+tree与B-tree区别:
mysql索引数据结构对经典的B+tree进行优化,在原B+tree基础上,增加了一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的B+tree,提高区间访问的性能
- Hash:
哈希索引就是采用一定的Hash算法,将键值换算成新的Hash值,映射到对应的槽位上,然后存储到Hash表中,如果俩个(或者多个)键值,映射到一个相同的槽位上,他们就产生了hash冲突(也称为hash碰撞),可以通过链表来解决

特点:
- hash索引只能用于对等比较(=、in),不支持范围查询(between、>、<),因为hash索引是无序的
- 无法利用索引来完成排序操作
- 查询效率高,通常只需要一次检索就可以了,效率通常要高于B+tree索引
引擎支持:mysql中,支持Hash索引的是Memory引擎,而InnoDB中具有自适应Hash功能,Hash索引是存储引擎根据B+tree索引在指定条件下自动构建的
为什么InnoDB存储引擎选择使用B+tree索引结构?
- 相对于二叉树,层级更少,搜索效率高
- 对于B-tree,无论是叶子节点还是非叶子节点,都会保持数据,这样导致一页中存储的键值减少,指针跟着减少,要同样保持大量数据,只能增加树的高度,导致性能降低
- 相对于Hash索引,B+tree支持范围匹配及排序操作
索引分类
分类 | 含义 | 特点 | 关键字 |
---|---|---|---|
主键索引 | 针对于表中主键创建索引 | 默认自动创建,只能有一个 | primary |
唯一索引 | 避免同一个表中某数据列中值重复 | 可以有多个 | unique |
常规索引 | 快速定位特点数据 | 可以有多个 | -- |
全文索引 | 全文索引查找的是文本中关键词,而不是比较索引中值 | 可以有多个 | fulltext |
在InnoDB存储引擎中,根据索引的存储形式,又可以分为以下俩种:
分类 | 含义 | 特点 |
---|---|---|
聚集索引 | 将数据存储与索引放在了一块,索引结构的叶子节点保存了行数据 | 必须有,而且只有一个 |
二级索引 | 将数据索引分开存储,索引结构的叶子节点关联的是对应的主键 | 可以存在多个 |

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

根据name='Arm'来查询,它先根据二级索引找到它对应的id值,而*指的是这一行的数据,然后再到聚集索引中去找到对应的行数据,这种也称为回表查询。
思考:
-
以下sql语句,哪个执行效率更高?
#id为主键,name字段创建的有索引
select * from user where id=10;
select * from user where name='Arm';
答:第一条执行效率更高,因为第一条是根据主键索引,主键索引是聚集索引,叶子节点下面存储的是行数据;而第二条sql语句是用二级索引进行查询的,叶子节点下面存储的是主键信息,有回表查询,效率相对更低
- InnoDB主键索引的B+tree高度为多高呢?

索引语法
#创建索引
create [unique|fulltext] index index_name on table_name(index_col_name,...);
#查看索引
show index from table_name;
#删除索引
drop index index_name on table_name;
#name字段为姓名字段,该字段值可能重复,为该字段创建索引
create index idx_name on tb_user(name);
#phone手机号字段值是非空并且唯一,为该字段创建唯一索引
create unique index idx_user_phone on tb_user(phone);
#为profession、age、status创建联合索引
create index idx_user_pro_age_sta on tb_user(profession,age,status);
SQL性能分析
SQL执行频率
mysql客户端连接成功后,通过show [session|global] status 命令可以提供服务器状态信息,通过如下指令,可以查看当前数据库的insert、update、delete、select 的访问频次。再决定对增删改查中的哪一个进行sql优化。
#查看访问频次
show global status like 'com_______'; 七个横杠
慢查询日志
慢查询日志记录了所有执行时间超过了指定参数(long_query_time,单位:秒,默认十秒的所有SQL语句的日志
mysql的慢查询日志默认是没有开启的,linux的需要在mysql的配置文件(/etc/my.cnf)中配置
#linux中,在/etc/my.cnf文件最后添加如下信息
slow_query_log=1 #开启慢查询开关
long_query_time=2 #设置慢日志时间为俩秒,sql语句超过了俩秒就会被记录在慢日志中
配置完成后,通过重新启动mysql服务器进行测试
linux查看慢日志记录的信息 查看文件:/var/lib/mysql/localhost-slow.log
profile
show profiles 能够在做sql优化时帮助我们了解时间都耗费到哪里去了,通过have_profiling参数,能够看到当前mysql是否支持profile操作。
#查看mysql是否支持profile操作
select @@have_profiling;
默认profiling是关闭的,可以通过set语句在session|global 级别开启profiling
#查看profiling开关有没有打开 ,0为关闭,1为打开
select @@profiling;
#将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_
explain执行计划
explain或者desc命令获取mysql如何执行select语句的信息,包括在select语句执行过程中表如何连接和连接顺序。
#语法 直接在select语句之前加上关键字explain
explain select 字段列表 from 表名 where 条件;
#例如
explain select * from tb_user where id=1;
输出形式:

-
id:select查询的序列号,表示查询中执行的select子句或者是操作表的顺序(id相同,执行顺序从上到下;id不相同,值越大,越先执行)
-
select_type:表示select类型
-
type:表示连接类型,性能由好到差的连接类型为Null、system、const、eq_ref、ref_range、index、all)
-
key:实际使用的索引,如果为null,则没有使用索引
-
filtered:表示返回结果的行数占需要读取行数的百分比,filtered的值越大越好
-
extra:using where 表示全局扫描;using index 表示用索引
索引使用
最左前缀法则
如果索引了多列(联合索引),要遵守最坐前缀法则,最左前缀法则指的是查询从索引的最左侧列开始,并且不跳过索引中的列。如果跳过某一列,索引将部分失效(后面的字段索引失效)。
create index indx_user_pro_age_sta on tb_user(profession,age,status); #为profession,age,status字段创建联合索引
explain select * from tb_user where profession='软件工程' and age=31 and status='0'; #用了索引,索引生效
explain select * from tb_user where age=31 and status='0'; #索引失效,没用到索引
explain select * from tb_user where profession='软件工程' ; #索引生效,用到了索引
最左侧的字段是profession,只有他存在,它才会用到索引,跟它在哪的位置无关
范围查询
联合索引中,出现范围查询(< ,>),范围查询的右侧的列索引失效
explain select * from tb_user where profession='软件工程' and age>30 and status='0'; #用到了索引,但没有用到status的索引列,status的索引列失效
#规避索引列失效的方法 加个=
explain select * from tb_user where profession='软件工程' and age>=30 and status='0';
索引列运算
索引列运算 :不要在索引列上进行运算操作,索引将会失效
#查询手机尾号是15的
explain select * from tb_user where substring(phone,10,2)='15'; #没有用到索引,索引失效
字符串不加引号
-
字符串不加引号 :字符串类型的字段使用时,不加引号,索引将会失效
explain select * from tb_user where phone=17779990015; #索引列失效
模糊查询
-
模糊查询 :如果仅仅是尾部模糊匹配,索引不会失效;如果是头部模糊匹配,索引失效
explain select * from tb_user where profession like '软件%'; #用到索引
explain select * from tb_user where profession like '%工程'; #索引失效
or 连接条件
-
or 连接条件 :用or分开的条件,如果or前的条件中的列有索引,而后面的列没有索引,那么索引失效
explain select * from tb_user where id=10 or age=33; #由于age没有索引,所以即使id有索引,索引也会失效,所以解决时需要针对age建立索引
数据分布影响
-
数据分布影响 :如果mysql评估使用索引比全表更慢,则不使用索引
select * from tb_user where phone>='1779999005'; #没有选择用索引,用全局扫描方式
#因为大部分数据都大于等于177999005,则mysql评估下来,走全局扫描更快,所以就不会选择有索引
SQL提示
- SQL提示 :当一个字段有俩个索引时,sql会进行评估,选择最好的索引,也可以认为控制用哪一个索引,sql提示就是在sql语句中加入一些人为的提示来达到优化操作的目的
use index :建议sql使用这个索引 ignore index :忽略这个索引 force index:强迫sql用这个索引
select * from tb_user use index(idx_user_pro) where profession='软件工程'; #idx_user_pro是索引的名字
select * from tb_user ignore index(idx_user_pro) where profession='软件工程';
select * from tb_user force index(idx_user_pro) where profession='软件工程';
覆盖索引
-
覆盖索引:尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在读索引中已经全部能够找到),减少select *。
- select id,profession from tb_user where profession='软件工程' and age=31 and status='0';
- select id,profession,age,status from tb_user where profession='软件工程' and age=31 and status='0';
- select * from tb_user where profession='软件工程' and age=31 and status='0';
#1和2俩条效率差不多并且大于第三个,因为profession,age,status是联合索引,而联合索引是属于二级索引的,二级索引的叶子节点存的就是id,所以第一个和第二个不需要回表查询去查其他字段的信息。
前缀索引
-
前缀索引:当字段类型为字符串时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO,影响查询效率,此时可以只得字符串的一部分前缀,建立索引,这样大大节约索引空间,从而提高索引效率。
#语法 idx_xxx指索引名,table_name指表名, n指长度
create index idx_xxx on table_name(column(n)); -
前缀长度:
可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1,这是最好的性能索引选择性,性能也是最好的。
#计算选择性,distinct指去重
select count(distinct email)/count(*) from tb_user;
#计算email的前五个的选择性
select count(distinct substring(email,1,5))/count(*) from tb_user;
#例如
create index in_mail on tb_user(email(5)); #将email的前五个设置为索引
select * from tb_user where email='akdfkjsak.com';
单列索引与联合索引
- 单列索引:一个索引只包含单个列
- 联合索引:一个索引包含了多个列
在业务场景中,如果存在多个查询条件,考虑针对于查询字段建立索引时,建议建立联合索引,而非单列索引
索引设计原则
- 针对于数据量较大,且查询比较频繁的表建立索引。
- 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引。
- 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高。
- 如果是字符串类型的字段,字段的长度较长,可以针对于字段的特点,建立前缀索引。
- 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。
- 要控制索引的数量,索引并不是多多益善,索引越多,雌护索引结构的代价也就越大,会影响增删改的效率。
- 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个
索引最有效地用于查询。
SQL优化-插入数据
insert 优化
在插入数据时,合理的方式使sql效率优化
- 批量插入
- 手动提交事务
- 主键顺序插入
用这些方式可以优化sql的效率
大批量插入数据
如果一次性需要插入大批量数据,使用insert语句插入性能较低,此时可以使用mysql数据库提供的 load 指令进行插入。
#客户端连接服务端时,加上参数--local-infile
mysql --local-infile -u root -p;
#查询local_infile是否开启,默认不开启
select @@local_infile;
#设置全局参数local_infile为1,开启从本地加载文件导入数据开关
set global local_infile=1;
#执行load指令将准备好的数据,加载到表结构中
load data local infile '文件路径' into table 表名 fields terminated by '分隔符' lines terminated by '\n';
#其中','指一行数据是用什么分隔的,'\n'指每一行之间用什么分隔
主键优化
- 数据组织方式:在InnoDB存储引擎中,表结构都是根据主键顺序组织存放,这种存储方式的表称为索引组织表

- 页分裂 :页可以为空,也可以填充一半,也可以填充百分百,每个页包含了2-N行数据(如果一行数据过大,会行溢出),根据主键排列。 主键乱序插入时,就会出现页分裂

-
页合并 :当删除一行记录时,实际上记录并没有被物理删除,只是记录被标记为删除并且它的空间变得允许被其他记录声明使用。 当页中删除的记录达到一定(默认为页的百分之五十),InnoDB会开始寻找最靠近的页(前或后)看看是否可以将俩个页合并以优化空间使用。
-
主键设计原则 :
1. 满足业务需求情况下,尽量降低主键长度 1. 插入数据时,尽量选择顺序插入,选择使用auto_increment自增的主键 1. 尽管不要使用UUID做主键或者是其他自然主键,如身份证 1. 业务操作时,避免对主键的修改
order by 优化
-
using filesort:通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫FileSort排序。
-
Using index : 通过有序索引顺序扫描直接返回有序数据,这种情况即为using index,不需要额外排序,操作效率高。前提得是覆盖索引。
explain select id,age,phone from tb_user order by age,phone; #输出中的extra显示using filesort
#给age,phone创建联合索引
create index idx_age_pho on tb_user(age,phone);
explain select id,age,phone from tb_user order by age,phone; #输出中的extra显示using indexexplain select id,age,phone from tb_user order by age desc,phone desc;
desc指降序,extra中显示Backward index scan(指按索引反向索引);using index
explain select id,age,phone from tb_user order by phone,age; #extra中显示using index;using filesort
explain select id,age,phone from tb_user order by age asc,phone desc;
#extra显示using index;using index ,原因是创建索引时,没指定顺序默认升序,先按age升序排,age相同再按phone升序排列
#若想继续优化,得再创建一个索引create index idx_user_age_pho_ad on tb_user(age asc,phone desc);
explain select id,age,phone from tb_user order by age asc,phone desc; extra显示using index

总结:
- 根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则。
- 尽量使用覆盖索引。
- 多字段排序,一个升序一个降序,此时需要注意联合索引在创建时的规则(ASC/DESC)。
- 如果不可避免的出现filesort,大数据量排序时,可以适当增大排序缓冲区大小sort buffer size(默认256k)。
group by 优化
#为profession,age,status建立联合索引
create index idx_pro_age_sta on tb_user;
#explain select profession,count(*) from tb_user group by profession; #extra中显示using index
#explain select age,count(*) from tb_user group by age; #extra中显示using index;using temporary 性能一般 不满足最左前缀发则
explain select profession,age from tb_user group by profession,age; #extra中显示using index
explain select age,count(*) from tb_user where profession='软件工程' group by age;
# extra中显示using index,因为有profession,满足最左前缀法则
在分组操作时,可以通过索引来提高效率
在分组操作时,索引的使用也是满足最左前缀法则
是通过索引直接返回排序结果的排序都叫FileSort排序。
-
Using index : 通过有序索引顺序扫描直接返回有序数据,这种情况即为using index,不需要额外排序,操作效率高。前提得是覆盖索引。
explain select id,age,phone from tb_user order by age,phone; #输出中的extra显示using filesort
#给age,phone创建联合索引
create index idx_age_pho on tb_user(age,phone);
explain select id,age,phone from tb_user order by age,phone; #输出中的extra显示using indexexplain select id,age,phone from tb_user order by age desc,phone desc;
desc指降序,extra中显示Backward index scan(指按索引反向索引);using index
explain select id,age,phone from tb_user order by phone,age; #extra中显示using index;using filesort
explain select id,age,phone from tb_user order by age asc,phone desc;
#extra显示using index;using index ,原因是创建索引时,没指定顺序默认升序,先按age升序排,age相同再按phone升序排列
#若想继续优化,得再创建一个索引create index idx_user_age_pho_ad on tb_user(age asc,phone desc);
explain select id,age,phone from tb_user order by age asc,phone desc; extra显示using index
外链图片转存中...(img-aoFQNyJH-1727666810310)
总结:
- 根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则。
- 尽量使用覆盖索引。
- 多字段排序,一个升序一个降序,此时需要注意联合索引在创建时的规则(ASC/DESC)。
- 如果不可避免的出现filesort,大数据量排序时,可以适当增大排序缓冲区大小sort buffer size(默认256k)。
group by 优化
#为profession,age,status建立联合索引
create index idx_pro_age_sta on tb_user;
#explain select profession,count(*) from tb_user group by profession; #extra中显示using index
#explain select age,count(*) from tb_user group by age; #extra中显示using index;using temporary 性能一般 不满足最左前缀发则
explain select profession,age from tb_user group by profession,age; #extra中显示using index
explain select age,count(*) from tb_user where profession='软件工程' group by age;
# extra中显示using index,因为有profession,满足最左前缀法则
在分组操作时,可以通过索引来提高效率
在分组操作时,索引的使用也是满足最左前缀法则