提高 mariaDB 查询的 sql 代码性能
Posted
技术标签:
【中文标题】提高 mariaDB 查询的 sql 代码性能【英文标题】:Improve sql code performance for a mariaDB query 【发布时间】:2019-12-05 22:55:11 【问题描述】:我在 DelphiXE8 中开发了一个连接到 mariaDB(Ver 15.1 Distrib 10.1.31-MariaDB,for Win32)的应用程序。 我想提高查询性能。 描述简化的场景:
de_User 表(innoDB)(行 81762)
ID_U INT PRIMARY KEY
Name VARCHAR(30)
INDEX ID_U, Name
de_doc 表(innoDB)(第 260452 行)
IDD INT PRIMARY KEY
DataFi Date
UserID INT
...
INDEX IDD, UserID, DataFi
----
CONSTRAINT UserID_LK
FOREIGN KEY de_Doc (UserID)
REFERENCES de_User (ID_U)
ON DELETE CASCADE
ON UPDATE CASCADE
我的查询
select User.*, Doc.LastDoc
FROM de_Users AS Us
LEFT JOIN (
SELECT UserID,MAX(DataFi) AS LastDoc
FROM de_doc
GROUP BY UserID
) as Doc on Doc.UserID = Us.ID_U
ORDER BY Us.Name ASC, Doc.LastDoc DESC;
-- 解释选择...
+------+-------------+----------------+-------+---------------+---------------+---------+----------------+--------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+----------------+-------+---------------+---------------+---------+----------------+--------+---------------------------------+
| 1 | PRIMARY | de_User | ALL | NULL | NULL | NULL | NULL | 81762 | Using temporary; Using filesort |
| 1 | PRIMARY | <derived2> | ref | key0 | key0 | 5 | Base.Us.ID_U | 10 | |
| 2 | DERIVED | de_Doc | index | NULL | UserID_LK| 4 | NULL | 260452 | |
+------+-------------+----------------+-------+---------------+---------------+---------+----------------+--------+---------------------------------+
我的.ini ...
# The mysql server
[mysqld]
...
key_buffer = 4096M
key_buffer_size=1024M
table_open_cache = 2048
query_cache_size = 128M
max_connections = 100
...
max_allowed_packet = 256M
sort_buffer_size = 4096M
net_buffer_length = 16M
read_buffer_size = 256M
myisam_sort_buffer_size = 256M
log_error = "mysql_error.log"
...
# Comment the following if you are using InnoDB tables
innodb_data_home_dir = "C:/xampp/mysql/data"
innodb_data_file_path = ibdata1:10M:autoextend
innodb_log_group_home_dir = "C:/xampp/mysql/data"
innodb_log_arch_dir = "C:/xampp/mysql/data"
## You can set .._buffer_pool_size up to 50 - 80 %
## of RAM but beware of setting memory usage too high
innodb_buffer_pool_size = 2048M
# DEPRECATED innodb_additional_mem_pool_size = 1024M
## Set .._log_file_size to 25 % of buffer pool size
innodb_log_file_size = 512M
innodb_log_buffer_size = 128M
innodb_flush_log_at_trx_commit = 1
innodb_lock_wait_timeout = 50
...
thread_concurrency = 4
...
[isamchk]
key_buffer = 1024M
sort_buffer_size = 256M
read_buffer = 8M
write_buffer = 16M
[myisamchk]
key_buffer = 1024M
sort_buffer_size = 256M
read_buffer = 8M
write_buffer = 8M
测试 phpmyadmin:
83705 total, the query employed 1,0000 sec.
if I remove "order by Doc.LastDoc DESC" it is very fast
83705 total, the query employed 0,0000 sec.
在我用 delphiEX8 开发的应用程序中测试
view table all rows 2,8 sec.
if I remove "order by Doc.LastDoc DESC" it is very fast
view table all rows 1,8 sec.
如何提高性能?
【问题讨论】:
如果不添加和共享explain
输出,您是否在表de_documents
中作为INDEX UserID
有索引
我不明白。我的代码要复杂得多,我写的是综合的。我描述了我在两个表和关系上创建了索引。我写了 EXPLAIN 结果和 my.ini 配置我写了要优化的查询。
是的,我能读懂你的问题。那你想说什么?
对不起,我想提高查询性能。我相信它可以更快。
这就是为什么我问userID
列是否有索引?
【参考方案1】:
尝试此查询并检查输出是否与您的查询相同
select Us.*, max(Doc.DataFi) as LastDoc
FROM de_Users AS Us
LEFT JOIN de_doc as Doc on Doc.UserID = Us.ID_U
group by Us.ID_U
ORDER BY Us.Name ASC, LastDoc DESC;
【讨论】:
花费相同的时间。没有改善。 输出是否正确?如果是这样,则通过删除联接中不需要的子查询来优化上述查询。现在explain
命令显示了什么?
使用select Us.*, .... group by Us.ID_U
在SQL 标准1992 中编写通常是错误的SQL。但它在SQL 标准1999+ 中有效,其中存在MySQL 5.7.5+ 支持的功能依赖... 但不要在低于 5.7.5 的 MySQL 版本中使用“功能依赖”
@RaymondNijland 你是说应该是列名而不是*
??
“你是说应该是列名而不是 * 吗?” 不是我说这个MySQL Handling of GROUP BY .. topicstarters 查询通常是正确的方法..【参考方案2】:
这是模棱两可的:INDEX IDD, UserID, DataFi
可能User.*
应该是Us.*
?请注意,“简化”查询可能会将其变成另一个问题。
可能LEFT JOIN
是不必要的;使用JOIN
。
你需要这个复合材料 INDEX(UserID, LastDoc)
您真的希望输出中有 82K 行吗?客户将如何处理这么多数据?我问是因为如果客户会进一步消化结果,也许在 SQL 中会更好。
计时时,请务必使用 SELECT SQL_NO_CACHE 避开查询缓存。
phpmyadmin 可能会设置一个 LIMIT,从而改变优化器的功能!
ORDER BY t1.a, t2.b
(不同的表)使得无法使用索引进行排序。这将防止任何形式的查询短路。
【讨论】:
*可能不需要 LEFT JOIN;使用加入。 [查看所有用户很重要] *你需要这个复合 INDEX(UserID, LastDoc) [我做过] *你真的想要输出中的 82K 行吗?客户将如何处理这么多数据?我问是因为如果客户会进一步消化结果,也许在 SQL 中这样做会更好。 [我需要一个用 Delphi EX8 开发的程序中的网格,它显示所有用户,然后能够使用 Delphi 程序在网格上应用过滤器。 “我一直都是这样做的,也许我必须做一些不同的事情?”] 加载82K行到程序中,然后做过滤?糟糕的设计。 在获取行时应该进行过滤。我不知道 Delphi 是否强制这样做,但您应该寻找更好的架构。磁盘驱动器和网络连接的速度非常快。 谢谢你,Rick James,现在该软件处于 betatest 状态,运行良好,没有错误,唯一不好的是用户搜索的性能(81k 用户搜索 260K 文档 = 1.9 秒)。我将在下一个软件中使用您的建议。 @carmelocony 如果您有时间,请在正常运行 24 小时后发布 A) SHOW GLOBAL STATUS LIKE '%used%'; 的文本结果;和 B) 显示 '%open%' 之类的全局状态;进行分析。您的主机 CPU 上有多少个核心?发布 EXPLAIN SELECT SQL_NO_CACHE 的当前结果(您的查询);包括 QUERY for Suggestions to IMPROVE your host server's response time。【参考方案3】:在 my.ini 中更改这些值,在 phpmyadmin 中这是改进的结果。
在我的 Delphi 应用程序中填充网格所需的时间,现在是 1.9 秒,之前是 2.8 秒。
我的电脑有 8Gb RAM;
我可以减少在 Delphi 中填充网格的时间吗?也许我必须为此提出新的要求。
innodb_buffer_pool_size = 2048M
# Set .._log_file_size to 25 % of buffer pool size
之前
innodb_log_file_size = 64M
(总共 83705 次删除,查询使用了 1,0000 秒。)
之后
innodb_log_file_size = 512M
(总计 83705 次删除,查询使用了 0,0000 秒。)
【讨论】:
【参考方案4】:对您的 my.ini [mysqld] 部分的建议
sort_buffer_size=2M # from 4096M (4G) of RAM per connection, next 2 are per connect also
read_buffer_size=256K # from 256M to reduce volume of data retrieved by 99%
read_rnd_buffer_size=256K # from ? to a reasonable size
这三个可以通过 SET GLOBAL variable_name=value 动态设置(作为根),请将 K 替换为 *1024,将 M 替换为 *1024*1024,以获取千字节和兆字节。请在正常运行时间的一个完整工作日后发布正面/负面结果。
【讨论】:
我进行了建议的更改。第一次测试的表现与之前的相同。我应用了 Wilson Hauck 建议的设置。谢谢 @carmelocony 请在正常运行 24 小时后发布 SHOW GLOBAL STATUS LIKE '%used%' 的文本结果;进行分析。您的主机 CPU 上有多少个核心?发布 EXPLAIN SELECT SQL_NO_CACHE 的结果(您的查询);包括QUERY。 @carmelocony 只有新的 SESSIONS 会继承修改后的全局变量设置。请尝试使用新登录 mysql 查询您的时间,请忽略第一次执行时间。 我得到了最好的结果:key_buffer= 2048M sort_buffer_size = 8M read_buffer_size = 2M read_rnd_buffer_size = 16M innodb_buffer_pool_size = 2048M innodb_log_file_size = 512M @carmelocony 感谢您分享您的设置以获得最佳效果。如果您有时间,请在正常运行时间 24 小时后发布 A) SHOW GLOBAL STATUS LIKE '%used%'; 的文本结果;和 B) 显示 '%open%' 之类的全局状态;进行分析。您的主机 CPU 上有多少个核心?发布 EXPLAIN SELECT SQL_NO_CACHE 的当前结果(您的查询);包括QUERY。【参考方案5】:如果你的目标是“grouwise-max”,那么你省略了一个子句:
select User.*, Doc.LastDoc
FROM de_Users AS Us
LEFT JOIN
(
SELECT UserID,MAX(DataFi) AS LastDoc
FROM de_doc
GROUP BY UserID
) as Doc ON Doc.UserID = Us.ID_U
AND Doc.LastDoc = Us.DataFi -- this was missing
ORDER BY Us.Name ASC, Doc.LastDoc DESC;
这也将导致交付的行数减少,从而解决性能问题。
【讨论】:
以上是关于提高 mariaDB 查询的 sql 代码性能的主要内容,如果未能解决你的问题,请参考以下文章