2025年12月20日/ 浏览 18
正文:
在日均百万级请求的电商系统中,我们曾因数据库瓶颈导致页面加载突破5秒。经过三轮深度优化,最终将核心接口压到800毫秒内。以下是用鲜血换来的PHP数据库优化实战经验:
场景:用户订单查询接口超时
问题SQL:
php
$sql = "SELECT * FROM orders WHERE user_id = 123 AND status = 1 ORDER BY create_time DESC";
优化步骤:
1. 建立联合索引:
sql
ALTER TABLE orders ADD INDEX idx_user_status (user_id, status);
2. 改写查询避免排序临时表:
php
// 利用索引天然排序特性
$sql = "SELECT * FROM orders
WHERE user_id = 123 AND status = 1
ORDER BY user_id DESC, create_time DESC";
效果:查询耗时从2.1s降至0.3s
关键细节:联合索引字段顺序遵循左前缀原则,区分度高的字段(如userid)应在前。排序字段若不在索引中,将引发filesort灾难。
经典反例:php
// 模糊查询导致全表扫描
$sql = “SELECT product_name FROM products WHERE description LIKE ‘%防水%'”;
// IN()失控
$ids = implode(‘,’, range(1,10000));
$sql = “SELECT * FROM users WHERE id IN ($ids)”;
优化方案:php
// 使用全文索引替代LIKE
ALTER TABLE products ADD FULLTEXT(description);
$sql = “SELECT product_name FROM products WHERE MATCH(description) AGAINST(‘防水’)”;
// 分批次处理大数据集
$chunkIds = array_chunk($largeIds, 500);
foreach ($chunkIds as $ids) {
$sql = “SELECT * FROM users WHERE id IN (“.implode(‘,’,$ids).”)”;
// 分批执行
}
致命场景:
高并发下频繁创建数据库连接,引发Too many connections错误
解决方案:
php
// 使用连接池(以Swoole为例)
$pool = new Swoole\Database\PDOPool(
(new Swoole\Database\PDOConfig())
->withHost('127.0.0.1')
->withPort(3306)
->withDbName('test')
->withCharset('utf8mb4')
->withUsername('root')
->withPassword('root')
, 16 // 连接池大小
);
配置要点:
1. 根据max_connections = (max_pool_size * worker_num) + buffer计算
2. 设置wait_timeout大于PHP脚本最大执行时间
php
// 路由到不同数据库
$isHot = $orderAmount > 1000; // 大额订单走热库
$db = $isHot ? $hotDB : $coldDB;
$db->query("SELECT * FROM orders WHERE id = {$orderId}");
php
// 非即时数据写入队列
$redis->lPush('order_queue', json_encode([
'user_id' => 123,
'product_id' => 456,
'amount' => 789
]));
// 后台Worker批量插入
php
$cacheKey = "product_{$id}";
if(!$data = $redis->get($cacheKey)) {
// 布隆过滤器前置校验
if(!$bloomFilter->check($id)) {
return null; // 拦截非法请求
}
$data = $db->query("SELECT * FROM products WHERE id = ?", [$id]);
$redis->setex($cacheKey, 300, $data);
}
SQL审计:
php
// 记录慢查询
$db->query("SET GLOBAL slow_query_log = ON");
$db->query("SET GLOBAL long_query_time = 0.5");
执行计划分析:
sql
EXPLAIN SELECT * FROM orders WHERE user_id = 123;
压力测试:
bash
ab -c 100 -n 5000 http://api.example.com/order/list
某用户画像系统优化案例:
原始状态:
– 3亿用户数据表
– 标签查询平均响应 4.2秒
优化措施:
1. 列存储引擎迁移:ALTER TABLE user_tags ENGINE=ColumnStore
2. 建立位图索引:
sql
CREATE BITMAP INDEX idx_tag_id ON user_tags(tag_id);
3. 查询重组:
php
// 从逐条查询改为批量处理
$sql = "SELECT user_id, GROUP_CONCAT(tag_id)
FROM user_tags
WHERE user_id IN ($chunkIds)
GROUP BY user_id";
结果:
查询速度提升至0.8秒,服务器资源消耗降低60%
结语:数据库优化是永无止境的修行。上周我们刚通过索引下推特性将某查询从1200ms压到300ms。记住:没有银弹,只有持续的量表监控、执行计划分析和代码重构。每一次优化都是与业务逻辑的深度对话。