select * from employee;
编写SQL之际,一个or条件竟然致使索引失去效用,查询速度迟缓到让用户径自投诉这般的状况,于开发进程当中实际上每天都在出现。你所撰写的代码究竟是帮公司获取收益还是使服务器陷入困境,常常就取决于这些细微之处。
select id,name, age from employee;
select * from user where userid=1 or age =18
//使用union all
select * from user where userid=1
union all
select * from user where age = 18
//或者分开两条sql写:
select * from user where userid=1
select * from user where age = 18
索引使用的致命陷阱
select id, order_date from order_tab
where user_id=666
order by create_date desc;
有不少开发者,在字段之上添加了索引后,便自认为一切都妥当无误了,然而,MySQL优化器却并非会依照你所设想的那般去运作。就拿where条件之中的or来讲,假定唯有age字段存在索引,而另一条件下不存在索引,优化器会对全表扫描、加索引扫描以及再加合并这三个步骤的成本进行判断,极有可能会直接舍弃索引。
List orderList = orderService.queryOrderListByUserId('666');
Date orderDate = orderList.get(0).getOrderDate();
select id, order_date from order_tab
where user_id=666
order by create_date desc limit 1;
2025年那段某电商大促的期间内,只因一项or条件,致使核心查询所耗时间,猛地由50毫秒激增至3秒,最终造成数据库连接池满溢。索引并非越多便越好,而且即便添加了也不一定会被运用,领会优化器的成本决策,相较于盲目构建索引,可要重要许多许多。
for(User u :list){
INSERT into user(name,age) values(#name#,#age#)
}
//一次500批量插入,分批进行
insert into user(name,age) values
(#{item.name},#{item.age})
字段定义的隐形深坑
select * from user where userid=1
union
select * from user where age = 10
select * from user where userid=1
union all
select * from user where age = 10
将表结构定义之际把字段设定成交许为 null状貌,看上去压根儿没什么了不起,然而这般会能够让索引存储朝更复杂方面发展,开展查询之时还势必得额外去针对 null值予以处置措施。阿里从事开发的手册同样明确给出了建议,在不存在特殊缘由之下字段悉皆应当被定义成 not null。
city VARCHAR(50) NOT NULL
使用0、1这类数字去替代字符串作为性别字段的表示,同样不失为一种良好的习惯。在某社交平台的早期阶段,其将性别存储设定为'Male'以及'Female',然而针对这个字段所添加的索引,几乎是无法起效,其原因归咎为重复值数量过多,致使优化器认定进行全表扫描相较于使用索引更为合算。而该平台之后把这种存储方式改为tinyint类型,在查询性能方面也就大幅度提升了40%。
select userId,loginTime from loginuser where Date_ADD(loginTime,Interval 7 DAY) >=now();
查询写法的性价比选择
explain select userId,loginTime from loginuser where loginTime >= Date_ADD(NOW(),INTERVAL - 7 DAY);
select * from user where age-1 =10;
在需要进行去重的时候,对于union all相较于union而言,是更获得推荐的,这是鉴于union会额外多开展一次去重排序的操作。要是你的业务场景自身就不会产生重复的数据,那么此次排序这就是白白给予的。对于in查询而言,其元素的数量也是价值留意的,要是超过200个则建议进行分批的处理。
select * from user where age =11;
杭州的某金融公司在2024年时出现过一回线上事故,in子查询一次性查出来20万条数据,致使接口直接超时,就连DBA都不清楚该如何进行优化,最终发现仅仅是需要对in元素数量加以限制。
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT,
city VARCHAR(50) NOT NULL,
amount DECIMAL(10, 2)
);
分页查询的高效方案
SELECT user_id, SUM(amount) AS total_amount
FROM orders
GROUP BY user_id
HAVING city = '北京';
拥有无数开发者对其感到恐惧的深分页这一问题,写成limit 100000,20这样的形式,并非是在进行数据查询操作,而是在将数据库的资源白白耗费掉,先行对前面的100000条数据予以完整扫描,之后又把这些已经扫描的数予以丢弃,在这整个过程当中所产生的I/O所消耗的资源完全没有必要存在。
SELECT user_id, SUM(amount) AS total_amount
FROM orders
WHERE city = '北京'
GROUP BY user_id;
解决这个问题行之有效的手段是延迟关联,先于二级索引获取主键ID,接着凭借主键ID与原表进行内连接,借此大幅削减了回表次数,某内容平台运用此方案后,千万级数据量的分页查询耗时由8秒降至0.3秒。
select userId,name from user where userId like '3';
select userId,name from user where userId like '123%';
连接查询的正确打开方式
子查询看起来,逻辑是清晰分明的,然而就是在性能这个方面,通常也就是每每都比不上连接查询。这是由于,在MySQL开展执行子查询这个动作的期间呢,常常是必须进而构建起临时表,以此来放置中间的运行结果,在查询操作终结之后,还得将其予以销毁,这些相关的操作,全部都是切切实实含有成本在内的。所以呐,要是能够运用inner join的话,那就千万不要使用子查询语句。
虽说连接表并非是数量越多便越好,当关联表之数量超过3个时,查询优化器要生成最佳的执行计划就变得极为困难了。有一个ERP系统,其中一张报表曾关联了7张表,每次打开该报表都会耗费0秒的时间,后来将其拆分成两次查询再加上内存组装的操作,响应时间便降低到了2秒以内。
SELECT * FROM customers c
WHERE EXISTS (
SELECT 1 FROM orders o WHERE o.customer_id = c.id
);
返回数据的精准控制
SELECT * FROM customers
WHERE id IN (
SELECT customer_id FROM orders
);
在查询环节,要是能添加limit那就务必得添加,就算你清楚结果仅有一条。此乃给予数据库的一个确切承诺,借此让优化器能够大胆地去挑选更为高效的执行计划。譬如查询最近登录的用户、查询最新订单这类场景,limit能够节省大量的资源。
给客户端返回的数据同样得加以控制,用户想要查看最近一年的直播记录,你难道真的非要把三百条数据一下子抛给前端?进行分批次加载,依照需求返回,这样既能减轻数据库的压力,又可以提升用户体验,某视频平台在优化这个逻辑之后,移动端流量消耗下降了35%。
于你所撰写的SQL语句当中,究竟是索引命中率较为高呢,还是全表扫描的情形更为多一些呢?欢迎在评论区域分享你自身的优化经验。
select user_id,name from user where user_id in (1,2,3...1000000);

Comments NOTHING