我在mariadb数据库上面临性能问题。在我看来,当使用子查询执行请求时,mariadb没有使用正确的索引,而在请求中手动注入子查询的结果成功使用了索引:
以下是具有错误行为的请求(请注意,第二个子查询读取的行数多于必要行):
ANALYZE SELECT `orders`.* FROM `orders`
WHERE `orders`.`account_id` IN (SELECT `accounts`.`id` FROM `accounts` WHERE `accounts`.`user_id` = 88144)
AND ( orders.type not in ("LimitOrder", "MarketOrder")
OR orders.type in ("LimitOrder", "MarketOrder") AND orders.state <> "canceled"
OR orders.type in ("LimitOrder", "MarketOrder") AND orders.state = "canceled" AND orders.traded_btc > 0 )
AND (NOT (orders.type = 'AdminOrder' AND orders.state = 'canceled')) ORDER BY `orders`.`id` DESC LIMIT 20 OFFSET 0 \G;
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: accounts
type: ref
possible_keys: PRIMARY,index_accounts_on_user_id
key: index_accounts_on_user_id
key_len: 4
ref: const
rows: 7
r_rows: 7.00
filtered: 100.00
r_filtered: 100.00
Extra: Using index; Using temporary; Using filesort
*************************** 2. row ***************************
id: 1
select_type: PRIMARY
table: orders
type: ref
possible_keys: index_orders_on_account_id_and_type,index_orders_on_type_and_state_and_buying,index_orders_on_account_id_and_type_and_state,index_orders_on_account_id_and_type_and_state_and_traded_btc
key: index_orders_on_account_id_and_type_and_state_and_traded_btc
key_len: 4
ref: bitcoin_central.accounts.id
rows: 60
r_rows: 393.86
filtered: 100.00
r_filtered: 100.00
Extra: Using index condition; Using where
当手动注入子查询的结果时,我有正确的行为(和预期的性能):
ANALYZE SELECT `orders`.* FROM `orders`
WHERE `orders`.`account_id` IN (433212, 433213, 433214, 433215, 436058, 436874, 437950)
AND ( orders.type not in ("LimitOrder", "MarketOrder")
OR orders.type in ("LimitOrder", "MarketOrder") AND orders.state <> "canceled"
OR orders.type in ("LimitOrder", "MarketOrder") AND orders.state = "canceled" AND orders.traded_btc > 0 )
AND (NOT (orders.type = 'AdminOrder' AND orders.state = 'canceled'))
ORDER BY `orders`.`id` DESC LIMIT 20 OFFSET 0\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: orders
type: range
possible_keys: index_orders_on_account_id_and_type,index_orders_on_type_and_state_and_buying,index_orders_on_account_id_and_type_and_state,index_orders_on_account_id_and_type_and_state_and_traded_btc
key: index_orders_on_account_id_and_type_and_state_and_traded_btc
key_len: 933
ref: NULL
rows: 2809
r_rows: 20.00
filtered: 100.00
r_filtered: 100.00
Extra: Using index condition; Using where; Using filesort
1 row in set (0.37 sec)
请注意,在加入两个表时,我遇到了完全相同的问题。
以下是我的订单表定义的摘录:
SHOW CREATE TABLE orders \G;
*************************** 1. row ***************************
Table: orders
Create Table: CREATE TABLE `orders` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`account_id` int(11) NOT NULL,
`traded_btc` decimal(16,8) DEFAULT '0.00000000',
`type` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`state` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `index_orders_on_account_id_and_type_and_state_and_traded_btc` (`account_id`,`type`,`state`,`traded_btc`),
CONSTRAINT `orders_account_id_fk` FOREIGN KEY (`account_id`) REFERENCES `accounts` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=8575594 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
有谁知道这里发生了什么? 有没有办法强制数据库在我的子请求中使用我的索引。
答案 0 :(得分:1)
IN ( SELECT ... )
优化得很差。通常的解决方案是变成JOIN
:
FROM accounts AS a
JOIN orders AS o ON a.id = o.account_id
WHERE a.user_id = 88144
AND ... -- the rest of your WHERE
或者就是你用&#34做的事情;注意我在加入两个表时遇到了完全相同的问题。&#34;?如果是这样,请查看查询及其EXPLAIN
。
您参考&#34;预期效果&#34; ...您是指EXPLAIN
中的数字?或者你有时间来支持断言?
我喜欢这样做,以便更细致地了解多少&#34;工作&#34;正在进行:
FLUSH STATUS;
SELECT ...;
SHOW SESSION STATUS LIKE 'Handler%';
这些数字通常表明是否涉及表扫描或查询是否在OFFSET+LIMIT
之后停止。这些数字是精确计数,与EXPLAIN
不同,这只是估算值。
据推测,您通常会通过orders
查看account_id
?以下是加速此类查询的方法:
替换当前的两个索引
PRIMARY KEY (`id`),
KEY `account_id__type__state__traded_btc`
(`account_id`,`type`,`state`,`traded_btc`),
用这些:
PRIMARY KEY (`account_id`, `type`, `id`),
KEY (id) -- to keep AUTO_INCREMENT happy.
这会聚合给定帐户的所有行,从而使查询运行得更快,尤其是当您现在 I / O绑定时。如果列的某些组合使得#34;自然&#34; PK,然后完全抛出id
。
(并注意我如何在不丢失任何信息的情况下缩短您的密钥名称?)
此外,如果您受I / O约束,通过将那些冗长的VARCHARs
(州和类型)转换为ENUMs
,可以缩小表格。
更多强>
鉴于该查询涉及
WHERE ... mess with INs and ORs ...
ORDER BY ...
LIMIT 20
和该用户有200万行,没有INDEX
可以通过WHERE
进入ORDER BY
以便它可以使用LIMIT
。也就是说,必须以这种方式执行:
ORDER BY
)2M行的一些重要部分我真的很惊讶IN( constants )
效果很好。
答案 1 :(得分:1)
我有同样的问题。请使用内部联接而不是子查询。