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

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

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

相关推荐
wen's22 分钟前
React Native安卓刘海屏适配终极方案:仅需修改 AndroidManifest.xml!
android·xml·react native
编程乐学1 小时前
网络资源模板--基于Android Studio 实现的聊天App
android·android studio·大作业·移动端开发·安卓移动开发·聊天app
叁沐2 小时前
MySQL 11 怎么给字符串字段加索引?
mysql
没有了遇见4 小时前
Android 通过 SO 库安全存储敏感数据,解决接口劫持问题
android
hsx6664 小时前
使用一个 RecyclerView 构建复杂多类型布局
android
hsx6664 小时前
利用 onMeasure、onLayout、onDraw 创建自定义 View
android
守城小轩4 小时前
Chromium 136 编译指南 - Android 篇:开发工具安装(三)
android·数据库·redis
whysqwhw4 小时前
OkHttp平台抽象机制分析
android
hsx6665 小时前
Android 内存泄漏避坑
android
whysqwhw5 小时前
OkHttp之okhttp-bom模块的分析
android