提高 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 代码性能的主要内容,如果未能解决你的问题,请参考以下文章

无论如何,为了提高 SQL 查询的性能,以按标签匹配计数查找具有顺序的行

提高 self-JOIN SQL Query 性能

MariaDB性能优化,我终于搞清楚了!

优化MariaDB数据库性能的思路与工具

mariadb-galera集群架构

实战mariadb-galera集群架构