Hiệu suất khác biệt của MySQL


8

Khi tôi thêm 'khác biệt' vào truy vấn của mình, thời gian truy vấn tăng từ 0,015 đến hơn 6 giây.

Tôi muốn tham gia một số bảng được liên kết thông qua các khóa ngoại và nhận một cột riêng biệt từ đó:

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

Truy vấn riêng biệt mất 6 giây mà dường như tôi có thể ứng biến được.

Với chọn:

thời lượng: 0,015s / tìm nạp: 5,532s (5.760.434 hàng)

Giải thích:

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  

Với lựa chọn riêng biệt:

thời lượng: 6.625 giây / tìm nạp: 0.000 giây (1000 hàng)

Giải thích:

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  

Cơ sở dữ liệu: Đoạn mã cơ sở dữ liệu

Mã kiểm tra / 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()

4
Và đó là cách đặt câu hỏi
Dâu

Tôi đang học ....
Langer

Câu trả lời:


2

Cảm ơn vì CREATE TABLEs; bạn có thể không bao giờ nhận được câu trả lời mà không có họ.

  • Mỗi bàn nên có a PRIMARY KEY. Nếu bạn có một cột (hoặc kết hợp các cột) hoạt động 'tự nhiên', hãy sử dụng cột đó. Khác sử dụng một AUTO_INCREMENT.
  • Khi truy vấn thời gian, (1) đảm bảo "Bộ đệm truy vấn" không được sử dụng và (2) chạy truy vấn hai lần để kiểm tra các biến thể khác trong thời gian.
  • INDEX(fkey)INVISIBLE, do đó không được sử dụng. Đừng lãng phí thời gian học tập trên VISIBLE/ INVISIBLE, bạn có thể không bao giờ cần chúng trong sự nghiệp của mình.
  • Khi thử nghiệm, hãy chắc chắn có nhiều hơn một vài hàng trong mỗi bảng và có các giá trị của chúng thay đổi theo cách thực tế. Khác, Trình tối ưu hóa có thể sử dụng các phím tắt sẽ chỉ gây nhầm lẫn kinh nghiệm học tập của bạn.
  • Và ...

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

Chú ý cách cả hai khoảng 6 giây. Chỉ là thời gian được phân chia khác nhau.

  • Với 6M hàng và không DISTINCT, truy vấn có thể bơm dữ liệu ngay lập tức, nhưng mất nhiều thời gian do độ trễ mạng.
  • Với DISTINCT, hàng đầu tiên không thể xuất hiện cho đến sau khi tạo thành "khử trùng lặp", có thể liên quan đến "tạm thời" (xem phần EXPLAIN) và sắp xếp. Vì vậy, bây giờ tất cả thời gian đều liên quan đến điện toán trước khi gửi dữ liệu.
  • Điều khó hiểu là bạn chỉ nhìn vào "thời lượng" chứ không phải tổng của hai lần. Đó là, tổng thời gian là điều quan trọng cần lưu ý.
  • Một DISTINCTcái chậm hơn một chút (tổng thời gian) vì có thêm bước thu thập và sắp xếp các hàng 5,7M.

'hàng đầu tiên không thể xuất hiện cho đến sau khi tạo thành "khử trùng lặp"' - Để trở thành mô phạm, hàng thứ hai.
philipxy

@philipxy - Điều đó phụ thuộc vào thuật toán. Tôi nghĩ rằng nó chọn giữa việc sắp xếp dữ liệu so với xây dựng hàm băm trong RAM. Bằng chứng: Đôi khi DISTINCTđược sắp xếp, đôi khi không.
Rick James

Tuyên bố được trích dẫn là về đầu ra của một số lớp triển khai mơ hồ làm một cái gì đó sau đó "khử trùng lặp". Bước cuối cùng của một cái gì đó hoặc bước đầu tiên của "khử" có thể là xuất ra bất kỳ hàng nào. Vì vậy, tuyên bố là sai. Hơn nữa, như một tuyên bố đúng hay sai, nó không "phụ thuộc" vào bất cứ điều gì. Tôi chắc chắn rằng chúng tôi sẽ đồng ý về một số tuyên bố khác là đúng và nhận xét của bạn là đúng với chúng, nhưng chúng không phải là những gì bạn đã viết. (Quan điểm ban đầu của tôi chỉ là chỉ ra một trường hợp cạnh.)
philipxy

1) Tôi đã bỏ qua primkey vì sẽ tốn nhiều công sức hơn để đưa dữ liệu vào. Chỉ cần đừng hỏi ... Nhưng nó không ảnh hưởng đến hiệu suất ở đây. 2) Điều bộ nhớ cache này đã giết điểm chuẩn của tôi rất thường xuyên ... nhưng không phải lúc này. Làm thế nào bạn có thể tránh sử dụng bộ đệm? 3) Chỉ số Invis: Được rồi, nhưng không thay đổi bất cứ điều gì trong hiệu suất. 4) Dữ liệu là thực tế. Nhiều hơn hoặc ít hơn .... Tôi biết ý của bạn. Nghỉ ngơi) Tôi có thể thêm 2 chữ số và xem tổng, nhưng cảm ơn vì bài học toán này;) Tôi đã tự hỏi tại sao sự khác biệt này lại mất nhiều thời gian và tôi muốn tăng tốc độ này. Tôi sẽ thăm dò. không tăng tốc độ mạng.
Langer

1
@Langer - Có thể tránh "Truy vấn bộ đệm" trên các lựa chọn : SELECT SQL_NO_CACHE ....
Rick James
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.