MySql 缓慢的请求时间
MySql Slow Request Time
我有一台非常好的电脑,我想知道为什么向我的 table.
提出一个非常简单的请求需要这么长时间
我的电脑:
- i9 10900kf(10 核)
- 64 GB 内存
- 2TB NVME SSD
- RTX 3090 文图斯
1:我的 table 有 531 732 行(这不是很多行)和 39 列
我的table有以下索引:
- 显示名称
- TmiSentTs
- 用户名
- 频道
- DisplayName_TmiSentTs
- Username_TmiSentTs
当我进行以下查询时,需要 66.016 秒 才能得到响应:
SELECT from_unixtime(TmiSentTs/1000,'%Y/%m/%d %H:%i:%s'),
DisplayName, message
FROM MY_TABLE
WHERE displayname LIKE '%ab%'
ORDER BY TmiSentTs DESC;
我认为这不正常。
我试过:
- 将我的 innodb_write_io_threads 从 4 更改为 32
- 将我的 innodb_read_io_threads 从 4 更改为 32
但是 none 这行得通,我有另一个 table(在另一个数据库中)有 37 325 332 行,类似的查询需要 2 秒。
编辑:
经过一番研究,我发现这
SELECT * FROM pepegaclapwr.twitchmessages where instr('aa',username)<>0;
比
快
SELECT * FROM pepegaclapwr.twitchmessages where username like '%aa%';
同样的结果
displayname
列上的索引无助于此查询。任何在您要搜索的模式开头带有通配符的 LIKE
条件都不能使用索引,因此它必然会执行 table-scan.
想想电话簿。如果我要你查找以“T”开头的姓氏,那很容易,因为这本书是按姓氏排序的。但是,如果我要求您查找“T”是第 4 个字母(或第一个字母之后的任何字母)的名称,那么这本书已排序这一事实无济于事。你还是要一页一页地看全书才能找到我要的名字。
要优化您上面显示的那种查询,您可能会发现使用全文索引更容易,但这仅适用于搜索整个单词的情况。看起来您正在搜索其中某处包含“ab”的任何字符串。这不是一个完整的单词,因此全文索引也无济于事。
在这种情况下,您唯一的解决方案是向 table 添加另一列以指示该行是否包含“ab”,然后索引该列。在MySQL 5.7 及更高版本中,您可以根据表达式创建虚拟列。
ALTER TABLE MyTable
ADD COLUMN displayname_ab TINYINT NOT NULL AS (displayname LIKE '%ab%'),
ADD INDEX (displayname_ab);
然后搜索:
SELECT ... FROM MyTable WHERE displayname_ab = true;
在MySQL8.0中你甚至不需要做一个虚拟列,你可以直接从现有列上的表达式做一个表达式索引:
ALTER TABLE MyTable
ADD INDEX ((displayname LIKE '%ab%'));
然后,如果您使用完全相同的表达式进行搜索,它将使用索引:
SELECT ... FROM MyTable WHERE displayname LIKE '%ab%';
但这会将您需要的字符串修复到虚拟列定义中。如果您明天需要搜索“cd”或任何其他模式,这将无济于事。
我有一台非常好的电脑,我想知道为什么向我的 table.
提出一个非常简单的请求需要这么长时间我的电脑:
- i9 10900kf(10 核)
- 64 GB 内存
- 2TB NVME SSD
- RTX 3090 文图斯
1:我的 table 有 531 732 行(这不是很多行)和 39 列
我的table有以下索引:
- 显示名称
- TmiSentTs
- 用户名
- 频道
- DisplayName_TmiSentTs
- Username_TmiSentTs
当我进行以下查询时,需要 66.016 秒 才能得到响应:
SELECT from_unixtime(TmiSentTs/1000,'%Y/%m/%d %H:%i:%s'),
DisplayName, message
FROM MY_TABLE
WHERE displayname LIKE '%ab%'
ORDER BY TmiSentTs DESC;
我认为这不正常。
我试过:
- 将我的 innodb_write_io_threads 从 4 更改为 32
- 将我的 innodb_read_io_threads 从 4 更改为 32
但是 none 这行得通,我有另一个 table(在另一个数据库中)有 37 325 332 行,类似的查询需要 2 秒。
编辑: 经过一番研究,我发现这
SELECT * FROM pepegaclapwr.twitchmessages where instr('aa',username)<>0;
比
快SELECT * FROM pepegaclapwr.twitchmessages where username like '%aa%';
同样的结果
displayname
列上的索引无助于此查询。任何在您要搜索的模式开头带有通配符的 LIKE
条件都不能使用索引,因此它必然会执行 table-scan.
想想电话簿。如果我要你查找以“T”开头的姓氏,那很容易,因为这本书是按姓氏排序的。但是,如果我要求您查找“T”是第 4 个字母(或第一个字母之后的任何字母)的名称,那么这本书已排序这一事实无济于事。你还是要一页一页地看全书才能找到我要的名字。
要优化您上面显示的那种查询,您可能会发现使用全文索引更容易,但这仅适用于搜索整个单词的情况。看起来您正在搜索其中某处包含“ab”的任何字符串。这不是一个完整的单词,因此全文索引也无济于事。
在这种情况下,您唯一的解决方案是向 table 添加另一列以指示该行是否包含“ab”,然后索引该列。在MySQL 5.7 及更高版本中,您可以根据表达式创建虚拟列。
ALTER TABLE MyTable
ADD COLUMN displayname_ab TINYINT NOT NULL AS (displayname LIKE '%ab%'),
ADD INDEX (displayname_ab);
然后搜索:
SELECT ... FROM MyTable WHERE displayname_ab = true;
在MySQL8.0中你甚至不需要做一个虚拟列,你可以直接从现有列上的表达式做一个表达式索引:
ALTER TABLE MyTable
ADD INDEX ((displayname LIKE '%ab%'));
然后,如果您使用完全相同的表达式进行搜索,它将使用索引:
SELECT ... FROM MyTable WHERE displayname LIKE '%ab%';
但这会将您需要的字符串修复到虚拟列定义中。如果您明天需要搜索“cd”或任何其他模式,这将无济于事。