Mysql索引最佳实践笔记0524

Wesley13
• 阅读 495

#mysql5.7 innodb默认存储引擎

一、关于索引
二、最佳实践
三、避坑实践

一、关于索引
1.索引的作用
-提高查询效率
-数据分组、排序
-避免回表查询
-优化聚集查询
-用于多表join关联查询
-利用唯一性约束、保证数据唯一性
-innodb行锁实现

#索引的“作用”(副作用)
-增加io成本
-增加磁盘空间
-不适合的索引,或索引过多,都不是好事

#索引类型
-BTREE (B+ tree或B-tree),INNODB &MYISAM
-Fractal TREE,TokuDB
-HASH(用于内存的存储引擎),HEAP,NDB,INNODB AHI
-RTEE
-FULLTEXT
#针对b+tree索引

#聚集索引

-一种特殊的索引,其key值顺序决定了表数据行的物理顺序(相对)顺序
-每张表只能建一个聚集索引,除了tokudb引擎
-innodb中,聚集索引即表,表即聚集索引(innodb存储了整行的数据)
-myisam没有聚集索引的概念

#聚集索引优先选择列
-int、bigint
-数据连续(单调顺序)递增、自增

#不建议的聚集索引类型
-频繁的修改列
-新增数据太过随机或离散
-uuid、字符串、随机数。不建议设置聚集索引

#主键索引(具体的索引列)
-由一个列或多个列组成
-用于唯一表示表中的某一条记录&外键约束
-主键值不能重复,也不能包含null

#主键选择建议:
-对业务透明,无意义,免受业务变化的影响
-几乎不修改和删除
-自增int、bigint最佳
#innodb聚集索引选择次序原则
1.显示声明的主键
2.第一个not nullable的唯一索引
3.上述两者都没,则选择内置rowid作为聚集索引(实例级,6bytes
#全局rowID
rowid作为聚集索引缺点:
1)整个实例来分配的rowid
2)不是每个表单独分配的rowid
3)rowid是全局的rowid,每个表的rowid都是跳跃的

#innodb主键特点:
-索引定义时,不管有无显式包含主键,实际都会存储主键值;
-在5.6.9以后,优化器异能自动识别索引末尾的主键值(index Extensions)
-,在这之前则需要显式加上主键才可以被识别;

-where c1=? and pk=?
-where ci=? order by pk(主键列)

#辅助索引
-又称为非聚集索引,或者二级索引,俗称普通索引
-当通过innodb辅助索引查找数据时,辅助索引会通过页级的指针找到主键索引的主键,
-然后通过该主键索引找到相应的行数据

ex:c1=>(c1.pk)=>pk=>row data

#聚集索引
ex:
create table x(
id int(10) unsigned not null auto_increment,
uid int(10) unsiged not null default ‘0’, #uid为辅助索引
user varchar(20) not null default ‘‘,
passwd varchar(40) not null default ‘‘,
primary key(id), #id为聚集索引
unique key uid(uid),#唯一的辅助索引
)ENGINE=Innodb


以下视图是开了,innodb—monitor内部结构显示的信息
TABLE:....
COLUMNS:...
uid:...
user:...
DB_ROW_ID:...
DB_ROLL_PTR:...
INDEX:name PRIMARY,id 54,fields 1/6,uniq 1,tyep 3 #聚集索引选择主键为聚集索引,总共6个列,显示定义一个列
root page 3,appr.key vals 0,leaf pages 1,size pages 1
FILEDS:id DB_TRX_ID DB_ROLL_PTR uid user passwd #聚集索引后面跟着两个隐藏列分别为:DB_TRX_ID事物ID,DB_ROLL_PTR回滚指针,还有其他的功能列
INDEX:name uid.id 55,fields 1/2,uniq 2,type 2 #辅助索引,总共2个类,定义了一个列。实际存储为两个列。辅助索引总是跟着主键的值,才能根据uid找到主键的值。
root page 4,appr.key vals 0,leaf pages 1,size pages 1
FIELDS:uid id

#innodb索引图

[ ]
+---+---+ internal nodes
|    |   |
[     ] [    ]  [   ]


[3 ]       [ ]        [99 ]        [ ]       [4700]
--> [TID] --> [ ] --> [TID] --> [ ] --> [TID ] -->      inndb clustered
[RP ]       [ ]       [RP ]        [ ]       [RP ]              indexleaf nodes
【93 】   【 】   【8 】     【 】    【13 】

[ ]Primary key columns (col1)
[TID]Transaction ID
[RP ]Rollback Pointer
【 】Non-Pk columns(col2)

#联合索引
-多列组成,所以也叫多列索引
-适合where条件中的多列组合
-有时候,还可以用于避免回表(覆盖索引)
-MySQL还不支持多列不同排序规则(MySQL8.0起支持)
nnodb_table_monitor
-联合索引建议:
-where条件中,经常同时出现的列放在联合索引中
-把选择性(过滤性、基数)大的列放在联合索引的最左边
ex:
a=? and b=? and c=?
a>? and b=? and c=?
建议:(b,c,a)
#部分索引

  • 部分索引的原因

-char/varchar太长全部做索引的话,效率太差,存在浪费
-或者blob/text类型不能整列作为索引列,因此需要使用前缀索引
-部分说要选择建议
-统计平均值
-满足80%~90%覆盖度就够
-缺点
-无法利用前缀索引完成排序
ex:
username varchar(50)
统计平均长度
selelct avg(length(username)) from tt;
50*3=150
username(18) 18*3=54
*3-表示utf8以三个字节表示的
#覆盖索引
-通过索引数据结构,即可直接返回数据,不需要回表
-执行计划中,Extra列会显示关键字using index

#innodb索引特点

  • 索引最大长度767bytes

-启用innodb_large_prefix,增加到3072bytes,只针对DYNAMIC、COMPRESSED格式管用
ex:
show table status like ‘x1‘\G
--------------1.row---------------
Name:x1
Engine:innodb
Version:10
Row_format:Dynamic
Rows:14
Avg_row_length:1170

-对于REDUNDANT、COMPACT格式,最大索引长度还是767bytes
-MyISAM表索引最大长度是1000bytes
-最大排序长度默认是1024(max_sort_length)

#索引统计
-show index from table
-select * from information_sechma.statistics
-mysql.innodb_index_stats

-innodb_stats_auto_recalc
-默认启用,当修改数据量>10%,自动更新统计信息
-innodb_stats_persistent
-统计信息持久化存储,默认启用
-innodb_stats_persistent_sample_pages
-统计信息持久化存储时,每次采集20个page
ex:
随机采集,不连续。五个索引,采集100page。每个表,每个分区,每个索引采集20个page

-inoodb_stats_on_metadata
-默认禁用,访问meta data时更新统计信息

-innodb_stats_persistent=0
-统计信息不持久化,每次动态采集,存储在内存中,重启实效(需要新统计),不推荐
-innodb_stats_transient_sample_pages
-动态采集page,默认8个
-每个表设定统计模式
-create/alter table ...stats_persistent=1,stats_auto_recalc=1,
stats_sample_pages=200;
-参考:细说ANALZE TABLE

#EXPLAIN之type
-all
-全表扫描,最糟糕的情况
-index
-全索引扫描,大部分情况下,一样糟糕

#EXPLAIN之key_len
-正常的等于索引列字节长度
-字符串类型需要同时考虑字符集因素
-若允许null,再+1
-变长类型(varchar),在+2
-key_len只计算利用索引完成数据过滤时索引长度
-不包括用于GROUP BY/ORDER BY 的索引长度

ex:
-int 4 bigint 8
-index:(int_col1,bigint_col2)
-where int_col1=? order by bigint_col2 #key_len 长度为4,不是4+8=12。key_len不包括order by后面的长度
#EXPLAIN之extra
-Using filesort
-没有办法利用现有索引进行排序,需要额外排序
-建议:根据排序需要,创建相应合适的索引
-Using index
-利用覆盖索引,无需回表即可取得结果数据
-Using temporary
-需要用临时表存储结果集,通常是因为group by的列上没有索引。也有可能是因为同时有group by和order by,但group by和order by的列又不一样

#查看索引利用率
-查看每个索引使用情况
#select index_name,rows_selected,rows_updated,
rows_deleted from schema_index_statistics where
table_schema=’world’ and table_name=’city’;
+---------------+----------------+----------------+--------------+
|index_name     | rows_selected| rows_updated | rows_deleted |
| PRIMARY     |            33131 |                      0 |                     0 |
| CountryCode |                103 |        0 |        0 |
|+---------------|----------------+-----------------+--------------+

#mysql 5.7 默认启用p_s

#查看冗余索引

#select * from schema_redundant_indexes where
table_schema=’test’ and table_name=’tutf8’\G
*******************1.row***********************
table_schema:test
table_name:t1
redundant_index_name:c2_2
redundant_index_columns:c2
redundant_index_non_unique:1
dominant_index_name:c2
doinant_index_columns:c2
dominant_index_non_unique:1
subpart_exists:1
sql_drop_index:ALTER TABLE ‘test’.’t1’ DROP INDEX ‘c2_2’

ex:
index1:c1
index2:c1,c2
index1是index2的冗余索引
#冗余索引不一定可以删除,特殊场景视情况分析

-index k1(a,b,c)
-index k2(a,b)
-一般认为,k2是k1的冗余索引
-但下面的sql则只有k2才管用

  • where a=? and b=? and pk=?;

-where a=? and b=? order by pk;

#查看无用索引
#select * from schema_unused_indexes
where object_schema=’test’ and object_name=’t1’;
+------------------+------------------+-------------+
| object_schema   | objdect_name | index_name |
+------------------+------------------+--------------+
| test        |        t1 |      k1 |
+------------------+-------------------+-------------+

#索引为何不可用
-通过索引扫描的记录数超过20%~30%,可能会变成全表扫描
-联合索引中,第一个查询条件不是最左索引列
-模糊查询条件列最左以通配符%开始
-HEAP表使用HASH索引时,使用范围检索或者ORDER BY
-多表关联时,排序字段不属于驱动表,无法利用索引完成排序
-JOIN查询时,关联列数据类型(字符集)不一定也会导致索引不可用

#类型隐式转换
#desc select * from t1 where u2=123\G
*****************1.row***************
id:1
select_type:SIMPLE
table:t1
partitions:NULL
type:ALL
possible_keys:u2
key:NULL
key_len:NULL
ref:NULL
rows:102176
filtered:10.00
Extra:Using where
#us=123 出现隐式转换无法利用到索引,视为ASCII
us=’123’ 可以利用索引,支持字符串

#MYSQL 5.6/5.7
#desc select * from t1 where gmt_create between date_add(now(),interval - 1 minute)
and date_add(now(),interval 15 minute)\G
**************1.row**********************
id:1
select_type:SIMPLE
table:t1
type:ALL
possible_keys:ind_gmt_create
key:NULL
rows:2849555
Extra:Using where

|Warning | 1739 | Cannot use range access on index ‘index_gmt_create’
due to type or collation conversion on field ‘gmt_create’

#gmt_create 日期时间会出现隐式转换,在底层被修改为int类型

二、索引最佳实践
-mysqld进程cpu占用长期较高时,99.99%时因为索引不当导致
#top 执行时:
%CPU 362.3 121.7
COMMAND mysqld
这种现象基本都是用不到索引,高频度列用不到索引

-mysql内用户线程状态经常看到Sending data,也基本上是因为索引不当导致
#mysqladmin pr|grep -v Sleep
+----+-------+--------------+--------+-----------+-------+-------------+------------+
| id | user | host | db | command | time | state | info |
+-----+-------+--------------|+-----+|---------+-------+-------------+--------------|
| 25 | x | 10.x.8519 | db | Query | 68 |Sending data | select ...|
| 26 | x | 10.x.8520 | db | Query | 65 |Sending data | select ...|
| 28 | x | 10.x.8522 | db | Query | 130 |Sending data | select ...|
| 27 | x | 10.x.8521 | db | Query | 167 |Sending data | select ...|
| 36 | x | 10.x.8727 | db | Query | 174 |Sending data | select ...|
+|-----+------+---------------|------|-----------|------|---+-------+---|----------+-|

三、避坑实践
-所有主要列都创建单独索引
-长varchar列创建索引
-基数低的列单独创建索引,或者放在联合索引的最左边
-mysql不支持位图索引

#相关参数优化
-sort-buffer-size/join-buffer-size/read-rnd-buffer-size,4~8MB为宜
-optimizer_swich=”index_condition_pushdown=on,mrr=on,mrr_cost_based=off,batched_key_access=on”
-tmp-table-size=max-heap-table-size,100MB左右为宜
-log-queries-not-using-indexes& log_throttle_queries_not_using_indexes

#MySQL8.0+新变化
-倒叙索引 #(a asc,b desc)
-不可见索引 #不常用索引

#附资料
-由浅入深理解innodb的索引实现
-mysql索引值聚集索引
-B+ 树索引和哈希索引的区别
-10分钟让你明白mysql是如何利用索引的
-老斯基带你解锁mysql8.0索引新姿势
-mysql排序内部原理探秘
-index extensions特性介绍
-细说ANALZE TABLE
-解读EXPLAIN执行计划中的key_len
-innblock|innodb page 观察利器
-听说join的列类型一定要一样
-UPDATE能走索引还会锁全表吗
-索引、提交频率对innodb表写入速度的影响
-为什么innodb表要建议自增列做主键
-典型性索引引发CPU负载飙升问题
-UPDATE时主键冲突引发的思考
-单节点可用方案http://github.com/zhishutech/

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这