我正在开设一家只通过贷款销售产品的电子商店。我在任何类别中每页显示10个产品,每个产品有3个不同的价格标签 - 3种不同的贷款类型。在测试时间一切都很顺利,查询执行时间很完美,但今天将更改转移到生产服务器时,网站在大约2分钟内“崩溃”。用于选择贷款类型的查询有时会挂起约10秒,并且经常发生,因此无法跟上并且 hella slow 。用于存储数据的表有大约2百万条记录,每个选择看起来像这样:
SELECT *
FROM products_loans
WHERE KOD IN("X17/Q30-10", "X17/12", "X17/5-24")
AND 369.27 BETWEEN CENA_OD AND CENA_DO;
3种贷款类型和需要在CENA_OD和CENA_DO之间的价格,因此返回3行。
但是由于我需要每页显示10个产品,我需要使用 OR 通过修改后的选择运行它,因为我没有找到任何其他解决方案。我问过它here,但没有回答。正如引用文章中所提到的,这必须单独完成,因为没有列可以在连接中使用(当然价格和代码除外,但结果非常非常糟糕)。以下是通过INDEX索引的show create table
,kod和CENA_OD / CENA_DO。
CREATE TABLE `products_loans` (
`KOEF_ID` bigint(20) NOT NULL,
`KOD` varchar(30) NOT NULL,
`AKONTACIA` int(11) NOT NULL,
`POCET_SPLATOK` int(11) NOT NULL,
`koeficient` decimal(10,2) NOT NULL default '0.00',
`CENA_OD` decimal(10,2) default NULL,
`CENA_DO` decimal(10,2) default NULL,
`PREDAJNA_CENA` decimal(10,2) default NULL,
`AKONTACIA_SUMA` decimal(10,2) default NULL,
`TYP_VYHODY` varchar(4) default NULL,
`stage` smallint(6) NOT NULL default '1',
PRIMARY KEY (`KOEF_ID`),
KEY `CENA_OD` (`CENA_OD`),
KEY `CENA_DO` (`CENA_DO`),
KEY `KOD` (`KOD`),
KEY `stage` (`stage`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
并选择所有贷款类型,然后通过php过滤它们并不好用,因为每种类型都有超过50k的记录,而选择也花费太多时间......
任何提高速度的想法都值得赞赏。
编辑:
这是解释
+----+-------------+----------------+-------+---------------------+------+---------+------+--------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+----------------+-------+---------------------+------+---------+------+--------+-------------+
| 1 | SIMPLE | products_loans | range | CENA_OD,CENA_DO,KOD | KOD | 92 | NULL | 190158 | Using where |
+----+-------------+----------------+-------+---------------------+------+---------+------+--------+-------------+
我已经尝试了组合索引,它将测试服务器的性能从0.44秒提高到0.06秒,但是我无法从家里访问生产服务器,所以明天我将不得不尝试。
答案 0 :(得分:2)
您的问题是您正在搜索包含点的间隔(而不是间隔中所有点的更常规查询)。这些查询与标准B树索引不兼容,因此您需要使用R-Tree索引。不幸的是,MySQL不允许您在列上选择R-Tree索引,但是您可以通过将列类型更改为GEOMETRY并使用几何函数来检查间隔是否包含该点来获得所需的索引。
请参阅Quassnoi的文章Adjacency list vs. nested sets: MySQL,在那里他会更详细地解释这一点。用例不同,但所涉及的技术是相同的。以下是本文相关部分的摘录:
还有一类任务需要搜索包含已知值的所有范围:
- 在IP范围禁令列表中搜索IP地址
- 在日期范围内搜索指定日期
和其他几个人。使用MySQL的R-Tree功能可以改善这些任务。
答案 1 :(得分:1)
尝试重构您的查询,如:
SELECT * FROM products_loans
WHERE KOD IN("X17/Q30-10", "X17/12", "X17/5-24")
AND CENA_OD >= 369.27
AND CENA_DO <= 369.27;
(选择索引时mysql不是很聪明)并检查性能。
下一个尝试是添加组合键 - (KOD,CENA_OD,CENA_DO)
接下来的主要尝试是重构你的基地,让产品与价格分开。这应该真的有帮助。
PS:你也可以迁移到postgresql,它在选择正确的索引时比mysql更聪明。
答案 2 :(得分:0)
MySQL只能使用1个密钥。如果您始终通过3列获得条目,则根据列中的实际数据(范围),以下其中一项可以很好地添加大量性能:
ALTER TABLE products_loans ADD INDEX(KOD, CENA_OD, CENA_DO);
ALTER TABLE products_loans ADD INDEX(CENA_OD, CENA_DO, KOD);
请注意,列的顺序很重要!如果这不能提高性能,请向我们提供查询的EXPLAIN
输出。