本文目录导读:

在ThinkPHP项目中,数据库索引优化是提升性能的关键环节,以下是系统性的索引提示与优化方案:
索引使用提示(SQL Hint)
强制索引(Force Index)
// 使用force索引提示
$data = Db::name('users')
->force('idx_username')
->where('username', 'admin')
->find();
// 多表查询强制索引
$data = Db::name('orders')
->alias('o')
->join('users u', 'o.user_id = u.id')
->force('idx_order_no')
->where('o.order_no', '20240101001')
->find();
忽略索引(Ignore Index)
// 忽略某个索引
$data = Db::name('users')
->ignore('idx_email')
->where('email', 'test@example.com')
->find();
建议索引(Use Index)
// 使用建议索引
$data = Db::name('products')
->useIndex('idx_category_price')
->where('category_id', 1)
->order('price desc')
->select();
SQL语句层面的索引优化
查询优化
// 避免使用函数导致索引失效
// 错误示例
$data = Db::name('users')
->where("DATE(create_time) = '2024-01-01'")
->select();
// 正确示例
$data = Db::name('users')
->where('create_time', '>=', '2024-01-01 00:00:00')
->where('create_time', '<=', '2024-01-01 23:59:59')
->select();
// 避免使用LIKE模糊查询开头通配符
$data = Db::name('products')
->where('name', 'like', '手机%') // 有效
->select();
$data = Db::name('products')
->where('name', 'like', '%手机%') // 索引失效
->select();
排序与分组优化
// 排序字段使用索引
$data = Db::name('orders')
->where('status', 1)
->order('create_time desc')
->limit(10)
->select();
// 分组查询优化
$data = Db::name('orders')
->field('user_id, COUNT(*) as total')
->group('user_id')
->having('total > 5')
->select();
联合索引优化
// 遵循最左前缀原则
// 联合索引(idx_user_id, idx_status, idx_create_time)
$data = Db::name('orders')
->where('user_id', 100)
->where('status', 1)
->order('create_time desc')
->select();
ThinkPHP查询构造器优化
字段选择优化
// 只查询需要的字段
$data = Db::name('users')
->field('id, username, email')
->where('status', 1)
->select();
// 避免SELECT *
$data = Db::name('users')
->where('id', 100)
->value('username');
分页优化
// 大分页优化
$offset = 100000;
$limit = 20;
// 优化前 - 大量回表
$data = Db::name('orders')
->limit($offset, $limit)
->select();
// 优化后 - 使用延迟关联
$ids = Db::name('orders')
->limit($offset, $limit)
->column('id');
$data = Db::name('orders')
->whereIn('id', $ids)
->select();
子查询优化
// 使用JOIN替代子查询
$data = Db::name('orders')
->alias('o')
->join('users u', 'o.user_id = u.id')
->field('o.*, u.username')
->where('o.status', 1)
->select();
索引设计优化
索引创建技巧
// 在模型初始化时定义索引(通过迁移文件)
// database/migrations/xxxx_create_users_table.php
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('username')->unique();
$table->string('email')->unique();
$table->string('phone', 20)->nullable();
$table->timestamp('created_at')->nullable();
// 复合索引
$table->index(['status', 'created_at']);
// 前缀索引(针对较长字符串)
$table->index('phone', 'idx_phone_prefix');
});
常用索引类型建议
// 覆盖索引 - 避免回表
$data = Db::name('users')
->field('id, username, email') // 这些字段都在索引中
->where('username', 'admin')
->select();
// 冗余索引避免
// 错误:已有idx_user_id,又创建idx_user_id_status
$table->index(['user_id'], 'idx_user_id');
$table->index(['user_id', 'status'], 'idx_user_id_status');
性能监控与分析
查询日志分析
// 开启查询日志
Db::listen(function($sql, $time, $explain) {
// 记录慢查询
if ($time > 1) { // 1秒以上
Log::warning('Slow query:', [
'sql' => $sql,
'time' => $time,
'explain' => $explain
]);
}
});
// 使用Debug工具
Debug::setConfig(['log' => true]);
EXPLAIN分析
// 通过查询构建器获取EXPLAIN信息
$sql = Db::name('orders')
->where('status', 1)
->buildSql();
$explain = Db::query('EXPLAIN ' . $sql);
缓存优化
查询结果缓存
// 缓存查询结果
$data = Cache::remember('users_list', 3600, function() {
return Db::name('users')
->where('status', 1)
->select();
});
// 缓存查询(ThinkPHP内置)
$data = Db::name('users')
->cache('users_list', 3600)
->where('status', 1)
->select();
数据库查询缓存
// 使用数据库查询缓存
return Db::name('users')
->fetchSql(true)
->where('id', 100)
->select();
最佳实践建议
索引监控表
// 创建索引使用建议表
CREATE TABLE index_optimization_log (
id INT PRIMARY KEY AUTO_INCREMENT,
sql_text TEXT,
table_name VARCHAR(100),
execution_time FLOAT,
index_used VARCHAR(100),
suggestion TEXT,
created_at TIMESTAMP
);
定期优化策略
// 定期执行优化命令
Artisan::command('db:optimize', function () {
// 分析表
$tables = Db::select('SHOW TABLES');
foreach ($tables as $table) {
$tableName = array_values((array)$table)[0];
// 分析表结构
Db::statement("ANALYZE TABLE {$tableName}");
// 优化表
Db::statement("OPTIMIZE TABLE {$tableName}");
}
});
索引使用率监控
// 查看索引使用情况
class IndexMonitor
{
public function check()
{
$sql = "
SELECT
TABLE_NAME,
INDEX_NAME,
CARDINALITY,
INDEX_TYPE
FROM
INFORMATION_SCHEMA.STATISTICS
WHERE
TABLE_SCHEMA = DATABASE()
";
return Db::select($sql);
}
}
常见性能陷阱
避免的写法
// 不要在索引列上使用运算
where('age * 2 > 100') // 错误
where('age > 50') // 正确
// 类型转换导致索引失效
where('user_id', '=', '100abc') // 字符串比较
// 两个table的字符集不同
推荐的做法
// 使用自增主键
// 使用短ID替代长字符串
// 对于频繁查询的字段添加索引
// 定期清理不用的索引
// 示例:优化后的查询
class OrderRepository
{
public function getActiveOrders($userId)
{
$query = Db::name('orders')
->where('user_id', $userId)
->where('status', 1)
->where('create_time', 'between', [date('Y-m-d', strtotime('-30 days')), date('Y-m-d')])
->field('id, order_no, amount, status')
->order('create_time desc')
->limit(20);
// 添加缓存
$cacheKey = "orders_{$userId}_active";
return $query->cache($cacheKey, 600)->select();
}
}
通过以上优化方案,可以显著提升ThinkPHP项目的数据库查询性能,建议根据实际业务场景,结合EXPLAIN分析结果,制定合适的索引策略。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播