MySQL

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 *。

    1. select id,profession from tb_user where profession='软件工程' and age=31 and status='0';
    2. select id,profession,age,status from tb_user where profession='软件工程' and age=31 and status='0';
    3. 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 index

    explain 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 index

    explain 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,满足最左前缀法则

在分组操作时,可以通过索引来提高效率

在分组操作时,索引的使用也是满足最左前缀法则

相关推荐
服装学院的IT男21 分钟前
【Android 14源码分析】WMS-窗口显示-流程概览与应用端流程分析
android·前端
vonlinee2 小时前
MySQL常用SQL语句(持续更新中)
数据库·sql·mysql
知识分享小能手2 小时前
mysql学习教程,从入门到精通,SQL UNION 运算符(27)
大数据·开发语言·数据库·sql·学习·mysql·数据分析
xs_20122 小时前
MySQL5.7实现分组排序
数据库·sql·mysql
可靠百灵鸟3 小时前
Python操作TXT文本:从入门到精通
数据库·python·mysql
逆流°只是风景-bjhxcc3 小时前
【表分区】MySQL表分区(partition)创建、查询、删除以及重建分区等
数据库·mysql
李小码哥3 小时前
mysql如何快速编写单表查询语句
数据库·mysql
潘多编程3 小时前
Spring Boot用Spring Security + JWT + MySQL实现基于Token的身份认证
spring boot·mysql·spring
计算机学姐3 小时前
基于SpringBoot+Vue的留学信息推荐系统
java·vue.js·spring boot·后端·mysql·spring·mybatis