• sql 索引优化


    1.  mysql的执行流程:

    客户端: 发送连接请求,然后发送增删改查sql语句进行执行

    服务端: 

    (1)  连接层: 在tcp协议下,提供和客户端连接的要求, 提供对线程并发的技术, 让多个用户登录到mysql中(show processlist; 可查看所有登录到数据库的用户进程)

    (2) 服务器: 提供各种接口(增删改查) , 分析器组件会解析各种sql命令, 先查询缓存, 如果缓存里面有数据, 则直接返回, 如果没有再执行, 如果发现命令的执行效率很低, 则会提交给优化器(mysql query optimizer)组件进行优化, 然后再执行

    (3) 存储引擎: 存取或者提取数据. Innodb支持事务处理, 行锁, 高并发.

    (4) 日志文件: 产生binlog日志(二进制文件)

    2. sql卡顿原因:

    (1) 硬盘读写数据,io延迟高,sql语句性能低,导致sql执行的时间漫长

    (2) 表中的数据没有任何索引, 并且数据量较大, 造成sql查询速度慢

    查询命令编写顺序:

    select...from...join on...where...group by...having...order by...limit

    sql解析顺序:

    from...join on...where...group by...having...select...order by...limit

    3. 索引: 

    索引概念: 是一种数状的数据结构(B树结构,分支节点>2) , 相当于字典的目录, 加快查询速度  

    常用的树: B树, 二叉树, 红黑树, hash树

    树节点: 根节点(最顶级节点), 分支节点(父节点, 子节点), 叶子节点(最后一层存储数据的节点), 树的高度(树的层级, 理想情况下是三层, 任何数据最多查询三次可得到结果, 支持百万级别的数据查询, 追求树的矮胖结构)

    B树索引

    (1) B+树模型: 在相邻的叶子节点上, 加入双向链表(指针), 当前叶子节点不仅存贮着数据, 还保存着上下两个节点的地址(小范围数据中, 加快查询速度)

    (2) B* 树模型: 在相连的分支节点上, 加入双向链表(指针), 当前叶子节点不仅存储着数据, 还保存着上下两个节点的地址(大范围数据中, 加快查询速度)

    B* 树索引

    磁盘块(block) 也叫数据页, 存储数据最多为16K, Innodb 和MyISAM都是B+树结构

    4.   Innodb 和MyISAM 的索引结构:

    (1) 聚集索引: Innodb存储引擎的特点, MySIAM不支持

    如果有主键, 自动以主键创建聚集索引的数据结构(数状结构), 如果没有主键, 选择唯一键, 如果都没有, 则自动生成隐藏的聚集索引, 构建一个占6个字节的长整形字段.

    叶子节点上面直接存储真实数据(索引和数据捆绑在一起), 分支节点存储的是索引的最小值, 用来划分范围.

    在数据量变大的时候, 尽量在树层级高度不变的情况下, 横向发展, 从而提升查询的效率, 减少io阻塞.

    (2) 非聚集索引: (辅助索引, 二级索引, 普通索引)

    先对创建索引的这个字段划分区间进行排序, 把索引值分布在叶子节点上.

    存储的是该字段的值以及映射出的主键ID, 没有存真实数据, 通过ID再去其他文件中找对应的数据. 

    (3) 两者区别: 

    myisam 和 innodb 使用的索引结构都是b+树,但是叶子节点存储的数据不同

    innodb文件结构中只有frm和ibd 直接把数据存在叶子节点上

    myisam文件结构中有frm,myi,myd,叶子节点上存储的索引值,通过索引找id,通过id找数据.

    (4) 性能优化:

    利用索引查询时,可以增快查询速度,但是增删改速度变慢,会改变树状结构

    追求尽量让叶子节点存储的数据类型小一点,让高度变矮,让数据页变少.

    5. 索引
    (1) 常用索引:
    单个字段索引
    -主键索引 primary key : 非空且唯一
    -唯一索引 unique : 唯一
    -普通索引 index : 单纯加个索引,为了提升查询效率
    联合索引
    primary key(字段1,字段2..) :联合主键索引
    unique(字段1,字段2..) :联合唯一索引
    index(字段1,字段2..) :联合普通索引

    (2) 应用场景
    编号:int
    姓名:varchar(255)
    身份证号:char(18)
    电话char(11)
    地址varchar(255)
    备注:text
    姓: varchar(10)
    名: varchar(10)

    编号: 主键
    姓名: 普通索引(注意在区分度高的字段上加)
    身份证:unique
    电话:unique
    备注:全文索引 , 借助第三方软件sphinx来运行
    姓和名:联合索引 , 联合在一起查,加快速度

    (3) 不同的存储引擎支持的数据结构
    innodb : 支持b-tree fulltext 不支持hash类型索引结构
    myisam : 支持b-tree fulltext 不支持hash类型索引结构
    memory : 支持b-tree hash类型 不支持fulltext索引

    hash类型索引: 数据放内存中,通过键来获取到值,单条数据查询快,一个范围内的数据慢
    b-tree : 最理想的三层结构,理论上可支撑百万条数据的查询;

    6. 建立索引: 三种方法

    (1) 方法一: 建表的时候直接创建索引 index 索引名(索引字段)

    create table t1(

    id int primary key,

    name char(10),

    index index_id(id)

    );

    (2) 方法二: 建表之后, 直接创建索引 create index 索引名  on 表名(索引字段)

    create table t2(

    id int primary key,

    name char(10)

    );

    create index index_id on t2(id);

    (3) 方法三: 改字段变索引, alter table 表名 add index 索引名(索引字段)

    create table t3(

    id int primary key,

    name char(10)

    );

    alter table t3 add index index_id(id);

    (4) 删除索引  

    drop index index_id on t3;

    7. 正确使用索引:

    (1)  在数据量大的情况下, 使用所以和不适用索引, 差别很大

    未加索引的查询时间:

    mysql> select * from s1 where id = 10000;
    +-------+--------+--------+-----------------+------------+-------------+
    | id | name | gender | email | first_name | last_name |
    +-------+--------+--------+-----------------+------------+-------------+
    | 10000 | rachel | women | rachel10000@liu | 刘10000 | 思敏10000 |
    +-------+--------+--------+-----------------+------------+-------------+
    1 row in set (1.28 sec)

    alter table s1 add index index_id(id);  

    添加索引之后的查询时间(加了索引之后, ibd文件会变大):

    mysql> select * from s1 where id = 10000;
    +-------+--------+--------+-----------------+------------+-------------+
    | id | name | gender | email | first_name | last_name |
    +-------+--------+--------+-----------------+------------+-------------+
    | 10000 | rachel | women | rachel10000@liu | 刘10000 | 思敏10000 |
    +-------+--------+--------+-----------------+------------+-------------+
    1 row in set (0.01 sec)

    (2) 把频繁作为搜索条件的字段作为索引,查单条数据,如果查询的是一个大范围中的数据,不能命中索引
    表达范围的符号: > < >= <= != like between and .. in
    select * from s1 where id > 5; # 表达一个大范围内的数据不能命中.
    select * from s1 where id < 5; # 表达一个小范围内的数据可以命中.

    (3) 选一个区分度较高的字段作索引
    选区分度低的字段做了索引,在查询数据的时候,先走索引建好的树状结构,在把数据搜出来
    因为树状结构中有大量的重复数据,会增加树的高度,反而速度不快,冗余数据过多

    默认系统会把主键或者unique标识的约束,自动创建索引,因为区分度较高,没有冗余数据;

    create index index_name on s1(name); # 不推荐把区分度不高的字段加索引

    (4) 在搜索条件中,不能让索引字段参与计算,这样不能命中索引,会让查询时间变长
    select * from s1 where id = 1000;
    select * from s1 where id*3 = 3000; # id = 1000

      (5)  如果条件中含有and, sql语句会通过优化器进行优化

    如果是and相连, 会找到第一个有索引的,并且树的高度最矮的字段进行优化

    select count(*) from s1 where email = "rachel1000000@liu";                                1 row in set (1.28 sec)

    select count(*) from s1 where email = "rachel1000000@liu" and id = 1000000;                     1 row in set (0.00 sec)

    select count(*) from s1 where email = "rachel1000000@liu" and name = "rachel";                   1 row in set (13.14 sec)

    select count(*) from s1 where email = "rachel1000000@liu" and name = "rachel" and id = 1000000;        1 row in set (0.01 sec)     

    如果是or相连, 所有语句从左到右执行, 索引没有了作用

    select count(*) from s1 where id = 1000 or name = "rachel";             1 row in set (18.45 sec)  

    select count(*) from s1 where id = 1000 or email = "rachel1000@liu";       1 row in set (1.82 sec) 

    (6) 联合索引: 遵循最左前缀原则, 被标记成MUL这个字段,必须存在在搜索条件中,就命中索引

    drop index index_id on s1;

    drop index index_name on s1;

    create index union_index on s1(first_name,last_name);    

    select count(*) from s1 where first_name = "刘1000" and last_name = "思敏1000";          1 row in set (0.00 sec)

    select count(*) from s1 where first_name = "刘1000";                            1 row in set (0.00 sec)

     select count(*) from s1 where last_name = "思敏1000" and first_name = "刘1000";         1 row in set (0.00 sec)

    select count(*) from s1 where last_name = "思敏1000";                            1 row in set (0.97 sec)   不能命中

    select count(*) from s1 where first_name = "刘1000" and name = "rachel";               1 row in set (0.01 sec)

    (7) 其他:

    数据类型不匹配, 不能命中索引

    select count(*) from s1 where first_name = 1000;              1 row in set, 65535 warnings (2.03 sec)  

    使用了函数不能命中

    select count(*) from s1 where reverse(first_name) = "0001刘";      1 row in set (1.02 sec)  

              

      

  • 相关阅读:
    【驾考】科目三上路笔记——3号线
    【操作系统】30天自制操作系统——第5天
    【驾考】科目三笔记
    【操作系统】30天自制操作系统——第4天
    【操作系统】 DOS命令windows批处理batch编程——第一章
    【操作系统】30天自制操作系统——第3天
    【操作系统】30天自制操作系统——第2天
    【操作系统】30天自制操作系统——第1天
    【机器学习】Pandas库练习-获取yahoo金融苹果公司的股票数据
    SOS团队介绍
  • 原文地址:https://www.cnblogs.com/fdsimin/p/13182481.html
Copyright © 2020-2023  润新知