每个页的大小书固定的,页可以为空,也可以填充一半,也可以填充100%,每个页包含了2-N行数据(如果一行数据过大,会行 溢出),根据主键排列
①. 从磁盘中申请页, 主键顺序插入
②. 第一个页没有满,继续往第一页插入
③. 当第一个也写满之后,再写入第二个页,页与页之间会通过指针连接
④. 当第二页写满了,再往第三页写入
①. 假如1#,2#页都已经写满了,存放了如图所示的数据
②. 此时再插入id为50的记录,就会发生页分裂。因为需要顺序存储,按照顺序,应该存储在47之后,
但是该页已经满了,则需要新申请一个3#页,将1#页后一半的数据,移动到3#页,然后在3#页,插入50。
重新设置链表指针
上述现象就叫做页分裂,是比较耗费性能的。
假如表中已有数据的索引结构(叶子节点)如下:
当我们对已有数据进行删除时,具体的效果如下:
当删除一行记录时,实际上记录并没有被物理删除,只是记录被标记(flaged)为删除并且它的空间
变得允许被其他记录声明使用。
当页中删除的记录达到 MERGE_THRESHOLD(默认为页的50%),InnoDB会开始寻找最靠近的页(前
或后)看看是否可以将两个页合并以优化空间使用。
上述现象就叫做页合并
如果我们需要一次性往数据库表中插入多条记录,可以从以下四个方面进行优化
如果一次性需要插入大批量数据(比如: 几百万的记录),使用insert语句插入性能较低,此时可以使用MySQL数据库提供的load指令进行插入。操作如下:
-- 客户端连接服务端时,加上参数 -–local-infile
mysql –-local-infile -u root -p
-- 设置全局参数local_infile为1,开启从本地加载文件导入数据的开关
set global local_infile = 1;
-- 执行load指令将准备好的数据,加载到表结构中
load data local infile '/root/sql1.log' into table tb_user fields terminated by ',' lines terminated by '\n' ;
MySQL的排序,有两种方式:
对于以上的两种排序方式,Using index的性能高,而Using filesort的性能低,我们在优化排序操作时,尽量要优化为 Using index。
测试:目前tb_user只有一个主键索引
explain select id,age,phone from tb_user order by age, phone
由于age、phone都没有索引,可以看到为Using filesort,需要到内存中重新排序。
create index idx_user_age_phone_aa on tb_user(age,phone);
explain select id,age,phone from tb_user order by age,phone;
可以看到建立索引之后,再次进行排序查询,就由原来的Using filesort, 变为了 Using index,性能
就是比较高的了。(仍然遵循最左前缀原则)
优化注意以下几点:
A. 根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则。
B. 尽量使用覆盖索引。
C. 多字段排序, 一个升序一个降序,此时需要注意联合索引在创建时的规则(ASC/DESC)。
D. 如果不可避免的出现filesort,大数据量排序时,可以适当增大排序缓冲区大小
GROUP BY 也会进行排序操作,与 ORDER BY 相比,GROUP BY 主要只是多了排序之后的分组操作,所以在 GROUP BY 的实现过程中,与 ORDER BY 一样也可以利用到索引
DROP INDEX idx_emp_age_salary ON emp;
EXPLAIN SELECT age,COUNT(*) FROM emp GROUP BY age;
Using temporary:表示 MySQL 需要使用临时表(不是 sort buffer)来存储结果集,常见于排序和分组查询
EXPLAIN SELECT age,COUNT(*) FROM emp GROUP BY age ORDER BY NULL;
CREATE INDEX idx_emp_age_salary ON emp(age, salary);
一个常见的问题是 LIMIT 200000,10,此时需要 MySQL 扫描前 200010 记录并进行排序,仅仅返回 200000 - 200010 之间的记录,其他记录丢弃,查询排序的代价非常大
EXPLAIN SELECT * FROM tb_user_1 LIMIT 200000,10;
EXPLAIN SELECT * FROM tb_user_1 t,(SELECT id FROM tb_user_1 ORDER BY id LIMIT 200000,10) a WHERE t.id = a.id;
EXPLAIN SELECT * FROM tb_user_1 WHERE id > 200000 LIMIT 10; -- 写法 1
EXPLAIN SELECT * FROM tb_user_1 WHERE id BETWEEN 200000 and 200010; -- 写法 2
在不同的 MySQL 引擎中,count(*) 有不同的实现方式:
解决方案:
count 函数的按照效率排序:count(字段) < count(主键id) < count(1) ≈ count(),所以建议尽量使用 count()