刚入行那会儿,我总纳闷为什么同样的查询需求,老张写的SQL跑3秒,我写的要跑3分钟。直到有次我把一段自认为"优化"过的SQL推上生产环境,直接把数据库CPU打满,才真正明白什么叫"SQL性能陷阱"。今天我们就来盘点8种专坑同事(包括曾经的自己)的SQL写法,这些坑轻则让查询变慢几十倍,重则引发线上事故。
新手最爱写的查询:
sql复制SELECT * FROM orders WHERE user_id = 10086;
老司机都知道该这么写:
sql复制SELECT order_id, create_time, amount
FROM orders
WHERE user_id = 10086;
为什么坑:
实战经验:某次排查发现一个简单分页查询耗时2秒,改成明确字段后降到200ms,因为原表有个5MB的合同附件字段
典型错误示范:
sql复制SELECT * FROM products
WHERE category = '电子' OR price > 5000;
优化方案:
sql复制SELECT * FROM products WHERE category = '电子'
UNION ALL
SELECT * FROM products WHERE price > 5000;
性能差异:
隐藏的坑:
sql复制SELECT * FROM users
WHERE phone = 13800138000; -- phone是varchar类型
正确写法:
sql复制SELECT * FROM users
WHERE phone = '13800138000';
背后原理:
危险写法:
sql复制SELECT * FROM customers
WHERE id NOT IN (SELECT customer_id FROM blacklist);
推荐方案:
sql复制SELECT c.* FROM customers c
LEFT JOIN blacklist b ON c.id = b.customer_id
WHERE b.customer_id IS NULL;
性能对比:
随机查询的坑:
sql复制SELECT * FROM products
ORDER BY RAND() LIMIT 10;
高效方案:
sql复制-- 先获取最大ID
SET @max_id = (SELECT MAX(id) FROM products);
-- 随机选择10个ID
SELECT * FROM products
WHERE id IN (
FLOOR(1 + RAND() * (@max_id - 10)),
FLOOR(1 + RAND() * (@max_id - 10)),
...
)
LIMIT 10;
实测数据:
深度分页陷阱:
sql复制SELECT * FROM logs
ORDER BY create_time DESC
LIMIT 1000000, 10;
优化方案:
sql复制SELECT * FROM logs
WHERE id > 1000000 -- 记住上次查询的最后ID
ORDER BY create_time DESC
LIMIT 10;
性能真相:
危险的连锁反应:
sql复制CREATE TRIGGER update_stats
AFTER INSERT ON orders
FOR EACH ROW
BEGIN
UPDATE user_stats SET order_count = order_count + 1
WHERE user_id = NEW.user_id;
UPDATE product_stats SET sale_count = sale_count + 1
WHERE product_id = NEW.product_id;
END;
潜在风险:
并发问题源头:
sql复制-- 会话1
BEGIN;
SELECT balance FROM accounts WHERE user_id = 1; -- 读到1000
-- 会话2
UPDATE accounts SET balance = 800 WHERE user_id = 1;
COMMIT;
-- 会话1继续
UPDATE accounts SET balance = 1000 - 100 WHERE user_id = 1;
-- 实际余额应该是700,但被覆盖为900
解决方案:
sql复制SELECT balance FROM accounts WHERE user_id = 1 FOR UPDATE;
-- 或使用乐观锁
去年双十一大促期间,我们遇到一个诡异现象:订单查询接口在每天上午10点准时超时。经过排查发现:
最终解决方案:
这个案例教会我们:SQL性能问题往往会在最意想不到的时间点爆发。