MySQL中有序视图的性能问题
Posted
技术标签:
【中文标题】MySQL中有序视图的性能问题【英文标题】:Performance problems with an ordered view in MySQL 【发布时间】:2017-07-17 12:33:06 【问题描述】:我是 mysql 性能调优的新手,我需要您的帮助来解决我们稍后在设计中将替换表的视图。
要替换的表名为users,具有以下属性: users2 视图具有以下属性:
当我对两个对象执行普通 SELECT 时,它们会同时响应:
SELECT *
FROM `users`
SELECT *
FROM `users2`
但是这些查询的有序版本会导致不同的性能:表有点慢(不到两秒),这次视图大约需要十倍:
SELECT *
FROM `users`
ORDER BY `lastName`, `firstName`
SELECT *
FROM `users2`
ORDER BY `lastName`, `firstName`
为了找出原因,我让EXPLAIN这两个cmets:
显然,属性 Countries_ID 上的表 'a' (addresses) 上的 ALL 正在制造麻烦,所以我做了以下:
ALTER TABLE addresses ADD INDEX (Countries_ID);
这个索引根本没有改变任何东西。所以,我问你的意见,什么可以做得更好。
注意 1:有没有办法在临时列 Countries_ID_2 上创建索引? 注意 2:users2 视图是使用以下 SQL 查询创建的:
CREATE OR REPLACE VIEW users2 AS
(SELECT p.username
, p.password
, p.firstName
, p.lastName
, p.eMail AS email
, a.settlement AS city
, s.name AS country
, pl.languages
, p.description
, p.ID AS ID
, p.phone1
, p.phone2
, CONCAT_WS(' ', a.street, a.addition) AS address
, p.status
, p.publicMail
, ad.name AS Betreuer
FROM addresses a
INNER JOIN addresses_have_persons ap ON a.ID = ap.Addresses_ID
INNER JOIN countries c ON a.Countries_ID = c.ID
INNER JOIN persons p ON a.ID = p.addressID
AND ap.Persons_ID = p.ID
INNER JOIN states s ON a.States_ID = s.ID
INNER JOIN persons_language pl ON p.ID = pl.ID
LEFT JOIN advisors ad ON p.advisorID = ad.ID
-- LEFT JOIN titles t ON t.ID = ad.titleID
);
注意3:虽然persons表中有很多字段是NULL,但是没有一行这些字段都是NULL。
编辑:
CREATE OR REPLACE VIEW persons_language AS
(SELECT lp.Persons_ID AS ID
, GROUP_CONCAT(DISTINCT l.name ORDER BY l.name SEPARATOR ', ') AS languages
FROM languages l
, languages_have_persons lp
WHERE l.ID = lp.Languages_ID
GROUP BY lp.Persons_ID);
没有ORDER BY,语言名称不是按字母顺序排列的,这是我目前想要的。也许,我们可以决定以任何顺序获得它们,但我们会看到。
目前,我做了以下修改,没有任何性能提升:
ALTER TABLE addresses ADD INDEX (Countries_ID);
ALTER TABLE addresses ADD INDEX (States_ID);
ALTER TABLE addresses_have_persons ADD INDEX (Addresses_ID);
ALTER TABLE languages ADD INDEX (name);
ALTER TABLE persons ADD INDEX (addressID);
ALTER TABLE persons ADD INDEX (address2ID);
ALTER TABLE persons ADD INDEX (address3ID);
ALTER TABLE persons ADD INDEX (advisorID);
编辑 2:
我也在另一个网站上讨论过这个问题。那里的讨论让我进行了以下更改以更接近第三范式:
CREATE OR REPLACE TABLE accounts
(ID INT NOT NULL AUTO_INCREMENT PRIMARY KEY
, username VARCHAR(50) NOT NULL UNIQUE
, password VARCHAR(255) NOT NULL
, eMail VARCHAR(100) NOT NULL
, Persons_ID INT NOT NULL
);
INSERT INTO accounts (username, password, eMail, Persons_ID)
SELECT username, password, eMail, ID
FROM persons;
persons 表确实只包含最必要的东西,现在具有以下结构:
新表 persons_information 包含所有附加信息:
我使用以下命令重新创建了 users2:
CREATE OR REPLACE VIEW users2 AS
(SELECT ac.username
, ac.password
, p.firstName
, p.lastName
, ac.eMail AS email
, adr.settlement AS city
, s.name AS country
, pl.languages
, pi.description
, ac.Persons_ID AS ID
, pi.phone1
, pi.phone2
, CONCAT_WS(' ', adr.street, adr.addition) AS address
, p.status
, pi.publicMail
, adv.name AS Betreuer
FROM accounts ac
INNER JOIN persons p ON ac.Persons_ID = p.ID
INNER JOIN persons_information pi ON p.ID = pi.ID
INNER JOIN addresses adr ON adr.ID = pi.addressID
INNER JOIN addresses_have_persons ap ON adr.ID = ap.Addresses_ID
AND ap.Persons_ID = p.ID
INNER JOIN countries c ON adr.Countries_ID = c.ID
INNER JOIN states s ON adr.States_ID = s.ID
INNER JOIN persons_language pl ON p.ID = pl.ID
LEFT JOIN advisors adv ON pi.advisorID = adv.ID
-- LEFT JOIN titles t ON t.ID = adv.titleID
);
SELECT _ FROM users2 很快,但是如果我添加一个 ORDER BY lastName, firstName,大约需要 25 秒才能得到响应。
以下是 *EXPLAIN SELECT * FROM users2* 命令的结果:
这里是另一个命令:
我还(重新)创建了以下索引:
ALTER TABLE addresses ADD INDEX (Countries_ID);
ALTER TABLE addresses ADD INDEX (States_ID);
ALTER TABLE addresses_have_persons ADD INDEX (Persons_ID);
ALTER TABLE languages ADD INDEX (name);
ALTER TABLE persons_information ADD INDEX (addressID);
ALTER TABLE persons_information ADD INDEX (address2ID);
ALTER TABLE persons_information ADD INDEX (address3ID);
ALTER TABLE persons_information ADD INDEX (advisorID);
我认为问题的一个原因是 person_language 视图创建如下:
CREATE OR REPLACE VIEW persons_language AS
(SELECT lp.Persons_ID AS ID
, GROUP_CONCAT(DISTINCT l.name ORDER BY l.name SEPARATOR ', ') AS languages
FROM languages l
INNER JOIN languages_have_persons lp ON l.ID = lp.Languages_ID
GROUP BY lp.Persons_ID);
编辑 3: 对于那些感兴趣的人,我为 persons_language 视图添加了 EXPLAIN:
编辑 4: 今天的数据库会议结束后,我们决定删除所有与地址信息相关的对象并重新创建视图
CREATE OR REPLACE VIEW `users2` AS
(SELECT ac.username
, ac.password
, p.firstName
, p.lastName
, ac.eMail AS email
, pl.languages
, pi.description
, ac.Persons_ID AS ID
, pi.phone1
, pi.phone2
, p.status
, pi.publicMail
, adv.name AS Betreuer
FROM accounts ac
INNER JOIN persons p ON ac.Persons_ID = p.ID
INNER JOIN persons_information pi ON p.ID = pi.ID
INNER JOIN persons_language pl ON p.ID = pl.ID
INNER JOIN advisors adv ON pi.advisorID = adv.ID
WHERE ac.password IS NOT NULL
);
我还用
创建了一个索引CREATE INDEX LanguagesPersonsIndex ON `languages_have_persons` (`Languages_ID`, `Persons_ID`);
EXPLAIN 命令显示新索引正在使用中,并且在带有 ORDER BY 子句的 SELECT 和新索引之后的延迟,较小的视图约为 18 秒。这是新的结果: 我的问题是:我还能做些什么来提高性能?
【问题讨论】:
【参考方案1】:关键故障一定是问题。 但是根据连接表上的数据量,它无论如何都会变慢。 尝试:
在所有用于建立关系的属性上实现 KeyIndexes。 (ap.Addresses_ID、a.Countries_ID、p.addressID、ap.Persons_ID、a.States_ID、p.advisorID)。 在所有“ID”列上声明 PK。 不要在视图构造中使用 ORDER 或 GROUP。 为最常用于搜索、排序或分组的属性声明键索引。提示:'INNER' (INNER JOIN) 不是必需的。和'JOIN'一样
您的 VIEW "persons_language" 最好这样:
SELECT lp.Persons_ID AS ID, GROUP_CONCAT(DISTINCT l.name ORDER BY l.name SEPARATOR ', ') AS languages
FROM languages_have_persons lp
JOIN languages l ON l.ID = lp.Languages_ID
GROUP BY lp.Persons_ID;
比较合适,因为'FROM'和'JOIN'子句在'WHERE'子句之前处理。
您可以提升您的 mysql 内存和缓存配置。 查看我的 mysql 服务器的配置(运行带有权重表和视图的 ERP):
join_buffer_size= 256M
key_buffer = 312M
key_buffer_size = 768M
max_allowed_packet = 160M
thread_stack = 192K
thread_cache_size = 8
query_cache_limit = 64M
innodb_buffer_pool_size = 1512M
table_cache = 1024M
read_buffer_size = 4M
query_cache_size = 768M
query_cache_limit = 128M
【讨论】:
感谢您的回复,@Renato Tarso!不幸的是,我有一个 ORDER BY 的必要视图。请参阅上面的问题编辑。所有顺序/分组/关系属性都是键。 “...是主键”(当然,意见顾问和persons_language 没有主键) @Sae1962,检查我的答案更改。我希望它有用。祝你好运。 如果您有至少 8GB 的 RAM,innodb_buffer_pool_size
应该是大约 70% 的 可用 RAM。如果 RAM 较小,则使用较小的 %。
是的...当然,kkk。这里有 16GB 可用,但服务器运行 apache 和其他服务。【参考方案2】:
SELECT *
FROM `users`
ORDER BY `lastName`, `firstName`
需要
INDEX(last_name, first_name) -- in that order
提防VIEWs
;有些VIEWs
优化得很好,有些则没有。
请为addresses
和addresses_have_persons
提供SHOW CREATE TABLE
。
在persons_language
,为什么需要DISTINCT
?它没有PRIMARY KEY(person, language)
(或相反的顺序)吗?让我们看看SHOW CREATE TABLE
。
对于您想讨论的任何问题,请提供EXPLAIN
。
【讨论】:
以上是关于MySQL中有序视图的性能问题的主要内容,如果未能解决你的问题,请参考以下文章