MySQL数据库-第六章:MySQL索引及执行计划

1. 上节回顾

1. 索引类型

BTREE  :  b-tree , b+tree  , b*tree
RTREE  
FULLTExT
HASH

2. 聚簇索引和辅助索引区别和联系

区别:

1. 一般选择主键生成聚簇索引,一张表只能一个 ,没有主键选择唯一键,都没有选择隐藏rowid,自动生成隐藏聚簇索引.
2. 聚簇索引叶子节点,存储的是整行的表数据.枝节点和根节点,叶子节点ID值的范围.
3. 辅助索引,可以有多个.
4. 辅助索引,叶子节点,存储的是索引列值+主键.

关系:

执行查询时,select * from t1 where name='bgx';
1. 首先根据name的索引,快速锁定bgx的主键ID
2. 根据ID列值回表查询聚簇索引,获取整行.

3. 管理

show  index from city;
alter table city add index idx_name(name);
alter table city add index idx_a_b_c(a,b,c);
alter table city add index idx_a(a(10));
alter table city add unique index idx_a(a(10));
alter table city drop index idx_name;   

4. explain / desc

type :  ALL   index range  ref  eq_ref  const(system)  NULL 
key_len

联合索引应用"道道" *****

-- 建立联合索引时,最左侧列,选择重复值最少的列.
alter table keyt add index idx(a,b,c);

-- 例子:

-- 哪些情况可以完美应用以上索引.
desc select *from student where xname='张三' and xage=11 and xgender='m';
desc select *from student where xage=11 and xgender='m' and xname='张三' ;
desc select *from student where xgender='m' and xname='张三' and xage=11 ;
-- 影响到联合索引应用长度的.
-- 缺失 联合索引最左列,不走任何索引
mysql> desc select *from student where xage=11 and xgender='m'  ;
-- 缺失中间部分,只能走丢失部分之前的索引部分
mysql> desc select *from student where xname ='张三'  and xgender='m'  ;

-- 查询条件中,出现不等值查询(> ,< ...like )

mysql> desc select *from student where xname ='张三' xage<18 and xgender='m'  ;
联合索引应用长度到不等值列截断了.

-- 多子句
按照 select 子句顺序创建联合索引.

mysqlslap --defaults-file=/etc/my.cnf --concurrency=100 --iterations=1 --create-schema='test' --query="select * from test.t100w where k2='780P'" engine=innodb --number-of-queries=2000 -uroot -p123 -verbose

=========================================================

索引续集

1. 索引应用规范

1.1 创建索引的条件

(1) 必须要有主键,建议是自增长的ID列
(2) 经常做为where条件列 order by group by join on, distinct 的条件(业务:产品功能+用户行为)
(3) 唯一值多的列作为联合索引最左列.
(4) 列值长度较长的索引列,我们建议使用前缀索引.
(5) 降低索引条目,一方面不要创建没用索引,不常使用的索引清理,percona toolkit(xxxxx)
(6) 索引维护要避开业务繁忙期
(7) 经常更新的列不要建索引

1.2 开发规范

(1) 没有查询条件,或者查询条件没有建立索引

mysql> desc select * from city; 
mysql> desc select * from city where true;
mysql> desc select * from city where 1=1;
mysql> desc select * from city where name='jinan';
mysql> desc select *from student where xage=11 and xgender='m'  ;

(2) 查询结果集是原表中的大部分数据,应该是20-30%以上。

1000w   200w-300w  ----> 有可能导致索引失效.

解决方案: 给范围查询增加上限和下限

(3) 索引本身失效,统计数据不真实,更新不及时
前几天运行的很快,突然有一天慢了.

desc select * from city where name='jinan';
解决方案:
    1. 手工触发更新统计信息
    ANALYZE TABLE city;
    optimize table city;
    2. 重建索引

(4) 查询条件使用函数在索引列上,或者对索引列进行运算,运算包括(+,-,*,/,! 等)

mysql> desc select * from city where id-1=9;

(5) 隐式转换导致索引失效.这一点应当引起重视.也是开发中经常会犯的错误.

mysql> desc  select * from tab where telnum='110';

(6) <> ,not in 不走索引(辅助索引)
(7) like "%_" 百分号在最前面不走
(8) 联合索引规范
联合索引(a,b,c) ----> bc ---> c 不走任何索引
联合索引(a,b,c) ----> ac 只能走部分
联合索引(a,b,c) 中间出现不等值(> < like)

2. 扩展

2.1 AHI 自适应hash索引
mysql> select @@innodb_adaptive_hash_index;

Adaptive Hash Indexes 原理

InnoDB存储引擎会监控对二级索引的查找,如果发现某一个二级索引被频繁访问,二级索引成为一个热数据。那么此时建立hash索引可以带来速度的提升
经常访问的二级索引数据会自动被生成到hash索引里面去(最近连续被访问三次的数据),自适应哈希索引通过缓冲池的B+树构造而来,
因此建立的速度很快。而且不需要将整个表都建哈希索引,InnoDB存储引擎会自动根据访问的频率和模式来为某些页建立哈希索引。

========================================================

查看使用状况:

show engine innodb status ;

可以通过观察show engine innodb status结果中的SEMAPHORES部分来决定是否使用自适应哈希索引。如果你看到很多线程都在btr0sea.c文件上创建rw-latch上waiting,那么建议关闭掉自适应哈希索引。高并发模式下AHI引起的竞争,需要关闭AHI.

设置参数

innodb_adaptive_hash_index=on/off

2.2 MySQL Insert Buffer技术

   插入缓冲技术,对于非聚集类索引的插入和更新操作,不是每一次都直接插入到索引页中,而是先插入到内存中。

具体做法是:如果该索引页在缓冲池中,直接插入;否则,先将其放入插入缓冲区中,再以一定的频率和索引页合并,
这时,就可以将同一个索引页中的多个插入合并到一个IO操作中,大大提高写性能。

   这个设计思路和HBase中的LSM树有相似之处,都是通过先在内存中修改,到达一定量后,再和磁盘中的数据合并,目的都是为了提高写性能

那么插入缓冲如何减少随机IO的呢?每个一段时间,insert buffer会去合并在insert buffer中的二级非唯一索引。通常情况下,
它会合并N个修改到同一个btree索引的索引页中,从而节约了很多IO操作。经测试,insert buffer可以提高15倍的插入速度。
在事务提交后,insert buffer可能还在合并写入。所以,假如当DB异常重启,reovery阶段,当有非常多的二级索引需要更新或插入时,
insert buffer将可能花费很长时间,甚至几个小时。在这个阶段,磁盘IO将会增加,那么就会导致IO-Bound类型的查询有显著的性能下滑。

=============================================================

2.3 Index Condition Pushdown (ICP)

mysql使用索引从表中检索行数据的一种优化方式,MySQL5.6开始支持
MySQL 5.6之前,存储引擎会通过遍历索引定位基表中的行,然后返回给Server层,再去为这些数据行进行WHERE后的条件的过滤。
mysql 5.6之后支持ICP后,如果WHERE条件可以使用索引
MySQL 会把这部分过滤操作放到存储引擎层,存储引擎通过索引过滤,把满足的行从表中读取出。
ICP能减少引擎层访问基表的次数和 Server层访问存储引擎的次数。

============================================================

联合索引(a,b,c) ----> ac 只能走部分
没有ICP
a ---> 从磁盘拿满足a条件的数据 加载到内存 ,再C过滤想要的结果 =====> SQL层 --->
有ICP
a ----> a + c =====> SQL 层

mysql> SET  @@optimizer_switch='index_condition_pushdown=on'
mysql> show variables like 'optimizer_switch%' \G
*************************** 1. row ***************************
Variable_name: optimizer_switch
Value: index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=on,block_nested_loop=on,batched_key_access=off,materialization=on,semijoin=on,loosescan=on,firstmatch=on,subquery_materialization_cost_based=on,use_index_extensions=on

2.4 MRR 的全称是 Multi-Range Read

Optimization,是优化器将随机 IO 转化为顺序 IO 以降低查询过程中IO开销的一种手段,这对IO-bound类型的SQL语句性能带来极大的提升,适用于range ref eq_ref类型的查询

MRR优化的几个好处

使数据访问有随机变为顺序,查询辅助索引是,首先把查询结果按照主键进行排序,按照主键的顺序进行书签查找
减少缓冲池中页被替换的次数
批量处理对键值的操作

mysql> SET  @@optimizer_switch='mrr=on,mrr_cost_based=off';
mysql> show variables like 'optimizer_switch%' \G
*************************** 1. row ***************************
Variable_name: optimizer_switch
        Value: index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on,mrr_cost_based=off,block_nested_loop=on,batched_key_access=on,materialization=on,semijoin=on,loosescan=on,firstmatch=on,subquery_materialization_cost_based=on,use_index_extensions=on
1 row in set (0.00 sec)

2.5 针对多表连接查询

Simple Nested Loops Join(SNL),简单嵌套循环算法
Index Nested Loops  Join(INL),索引嵌套循环连接​​​​
Block Nested Loops  Join(BNL),块嵌套循环连接
Batched Key Access  join(BKA) ,  BNL+MRR

说明:

  1. batched_key_access=on
  2. mrr必须开启 ,mrr=on,mrr_cost_based=off
  3. 被驱动表,关联列必须有索引.

作用:

  1. 减少了 Nested Loops 次数
  2. 将扫描非驱动表时,可以将大量的随机IO转变为顺序IO
A
id   name   age      
1     zs     12
2     l4     13 
3     w5     14
B 
id    addr   telnum 
1     bj      110
2     sh      120 
3     tj      119    
select name,age,telnum
from a  join b  
on A.id=b.id
where name like  '张%'

提高表join性能的算法。当被join的表能够使用索引时,就先排好顺序,然后再去检索被join的表,听起来和MRR类似,实际上MRR也可以想象成二级索引和 primary key的join
如果被Join的表上没有索引,则使用老版本的BNL策略(BLOCK Nested-loop)

SET optimizer_switch='mrr=on,mrr_cost_based=off,batched_key_access=on';
mysql> show variables like 'optimizer_switch%' \G
*************************** 1. row ***************************
Variable_name: optimizer_switch
        Value: index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on,engine_condition_pushdown=on,index_condition_pushdown=on,mrr=on, ,block_nested_loop=on,batched_key_access=on,materialization=on,semijoin=on,loosescan=on,firstmatch=on,subquery_materialization_cost_based=on,use_index_extensions=on
1 row in set (0.00 sec)

=======================================================

第五章节 存储引擎

1. 存储引擎介绍

相当于Linux 文件系统.组织存储表数据.

2. 存储引擎的种类

show engines;
InnoDB 
MyISAM
CSV
Memory

其他的存储引擎:

MariaDB : InnoDB,TokuDB ,Myrocks
percona : xtradb ,TokuDB ,Myrocks

TokuDB ,Myrocks : 比较适合于在写入操作较多的场景,数据量级大的场景.
原因是: 插入性能很高, 压缩比较高.
监控类的业务.

学员案例:

环境: zabbix 3.x mariaDB 5.5 centos 7.3

现象 : zabbix卡的要死 , 每隔3-4个月,都要重新搭建一遍zabbix,存储空间经常爆满.

问题 :
  1. zabbix 版本
  2. 数据库版本 ---> 5.5 ----> ibdata1 ----> 5.7 ,8.0
  3. zabbix数据库500G,存在一个文件里
优化建议:

1.数据库版本升级到Mairia 10.x版本,zabbix升级更高版本
2.存储引擎改为tokudb
3.监控数据按月份进行切割(二次开发:zabbix 数据保留机制功能重写,数据库分表)
4.关闭binlog和双1 等安全参数需要关闭
5.参数调整....
优化结果:
监控状态良好

select concat("alter table zabbix.",table_name," engine=tokudb") from information_schema.tables
where table_schema='zabbix';
为什么?
  1. 原生态支持TokuDB,另外经过测试环境,10.x要比5.5 版本性能 高 2-3倍
  2. TokuDB:insert数据比Innodb快的多,数据压缩比要Innodb高
    3.监控数据按月份进行切割,为了能够truncate每个归档表,立即释放空间
    4.关闭binlog ----->减少无关日志的记录.
    5.参数调整...----->安全性参数关闭,提高性能.

3. InnoDB存储引擎的核心特性

====================================================

面试题:
(1) InnoDB存储引擎的特性 
(2) InnoDB和MyISAM的区别

====================================================

  1. MVCC 多版本并发控制
  2. 聚簇索引 PK
  3. 事务
    4.行级锁 MyISAM支持表锁
  4. 外键 FK
  5. 复制支持高级特性: GTID等高级复制
  6. 自适应hash索引
  7. 支持热备,MyISAM支持温备份
  8. ACSR(自动故障恢复)

====================================================

4. 存储引擎的操作

4.1 查看存储引擎

mysql> show engines;
mysql> select @@default_storage_engine;
mysql> create table mt (id int) engine=myisam;
mysql> create table et (id int) engine=innodb;
查询所有非INNODB的表 , 并且提出修改建议
mysql> SELECT 
    -> table_schema,
    -> table_name ,
    -> ENGINE ,
    -> CONCAT("alter table ",table_schema,".",table_name," engine=innodb;") AS "修改建议"
    -> FROM information_schema.tables
    -> WHERE table_schema NOT IN ('sys','information_schema','performance_schema','mysql')
    -> AND ENGINE <> 'innodb';
+--------------+------------+--------+--------------------------------------+
| table_schema | table_name | ENGINE | 修改建议                             |
+--------------+------------+--------+--------------------------------------+
| test         | mt         | MyISAM | alter table test.mt engine=innodb;   |
| test         | test       | MyISAM | alter table test.test engine=innodb; |
+--------------+------------+--------+--------------------------------------+
2 rows in set (0.01 sec)
mysql> 

4.2 修改表的存储引擎

alter table test.test engine=innodb; 
扩展: 碎片问题解决.

由于业务中有大量的delete操作,产生了大量的碎片.
(1) 表数据逻辑导出,删除原表,重新导入.
(2) mysql> alter table test.test engine=innodb;

=====================================================

小项目:

环境:centos7.4,MySQL 5.7.20,InnoDB存储引擎
业务特点:数据量级较大,经常需要按月删除历史数据.
问题:磁盘空间占用很大,不释放
处理方法:
以前:将数据逻辑导出,手工truncate表,然后导入进去
现在:
对表进行按月进行归档表
业务替换为truncate方式

=============================================

学员案例:

环境:
zabbix 3.x mariaDB 5.5 centos 7.3
现象 :
zabbix卡的要死 , 每隔3-4个月,都要重新搭建一遍zabbix,存储空间经常爆满.
问题 :

  1. zabbix 版本
  2. 数据库版本 ---> 5.5 ----> ibdata1 ----> 5.7 ,8.0
  3. zabbix数据库500G,存在一个文件里
优化建议:

1.数据库版本升级到Mairia 10.x版本,zabbix升级更高版本
2.存储引擎改为tokudb
3.监控数据按月份进行切割(二次开发:zabbix 数据保留机制功能重写,数据库分表)
4.关闭binlog和双1 等安全参数需要关闭
5.参数调整....

优化结果:监控状态良好

select concat("alter table zabbix.",table_name," engine=tokudb") from information_schema.tables
where table_schema='zabbix';
为什么?
  1. 原生态支持TokuDB,另外经过测试环境,10.x要比5.5 版本性能 高 2-3倍
  2. TokuDB:insert数据比Innodb快的多,数据压缩比要Innodb高
    3.监控数据按月份进行切割,为了能够truncate每个归档表,立即释放空间
    4.关闭binlog ----->减少无关日志的记录.
    5.参数调整...----->安全性参数关闭,提高性能.

==================================================

实施过程:

1.部署 Mariadb 环境 10.1

(1) 上传解压

[root@zabbix-server local]# tar xf mariadb-10.2.30-linux-glibc_214-x86_64.tar.gz 
[root@zabbix-server mariadb]# ln -s mariadb-10.2.30-linux-glibc_214-x86_64 mariadb
[root@zabbix-server mariadb]# chown -R mysql.mysql /usr/local/mariadb
[root@zabbix-server mariadb]# mkdir -p /data/mysql/data
[root@zabbix-server mariadb]# chown -R mysql.mysql data
[root@zabbix-server mariadb]# mv /etc/my.cnf /etc/my.cnf.bak

(2) 备份原数据库zabbix数据

mysqldump -B zabbix  > /tmp/zabbix.sql 

(3) 停源库 ,启新库

[root@zabbix-server local]# systemctl stop mariadb
/usr/local/mariadb/scripts/mysql_install_db --user=mysql --basedir=/usr/local/mariadb --datadir=/data/mysql/data
[root@zabbix-server data]# cd /usr/local/mariadb/support-files/
[root@zabbix-server support-files]# cp mysql.server /etc/init.d/mysqld 
[root@zabbix-server support-files]# chkconfig --add mysqld

实施过程:zabbix库替换存储引擎

1.部署 Mariadb 环境 10.0.38
[root@db01 mysql]# vim /etc/yum.repos.d/mariadb.repo 
[mariadb]
name = MariaDB
baseurl = http://yum.mariadb.org/10.1/centos7-amd64
gpgkey=https://yum.mariadb.org/RPM-GPG-KEY-MariaDB
gpgcheck=0
enabled=1
tar   xf mariadb-10.0.38-rhel-7-x86_64-rpms.tar
2. 改配置文件
[root@db01 my.cnf.d]# vim /etc/my.cnf.d/tokudb.cnf 

[mariadb]
plugin-load-add=ha_tokudb.so
plugin-dir=/data/tokudb/plugin/
tokudb_commit_sync=ON
tokudb_cache_size=128M
tokudb_directio=ON
tokudb_row_format=tokudb_fast
tokudb_tmp_dir=/data/tokudb/tmp
tokudb_write_status_frequency =1
tokudb_read_status_frequency=1
tokudb_data_dir=/data/tokudb/data
tokudb_log_dir=/data/tokudb/log
~ 
mkdir -p ~    /data/tokudb/{plugin,tmp,data,log}                                       
chown -R mysql.mysql /data/
cd /usr/lib64/mysql/plugin/
cp -a * /data/tokudb/plugin/
chown -R mysql.mysql /data/
3. 启动数据库
[root@db01 data]# mysqld_safe &
4. 生成批量替换语句
select concat('alter table ',table_schema,'.',table_name,' drop FOREIGN KEY ', CONSTRAINT_NAME,";") from information_schema.TABLE_CONSTRAINTS  where table_schema='zabbix'  and CONSTRAINT_TYPE='FOREIGN KEY';
select concat('alter table ',table_schema,'.'table_name,' engine=tokudb') from information_schema.tables where table_schema='zabbix' into outfile '/tmp/alter.sql';
percona-server+tokudb+zabbix
https://www.jianshu.com/p/898d2e4bd3a7

=====================================================

5. InnoDB存储引擎物理存储结构

5.1 表空间(tablespace) ***

(1) MySQL5.5版本出现了共享表空间模式(移植了Oracle)
实现了较为方便的扩容功能,但是所有的表数据都集中在几个文件中,管理十分不方便.
(2) MySQL 5.6中 ,默认使用独立表空间模式实现数据的存储.
保留了共享表空间,只用来存储系统相关数据(数据字典+undo+tmp表空间)
把用户表数据和索引单独存储(独立表空间)
(3) MySQL 5.7
保留了共享表空间ibdata1,只用来存储系统相关数据(数据字典+undo),undo在5.7 手工配置将他独立出来=
(4) MySQL 8.0
保留了共享表空间ibdata1,只用来存储系统相关数据(dw,cb)
undo自动独立出来,移除了数据字典的存储.

5.2 表空间管理

查看表空间模式:

mysql> select @@innodb_file_per_table;

共享表空间的设置:

mysql> select @@innodb_data_file_path;
ibdata1:332M;ibdata2:128M:autoextend

一般情况下: 安装MySQL ,提前设置好

mysqld --initialize-insecure ....
my.cnf 
ibdata1:512M:ibdata2:512M:autoextend

5.3 表的物理存储介绍

t1表:
ibd : 数据行

frm : 数据字典部分信息(列,列属性)
ibdata1 : 整个数据库的数据字典(所有表的列信息,列属性....),undo
ib_logfileN : redo事务日志

5.4 表空间迁移(快速迁移部分表数据)

(1) 准备一个新环境
(2) 创建和原表结构一样的表
show create table t1 ;
create ....
(3) 删除空表的ibd表空间文件
alter table t1 discard tablespace;
(4) cp 原表的ibd表空间到新环境
[root@db01 test]# cp -a t1.ibd /data/mysql/data_3307/db1
(5) 导入表空间文件.
alter table t1 import tablespace;

5.5 学员的项目

案例背景:
硬件及软件环境:
联想服务器(IBM)
磁盘500G 没有raid
centos 6.8
mysql 5.6.33 innodb引擎 独立表空间

备份没有,日志也没开

开发用户专用库:
jira(bug追踪) 、 confluence(内部知识库) ------>LNMT

故障描述:
断电了,启动完成后“/” 只读,fsck 重启,系统成功启动,mysql启动不了。
结果:confulence库在 , jira库不见了

学员求助内容:
求助:这种情况怎么恢复?
我问:有备份没
求助:连二进制日志都没有,没有备份,没有主从
我说:没招了,jira需要硬盘恢复了。

求助:
1、jira问题拉到中关村了
2、能不能暂时把confulence库先打开用着

将生产库confulence,拷贝到1:1虚拟机上/var/lib/mysql,直接访问时访问不了的

问:有没有工具能直接读取ibd
我说:我查查,最后发现没有

我想出一个办法来:
表空间迁移:

create table xxx
alter table  confulence.t1 discard tablespace;
alter table confulence.t1 import tablespace;

虚拟机测试可行。
处理问题思路:
confulence库中一共有107张表。

1、创建107和和原来一模一样的表。
他有2016年的历史库,我让他去他同时电脑上 mysqldump备份confulence库
mysqldump -uroot -ppassw0rd -B  confulence --no-data >test.sql
拿到你的测试库,进行恢复
到这步为止,表结构有了。

2、表空间删除。
select concat('alter table ',table_schema,'.'table_name,' discard tablespace;') from information_schema.tables where table_schema='confluence' into outfile '/tmp/discad.sql';

source /tmp/discard.sql

执行过程中发现,有20-30个表无法成功。主外键关系
很绝望,一个表一个表分析表结构,很痛苦。
set foreign_key_checks=0 跳过外键检查。
把有问题的表表空间也删掉了。

3、拷贝生产中confulence库下的所有表的ibd文件拷贝到准备好的环境中
select concat('alter table ',table_schema,'.'table_name,' import tablespace;') from information_schema.tables where table_schema='confluence' into outfile '/tmp/discad.sql';

4、验证数据
表都可以访问了,数据挽回到了出现问题时刻的状态(2-8)

你可能感兴趣的:(MySQL数据库-第六章:MySQL索引及执行计划)