所以我有两个表,我需要能够得到计数.其中一个包含内容,另一个包含它与类别表之间的关系.这是DDl:
CREATE TABLE content_en (
id int(11) NOT NULL AUTO_INCREMENT,
title varchar(100) DEFAULT NULL,
uid int(11) DEFAULT NULL,
date_added int(11) DEFAULT NULL,
date_modified int(11) DEFAULT NULL,
active tinyint(1) DEFAULT NULL,
comment_count int(6) DEFAULT NULL,
orderby tinyint(4) DEFAULT NULL,
settings text,
permalink varchar(255) DEFAULT NULL,
code varchar(3) DEFAULT NULL,
PRIMARY KEY (id),
UNIQUE KEY id (id),
UNIQUE KEY id_2 (id) USING BTREE,
UNIQUE KEY combo (id,active) USING HASH,
KEY code (code) USING BTREE
) ENGINE=MyISAM AUTO_INCREMENT=127126 DEFAULT CHARSET=utf8;
Run Code Online (Sandbox Code Playgroud)
而对于另一张桌子
CREATE TABLE content_page_categories (
catid int(11) unsigned NOT NULL,
itemid int(10) unsigned NOT NULL,
main tinyint(1) DEFAULT NULL,
KEY itemid (itemid),
KEY catid (catid),
KEY combo (catid,itemid) USING BTREE
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Run Code Online (Sandbox Code Playgroud)
我正在运行的查询是:
SELECT count(*)
FROM content_page_categories USE INDEX (combo)
INNER JOIN content_en USE INDEX (combo) ON (id = itemid)
WHERE catid = 1 AND active = 1 ;
Run Code Online (Sandbox Code Playgroud)
两个表都有125k行,我无法让计数查询运行得足够快.我得到的最佳时机是0.175,这对于大量的行来说是可怕的.选择100行的速度最快为0.01.我试过这个查询的3个或4个变种,但最后的时间差不多.此外,如果我不做USE INDEX时间变慢3倍.
还尝试了以下内容:
SELECT COUNT( *) FROM content_page_categories
INNER JOIN content_en ON id=itemid
AND catid = 1 AND active = 1 WHERE 1
并且:
SELECT SQL_CALC_FOUND_ROWS catid,content_en.* FROM content_page_categories
INNER JOIN content_en ON (id=itemid)
WHERE catid =1 AND active = 1 LIMIT 1;
SELECT FOUND_ROWS();
索引定义:
content_en 0 PRIMARY 1 id A 125288 BTREE
content_en 0 id 1 id A 125288 BTREE
content_en 0 id_2 1 id A 125288 BTREE
content_en 0 combo 1 id A BTREE
content_en 0 combo 2 active A YES BTREE
content_en 1 code 1 code A 42 YES BTREE
content_page_categories 1 itemid 1 itemid A 96842 BTREE
content_page_categories 1 catid 1 catid A 10 BTREE
content_page_categories 1 combo 1 catid A 10 BTREE
content_page_categories 1 combo 2 itemid A 96842 BTREE
SELECT COUNT( *) FROM content_page_categories
INNER JOIN content_en ON id=itemid
AND catid = 1 AND active = 1 WHERE 1
有任何想法吗?
[编辑]
我在这里上传了这些表的样本数据
解释结果:
mysql> explain SELECT count(*) FROM content_page_categories USE INDEX (combo) I<br>
NNER JOIN content_en USE INDEX (combo) ON (id = itemid) WHERE catid = 1 AND act<br>
ive = 1 ;
+----+-------------+-------------------------+-------+---------------+-------+---------+--------------------------+--------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------------------+-------+---------------+-------+---------+--------------------------+--------+--------------------------+
| 1 | SIMPLE | content_en | index | combo | combo | 6 | NULL | 125288 | Using where; Using index |
| 1 | SIMPLE | content_page_categories | ref | combo | combo | 8 | const,mcms.content_en.id | 1 | Using where; Using index |
+----+-------------+-------------------------+-------+---------------+-------+---------+--------------------------+--------+--------------------------+
2 rows in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
Bil*_*win 12
我下载了你的数据,并尝试了一些实验.我在Macbook Pro上的CentOS虚拟机上运行MySQL 5.6.12.我观察到的时间可用于比较,但您的系统可能具有不同的性能.
首先,我尝试没有USE INDEX子句,因为我尽可能避免优化覆盖.在大多数情况下,像这样的简单查询应该使用正确的索引(如果可用).对查询中的索引选项进行硬编码会使以后使用更好的索引变得更加困难.
我还使用相关名称(表别名)来使查询更清晰.
mysql> EXPLAIN SELECT COUNT(*) FROM content_en AS e
INNER JOIN content_page_categories AS c ON c.itemid = e.id
WHERE c.catid = 1 AND e.active = 1\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: c
type: ref
possible_keys: combo,combo2
key: combo
key_len: 4
ref: const
rows: 71198
Extra: Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: e
type: eq_ref
possible_keys: PRIMARY,combo2,combo
key: PRIMARY
key_len: 4
ref: test.c.itemid
rows: 1
Extra: Using where
Run Code Online (Sandbox Code Playgroud)
我也想在第二个表上获得"使用索引",所以我需要按顺序使用(active,id)索引.在这种情况下,我不得不使用INDEX来说服优化器不要使用主键.
mysql> ALTER TABLE content_en ADD KEY combo2 (active, id);
mysql> explain SELECT COUNT(*) FROM content_en AS e USE INDEX (combo2)
INNER JOIN content_page_categories AS c ON c.itemid = e.id
WHERE c.catid = 1 AND e.active = 1\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: c
type: ref
possible_keys: combo,combo2
key: combo
key_len: 4
ref: const
rows: 71198
Extra: Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: e
type: ref
possible_keys: combo2
key: combo2
key_len: 6
ref: const,test.c.itemid
rows: 1
Extra: Using where; Using index
Run Code Online (Sandbox Code Playgroud)
在rows
由EXPLAIN报告是它有多少工作要采取执行查询的一个重要指标.请注意rows
,上面的EXPLAIN只有71k,比你先扫描content_en表时得到的125k行要小得多.
我尝试了与上面相同的覆盖索引解决方案,但使用InnoDB作为存储引擎.
mysql> ALTER TABLE content_en ENGINE=InnoDB;
mysql> ALTER TABLE content_page_categories ENGINE=InnoDB;
Run Code Online (Sandbox Code Playgroud)
这与EXPLAIN报告相同.它花了1或2次迭代来加热缓冲池,但随后查询的性能增加了两倍.
这在0.16秒内执行.
我也尝试删除USE INDEX,时间略有增加,达到0.17秒.
mysql> SELECT straight_join count(*)
FROM content_en
INNER JOIN content_page_categories use index (combo)
ON (id = itemid)
WHERE catid = 1 AND active = 1;
Run Code Online (Sandbox Code Playgroud)
我尝试了@bobwienholt提出的解决方案,使用非规范化将active
属性复制到content_page_categories
表中.
mysql> ALTER TABLE content_page_categories ADD COLUMN active TINYINT(1);
mysql> UPDATE content_en JOIN content_page_categories ON id = itemid
SET content_page_categories.active = content_en.active;
mysql> ALTER TABLE content_page_categories ADD KEY combo3 (catid,active);
mysql> SELECT COUNT(*) FROM content_page_categories WHERE catid = 1 and active = 1;
Run Code Online (Sandbox Code Playgroud)
这在0.037 - 0.044秒内执行.因此,如果您可以使冗余active
列与content_en
表中的值保持同步,那么这样做会更好.
我尝试了@Quassnoi提出的解决方案,以维护一个表,其中包含每个catid和活动的预计算计数.该表应该只有很少的行,并且查找您需要的计数是主键查找并且不需要JOIN.
mysql> CREATE TABLE page_active_category (
active INT NOT NULL,
catid INT NOT NULL,
cnt BIGINT NOT NULL,
PRIMARY KEY (active, catid)
) ENGINE=InnoDB;
mysql> INSERT INTO page_active_category
SELECT e.active, c.catid, COUNT(*)
FROM content_en AS e
JOIN content_page_categories AS c ON c.itemid = e.id
GROUP BY e.active, c.catid
mysql> SELECT cnt FROM page_active_category WHERE active = 1 AND catid = 1
Run Code Online (Sandbox Code Playgroud)
这在0.0007 - 0.0017秒内执行.因此,如果您可以使用聚合计数维护表,那么这是一个数量级的最佳解决方案.
您可以从中看到,不同类型的非规范化(包括汇总表)是一个非常强大的工具,虽然它有一些缺点,因为维护冗余数据可能会带来不便并使您的应用程序更加复杂.
记录的记录太多了.
如果您想要更快的解决方案,则必须存储汇总数据.
MySQL不支持物化视图(或SQL Server术语中的索引视图),因此您需要自己创建和维护它们.
创建一个表:
CREATE TABLE
page_active_category
(
active INT NOT NULL,
catid INT NOT NULL,
cnt BIGINT NOT NULL,
PRIMARY KEY
(active, catid)
) ENGINE=InnoDB;
Run Code Online (Sandbox Code Playgroud)
然后填充它:
INSERT
INTO page_active_category
SELECT active, catid, COUNT(*)
FROM content_en
JOIN content_page_categories
ON itemid = id
GROUP BY
active, catid
Run Code Online (Sandbox Code Playgroud)
现在,您将每一次,删除或更新任何记录content_en
或者content_page_categories
,你应该更新相应的记录page_active_category
.
这是可行的与双方两个简单的触发器content_en
和content_page_categories
.
这样,您的原始查询可能会被重写为仅仅:
SELECT cnt
FROM page_active_category
WHERE active = 1
AND catid = 1
Run Code Online (Sandbox Code Playgroud)
这是一个主键查找,因此是即时的.
归档时间: |
|
查看次数: |
5394 次 |
最近记录: |