存储引擎就是存储数据、建立索引、查询/更新/删除数据等的具体实现。存储引擎是基于表的,不是基于库的,一个数据库下不同表可以使用不同存储引擎,所以存储引擎也可被称为表类型。
create table if not exists department(
did numeric(4),
dname varchar(20),
dlocation varchar(20),
primary key(did)
)ENGINE = INNODB COMMENT "部门表";
逻辑存储结构
表空间、段、区、页、行
每个区的大小为1M,每个页的大小为16k。
一个表空间的物理实现就是一个ibd文件
MySQL中,默认情况下,每一张表对应一张表空间。表空间存储该表的表结构、数据和索引。
show variables like 'innodb_file_per_table';
可能一台服务器的80%内存分配给存储引擎的缓冲区
自动开启
表空间是InnoDB数据的物理存储组织方式
表空间就是那个文件夹吧
后台线程
-- 职员表
drop table sqlexercise.staff;
create table if not exists staff(
id int auto_increment,
sid int(4),
sname varchar(20),
sex numeric(1) not null comment '0:未知 1:男 2:女',
did numeric(4),
leader numeric(4),
hiredate datetime,
primary key(id),
UNIQUE INDEX uidx_sid(sid ASC),
INDEX idx_sname(sname ASC),
INDEX idx_did_leader_hiredate(did,leader,hiredate)
);
也可以手动创建索引
show index from staff
sql优化->查询优化->索引优化
覆盖索引:select的数据列只用从索引中就可以获得了,不必读取数据行。Using index:表明相应的select操作使用了覆盖索引,避免了访问表的数据行。
show [session|global] status命令可以提供服务器状态信息。
-- 查看INSERT、UPDATE、SELECT语句执行次数
show global status like 'Com_______';
慢查询日志
慢日志中记录所有执行实践超过long_query_time(单位为秒)的所有SQL语句的日志。默认情况下,慢查询日志没有开启,慢查询日志long_query_time默认值为10s。
# m.cnf
-- 设置开启慢查询日志,0表示关闭,1表示开启,默认值为0
slow_query_log=1
-- 设置慢查询阈值,单位为秒,默认值为10
long_query_time=2
profile
-- 查看每一条SQL耗时
show profiles;
-- 查看指定query_id的SQL语句各个阶段耗时情况
show profile for query query_id;
-- 查看指定query_id的SQL语句CPU使用情况
show profile cpu for query query_id;
explain
EXPLAIN或DESC获取MySQL执行SELECT的执行计划。
explain select * from staff;
全索引扫描
explain select id, leader, hiredate from staff;
explain select did, leader, hiredate from staff order by did, leader;
explain select did, leader, hiredate from staff order by did desc, leader desc;
若一正一反就不可利用索引排序,索引的升降,必须要做file_sort
这个时候需要反向建立索引
create index idx_desc_did_leader on staff(did desc, leader asc);
explain select id, leader, hiredate from staff order by leader, hiredate;
order by优化
- using index:通过有序索引顺序扫描直接返回有序数据,无需额外排序,效率高
- using filesort:通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,只要并非using index的排序均为using filesort
order by优化的关键点在于using index。前提是select的字段用到了且没有多余字段,否则filesort。
- 根据排序字段建立合适索引,多字段排序时,同样遵从最左前缀匹配
- 尽量使用覆盖索引,可以using index
- 多字段排序,注意各个字段的升序和降序,然后再创建复合索引
- 若不可避免出现filesort,大数据量排序时,有可能会数据大小会超出排序缓冲区大小,这时会在磁盘进行外部排序,所以可以适当增加排序缓冲区大小sort_buffer_size,其默认值为256k。
https://blog.csdn.net/weixin_39604280/article/details/111689218
由于order by不保证排序稳定性,所以同一条语句中相同排序字段的数据顺序不一定,这样会导致使用limit时,同一条orderby limit语句返回的数据不一样。解决办法是使用一个额外的字段参与排序保证字段顺序的一致,推荐使用主键。
select * from table order by category, id;
limit优化
limit问题在于limit 2000000, 10,此时需要获取前2000010条数据然后舍弃前2000000条数据,查询的代价非常大。
MySQL 的limit m n工作原理就是先读取前面m+n条记录,然后抛弃前m条,读后面n条想要的,所以m越大,偏移量越大,性能就越差
优化思路:一般分页查询时,通过创建覆盖索引能够比较好地提高性能,可以通过覆盖索引加子查询形式进行优化。
count优化
https://dev.mysql.com/doc/refman/8.4/en/aggregate-functions.html#function_count
count(expr)返回 SELECT 语句检索的行中 expr 非 NULL 值的数量。
count(field)返回所有数据中满足field列非NULL的总行数
count(1)返回所有数据中满足1非NULL的总行数,由于1总是不等于NULL,故count(1)相当于求全表数据量,实际上count(-1)、count(0)同理,而count(NULL)则为0
count(*)返回表中所有数据总行数
count(1)与count(*)两者功能相同,不同的是,count(1)基于原本函数的作用获取结果,而count(*)则是专用于求表总数据量的语法。
count(*)在实现上与存储引擎有关。
对于MyISAM,由于该存储引擎没有行锁,只有表锁,每次写数据库要对整个表加锁,故其将表中数据总量存储在了表的元数据中,对于MyISAM来说,count(*)效率很高,而count(1)反而需要全表查询。
对于InnoDB,由于该存储引擎中有行锁,其无法直接存储表数据总量,因此每次都需全表扫描,这时count(*)与count(1)并无本质区别,据官方文件所言,两者性能没有任何区别。
所以当我们使用InnoDB时,count(*)和count(1)均可,但由于count(*)为标准语法,建议使用count(*)。
重点:没有索引是表锁
因为name不是唯一的所以在修改的时候锁的是整表,而id是主键约束所以只是行锁
mysql可重复读并未完全解决幻读问题
多看看官方文档吧