Mysql索引最佳实践笔记0524

#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/

猜你喜欢

转载自www.cnblogs.com/drizzle-xu/p/9088011.html