【问题标题】:MySQL distinct performanceMySQL 独特的性能
【发布时间】:2020-05-04 07:23:16
【问题描述】:

当我在查询中添加“distinct”时,查询时间从 0.015 增加到 6 秒以上。

我想加入几个表,这些表通过外键链接并从中获取不同的列:

select distinct table3.idtable3 from 
    table1
    join table2 on table1.idtable1 = table2.fkey
    join table3 on table2.idtable2 = table3.fkey
    where table1.idtable1 = 1 

不同的查询需要 6 秒,在我看来这是可以改进的。

带选择:

持续时间:0.015s / fetch:5.532s(5.760.434 行)

解释:

id, select_type, table, partitions, type, possible_keys, key, key_len, ref, rows, filtered, Extra
1   SIMPLE  table1      index   asd asd 137     10  10.00   Using where; Using index
1   SIMPLE  table2      ALL idtable2                200 25.00   Using where; Using join buffer (Block Nested Loop)
1   SIMPLE  table3      ref fkey_table2_table_3_idx fkey_table2_table_3_idx 138 mydb.table2.idtable2    66641   100.00  

使用不同的选择:

持续时间:6.625s / fetch:0.000s(1000 行)

解释:

id, select_type, table, partitions, type, possible_keys, key, key_len, ref, rows, filtered, Extra
1   SIMPLE  table1      index   asd asd 137     10  10.00   Using where; Using index; Using temporary
1   SIMPLE  table2      ALL idtable2                200 25.00   Using where; Using join buffer (Block Nested Loop)
1   SIMPLE  table3      ref fkey_table2_table_3_idx fkey_table2_table_3_idx 138 mydb.table2.idtable2    66641   100.00  

数据库: Database snippet

测试代码/MCRE:

import mysql.connector
import time
import numpy as np




""" 
-- MySQL Script generated by MySQL Workbench
-- Fri Jan 17 12:19:26 2020
-- Model: New Model    Version: 1.0
-- MySQL Workbench Forward Engineering

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';

-- -----------------------------------------------------
-- Schema mydb
-- -----------------------------------------------------

-- -----------------------------------------------------
-- Schema mydb
-- -----------------------------------------------------
CREATE SCHEMA IF NOT EXISTS `mydb` DEFAULT CHARACTER SET utf8 ;
USE `mydb` ;

-- -----------------------------------------------------
-- Table `mydb`.`table1`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`table1` (
  `idtable1` VARCHAR(45) NOT NULL,
  INDEX `asd` (`idtable1` ASC) VISIBLE)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`table2`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`table2` (
  `idtable2` VARCHAR(45) NOT NULL,
  `fkey` VARCHAR(45) NULL,
  INDEX `link_table1_table2_idx` (`fkey` ASC) INVISIBLE,
  INDEX `idtable2` (`idtable2` ASC) VISIBLE,
  CONSTRAINT `link_table1_table2`
    FOREIGN KEY (`fkey`)
    REFERENCES `mydb`.`table1` (`idtable1`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


-- -----------------------------------------------------
-- Table `mydb`.`table3`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `mydb`.`table3` (
  `idtable3` VARCHAR(45) NOT NULL,
  `fkey` VARCHAR(45) NULL,
  INDEX `fkey_table2_table_3_idx` (`fkey` ASC) VISIBLE,
  CONSTRAINT `fkey_table2_table_3`
    FOREIGN KEY (`fkey`)
    REFERENCES `mydb`.`table2` (`idtable2`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;


SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;


"""


def insertData():
    for i in range(2):
        num_distinct_table1_values = 5
        num_distinct_table2_values = 10
        num_distinct_table3_values = 1000

        num_entries_table1 = int(num_distinct_table1_values)
        num_entries_table2 = int(num_distinct_table2_values * 10)
        num_entries_table3 = int(num_distinct_table3_values * 300)

        random_numbers_table1_id = range(num_distinct_table1_values)

        random_numbers_table2_id = np.random.randint(num_distinct_table2_values, size=int(num_entries_table2))
        random_numbers_table2_fkey = np.random.randint(num_distinct_table1_values, size=int(num_entries_table2))

        random_numbers_table3_id = np.random.randint(num_distinct_table3_values, size=int(num_entries_table3))
        random_numbers_table3_fkey = np.random.randint(num_distinct_table2_values, size=int(num_entries_table3))

        value_string_table1 = ','.join([f"('{i_name}')" for i_name in random_numbers_table1_id])
        value_string_table2=""
        for i in range(num_entries_table2):
            value_string_table2 = value_string_table2+','.join(
                ["('{id}','{fkey}'),".format(id=random_numbers_table2_id[i], fkey=random_numbers_table2_fkey[i])])

        value_string_table3=""
        for i in range(num_entries_table3):
            value_string_table3 = value_string_table3+','.join(
                ["('{id}','{fkey}'),".format(id=random_numbers_table3_id[i], fkey=random_numbers_table3_fkey[i])])

        # fill table 1
        mySql_insert_query = f"INSERT INTO table1 (idtable1) VALUES {value_string_table1}"
        cursor.execute(mySql_insert_query)
        conn.commit()
        print("Done table 1")
        # fill table 2
        mySql_insert_query = f"INSERT INTO table2 (idtable2, fkey) VALUES {value_string_table2}"
        mySql_insert_query=mySql_insert_query[0:-1]
        cursor.execute(mySql_insert_query)
        print("Done table 2")
        # fill table 3
        mySql_insert_query = f"INSERT INTO table3 (idtable3, fkey) VALUES {value_string_table3}"
        mySql_insert_query = mySql_insert_query[0:- 1]
        cursor.execute(mySql_insert_query)
        print("Done table 3")

        conn.commit()

conn = mysql.connector.connect(user='root', password='admin', host='127.0.0.1',
                               database='mydb', raise_on_warnings=True, autocommit=False)
cursor = conn.cursor()


insertData()


conn.close()

【问题讨论】:

  • 这就是提问的方式
  • 我在学习....

标签: mysql join optimization distinct innodb


【解决方案1】:

感谢CREATE TABLEs;没有他们,你可能永远不会得到答案。

  • 每个表都应该有一个PRIMARY KEY。如果您有一个“自然”有效的列(或列组合),请使用它。否则使用AUTO_INCREMENT
  • 计时查询时,(1) 确保“查询缓存”未被使用,(2) 运行两次查询以检查其他计时变化。
  • INDEX(fkey)INVISIBLE,因此未使用。不要在VISIBLE/INVISIBLE 上浪费学习时间,你的职业生涯可能永远不需要它们。
  • 进行试验时,请确保每个表中的行数超过几行,并且它们的值以实际的方式变化。否则,优化器可能会走捷径,只会混淆您的学习体验。
  • 还有……

    duration : 0.015s / fetch:5.532s (5.760.434 rows)
    duration : 6.625s / fetch:0.000s (1000 rows)
    

请注意两者都是大约 6 秒。只是时间划分不同而已。

  • 6M 行且没有DISTINCT,查询可以立即抽出数据,但由于网络延迟而需要很长时间。
  • 使用DISTINCT,在执行“重复数据删除”之前,第一行不能出来,这可能涉及“临时”(参见EXPLAIN)和排序。所以,现在所有的时间都在发送数据之前进行计算。
  • 令人困惑的是,您只查看了“持续时间”而不是两次的总和。也就是说,总时间是需要注意的重要因素。
  • DISTINCT 稍慢(总时间),因为收集和排序 570 万行的额外步骤。

【讨论】:

  • '在执行“去重”之前,第一行不能出来'--迂腐,第二行。
  • @philipxy - 这取决于算法。我认为它在对数据进行排序与​​在 RAM 中构建哈希之间进行选择。证据:有时DISTINCT 已排序,有时未排序。
  • 引用的语句是关于一些模糊类实现的输出,这些实现类做了一些事情然后“去重复”。某事的最后一步或“去复制”的第一步可能是输出任何行。所以这个说法是错误的。此外,作为对或错的陈述,它不“依赖”任何东西。我相信我们会同意某些其他陈述是真实的,并且你的评论是真实的,但它们不是你写的。 (我最初的观点只是指出一个极端情况。)
  • 1) 我省略了 primkey,因为将数据放入其中会更费力。只是不要问...但是它不会影响这里的性能。 2)这个缓存的东西经常杀死我的基准测试......但这次不是。如何避免使用缓存? 3) Invis 指数:好的,但在性能上没有任何改变。 4)数据是现实的。或多或少......我知道你的意思。休息)我可以添加 2 个数字并查看总和,但是感谢这个数学课;)我想知道为什么这个不同需要这么长时间,我想加快速度。我会试探。不会提高网络速度。
  • @Langer - 在选择时可以避免“查询缓存”:SELECT SQL_NO_CACHE ...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多