• sql更细


     (1)连接器: 主要负责跟客户端建立连接、获取权限、维持和管理连接

    (2)查询缓存: 优先在缓存中进行查询,如果查到了则直接返回,如果缓存中查询不到,在去数据库中查询。

    MySQL缓存是默认关闭的,也就是说不推荐使用缓存,并且在MySQL8.0 版本已经将查询缓存的整块功能删掉了。这主要是它的使用场景限制造成的:

    • 先说下缓存中数据存储格式:key(sql语句)- value(数据值),所以如果SQL语句(key)只要存在一点不同之处就会直接进行数据库查询了;
    • 由于表中的数据不是一成不变的,大多数是经常变化的,而当数据库中的数据变化了,那么相应的与此表相关的缓存数据就需要移除掉;

    (3)解析器/分析器: 分析器的工作主要是对要执行的SQL语句进行词法解析、语法解析,最终得到抽象语法树,然后再使用预处理器对抽象语法树进行语义校验,判断抽象语法树中的表是否存在,如果存在的话,在接着判断select投影列字段是否在表中存在等。

    (4)优化器: 主要将SQL经过词法解析、语法解析后得到的语法树,通过数据字典和统计信息的内容,再经过一系列运算 ,最终得出一个执行计划,包括选择使用哪个索引

    (5)执行器: 根据一系列的执行计划去调用存储引擎提供的API接口去调用操作数据,完成SQL的执行。

    SQL语句及索引的优化

    1. 尽量避免使用子查询

    例:

    SELECT * FROM t1 WHERE id (SELECT id FROM t2 WHERE name = 'chackca');

    其子查询在Mysql5.5版本里,内部执行计划是这样:先查外表再匹配内表,而不是先查内表t2,当外表的数据很大时,查询速度会非常慢。

    在MariaDB10/Mysql5.6版本里,采用join关联方式对其进行了优化,这条SQL语句会自动转换为:SELECT t1.* FROM t1 JOIN t2 on t1.id = t2.id

    但请注意的是:优化只针对SELECT有效,对UPDATE/DELETE子查询无效,固生产环境应避免使用子查询

    由于MySQL的优化器对于子查询的处理能力比较弱,所以不建议使用子查询,可以改写成Inner Join,之所以 join 连接效率更高,是因为 MySQL不需要在内存中创建临时表

    2. 用IN来替换OR
    • 低效查询:SELECT * FROM t WHERE id = 10 OR id = 20 OR id = 30;
    • 高效查询:SELECT * FROM t WHERE id IN (10,20,30);

    另外,MySQL对于IN做了相应的优化,即将IN中的常量全部存储在一个数组里面,而且这个数组是排好序的。但是如果数值较多,产生的消耗也是比较大的。再例如:select id from table_name where num in(1,2,3) 对于连续的数值,能用 between 就不要用 in 了;

    3. 读取适当的记录LIMIT M,N,而不要读多余的记录
    select id,name from t limit 866613, 20

    使用上述sql语句做分页的时候,可能有人会发现,随着表数据量的增加,直接使用limit分页查询会越来越慢。

    对于 limit m, n 的分页查询,越往后面翻页(即m越大的情况下)SQL的耗时会越来越长,对于这种应该先取出主键id,然后通过主键id跟原表进行Join关联查询。因为MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后放弃前 offset 行,返回 N 行,那当 offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行 SQL 改写。

          优化的方法如下:可以取前一页的最大行数的id(将上次遍历到的最末尾的数据ID传给数据库,然后直接定位到该ID处,再往后面遍历数据),然后根据这个最大的id来限制下一页的起点。比如此列中,上一页最大的id是866612。sql可以采用如下的写法:

    select id,name from table_name where id> 866612 limit 20

    4. 禁止不必要的Order By排序

    如果我们对结果没有排序的要求,就尽量少用排序;

    如果排序字段没有用到索引,也尽量少用排序;

    另外,分组统计查询时可以禁止其默认排序

    SELECT goods_id,count(*) FROM t GROUP BY goods_id;

    默认情况下,Mysql会对所有的GROUP BT col1,col2…的字段进行排序,也就是说上述会对 goods_id进行排序,如果想要避免排序结果的消耗,可以指定ORDER BY NULL禁止排序:

    SELECT goods_id,count(*) FROM t GROUP BY goods_id ORDER BY NULL


    5. 总和查询可以禁止排重用union all

    union和union all的差异主要是前者需要将结果集合并后再进行唯一性过滤操作,这就会涉及到排序,增加大量的CPU运算,加大资源消耗及延迟。

    当然,union all的前提条件是两个结果集没有重复数据。所以一般是我们明确知道不会出现重复数据的时候才建议使用 union all 提高速度。

    6. 避免随机取记录
    SELECT * FROM t1 WHERE 1 = 1 ORDER BY RAND() LIMIT 4;
    SELECT * FROM t1 WHERE id >= CEIL(RAND()*1000) LIMIT 4;


    补充: CEIL(X) CEILING(X) 返回比X大的最小整数 =====》select CEIL(6.99) ->7 ;select CEILING(6.111) -> 7
    7. 将多次插入换成批量Insert插入
    INSERT INTO t(id, name) VALUES(1, 'aaa');
    INSERT INTO t(id, name) VALUES(2, 'bbb');
    INSERT INTO t(id, name) VALUES(3, 'ccc');
    —>
    INSERT INTO t(id, name) VALUES(1, 'aaa'),(2, 'bbb'),(3, 'ccc');


    8. 只返回必要的列,用具体的字段列表代替 select * 语句

    SELECT * 会增加很多不必要的消耗(cpu、io、内存、网络带宽);增加了使用覆盖索引的可能性;当表结构发生改变时,前者也需要经常更新。所以要求直接在select后面接上字段名。

    MySQL数据库是按照行的方式存储,而数据存取操作都是以一个页大小进行IO操作的,每个IO单元中存储了多行,每行都是存储了该行的所有字段。所以无论取一个字段还是多个字段,实际上数据库在表中需要访问的数据量其实是一样的。

    但是如果查询的字段都在索引中,也就是覆盖索引,那么可以直接从索引中获取对应的内容直接返回,不需要进行回表,减少IO操作。除此之外,当存在 order by 操作的时候,select 子句中的字段多少会在很大程度上影响到我们的排序效率。

    9. 区分in和exists
    select * from 表A where id in (select id from 表B)

    上面的语句相当于:

    select * from 表A where exists(select * from 表B where 表B.id=表A.id)

    区分in和exists主要是造成了驱动顺序的改变(这是性能变化的关键),如果是exists,那么以外层表为驱动表,先被访问,如果是IN,那么先执行子查询。所以IN适合于外表大而内表小的情况;EXISTS适合于外表小而内表大的情况。

    10. 优化Group By语句

    如果对group by语句的结果没有排序要求,要在语句后面加 order by null(group 默认会排序);

    尽量让group by过程用上表的索引,确认方法是explain结果里没有Using temporary 和 Using filesort

    如果group by需要统计的数据量不大,尽量只使用内存临时表;也可以通过适当调大tmp_table_size参数,来避免用到磁盘临时表;

    • 如果数据量实在太大,使用SQL_BIG_RESULT这个提示,来告诉优化器直接使用排序算法(直接用磁盘临时表)得到group by的结果。

    使用where子句替换Having子句:避免使用having子句,having只会在检索出所有记录之后才会对结果集进行过滤,这个处理需要排序分组,如果能通过where子句提前过滤查询的数目,就可以减少这方面的开销。

    • 低效: SELECT JOB, AVG(SAL) FROM EMP GROUP by JOB HAVING JOB = ‘PRESIDENT’ OR JOB = ‘MANAGER’
    • 高效: SELECT JOB, AVG(SAL) FROM EMP WHERE JOB = ‘PRESIDENT’ OR JOB = ‘MANAGER’ GROUP by JOB

    ===================

    MySQL强制性操作 

    1、强制索引FORCE INDEX

    SELECT FROM TABLE1 FORCE INDEX (FIELD1)

    只使用建立在FIELD1上的索引,而不使用其它字段上的索引。

    2、忽略索引 IGNORE INDEX

          SELECT FROM TABLE1 IGNORE INDEX (FIELD1, FIELD2) …

     

      TABLE1表中FIELD1和FIELD2上的索引不被使用。

    3、关闭/打开查询缓冲 SQL_NO_CACHE/SQL_CACHE

    SELECT SQL_NO_CACHE field1, field2 FROM TABLE1;

    SELECT SQL_CACHE field1, field2 FROM TABLE1;
     

    4、优先操作 HIGH_PRIORITY

    HIGH_PRIORITY可以使用在select和insert操作中,让MYSQL知道,这个操作优先进行。

    SELECT HIGH_PRIORITY * FROM TABLE1;

    5、滞后操作 LOW_PRIORITY

    LOW_PRIORITY可以使用在insert和update操作中,让mysql知道,这个操作滞后。

    update LOW_PRIORITY table1 set field1= where field1= …

    6、延时插入 INSERT DELAYED

    INSERT DELAYED INTO table1 set field1= …

    INSERT DELAYED INTO,是客户端提交数据给MySQL,MySQL返回OK状态给客户端。而这是并不是已经将数据插入表,而是存储在内存里面等待排队。当mysql有空余时,再插入。另一个重要的好处是,来自许多客户端的插入被集中在一起,并被编写入一个块。这比执行许多独立的插入要快很多。坏处是,不能返回自动递增的ID,以及系统崩溃时,MySQL还没有来得及插入数据的话,这些数据将会丢失。

    7、强制连接顺序 STRAIGHT_JOIN

    SELECT TABLE1.FIELD1, TABLE2.FIELD2 FROM TABLE1 STRAIGHT_JOIN TABLE2 WHERE 

    通过STRAIGHT_JOIN强迫MySQL按TABLE1、TABLE2的顺序连接表。如果你认为按自己的顺序比MySQL推荐的顺序进行连接的效率高的话,就可以通过STRAIGHT_JOIN来确定连接顺序。

    8、强制使用临时表 SQL_BUFFER_RESULT

    SELECT SQL_BUFFER_RESULT * FROM TABLE1 WHERE 

    当我们查询的结果集中的数据比较多时,可以通过SQL_BUFFER_RESULT.选项强制将结果集放到临时表中,这样就可以很快地释放MySQL的表锁(这样其它的SQL语句就可以对这些记录进行查询了),并且可以长时间地为客户端提供大记录集。

    9、分组使用临时表 SQL_BIG_RESULT和SQL_SMALL_RESULT

    SELECT SQL_BIG_RESULT FIELD1, COUNT(*) FROM TABLE1 GROUP BY FIELD1;

    一般用于分组或DISTINCT关键字,这个选项通知MySQL,如果有必要,就将查询结果放到临时表中,甚至在临时表中进行排序。SQL_SMALL_RESULT比起SQL_BIG_RESULT差不多

    ======================================= 
    11. 尽量使用数字型字段

    若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能。引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

    12. 优化Join语句

    当我们执行两个表的Join的时候,就会有一个比较的过程,逐条比较两个表的语句是比较慢的,因此可以把两个表中数据依次读进一个内存块中,在Mysql中执行:show variables like ‘join_buffer_size’,可以看到join在内存中的缓存池大小,其大小将会影响join语句的性能。在执行join的时候,数据库会选择一个表把他要返回以及需要进行和其他表进行比较的数据放进join_buffer

    • left join 前面的表是驱动表,后面的表是被驱动表
    • right join 后面的表是驱动表,前面的表是被驱动表
    • inner join / join 会自动选择表数据比较少的作为驱动表
    • straight_join(≈join) 直接选择左边的表作为驱动表(语义上与join类似,但去除了join自动选择小表作为驱动表的特性)

    索引的优化/如何避免索引失效

    1.最佳左前缀法则

    如果索引了多列,要遵守最左前缀法则,指的是查询从索引的最左前列开始并且不跳过索引中的列。Mysql查询优化器会对查询的字段进行改进,判断查询的字段以哪种形式组合能使得查询更快,所有比如创建的是(a,b)索引,查询的是(b,a),查询优化器会修改成(a,b)后使用索引查询。

    2.不在索引列上做任何操作

    1.计算:对索引进行表达式计算会导致索引失效,如 where id + 1 = 10,可以转换成 where id = 10 -1,这样就可以走索引

    2.函数:select * from t_user where length(name)=6; 此语句对字段使用到了函数,会导致索引失效

    3.存储引擎不能使用索引中范围条件右边的列。

    如这样的sql: select * from user where username='123' and age>20 and phone='1390012345',其中username, age, phone都有索引,只有username和age会生效,phone的索引没有用到。

    4.尽量使用覆盖索引(只访问索引的查询(索引列和查询列一致))

    select age from user,减少select *

    5.mysql在使用负向查询条件(!=、<>、not in、not exists、not like)的时候无法使用索引会导致全表扫描。

    6.is null, is not null 也无法使用索引,在实际中尽量不要使用null(避免在 where 子句中对字段进行 null 值判断) 不过在mysql的高版本已经做了优化,允许使用索引

    对于null的判断会导致引擎放弃使用索引而进行全表扫描。

    7.like 以通配符开头(%abc..)时,mysql索引失效会变成全表扫描的操作。

    所以最好用右边like ‘abc%’。如果两边都要用,可以用select username from user where username like '%abc%',其中username是必须是索引列,才可让索引生效.

    8.少用or,在 WHERE 子句中,如果在 OR 前的条件列是索引列,而在 OR 后的条件列不是索引列,那么索引会失效。

  • 相关阅读:
    Nginx下配置SSL安全协议
    Java嵌套类
    python命名空间与作用域
    Effective Java 4:类和接口
    VIM快捷键(转载)
    Eclipse快捷键
    Effective Java 3:对于所有对象都通用的方法
    Effective Java 1:创建和销毁对象-续
    Effective Java 1:创建和销毁对象
    python文件操作(转载)
  • 原文地址:https://www.cnblogs.com/KL2016/p/16370343.html
Copyright © 2020-2023  润新知