zoukankan      html  css  js  c++  java
  • mysql索引

     

    1 初识索引  

      索引在MySQL中也叫是一种“键”,是存储引擎用于快速找到记录的一种数据结构 —— 排好序的快速查找数据结构

      数据本身之外,数据库还维护着一个满足特定查找算法的数据结构,这些数据结构以某种方式指向数据,这样就可以在这些数据结构的基础上实现高级查找算法,这种数据结构就是索引。

      索引对于良好的性能非常关键,尤其是当表中的数据量越来越大时,索引对于性能的影响愈发重要。索引优化应该是对查询性能优化最有效的手段了。索引能够轻易将查询性能提高好几个数量级。

      索引相当于字典的音序表,如果要查某个字,如果不使用音序表,则需要从几百页中逐页去查。

    2 索引的原理

      索引的目的在于提高查询效率,与我们查阅图书所用的目录是一个道理:先定位到章,然后定位到该章下的一个小节,然后找到页数。相似的例子还有:查字典,查火车车次,飞机航班等

      索引的原理:通过不断地缩小想要获取数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是说,有了这种索引机制,我们可以总是用同一种查找方式来锁定数据。

      索引的本质:实际上是一张表,该表保存了主键和索引字段,并指向实体表的记录,所以索引列也是要占用空间的一般来说索引本身也很大,不可能全部存储在内存中,因此索引往往以文件形式存储在硬盘上。

    优势

      (1)类似大学图书馆建书目索引,提高数据检索效率,降低数据库的IO成本

      (2)通过索引列对数据进行排序,降低数据排序成本,降低了CPU的消耗

    劣势

      (1)虽然索引大大提高了查询速度,同时却会降低更新表(增删改)的速度。因为更新表时,MySQL不仅要不存数据,还要保存一下索引文件每次更新添加了索引列的字段,都会调整因为更新所带来的键值变化后的索引信息。

      (2)索引只是提高效率的一个因素,如果你的MySQL有大数据量的表,就需要花时间研究建立优秀的索引,或优化查询语句(需要不断调整优化)。

    3 MySQL索引

      3.1 索引分类

    #### 常用索引 ####
    
    #1、单值索引:即一个索引只包含单个列,一个表可以有多个单列索引
    
    #2、唯一索引:索引列的值必须唯一,但允许有空值
        -主键索引PRIMARY KEY:加速查找+约束(不为空、不能重复)
        -唯一索引UNIQUE:加速查找+约束(不能重复)
    
    #3、联合索引:即一个索引包含多个列
        -PRIMARY KEY(id,name):联合主键索引
        -UNIQUE(id,name):联合唯一索引
        -INDEX(id,name):联合普通索引
    # 组合索引(最左前缀匹配):
        - create unique index 索引名称 on 表名(列名,列名)
        - drop unique index 索引名称 on 表名
        
        - create index ix_name_email on userinfo3(name,email,)
        - 最左前缀匹配【一定要包含最左边的】
              √ select  * from userinfo3 where name='alex';
              √ select  * from userinfo3 where name='alex' and email='asdf';
                 
              × select  * from userinfo3 where email='alex@qq.com';
            
    # 组合索引效率 > 索引合并 
        组合索引
            - (name,email,)
            select  * from userinfo3 where name='alex' and email='asdf';
            select  * from userinfo3 where name='alex';
                    
        索引合并【把多个单列索引合并使用】:
            - name
            - email
            select  * from userinfo3 where name='alex' and email='asdf';
            select  * from userinfo3 where name='alex';
            select  * from userinfo3 where email='asdf';
        
    #4、组合索引(最左前缀匹配)
    #覆盖索引:在索引文件中直接获取数据。
        
            即从辅助索引中就可以得到查询记录,而不需要查询聚集索引中的记录。
    #5、覆盖索引

      3.2  基本语法

    (1)建表时添加索引

      建表同时建立单索引

    CREATE TABLE t_user1(id INT ,
    userName VARCHAR(20),
    PASSWORD VARCHAR(20),
    INDEX (userName) #关键字INDEX
    );

      建表同时建立唯一索引(可以是单或多)

    CREATE TABLE t_user2(id INT ,
    userName VARCHAR(20),
    PASSWORD VARCHAR(20),
    UNIQUE INDEX index_userName(userName) #关键字UNIQUE和INDEX
    );

      建表同时建立联合索引

    CREATE TABLE t_user3(id INT ,
    userName VARCHAR(20),
    PASSWORD VARCHAR(20),
    INDEX index_userName_password(userName,PASSWORD)
    );

    (2)给已存在表添加索引

    -- 单列索引
    CREATE INDEX index_userName ON t_user(userName);
    -- 唯一索引
    CREATE UNIQUE INDEX index_userName ON t_user(userName);
    -- 联合索引
    CREATE INDEX index_userName_password ON t_user(userName,PASSWORD);

    (3)删除索引

    DROP INDEX index_userName ON t_user;
    DROP INDEX index_userName_password ON t_user;

      3.3  MySQL常见瓶颈

      •  CPU:CPU在饱和的时候一般发生在数据装入在内存或从磁盘上读取数据时候
      •  IO:磁盘I/O瓶颈发生在装入数据远大于内存容量时
      •  服务器硬件的性能瓶颈:top,free,iostat和vmstat来查看系统的性能状态

    4 正确使用索引

    4.1 哪些情况需要创建索引

    • 主键自动建立唯一索引
    • 频繁作为查询的条件的字段应该创建索引
    • 查询中与其他表关联的字段,外键关系建立索引
    • 单间/组合索引的选择问题,who?(在高并发下倾向创建组合索引)
    • 查询中排序的字段,排序字段若通过索引去访问将大大提高排序的速度
    • 查询中统计或者分组字段
    • 哪些字段可以建索引? 一般都where、order by 或者 group by 后面的字段。

    4.2 哪些情况不要创建索引

    • Where条件里用不到的字段不创建索引
    • 表记录太少
    • 修改性能(频繁更新的字段)远远大于检索性能时,不应该创建索引。这是因为,修改性能和检索性能是互相矛盾的,因为每次更新不单单是更新了记录还会更新索引,加重IO负担)
    • 数据重复且分布平均的表字段,因此应该只为经常查询和经常排序的数据列建立索引。注意,如果某个数据列包含许多重复的内容,为它建立索引就没有太大的实际效果。
    • 对于那些定义为text, image和bit数据类型的列不应该增加索引。这是因为,这些列的数据量要么相当大,要么取值很少。
    • 对于那些在查询中很少使用或者参考的列不应该创建索引
    #1、and与or的逻辑
        条件1 and 条件2:所有条件都成立才算成立,但凡要有一个条件不成立则最终结果不成立
        条件1 or 条件2:只要有一个条件成立则最终结果就成立
    
    #2、and的工作原理
        条件:
            a = 10 and b = 'xxx' and c > 3 and d =4
        索引:
            制作联合索引(d,a,b,c)
        工作原理:
            对于连续多个and:mysql会按照联合索引,从左到右的顺序找一个区分度高的索引字段(这样便可以快速锁定很小的范围),加速查询,即按照d—>a->b->c的顺序
    
    #3、or的工作原理
        条件:
            a = 10 or b = 'xxx' or c > 3 or d =4
        索引:
            制作联合索引(d,a,b,c)
            
        工作原理:
            对于连续多个or:mysql会按照条件的顺序,从左到右依次判断,即a->b->c->d
    and/or工作原理

    4.3 mysql索引失效case及优化:

    1,全值匹配我最爱  
    -- 【optimizer会对查询顺序自动进行优化调整】
    
    2,最佳左前缀法则  【带头大哥不能死,中间兄弟不能断】
    --   如果索引了多例,要遵守最左前缀法则。指的是查询从索引的最左前列开始并且不跳过索引中的列。
    
    3,索引列少计算     
    -- 不在索引列上做任何操作(计算、函数、(自动or手动)类型转换),会导致索引失效而转向全表扫描
    
    4,范围之后全失效  
    -- 存储引擎不能使用索引中范围条件右边的列
    
    5,覆盖索引代替select * 
    -- 尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致)),减少select*
    
    6,少用 is null或is not null
    -- is null,is not null 也无法使用索引
    
    7,少用!=<>
    -- mysql在使用不等于(!=或者<>)的时候无法使用索引会导致全表扫描
    
    8,少用or
    -- 少用or,用它连接时会索引失效【查看上边and/or 工作原理】
    
    9like %放右边
    -- 【like以通配符开头('%abc...')mysql索引失效会变成全表扫描操作】
    -- 【放左边是all,连带他本身全失效】
    -- 【可用覆盖索引(varchar为380失效)或主键索引】
    
    10,varchar类型要加''单引号,切记!!!
    -- 【不加会计算-自动转型】
    -- 【导致行锁升级为表锁】
    
    11,使用连接(JOIN)来代替子查询(Sub-Queries)
    
    12,使用count(1)

    5 查询优化神器-explain

    5.1 是什么 —— 查看执行计划

      使用EXPLAIN关键字可以模拟优化器执行SQL语句,从而知道MySQL是如何处理你的SQL语句的。分析你的查询语句或是结构的性能瓶颈

      优化语句基本上都是在优化rows。具体用法和字段含义可以参考官网 explain-output

    5.2 能干嘛

    • 表的读取顺序
    • 数据读取操作的操作类型
    • 哪些索引可以使用
    • 哪些索引被实际使用
    • 表之间的引用
    • 每张表有多少行被优化器查询
    • 索引长度

    5.3  执行计划包含的信息

    Id

    Select_type

    Table

    Type

    Possible_keys

    Key

    Key_len

    Ref

    Rows

    Extra

    【详解】

    1】id :select查询的序列号,包含一组数字,表示查询中执行select子句或操作表的顺序
    -- id相同,执行顺序由上至下
    -- id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
    -- id相同不同,同时存在
    2】select_type:查询的类型,主要用于区别普通查询、联合查询、子查询等的复杂查询
    -- SIMPLE:简单的select查询,查询中不包含子查询或者UNION
    -- PRIMARY:查询中若包含任何复杂的子部分,最外层查询则被标记为
    -- SUBQUERY:在SELECT或者WHERE列表中包含了子查询
    -- DERIVED:在FROM列表中包含的子查询被标记为DERIVED(衍生)
    MySQL会递归执行这些子查询,把结果放在临时表里。
    -- UNION:若第二个SELECT出现在UNION之后,则被标记为UNION;
    若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED
    -- UNION RESULT:从UNION表获取结果的SELECT
    3table:显示这一行的数据是关于哪张表的
    
    【4】type:显示查询使用了何种类型,从最好到最差依次是:-- system>const>eq_ref>ref>range>index>ALL
        -- system:表只有一行记录(等于系统表),这是const类型的特例,平时不会出现,这个也可以忽略不计
        -- const:表示通过索引一次就找到了,const用于比较primary key或者unique索引。因为只匹配一行数据,所以很快。如将主键至于where列表中,MySQL就能将该查询转换为一个常量
        -- eq_ref:唯一性索引,对于每个索引键,表中只有一条记录与之匹配,常见于主键或唯一索引扫描
        -- ref:非唯一索引扫描,返回匹配某个单独值的所有行。
            本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体
        -- range:只检索给定范围的行,使用一个索引来选择行。key列显示使用了哪个索引
           一般就是在你的where语句中出现了between、<>、in等的查询这种范围扫描索引扫描比全表扫描要好,因为他只需要开始索引的某一点,而结束语另一点,不用扫描全部索引。
        -- index:Full Index Scan,index与ALL区别为index类型只遍历索引树。
            这通常比ALL快,因为索引文件通常比数据文件小。(也就是说虽然all和index都是读全表,但index是从索引中读取的,而all是从硬盘中读的)
        -- all:FullTable Scan,将遍历全表以找到匹配的行
    5】possible_keys:显示可能应用在这张表中的索引,一个或多个。
    --  查询涉及的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用
    6key:实际使用的索引。如果为null则没有使用索引
    -- 查询中若使用了覆盖索引,则索引和查询的select字段重叠(USING index)
    7】key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度。在不损失精确性的情况下,长度越短越好
    -- key_len显示的值为索引最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的
    8】ref:显示索引那一列被使用了,如果可能的话,是一个常数。那些列或常量被用于查找索引列上的值
    
    【9】rows:根据表统计信息及索引选用情况,大致估算出找到所需的记录所需要读取的行数
    
    【10】Extra:包含不适合在其他列中显示但十分重要的额外信息
    -- Using filesort(不好):说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成排序操作成为“文件排序”
    
    -- Using temporary:使用了临时表保存中间结果,MySQL在对查询结果排序时使用临时表。常见于排序order by 和分组查询 group by
    
    -- Using where:表明使用了where过滤
    
    -- USING index:表示相应的select操作中使用了覆盖索引(Coveing Index),避免访问了表的数据行,效率不错!
        -- 如果同时出现using where,表明索引被用来执行索引键值的查找;
        -- 如果没有同时出现using where,表面索引用来读取数据而非执行查找动作。     

    【type类型说明】

      从最好到最差依次是:system>const>eq_ref>ref>range>index>ALL

      一般来说,得保证查询只是达到range级别,最好达到ref

    6 慢查询优化的基本步骤

    # 0.先运行看看是否真的很慢,注意设置SQL_NO_CACHE
    # 1.where条件单表查,锁定最小返回记录表。这句话的意思是把查询语句的where都应用到表中返回的记录数最小的表开始查起,单表每个字段分别查询,看哪个字段的区分度最高
    # 2.explain查看执行计划,是否与1预期一致(从锁定记录较少的表开始查询)
    # 3.order by limit 形式的sql语句让排序的表优先查
    # 4.了解业务方使用场景
    # 5.加索引时参照建索引的几大原则
    # 6.观察结果,不符合预期继续从0分析

    7  MySQL锁机制

    7.1  锁的分类

      (1)从数据操作的类型(读、写)分

    • 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响
    • 写锁(排它锁):当前写操作没有完成前,它会阻断其他写锁和读锁。
    • 悲观锁:
      •  总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。
      • 传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现。
    • 乐观锁:
      •  总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。
      • 乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

      (2)从对数据操作的颗粒度

    • 表锁
    • 行锁

    7.2  表锁(MyISAM)—— 偏读

     特点:偏向MyISAM存储引擎,开销小,加锁快,无死锁,锁定粒度大,发生锁冲突的概率最高,并发最低

    7.3  行锁(InnoDB)—— 偏写

    特点:偏向InnoDB存储引擎,开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

    -- InnoDB与MyISAM的最大不同有两点:
    一是支持事务(TRANSACTION);
    二是采用了行级锁

    7.4  页锁(了解即可)

      开销和加锁时间界于表锁和行锁之间:会出现死锁;

           锁定粒度界于表锁和行锁之间,并发度一般。

    7.5 优化建议

    • 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁
    • 合理设计索引,尽量缩小锁的范围
    • 尽可能较少检索条件,避免间隙锁
    • 尽量控制事务大小,减少锁定资源量和时间长度
    • 尽可能低级别事务隔离

    8 慢查询日志

      查看是否开启及如何开启:

    -- 默认:
    SHOW VARIABLES LIKE '%slow_query_log%'
    
    -- 开启:
    set global slow_query_log = 1
    # 慢日志
      - 执行时间 > 10
      - 未命中索引
      - 日志文件路径
                
            配置:
                - 内存
                    show variables like '%query%';
                    show variables like '%queries%';
                    set global 变量名 =- 配置文件
                    mysqld --defaults-file='E:wupeiqimysql-5.7.16-winx64mysql-5.7.16-winx64my-default.ini'
                    
                    my.conf内容:
                        slow_query_log = ON
                        slow_query_log_file = D:/....
                        
    # 注意:修改配置文件之后,需要重启服务
    MySQL日志管理
    ========================================================
    错误日志: 记录 MySQL 服务器启动、关闭及运行错误等信息
    二进制日志: 又称binlog日志,以二进制文件的方式记录数据库中除 SELECT 以外的操作
    查询日志: 记录查询的信息
    慢查询日志: 记录执行时间超过指定时间的操作
    中继日志: 备库将主库的二进制日志复制到自己的中继日志中,从而在本地进行重放
    通用日志: 审计哪个账号、在哪个时段、做了哪些事件
    事务日志或称redo日志: 记录Innodb事务相关的如事务执行时间、检查点等
    ========================================================
    # 一、bin-log
    1. 启用
    # vim /etc/my.cnf
    [mysqld]
    log-bin[=dir[filename]]
    # service mysqld restart
    2. 暂停
    //仅当前会话
    SET SQL_LOG_BIN=0;
    SET SQL_LOG_BIN=1;
    3. 查看
    查看全部:
    # mysqlbinlog mysql.000002
    按时间:
    # mysqlbinlog mysql.000002 --start-datetime="2012-12-05 10:02:56"
    # mysqlbinlog mysql.000002 --stop-datetime="2012-12-05 11:02:54"
    # mysqlbinlog mysql.000002 --start-datetime="2012-12-05 10:02:56" --stop-datetime="2012-12-05 11:02:54" 
    
    按字节数:
    # mysqlbinlog mysql.000002 --start-position=260
    # mysqlbinlog mysql.000002 --stop-position=260
    # mysqlbinlog mysql.000002 --start-position=260 --stop-position=930
    4. 截断bin-log(产生新的bin-log文件)
    a. 重启mysql服务器
    b. # mysql -uroot -p123 -e 'flush logs'
    5. 删除bin-log文件
    # mysql -uroot -p123 -e 'reset master' 
    
    
    # 二、查询日志
    启用通用查询日志
    # vim /etc/my.cnf
    [mysqld]
    log[=dir[filename]]
    # service mysqld restart
    
    # 三、慢查询日志
    3.1 启用慢查询日志
    # vim /etc/my.cnf
    [mysqld]
    log-slow-queries[=dir[filename]]
    long_query_time=n
    # service mysqld restart
    MySQL 5.6:
    slow-query-log=1
    slow-query-log-file=slow.log
    long_query_time=3  单位为秒
    
    3,2 查看慢查询日志
    测试:BENCHMARK(count,expr)
    SELECT BENCHMARK(50000000,2*3);
    MySQL日志管理

    9 日志分析工具

    9.1 mysqldumpshow

      查看mysqldumpshow的帮助信息

    s:是表示按何种方式排序
    c:访问次数
    l:锁定时间
    r:返回记录
    t:查询时间
    al:平均锁定时间
    ar:平均返回记录数
    at:平均查询时间
    t:即为返回前面多少条的数据
    g:后边搭配一个正则匹配模式,大小写不敏感的

    9.2 Show profiles

      (1)是什么

        是mysql提供可以用来分析当前会话中语句执行的资源消耗情况。可以用于SQL的调优测量。

        官网:http://dev.mysql.com/doc/refman/5.5/en/show-profile.html

        默认情况下,参数处于关闭状态,并保存最近15次的运行结果

      (2)分析步骤

    1.是否支持,看看当前的SQL版本是否支持
    2.开启功能,默认是关闭,使用前需要开启
    3.运行SQL
    -- select * from emp group by id%10 limit 150000
    -- select * from emp group by id%20 order by 5
    4.查看结果,show profiles;
    5.诊断SQL,show profile cpu,block io for query 上一步前面的问题SQL 数字号码;
    6.日常开发需要注意的结论
    -- converting HEAP to MyISAM 查询结果太大,内存都不够用了往磁盘上搬了。
    -- Creating tmp table 创建临时表
        -- 拷贝数据到临时表
        --  用完再删除
    -- Copying to tmp table on disk 把内存中临时表复制到磁盘,危险!!!
    -- locked

    推荐视频:
    https://www.bilibili.com/video/av49181542 

    配套资料

    链接:https://pan.baidu.com/s/1WMqGfH1Kf1aDuQfzHNg4cQ
    提取码:edde

  • 相关阅读:
    李洪强经典面试题51-KVO-KVC
    李洪强经典面试题50-内存管理
    李洪强经典面试题49-Objective-C
    李洪强经典面试题48-C语言
    李洪强经典面试题47--UNIX常用命令
    李洪强经典面试题46-版本控制
    绿麻雀网贷系统通用注入分析
    跟着百度学PHP[5]函数篇2-PHP中的特殊形式的函数
    报错注入分析之(count()、rand()、group by)分析,被大佬称为floor报错注入
    跟着百度学PHP[5]函数篇1-参数
  • 原文地址:https://www.cnblogs.com/timetellu/p/10716773.html
Copyright © 2011-2022 走看看