代码之家  ›  专栏  ›  技术社区  ›  shreddish

“相同”服务器上的SQL查询速度较慢

  •  1
  • shreddish  · 技术社区  · 6 年前

    我有多个SQL查询,在一个开发服务器上运行的速度要比另一个慢得多(长2-4倍)。我使用的是DigitalOcean,两台服务器都是由同一个快照制作的,在硬件和数据方面都是相同的。 开发版本1 是我优化代码的主要开发服务器,所以数据是 轻微地 不一样。我觉得我可能已经更改或修改了一个没有被复制的mysql设置 Dev2版本 但我找不到任何区别。在我能想到的所有配置文件上运行diff并没有产生任何线索。两台服务器都在运行

    • 乌布努图14.04
    • 64 GB内存
    • MySql 5.6版
    • 菲律宾比索5.5

    我的后端使用PHP,当准备一个包含大量绑定值的语句时,PDO中有一个bug。为了解决这个问题,我创建了临时表,并将它们用作where语句的子查询。所有这些都在MySql CLI中运行,以排除任何PHP问题。

    CREATE TEMPORARY TABLE IF NOT EXISTS temp_a LIKE a;
    
    INSERT INTO temp_a SELECT * FROM a WHERE id in (1,2,3,4,8,30,31,128,129,130,197,198,199,200,201,204,206,207,208,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,256,271,273,284,641,4691,5313,6845,5,7,262,6,528,817,818,819,820,821,9,628,5178;
    
    SELECT t1.pId, t1.*, t2.username, t2.email, t2.customer_id 
    FROM b_actions t1 
    JOIN users t2 
    ON t1.user_id = t2.id 
    WHERE t1.action_name NOT IN ('action1', 'action2', 'action3') AND t1.user_dismissed = 0 
    AND t1.pId IN (SELECT id FROM temp_a) 
    ORDER BY datetime DESC`);
    

    表b_操作架构

    CREATE TABLE `b_actions` (
    `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
    `datetime` datetime NOT NULL,
    `user_id` int(11) DEFAULT NULL,
    `pId` int(11) NOT NULL,
    `action_name` varchar(255) CHARACTER SET latin1 DEFAULT NULL,
    `data` mediumtext COLLATE utf8_unicode_ci,
    `schedule_type` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
    `schedule_datetime` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
    `action_state` int(11) NOT NULL,
    `user_dismissed` smallint(1) NOT NULL,
    `message` varchar(500) COLLATE utf8_unicode_ci DEFAULT NULL,
    `created_on` datetime DEFAULT CURRENT_TIMESTAMP,
    `updated_on` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
    PRIMARY KEY (`id`),
    KEY `pId` (`pId`),
    KEY `datetime` (`datetime`),
    KEY `user_id` (`user_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
    

    开发版本1: 快速查询(5377行,0.79秒) -说明:

    *************************** 1. row ***************************
            id: 1
    select_type: SIMPLE
            table: temp_a
            type: index
    possible_keys: PRIMARY,id
            key: id
        key_len: 4
            ref: NULL
            rows: 5889
            Extra: Using index; Using temporary; Using filesort
    *************************** 2. row ***************************
            id: 1
    select_type: SIMPLE
            table: t1
            type: ref
    possible_keys: pId,user_id
            key: pId
        key_len: 4
            ref: db.temp_a.id
            rows: 15
            Extra: Using index condition; Using where
    *************************** 3. row ***************************
            id: 1
    select_type: SIMPLE
            table: t2
            type: eq_ref
    possible_keys: id
            key: id
        key_len: 4
            ref: db.t1.user_id
            rows: 1
            Extra: Using where
    

    Dev2版本: 较慢的查询(5386行(1.31秒)) -解释

    *************************** 1. row ***************************
            id: 1
    select_type: SIMPLE
            table: temp_a
            type: index
    possible_keys: PRIMARY,id
            key: id
        key_len: 4
            ref: NULL
            rows: 8866
            Extra: Using index; Using temporary; Using filesort
    *************************** 2. row ***************************
            id: 1
    select_type: SIMPLE
            table: t1
            type: ref
    possible_keys: pId,user_id
            key: pId
        key_len: 4
            ref: db.temp_a.id
            rows: 23
            Extra: Using index condition; Using where
    *************************** 3. row ***************************
            id: 1
    select_type: SIMPLE
            table: t2
            type: eq_ref
    possible_keys: id
            key: id
        key_len: 4
            ref: db.t1.user_id
            rows: 1
            Extra: Using where
    

    mysql/my.cnf都是相同的

    #
    # The MySQL database server configuration file.
    #
    # You can copy this to one of:
    # - "/etc/mysql/my.cnf" to set global options,
    # - "~/.my.cnf" to set user-specific options.
    # 
    # One can use all long options that the program supports.
    # Run program with --help to get a list of available options and with
    # --print-defaults to see which it would actually understand and use.
    #
    # For explanations see
    # http://dev.mysql.com/doc/mysql/en/server-system-variables.html
    
    # This will be passed to all mysql clients
    # It has been reported that passwords should be enclosed with ticks/quotes
    # escpecially if they contain "#" chars...
    # Remember to edit /etc/mysql/debian.cnf when changing the socket location.
    [client]
    port        = 3306
    socket      = /var/run/mysqld/mysqld.sock
    
    # Here is entries for some specific programs
    # The following values assume you have at least 32M ram
    
    # This was formally known as [safe_mysqld]. Both versions are currently parsed.
    [mysqld_safe]
    socket      = /var/run/mysqld/mysqld.sock
    nice        = 0
    
    [mysqld]
    #
    # * Basic Settings
    #
    user        = mysql
    pid-file    = /var/run/mysqld/mysqld.pid
    socket      = /var/run/mysqld/mysqld.sock
    port        = 3306
    basedir     = /usr
    datadir     = /var/lib/mysql
    tmpdir      = /tmp
    lc-messages-dir = /usr/share/mysql
    skip-external-locking
    #
    # Instead of skip-networking the default is now to listen only on
    # localhost which is more compatible and is not less secure.
    bind-address        = 127.0.0.1
    #
    # * Fine Tuning
    #
    key_buffer      = 4G
    max_allowed_packet  = 16M
    thread_stack        = 192K
    thread_cache_size       = 8
    # This replaces the startup script and checks MyISAM tables if needed
    # the first time they are touched
    myisam-recover         = BACKUP
    max_connections        = 2000
    # table_cache            = 1024
    #thread_concurrency     = 10
    #
    # * Query Cache Configuration
    #
    query_cache_limit   = 10M
    query_cache_size        = 256M
    #
    # * Logging and Replication
    #
    # Both location gets rotated by the cronjob.
    # Be aware that this log type is a performance killer.
    # As of 5.1 you can enable the log at runtime!
    #general_log_file        = /var/log/mysql/mysql.log
    #general_log             = 1
    #
    # Error log - should be very few entries.
    #
    log_error = /var/log/mysql/error.log
    #
    # Here you can see queries with especially long duration
    #log_slow_queries   = /var/log/mysql/mysql-slow.log
    #long_query_time = 2
    #log-queries-not-using-indexes
    #
    # The following can be used as easy to replay backup logs or for replication.
    # note: if you are setting up a replication slave, see README.Debian about
    #       other settings you may need to change.
    #server-id      = 1
    #log_bin            = /var/log/mysql/mysql-bin.log
    expire_logs_days    = 10
    max_binlog_size         = 100M
    #binlog_do_db       = include_database_name
    #binlog_ignore_db   = include_database_name
    #
    # * InnoDB
    #
    # InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/.
    # Read the manual for more InnoDB related options. There are many!
    #
    # * Security Features
    #
    # Read the manual, too, if you want chroot!
    # chroot = /var/lib/mysql/
    #
    # For generating SSL certificates I recommend the OpenSSL GUI "tinyca".
    #
    # ssl-ca=/etc/mysql/cacert.pem
    # ssl-cert=/etc/mysql/server-cert.pem
    # ssl-key=/etc/mysql/server-key.pem
    
    
    
    slow-query-log = 1
    slow-query-log-file = /var/log/mysql/localhost-slow.log
    long_query_time = 1
    #log-queries-not-using-indexes
    
    
    [mysqldump]
    quick
    quote-names
    max_allowed_packet  = 16M
    
    [mysql]
    #no-auto-rehash # faster start of mysql but no tab completition
    
    
    [isamchk]
    key_buffer      = 16M
    
    #
    # * IMPORTANT: Additional settings that can override those from this file!
    #   The files must end with '.cnf', otherwise they'll be ignored.
    #
    !includedir /etc/mysql/conf.d/
    
    2 回复  |  直到 6 年前
        1
  •  0
  •   Tomer Shay    6 年前

    可能不是您所要的,但这些建议有助于优化所有服务器上的查询:

    首先,增加相关指标:

    ALTER TABLE `b_actions` ADD INDEX `b_actions_idx_dismiss_id_name_pid` (`user_dismissed`,`user_id`,`action_name`,`pId`);
    ALTER TABLE `users` ADD INDEX `users_idx_id_usernam_email_id` (`id`,`username`,`email`,`customer_id`);
    ALTER TABLE `temp_a` ADD INDEX `temp_a_idx_id` (`id`);
    

    然后运行此查询转换,而不是原始查询。主要的变化是,我已经将IN子查询转换为EXISTS子查询,因为它通常执行得更好。

    SELECT
            t1.pId,
            t1.*,
            t2.username,
            t2.email,
            t2.customer_id 
        FROM
            b_actions t1 
        JOIN
            users t2 
                ON t1.user_id = t2.id 
        WHERE
            t1.action_name NOT IN (
                'action1', 'action2', 'action3'
            ) 
            AND t1.user_dismissed = 0 
            AND EXISTS (
                SELECT
                    1 
                FROM
                    temp_a 
                WHERE
                    t1.pId = temp_a.id
            ) 
        ORDER BY
            t1.datetime DESC
    
        2
  •  0
  •   Rick James diyism    6 年前

    我看不出 innodb_buffer_pool_size . 这是InnoDB表最重要的设置。 50G 可能是个不错的环境。

    使用 JOIN 而不是 AND t1.pId IN (SELECT id FROM temp_a) .

    之后,添加

    INDEX(user_dismissed, pId)