• 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=123G
                  *****************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/

  • 相关阅读:
    进程间通信(管道和有名管道)
    BAT面试需要什么样的程序员?
    深入剖析Redis系列: Redis哨兵模式与高可用集群
    七大进程间通信和线程同步
    详解Redis 的持久化机制--RDB和AOF
    大型网站技术架构演进
    Coding Standard(编程规范)
    @RequestBody,415Unsupported Media Type错误
    排序算法
    单例设计模式
  • 原文地址:https://www.cnblogs.com/drizzle-xu/p/9088011.html
Copyright © 2020-2023  润新知