ThinkPHP不感知PolarDB-X的GSI,需通过原生SQL加/+TDDL:INDEX/ Hint或手动插入Hint引导路由,且GSI由数据库层管理,配置文件中无相关选项。

ThinkPHP 本身不感知 PolarDB-X 的全局二级索引(GSI),它只把 SQL 当作普通查询执行;真正起作用的是你写的 SQL 是否命中 GSI,以及 PolarDB-X 的优化器是否能自动路由到索引表——而这个过程在 ThinkPHP 中完全透明,**不靠配置,靠写法和 Hint**。
为什么 ThinkPHP 的 database.php 配置里没有 GSI 相关字段
PolarDB-X 的 GSI 是数据库服务层的逻辑索引表,不是客户端驱动或 ORM 层的概念。ThinkPHP 连接的是 PolarDB-X 的 MySQL 兼容协议入口(比如 mysql://user:pass@host:port/db),它看到的只是一个“看起来像 MySQL”的数据库。所有 GSI 的创建、覆盖列定义、分区规则,都在建表或 CREATE GLOBAL INDEX 时由 DBA 或部署脚本完成,ThinkPHP 不参与也不需要知道。
常见误区是想在 database.php 里加个 'gsi_enabled' => true 或类似配置——这不存在,也无效。
在 ThinkPHP 查询中手动指定 GSI 的两种可靠方式
当你的查询条件不含主表分区键(比如主表按 order_id 分区,但你想查 buyer_id),必须显式引导 PolarDB-X 使用 GSI,否则会全分片扫描。ThinkPHP 提供了两个底层可控入口:
立即学习“PHP免费学习笔记(深入)”;
- 用
query()+ 原生 SQL +/+TDDL:INDEX(t_order, g_i_buyer)/注释式 Hint:这是最稳定的方式,绕过 QueryBuilder 的解析限制 - 用
table()->where()->fetchSql(true)生成 SQL 后,手动插入 Hint,再交给query()执行 - 避免用
forceIndex()方法:ThinkPHP 的forceIndex()生成的是 MySQL 原生FORCE INDEX,而 PolarDB-X 对 GSI 不识别该语法,只会 fallback 到主表扫描
示例:
$sql = "/+TDDL:INDEX(t_order, g_i_buyer)/ SELECT * FROM t_order WHERE buyer_id = ?"; $result = Db::query($sql, [$buyerId]);
COVERING 列设计直接影响 ThinkPHP 查询性能
GSI 的 COVERING 定义决定了是否需要回表。如果 ThinkPHP 的查询语句里 SELECT * 或包含未被 COVERING 的字段(比如 order_detail),PolarDB-X 就得先查 GSI 表拿到主键/分片键,再跨分片回查主表——这会让一次查询变成两次网络往返,延迟翻倍。
建议在建 GSI 时就预判业务常用字段:
- 如果 ThinkPHP 接口常返回
order_id,buyer_id,status,就把这三个列加进COVERING - 避免在 GSI 中
COVERING大字段(如longtext),会显著增大索引表体积和同步延迟 - 用
EXPLAIN验证:在数据库终端执行带 Hint 的 SQL,看输出里是否有LogicalView(tables="t_order_gsi_...")和MultiRead(表示已走 GSI)
ALTER TABLE 修改主表结构时 GSI 的兼容性风险
ThinkPHP 的迁移命令(php think migrate:run)若调用了 ALTER TABLE,需特别注意 PolarDB-X 对含 GSI 表的限制:
-
ADD COLUMN允许,但新列不会自动加入已有 GSI 的COVERING列,需单独ALTER GLOBAL INDEX ... COVERING -
MODIFY COLUMN类型变更(如varchar(20) → varchar(50))允许;但若改的是 GSI 的索引列(如buyer_id),会失败并报错ERR_GSI_INDEX_COLUMN_TYPE_MISMATCH - 绝对禁止修改主表的拆分键(
dbpartition by列)或主键——这会导致 GSI 元数据失效,后续所有带 Hint 的查询都可能路由错误
这类操作一旦出错,恢复成本高,且 ThinkPHP 无法捕获 PolarDB-X 特有的 DDL 错误码,日志里只显示通用 SQL 异常。
真正难的不是怎么写 Hint,而是理解哪些查询路径天然无法走 GSI:比如 LIKE '%xxx'、函数包裹索引列(WHERE MD5(buyer_id) = ?)、或跨多个 GSI 列的 OR 条件。这些场景下,Hint 无效,只能重构查询或调整业务逻辑。



















