MYSQL // "DISTINCT" 的性能在减少不同条目的数量时下降了大约 100000 个条目
MYSQL // Performance of "DISTINCT" drops around 100000 entries when recucing the amount of distinct entries
我们在 SQL 查询中插入 DISTINCT 命令时遇到了一些性能问题。
该问题仅在以下情况下发生:100000 个条目(或更多),其中只有 ~1%(或更少)的不同值。
我们将问题归结为以下最小的 python 示例(但它与 python 无关,mysql workbench 的行为相同):
import mysql.connector
import time
import numpy as np
conn = mysql.connector.connect(user='user', password='password', host='server',
database='database', raise_on_warnings=True, autocommit=False)
cursor = conn.cursor()
#define amount of entries
max_exponent = 4.7
n_entry = 10**max_exponent
# fill table with 10, 100, ... distinct entries
for n_distinct in np.logspace(1, max_exponent, num=int(max_exponent)):
# Dropping BENCHMARK table if already exists and create new one
cursor.execute("DROP TABLE IF EXISTS BENCHMARK")
cursor.execute('CREATE TABLE BENCHMARK(ID INT)')
# create distinct number set and insert random permutation of it into table
distinct_numbers = range(int(n_distinct))
random_numbers = np.random.randint(len(distinct_numbers), size=int(n_entry))
value_string = ','.join([f"({i_name})" for i_name in random_numbers])
mySql_insert_query = f"INSERT INTO BENCHMARK (ID) VALUES {value_string}"
print(f'filling table with {n_entry:.0f} random values of {n_distinct:.0f} distinct numbers')
cursor.execute(mySql_insert_query)
conn.commit()
# benchmark distinct call
start = time.time()
sql_query = 'SELECT DISTINCT ID from BENCHMARK'
cursor.execute(sql_query)
result = cursor.fetchall()
print(f'Time to read {len(result)} distinct values: {time.time()-start:.2f}')
conn.close()
提取的基准时间显示出一种违反直觉的行为,其中时间突然增加,因为 table:
中不同的值较少
如果我们在不使用 DISTINCT 的情况下进行查询,时间将下降到 170 毫秒,与不同条目的数量无关。
我们无法理解这种依赖性(除了某些 "hardware limitation",但 100000 个条目应该......可以忽略不计?),因此我们要求您了解这种行为的根本原因可能是什么。
我们用于数据库的机器具有以下规格:
- CPU:Intel i5 @ 3.3GHz(CPU执行期间负载达到 30%)
- Ram: 8 GB (mysqld占用约2.4GB,查询执行期间不上升,InnoDB Buffer使用率保持在42%,buffer_size = 4GB)
- HDD:500GB,~90% 空
- OS, Mysql: Windows 10, Mysql 服务器 8.0.18
感谢阅读!
在 id
上有与没有索引可能会产生巨大的差异。
在某些时候,MySQL 换档 -- 有多种方法可以执行 GROUP BY
或 DISTINCT
查询:
- 在内存中有一个散列并计算每个散列有多少。
- 写入一个临时文件 table,对其进行排序,然后遍历它计算有多少不同的值
- 如果有可用的索引,则从一个值跳到下一个值。
优化器不一定能预测给定情况的最佳方式,因此有时它可能无法选择最佳情况。在旧的 5.5 版本(将近十年前)中可能无法深入了解优化器选择做什么。较新的版本有 EXPLAIN FORMAT=JSON
和 "Optimizer Trace"。
另一个问题是 I/O。从磁盘读取数据会使查询速度减慢十倍。但是,这似乎不是问题,因为 table 相当小。而且您似乎 运行 在构建 table 之后立即查询;也就是说,table 可能完全缓存在 RAM 中(buffer_pool)。
我希望这能为评论说基准测试很困难添加一些细节。
我们在 SQL 查询中插入 DISTINCT 命令时遇到了一些性能问题。 该问题仅在以下情况下发生:100000 个条目(或更多),其中只有 ~1%(或更少)的不同值。
我们将问题归结为以下最小的 python 示例(但它与 python 无关,mysql workbench 的行为相同):
import mysql.connector
import time
import numpy as np
conn = mysql.connector.connect(user='user', password='password', host='server',
database='database', raise_on_warnings=True, autocommit=False)
cursor = conn.cursor()
#define amount of entries
max_exponent = 4.7
n_entry = 10**max_exponent
# fill table with 10, 100, ... distinct entries
for n_distinct in np.logspace(1, max_exponent, num=int(max_exponent)):
# Dropping BENCHMARK table if already exists and create new one
cursor.execute("DROP TABLE IF EXISTS BENCHMARK")
cursor.execute('CREATE TABLE BENCHMARK(ID INT)')
# create distinct number set and insert random permutation of it into table
distinct_numbers = range(int(n_distinct))
random_numbers = np.random.randint(len(distinct_numbers), size=int(n_entry))
value_string = ','.join([f"({i_name})" for i_name in random_numbers])
mySql_insert_query = f"INSERT INTO BENCHMARK (ID) VALUES {value_string}"
print(f'filling table with {n_entry:.0f} random values of {n_distinct:.0f} distinct numbers')
cursor.execute(mySql_insert_query)
conn.commit()
# benchmark distinct call
start = time.time()
sql_query = 'SELECT DISTINCT ID from BENCHMARK'
cursor.execute(sql_query)
result = cursor.fetchall()
print(f'Time to read {len(result)} distinct values: {time.time()-start:.2f}')
conn.close()
提取的基准时间显示出一种违反直觉的行为,其中时间突然增加,因为 table:
中不同的值较少如果我们在不使用 DISTINCT 的情况下进行查询,时间将下降到 170 毫秒,与不同条目的数量无关。 我们无法理解这种依赖性(除了某些 "hardware limitation",但 100000 个条目应该......可以忽略不计?),因此我们要求您了解这种行为的根本原因可能是什么。
我们用于数据库的机器具有以下规格:
- CPU:Intel i5 @ 3.3GHz(CPU执行期间负载达到 30%)
- Ram: 8 GB (mysqld占用约2.4GB,查询执行期间不上升,InnoDB Buffer使用率保持在42%,buffer_size = 4GB)
- HDD:500GB,~90% 空
- OS, Mysql: Windows 10, Mysql 服务器 8.0.18
感谢阅读!
在 id
上有与没有索引可能会产生巨大的差异。
在某些时候,MySQL 换档 -- 有多种方法可以执行 GROUP BY
或 DISTINCT
查询:
- 在内存中有一个散列并计算每个散列有多少。
- 写入一个临时文件 table,对其进行排序,然后遍历它计算有多少不同的值
- 如果有可用的索引,则从一个值跳到下一个值。
优化器不一定能预测给定情况的最佳方式,因此有时它可能无法选择最佳情况。在旧的 5.5 版本(将近十年前)中可能无法深入了解优化器选择做什么。较新的版本有 EXPLAIN FORMAT=JSON
和 "Optimizer Trace"。
另一个问题是 I/O。从磁盘读取数据会使查询速度减慢十倍。但是,这似乎不是问题,因为 table 相当小。而且您似乎 运行 在构建 table 之后立即查询;也就是说,table 可能完全缓存在 RAM 中(buffer_pool)。
我希望这能为评论说基准测试很困难添加一些细节。