目录
前言基础知识MySQL增删改查...代码讲解
MySQL服务器端的逻辑架构说明
1. 存储引擎
1.1. INNODB 存储文件结构
1.2. MYISAM引擎
1.3. Archive引擎
1.4. CSV引擎
1.5. Memory引擎
2. 索引
2.1 常见索引
2.2. 索引的声明和使用
2.3. 那些字段适合创建索引?
2.4. 索引的优化
2.5. 那些情况不适合索引
3. 性能优化工具
3.1. EXPLAIN性能优化工具的使用
4. 索引优化案列和失效案列
4.1. 索引失效案列
4.2. 索引优化
5. 数据库设计范式
5.1. 范式(Normal Form)
5.2 范式的优缺点
6. 事务
6.1. 事务的特性ACID
6.2. 事务的状态
6.3. 事务语法
6.4. 事务的并发问题
6.5. 隔离级别
6.6. MySQL事务日志
6.7. 锁机制
默认的存储引擎是INNODB 存储引擎是正对表的“表处理器”
INNODB引擎:支持外键,事务存储引擎
# .frm 文件是存放的表结构,表的定义信息;
# .ibd 表数据和索引;(数据既索引)
# INNODB写的处理效率差一些,对内存要求高。
# .frm文件是存放的表结构,表的定义信息;
# .myd文件是存放着表中的数据;
# .myi文件存放着表的索引信息;
索引是一种数据结构用于快速找到某一条数据,比如字典里面的目录页,图书馆的存放目录。
# 优点:提高检索速率,保证数据库表中的唯一性,加速表之间的连接,减少查询中分组排序的花间
# 缺点:创建和维护索引需要耗费时间表,索引需要存储空间的,占用磁盘空间,索引减少表数据的更新速度
索引的数据结构可以参考这篇文献
MySQL索引的数据结构_杉菜酱_的博客-CSDN博客_mysql索引数据结构shancaijiangzi.blog.csdn.net/article/details/122640854正在上传…重新上传取消
# 普通索引
CREATE TABLE index_test(
id INT,
name VARCHAR(18),
age INT
...
INDEX index_name(id) #声明索引
);
# 为已创建成功的表创建索引
ALTER TABLE index_test ADD INDEX index_name(id);
OR
CREATE INDEX index_name(id) ON index_test;
# 删除索引的方式
ALTER TABLE index_test DROP INDEX index_name;
OR
DROP INDEX index_name ON index_test;
# 唯一索引
CREATE TABLE index_test(
id INT,
name VARCHAR(18),
age INT
...
UNIQUE INDEX index_name(id) #声明索引
);
# 为已创建成功的表创建唯一索引
ALTER TABLE index_test ADD UNIQUE INDEX index_name(id);
# 主键索引
CREATE TABLE index_test(
id INT PRIMARY KEY ,
name VARCHAR(18),
age INT
...
#声明索引
#主键索引是隐式创建
);
# 全文索引
CREATE TABLE index_test(
id INT,
name VARCHAR(18),
age INT,
detail TEXT,
...
FULLTEXT INDEX index_detail(detail) # 声明索引
);
# 为已创建成功的表创建全文索引
ALTER TABLE index_test ADD FULLTEXT INDEX index_detail(detail);
# 单列索引
CREATE TABLE index_test(
id INT,
name VARCHAR(18),
age INT
...
UNIQUE INDEX index_name(id) # 声明索引
);
#为已创建成功的表创建单列索引
ALTER TABLE index_test ADD UNIQUE INDEX index_name(id);
# 多列索引
CREATE TABLE index_test(
id INT,
name VARCHAR(18),
age INT
...
INDEX index_name_id(id,name) # 声明索引
);
#为已创建成功的表创建多列索引
ALTER TABLE index_test ADD FULLTEXT INDEX INDEX index_name_id(id,name);
# 空间索引
......
#查看索引的方式
SHOW INDEX FROM employees;
#8.0后 索引支持了降序索引 和 隐藏索引
# 如果AB都出现可以考虑一下建立联合索引并且通常以A字段在前B写在后面
# 连接表不能超过3张
# 对WHERE条件创建索引
# 对用于连接表的字段创建索引,连接字段多表中类型必须一致
# note:在varchar字段创建索引最好设置字段索引长度阿里巴巴手册规定长度为20
# note:超过33%散列度即适合作为索引
# note:每个索引都会占用存储空间,索引的创建会降低插入,修改和删除的性能,会增加MYSQL优化器生成执行计划的时间,降低了查询的性能。
# note:也就是在一个列定义多种索引
#基本语法
EXPLAIN SELECT * FROM empl1;
EXPLAIN SELECT * FROM empl1;执行结果
3.3.1. EXPLAIN优化字段说明
# id :
id如果相同,可以认为是一组,从上向下顺序执行
所在组中id值越大,优先级越高,越先执行
关注点:每个id号表示一趟独立查询,一个sql语句查询的趟数越少越好
# select_type :SELEC关键字对应查询的类型,确定小查询在大查询中扮演的是一个什么角色
SIMPLE:查询语句中不包含“UNION”或者子查询都算是“SIMPLE”类型
PRIMARY:包含“UNION”,“UNION ALL”或者子查询的来说,它是有几个小查询组成,其中最左边的查询的select_type的值就是“PRIMARY”,其余的
全部是“UNION”
SUBQUERY:如果包含子查询语句不能够转换对应的连表查询形式,并且该子查询不是相关子查询,该子查询的第一个
‘SELECT’关键字代表的那个查询的‘select_type’就是‘SUBQUERY’,如果该子查询是相关子查询则‘SELECT’关键字代
表的那个查询的‘select_type’就是‘DEPENDMENT SUBQUERY’
# table :表名
#查询的每一行记录对应一个单表
EXPLAIN SELECT * FROM empl1;
#驱动表和被驱动表(语句左侧的是驱动表右侧的是被驱动表)
# partitions :
# type :针对单表的访问方法
system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique > unique_subquery >
index_subquery > rang > index > all
其中前4个select的访问方式是比较重要的。SQL性能优化的目标:至少要达到rang级别,要求是ref级别,最好是const级别
# possible_key :
执行计划中可能使用到的索引
# key :
实际执行中正真使用的索引
# key_len :
实际使用的索引长度(即:字节数)帮你检查是否充分的利用上了索引
# ref :
当使用索引等值查询时与索引列进行等值匹配的对象信息
# rows :
预计需要读取的记录数
# filtered :
某个表经过搜索条件过滤后剩余记录条数的百分比
# Extra :
一些额外的信息
note:(索引命名即为索引创建的字段顺序)例如 index_id_name_age
即为 CREATE INDEX index_id_name_age(id,name,age) ON table_name;
index_id,index_id_name,index_id_name_age三个索引效果最好的时index_id_name_age
...WHERE id = ? AND name = ? AND age = ? # index_id_name_age效果最好 > index_id_name > index_id
MySQL可以为多个字段创建索引,一个索引可以包括16个字段。对于多列索引,过滤条件
要使用索引必须按照索引建立时的字段顺序,依次满足,一旦跳过某个字段,索引后面的字段都无法被使用
例:学生表中有 id,name,age,addir等字段 我们建立了联合索引(index_id_name_age),
当查询语句WHERE后面使用字段都会影响索引的使用
...id = ? AND name = ? AND age = ? # 完全使用index_id_name_age索引
...id = ? AND age = ? # 不完全使用index_id_name_age索引,只能使用id这个字段索引,虽然age字段也索引了,但是跳过了nage字段因此不能使用
...name = ? AND age = ? # 索引失效,必须最先使用id,再 name 最后age
...id = ? AND name = ? # 完全使用index_id_name_age索引
# 函数导致的索引失效
...WHERE LIKE name 'abc%'; # 使用索引
...WHERE `LEFT`(name,3) = 'abc'; # 索引失效
# 字段name为VARCHAR类型,并设置了索引,以下情况索引失效
...WHERE name = 123; # 索引失效(类型转换了INT转化为VARCHAR)
...WHERE name = '123'; # 使用索引
# 创建索引index_id_age_name
...WHERE id = 10 AND age > 20 AND name = 'abc'; # 索引不完全被使用只会使用索引中id字段和name字段
age后条件为范围导致age字段索引失效
# 优化
将索引字段创建顺序改变,将判断条件是范围的字段创建时排在最右测 index_id_name_age
总结:保持良好的开发习惯,我们在编写索引时应该将范围的字段习惯性的放在最右
...WHERE name != 'abc'; >> 索引失效
...WHERE name <> 'abc'; >> 索引失效
... WHERE age IS NULL; # 正常索引
... WHERE age IS NOT NULL; # 索引失效
结论:在设计数据表时将字段设置为非空约束,可以设置默认值
...WHERE name LIKE '%abc'; # 索引失效
结论:页面搜索严禁在左模糊或者全模糊,如果需要请走搜索引擎来解决
# 现有索引index_id
...WHERE id = 10 OR name = 'abc'; # index_id失效,原因(即使使用了id索引,但是OR中其他的字段一样的要全盘检索)
即:新增index_name 或者 创建index_id_name
原因:不同的字符集会字符集转换
优化总结:
# ①使用最频繁的列放在联合索引的最左测
# ②在多个字段都要创建索引的情况下,联合索引优于单值索引
# ③限制索引的数目:不超过6个
note:每个索引都会占用存储空间,索引的创建会降低插入,修改和删除的性能,会增加MYSQL优化器生成执行计划的时间,降低了查询的性能。
-- 左外连接
-- 内连接
对于内连接来说,在两个连接条件都有索引时,结果集小的表就为驱动表,结果集大的表就是被驱动表
概念:一个索引包含了满足查询结果的数据列就叫覆盖索引
理解:在联合索引使用不完全的情况下,利用没使用的索引字段过滤后再回表。
# 现有索引 index_id_name_age
EXPLAIN SELECT * FROM table_name
WHERE
id = 10 # id 字段可以索引
AND name = '%as' # name字段由于通配符%在前 索引字段name失效
AND age > 20; # age字段由于属于范围条件 age字段索引也失效
# 假设满足 id = 10 的记录有1000条,
# 满足id = 10 AND name = '%as' 的100条
# 满足id = 10 AND name = '%as' AND age > 20 的10条
#再没有开启ICP的情况下的执行逻辑:通过id索引找到id = 10的数据,满足条件的1000条数据,
然后开始1000次表操作去一次找到符合name,age符合条件的记录
# 再开启ICP的情况下的执行逻辑:通过id索引找到id = 10的数据,满足条件的1000条数据,然后索引条件下推
虽然name字段索引没有用上,但是我们索引中包含了name和age字段,于是我们就可以再找到1000满足id的记录后
先不回表,先过滤name,age最后再回表,即使用ICP后回表次数为10次,从而提升了效率。
索引条件下推ICP的使用条件
# EXISTS 和 IN的 选择?
# 驱动表A和被驱动表B
SELECT * FROM A WHERE c IN(SELECT c FROM B); # 当驱动表A数据多,被驱动表B数据少时适合用IN
SELECT * FROM A WHERE EXISTS (SELECT c FROM B WHERE B.c = A.c); # 当驱动表A数据少,被驱动表B数据多时适合用EXISTS
# 问: COUNT(*),COUNT(1),COUNT(字段)三者的速率?
# 答:COUNT(*),COUNT(1)执行效率差不多,但在MyISAM中由于表中默认设置了隐藏字段row_count,所以时间复杂度时O(1);
在InnoDB中时全表扫描,时间复杂度时O(n)
# 问:SELECT(*)的使用?
# 答:避免使用SELECT(*)①因为SQL在解析过程会通过数字字典将“*”转化为对应的字段名,会大大消耗资源和时间。
②无法使用覆盖索引
# 问: LIMIT 1的影响;
# 答:在没有索引的条件下LIMIT 1会再找到结果后终止继续扫面,但是再使用唯一索引的情况下再查找数据时不会全盘扫描,所以不需要LIMIT 1
# 多使用COMMIT
数据库设计应该符合安全性,正确性,数据冗余,方便程度的角度考虑
# 六种常见的范式,按照范式级别,从低到高
# 第一范式 - 第二范式 -第三范式 -巴斯范式 -第四范式 -第五范式
EG:字段名称user_info 字段说明:用户信息(包含真实姓名,电话,住址)
改写符合第一范式格式:
user_info改写成三个字段,分别是真实姓名,电话,住址
# 通俗的理解就是一张表表示一个对象
原表:PK(球员编号,球赛编号) -> (球员名字,比赛时间,比赛得分)
# 满足第二范式写法
表1:PK(球员编号) -> (球员名字)
表2:(球员编号,球赛编号)
表3:PK(球赛编号) -> (比赛时间,比赛得分)
# 原表:PK(员工ID,部门ID) -> (员工名字,员工薪资,部门名字,部门总人数)
# 满足第三范式写法
表1:PK(员工ID) -> (员工名字,员工薪资,部门名字)
表2:PK(部门ID) -> (部门名字,部门总人数)
优点:减小数据冗余
缺点:降低查询效率,关联了多张表,也可能使索引无效
概念:一组逻辑操作单元,使数据从一种状态变换到另一种状态
# step1 :开启事务 START TRANSACTION 或者 BEGIN
# step2 :一系列的操作
# step3 :结束状态:提交(COMMIT)或者中止(ROLLBACK)
隔离级别和并发性能成反比,隔离级别越高并能性就越差
隔离级别 | 脏写 | 脏读 | 不可重复读 | 幻读 | 加锁读 |
---|---|---|---|---|---|
READ UNCONMITED | NO | YES | YES | YES | NO |
READ COMMITED | NO | NO | YES | YES | NO |
REPEATABLE READ | NO | NO | NO | YES | NO |
SERIALIZABLE | NO | NO | NO | NO | YES |
事务的原子性,一致性和持久性是用redo log和undo log来保证的。
原理:在事务每次提交后,把事务修改的东西全都记录一下,以免在刷盘时候宕机,可以使用记录再重新执行刷盘,从而保证持久性。WAL:先写日志再写磁盘。只有日志写入成功才算事务提交成功。
特点:
日志的刷盘流程
innodb_flush_log_at_tx_commit 日志刷盘策略
innodb_flush_log_at_tx_commit = 1 # 默认的日志刷盘策略,安全性最好,执行最慢。
执行策略:事务执行中 > 不断的写日志redo log并写入到buffer > 事务提交立刻将buffer刷到page ache 并且立刻将日志从page ache 刷盘 (file)
innodb_flush_log_at_tx_commit = 2 : # 快,但相对不安全,在SQL宕机不影响,但操作系统宕机 前page ache 还没刷盘 就GG
执行策略:事务执行中 > 不断的写日志redo log并写入到buffer > 事务提交立刻将日志刷到page ache 等待系统进程(每秒执行一次)将日志从page ache 刷盘 (file)
innodb_flush_log_at_tx_commit = 0 : 快,但是最不安全,SQL在事务提交后并且还没有将日志刷盘就宕机就GG
执行策略:事务执行中 > 不断的写日志redo log并写入到buffer > 事务提交 > 等待系统进程(每秒执行一次)将buffer刷到page ache 再将buffer从page ache 刷盘 (file)
针对 INSERT # undo会记录主键值,当需要回滚时InnoDB会执行一个DELETE
针对 DELETE # undo会记录删除前记录的呢内容,当需要回滚时InnoDB会执行一个INSERT
针对 UPDATE # undo会记录修改前的被修改的字段内容,当需要回滚时InnoDB会执行一个与之相反的UPDATE将数据修改回去
事务的隔离性用锁机制来保证的
MySQL并发事务访问相同记录的情况
# 采用MVCC方式的话,读-写 操作彼此并不冲突,性能更高
# 采用加锁的方式的话,读-写 操作彼此需要排队,影响性能
锁的划分
# 加锁:
SELECT ... LOCK IN SHARE MODE; # S锁/读锁/共享锁
SELECT ... FOR UPDATE; # X锁/写锁/排他锁
表锁(粒度最大):开销小,并发差,无死锁问题
LOCK TABELE name READ/WRITE;
UNLOCK table_name;
innoDB中的行锁
6.7. MVCC(多版本并发控制)
MVCC(多版本并发控制) 多版本依靠undo log ,控制依靠tx_id 和 ReadView