在应用开发的早期,数据量少,开发人员开发功能时更重视功能上的实现,随着生产数据的增长,很多 SQL 语句开始暴露出性能问题,对生产的影响也越来越大,有时可能这些有问题的 SQL 就是整个系统性能的瓶颈。
提示:以下是本篇文章正文内容,下面案例可供参考
FROM(form)
先确定从哪个表中取数据,所以最先执行from tab。存在多表连接,from tab1,tab2。可以对表加别名,方便后面的引用。
ON(on)
连接多张表的附加条件
JOIN(join)
连接多张表的关键字
WHERE(where)
where语句是对条件加以限定,如果没有需要限定的,那就写成where 1=1,表示总为true,无附加条件。
GROUP BY(group by)
分组语句,比如按照员工姓名分组,要就行分组的字段,必须出现在select中,否则就会报错
聚合函数
常用的聚合函数有max(),min(), count(),sum(),聚合函数的执行在group by之后,having之前。如果在where中写聚合函数,就会出错
HAVING(having)
having是和group by配合使用的,用来作条件限定
SELECT(select)
选出要查找的字段,如果全选可以select *
DISTINCT(distinct)
去除数据中重复内容
ORDER BY(order by)
排序语句,**默认为升序(asc)**排列。如果要降序排列,就写成order by [XX] desc。order by语句在最后执行,只有select选出要查找的字段,才能进行排序。
LIMIT(limit)
分页关键字
EXPLAIN是MySQl必不可少的一个分析工具,主要用来测试sql语句的性能及对sql语句的优化,或者说模拟优化器执行SQL语句。在select语句之前增加explain关键字,执行后MySQL就会返回执行计划的信息,而不是执行sql
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FOSdRjXz-1650004751129)(D:\JAVA\javabasic81\博客\img\image-20220414112811348.png)]
作用:了解 SQL 执行的线程的状态及消耗的时间。
show profile: 展示最近一条语句执行的详细资源占用信息,默认显示 Status和Duration两列
默认是关闭的,开启语句“set profiling = 1;”
这个执行语句的剖析信息存储在这个会话中。使用SHOW PROFILES进行查 看。
show profiles :列表,显示最近发送到服务器上执行的语句的资源使用情况.
-- limit n,m是怎么回事,首先它要获取到第一个参数游标n的位置,那么它就必须得扫描到n的位置,接着从此位置起往后取m条数据,不足m条的返回实际的数量。那么这就会有一个性能的问题
-- 耗时0.006s
SELECT * FROM `wms_inventory` limit 1000
-- 耗时0.006s
SELECT * FROM `wms_inventory` limit 1,1000
-- 耗时0.009s
SELECT * FROM `wms_inventory` limit 1000,1000
KEY `idx_shopid_orderno` (`shop_id`,`order_no`)
select * from _t where orderno=''
查询匹配从左往右匹配,要使用 order_no 走索引,必须查询条件携带 shop_id 或者索引(shop_id,order_no)调换前后顺序
KEY `idx_phone` (`phone`)
select * from _user where phone=12345678901
隐式转换相当于在索引上做运算,会让索引失效。mobile 是字符类型,使用了数字,应该使用字符串匹配,否则 MySQL 会用到隐式替换,导致索引失效。
MySQL 不能利用索引进行混合排序。但在某些场景,还是有机会使用特殊方法提升性能的。
例如:
SELECT *
FROM my_order o
INNER JOIN my_appraise a ON a.orderid = o.id
ORDER BY a.is_reply ASC,
a.appraise_time DESC
LIMIT 0, 20
explain 执行计划分析显示a表为全表扫描
+----+-------------+-------+--------+-------------+---------+---------+---------------+---------+-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra
+----+-------------+-------+--------+-------------+---------+---------+---------------+---------+-+
| 1 | SIMPLE | a | ALL | idx_orderid | NULL | NULL | NULL | 1967647 | Using filesort |
| 1 | SIMPLE | o | eq_ref | PRIMARY | PRIMARY | 122 | a.orderid | 1 | NULL |
+----+-------------+-------+--------+---------+---------+---------+-----------------+---------+-+
由于 is_reply 只有0和1两种状态,我们按照下面的方法重写后,执行时间从1.58秒降低到2毫秒。
SELECT *
FROM ((SELECT *
FROM my_order o
INNER JOIN my_appraise a
ON a.orderid = o.id
AND is_reply = 0
ORDER BY appraise_time DESC
LIMIT 0, 20)
UNION ALL
(SELECT *
FROM my_order o
INNER JOIN my_appraise a
ON a.orderid = o.id
AND is_reply = 1
ORDER BY appraise_time DESC
LIMIT 0, 20)) t
ORDER BY is_reply ASC,
appraisetime DESC
LIMIT 20;
总结:有些时候不能用索引进行混合排序,但是可以根据字段的特殊值进行变相优化。
KEY `idx_shopid_status_created` (`shop_id`, `order_status`, `created_at`)
select * from _order where shop_id = 1 and order_status in (1, 2, 3) order by created_at desc limit 10
in 查询在 MySQL 底层是通过 n*m 的方式去搜索,类似 union,但是效率比 union 高。
in 查询在进行 cost 代价计算时(代价 = 元组数 * IO 平均值),是通过将 in 包含的数值,一条条去查询获取元组数的,因此这个计算过程会比较的慢。
所以 MySQL 设置了个临界值(eq_range_index_dive_limit),5.6 之后超过这个临界值后该列的 cost 就不参与计算了。因此会导致执行计划选择不准确。
默认是 200,即 in 条件超过了 200 个数据,会导致 in 的代价计算存在问题,可能会导致 MySQL 选择的索引不准确。
处理方式:可以(order_status,created_at)互换前后顺序,并且调整 SQL 为延迟关联。
总结:在使用索引查询和in的语法时,尽量把索引字段放在最前面。
KEY `idx_shopid_created_status` (`shop_id`, `created_at`, `order_status`)
select * from _order where shop_id = 1 and created_at > '2021-01-01 00:00:00' and order_status = 10
范围查询还有“IN、between”。
在索引上,避免使用 NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE等。
select * from _t where a=1 order by b desc, c asc
总结:desc 和 asc 混用时会导致索引失效
如果要求访问的数据量很小,则优化器还是会选择辅助索引,但是当访问的数据占整个表中数据的蛮大一部分时(一般是 20% 左右),优化器会选择通过聚集索引来查找数据。
select * from _order where order_status = 1
查询出所有未支付的订单,一般这种订单是很少的,即使建了索引,也没法使用索引。
KEY `idx_a_b_c` (`a`, `b`, `c`)
select * from _t where a = 1 and b = 2 order by c desc limit 10000, 10;
对于大分页的场景,可以优先让产品优化需求,如果没有优化的,有如下两种优化方式:
select t1.* from _t t1, (select id from _t where a = 1 and b = 2 order by c desc limit 10000, 10) t2 where t1.id = t2.id;
我们应该为数据库里的每张表都设置一个ID做为其主键,而且最好的是一个INT型的(推荐使用UNSIGNED),并设置上自动增加的 AUTO_INCREMENT标志。
就算是你 users 表有一个主键叫 “email”的字段,你也别让它成为主键。使用 VARCHAR 类型来当主键会使用得性能下降。
对于大多数的数据库引擎来说,硬盘操作可能是最重大的瓶颈。所以,把你的数据变得紧凑会对这种情况非常有帮助,因为这减少了对硬盘的访问。
参看 MySQL 的文档 Storage Requirements 查看所有的数据类型。
如果一个表只会有几列罢了(比如说字典表,配置表),那么,我们就没有理由使用 INT 来做主键,使用 MEDIUMINT, SMALLINT 或是更小的 TINYINT 会更经济一些。如果你不需要记录时间,使用 DATE 要比 DATETIME 好得多。
如果你的应用程序有很多 JOIN 查询,你应该确认两个表中Join的字段是被建过索引的。这样,MySQL内部会启动为你优化Join的SQL语句的机制。
而且,这些被用来Join的字段,应该是相同的类型的。例如:如果你要把 DECIMAL 字段和一个 INT 字段Join在一起,MySQL就无法使用它们的索引。对于那些STRING类型,还需要有相同的字符集才行。(两个表的字符集有可能不一样)
// 在state中查找company
SELECT company_name FROM users
LEFT JOIN companies ON (users.state = companies.state)
WHERE users.id = $user_id
// 两个 state 字段应该是被建过索引的,而且应该是相当的类型,相同的字符集。
explain有sql分析性能
show profile分析性能
当只有一行数据时使用 LIMIT 1
为搜索字段建索引
索引并不一定就是给主键或是唯一的字段。如果在你的表中,有某个字段你总要会经常用来做搜索,那么,请为其建立索引吧。
索引失效的情况:
1.如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因),要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引
2.对于多列索引,不是使用的第一部分,则不会使用索引最左匹配原则
3.like查询以%开头
4.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引(隐式转换)
5.如果mysql估计使用全表扫描要比使用索引快,则不使用索引
6.desc 和 asc 混用时会导致索引失效
在索引上,避免使用 NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE等。
如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引(隐式转换)
如果mysql估计使用全表扫描要比使用索引快,则不使用索引
desc 和 asc 混用时会导致索引失效
在索引上,避免使用 NOT、!=、<>、!<、!>、NOT EXISTS、NOT IN、NOT LIKE等。
在Join表的时候使用相当类型的列,并将其索引
永远为每张表设置一个ID
越小的列会越快
采用延迟关联的方式进行处理,减少 SQL 回表
在使用索引查询和in的语法时,尽量把索引字段放在最前面
温馨提示:
总结内容均可在优化场景分析和手段中找到啊!!!!!!!!!!!!!