Par*_*ser 5 mysql index order-by
我试图获取有关为什么 MySQL 在创建索引时不使用我的索引inner join并ORDER BY最终尝试使用的信息。
我在这里有我的 SQL 查询:
SELECT
*
FROM
product p INNER JOIN productStore ps ON p.productUUID = ps.productUUID
ORDER BY
ps.storeTitle
LIMIT 50;
Run Code Online (Sandbox Code Playgroud)
当我通过这个选择使用订单超过 3,5 秒时,当我通过它花费 1,6 毫秒来删除订单以运行相同的 SQL 时,我的解释 SQL 如下
与ORDER BY:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE ps ALL PRIMARY NULL NULL NULL 942187 Using filesort
1 SIMPLE p eq_ref PRIMARY PRIMARY 16 foeniks_core.ps.productUUID 1 NULL
Run Code Online (Sandbox Code Playgroud)
没有ORDER BY:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE ps ALL PRIMARY NULL NULL NULL 942187 NULL
1 SIMPLE p eq_ref PRIMARY PRIMARY 16 foeniks_core.ps.productUUID 1 NULL
Run Code Online (Sandbox Code Playgroud)
没有索引的字段是长度为 282 的 varchar。
我的桌子设计在这里:
CREATE TABLE `productStore` (
`productUUID` binary(16) NOT NULL,
`storeUUID` binary(16) NOT NULL,
`distributorLastUsed` binary(16) DEFAULT NULL,
`storeTitle` varchar(282) DEFAULT NULL,
`storeUrl` varchar(282) DEFAULT NULL,
`storeDescription` text,
`storeDescriptionDemo` text,
`storePrice` int(11) NOT NULL DEFAULT '0',
`storePriceNext` int(11) NOT NULL DEFAULT '0',
`storePriceCost` int(11) NOT NULL DEFAULT '0',
`overwrites` int(11) NOT NULL DEFAULT '0',
`updated` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`added` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`allowDisplay` tinyint(1) NOT NULL DEFAULT '0',
`activated` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`productUUID`,`storeUUID`),
KEY `productStoreLanguageToStore_idx` (`storeUUID`),
KEY `productStoreToDistributor_idx` (`distributorLastUsed`),
KEY `storeUrl` (`storeUrl`(180)) USING BTREE,
KEY `testStoreTitle` (`storeTitle`(182)),
CONSTRAINT `productStoreToDistributor` FOREIGN KEY (`distributorLastUsed`) REFERENCES `distributor` (`distributorUUID`) ON DELETE SET NULL ON UPDATE CASCADE,
CONSTRAINT `productStoreToProduct` FOREIGN KEY (`productUUID`) REFERENCES `product` (`productUUID`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `productStoreToStore` FOREIGN KEY (`storeUUID`) REFERENCES `store` (`storeUUID`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Run Code Online (Sandbox Code Playgroud)
产品表:
CREATE TABLE `product` (
`productUUID` binary(16) NOT NULL,
`productManufacturerUUID` binary(16) NOT NULL,
`productManufacturerSKU` varchar(40) DEFAULT NULL,
`productEan` varchar(40) DEFAULT NULL,
`cnetID` varchar(10) DEFAULT NULL,
`edbID` int(10) DEFAULT NULL,
`overwrites` int(10) NOT NULL DEFAULT '0',
`updated` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`added` datetime NOT NULL DEFAULT '1000-01-01 00:00:00',
`activated` tinyint(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`productUUID`),
KEY `manufacturerSKU` (`productManufacturerSKU`(16)),
KEY `productToManufacturer_idx` (`productManufacturerUUID`),
KEY `cnetID` (`cnetID`),
KEY `productEAN` (`productEan`),
CONSTRAINT `productToManufacturer` FOREIGN KEY (`productManufacturerUUID`) REFERENCES `manufacturer` (`manufacturerUUID`) ON DELETE NO ACTION ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Run Code Online (Sandbox Code Playgroud)
除了“令人鼓舞”的建议外,David Spillett 的回答在所有方面都是正确的。
这是一种不仅鼓励而且(在几乎所有版本中)强制优化器选择使用所需索引来查找 50 行的计划的方法 - 并且仅在此之后执行连接。它不能总是使用,但FOREIGN KEY约束确保在这种情况下两个查询将产生相同的结果。
我称这种技术为“先LIMIT,然后JOIN”:
SELECT p.*, ps.*
FROM ( SELECT *
FROM productStore
ORDER BY storeTitle
LIMIT 50
) ps
INNER JOIN product p
ON p.productUUID = ps.productUUID
ORDER BY ps.storeTitle ;
Run Code Online (Sandbox Code Playgroud)
真正的答案是“前缀”索引实际上毫无用处。我指的是
KEY `testStoreTitle` (`storeTitle`(182))
Run Code Online (Sandbox Code Playgroud)
由于索引仅包含截断的值,因此它没有完全排序的标题列表,因此不能轻松地用于执行ORDER BY.
InnoDB 的限制为 767字节(utf8 的最大值VARCHAR(255))。这可以通过一组复杂的步骤来增加:
SET GLOBAL innodb_file_format=Barracuda;SET GLOBAL innodb_file_per_table=ON;ALTER TABLE tbl
DROP INDEX testStoreTitle,
ADD INDEX(storeTitle)
ROW_FORMAT=DYNAMIC; - (或者COMPRESSED)我同意 ypercube 建议的“加入之前限制(或分组依据)”。该解决方案大部分与此解决方案正交。我的解决方案可能会快得多,因为它不需要扫描 942187 任何东西。
| 归档时间: |
|
| 查看次数: |
3473 次 |
| 最近记录: |