MySQL 8.0 升级后特定查询性能不佳

2024-03-15

编辑:我在 Python 中看到与 PHP 相同的行为。好像和MySQL有关。

我们正在尝试从 MySQL 5.7 升级到 8.0。我们的代码库使用 PHP MySQLi 来查询 MySQL 服务器。在我们的测试设置中,我们发现绑定大量参数的某些查询的性能较差(慢 50 倍)。我们希望看到 MySQL 8.0 的运行时间与 5.7 相似。下面是示例表结构和故障查询。

CREATE TABLE IF NOT EXISTS `a` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `name` (`name`) USING BTREE,
  KEY `name_id` (`id`,`name`) USING BTREE
);

CREATE TABLE IF NOT EXISTS `b` (
  `id` int NOT NULL AUTO_INCREMENT,
  `a_id` int NOT NULL,
  `value` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `uniquevalue` (`a_id`,`value`) USING BTREE,
  KEY `a_id` (`a_id`) USING BTREE,
  KEY `v` (`value`) USING BTREE,
  CONSTRAINT `b_ibfk_1` FOREIGN KEY (`a_id`) REFERENCES `a` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
);

CREATE TABLE IF NOT EXISTS `c` (
  `product` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `b_id` int NOT NULL,
  PRIMARY KEY (`product`,`b_id`) USING BTREE,
  KEY `b_id` (`b_id`),
  KEY `product` (`product`),
  CONSTRAINT `c_ibfk_2` FOREIGN KEY (`b_id`) REFERENCES `b` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
);
-- example trouble query
SELECT c.product, a.name, b.value
FROM b
INNER JOIN a ON b.a_id = a.id AND a.name IN ('1be6f9eb563f3bf85c78b4219bf09de9')
-- this hash is from the dataset (linked below) but it should match a record in the 'a' table that has an associated record in the 'b' table that in turn has an associated record in the 'c' table
INNER JOIN c on c.b_id = b.id and c.product IN (?, ?, ?...) -- ... meaning dynamic number of parameters

如果将查询修改为只返回一条记录(限制1),查询仍然很慢。所以这与返回的数据量无关。如果查询以非参数化方式运行(使用字符串连接),则查询运行时间在所有环境中都是可接受的。添加的参数越多,查询速度就越慢(线性)。对于 7,000 个绑定参数,查询在 MySQL 5.7 中运行时间为 100 - 150 毫秒,在 MySQL 8.0.28 中运行时间约为 10 秒。我们在 PHP 7.4 和 8.0 中看到相同的结果。我们使用 MySQLi 或 PDO 看到相同的结果。

这告诉我这与参数绑定有关。我启用了分析并检查了查询结果。查询的大部分时间 (~95%) 花费在执行步骤,而不是参数绑定步骤。另外,我看到 mysql 8 进程 CPU 在查询运行时被固定。我对这个很困惑。

这里是MySQL 8.0的解释。

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE a const PRIMARY,name,name_id name 1022 const 1 100 Using index
1 SIMPLE c ref PRIMARY,b_id,product product 152 const 1 100 Using index
1 SIMPLE b eq_ref PRIMARY,uniquevalue,a_id PRIMARY 4 DefaultWeb.c.b_id 1 5 Using where

这里是MySQL 5.7的解释。

id select_type table partitions type possible_keys key key_len ref rows filtered Extra
1 SIMPLE a const PRIMARY,name,name_id name 257 const 1 100 Using index
1 SIMPLE c ref PRIMARY,b_id,product PRIMARY 152 const 1 100 Using index
1 SIMPLE b eq_ref PRIMARY,uniquevalue,a_id PRIMARY 4 DefaultWeb.c.b_id 1 5 Using where

这两种解释之间存在一些差异,但同样,这个问题仅发生在 PHP 中的准备好的语句中。

下面是一些演示该问题的 php 代码。编写此代码是为了针对我在下面的 Google Drive 链接中提供的数据集。我还将 MySQL 变量包含在 CSV 中。

<?php
// Modify these to fit your DB connection.
const HOST = '127.0.0.1';
const USER = 'root';
const PASS = 'localtest';
const DB_NAME = 'TestDatabase';

// As the number of parameters increases, time increases linearly.
// We're seeing ~10 seconds with 7000 params with this data.
const NUM_PARAMS = 7000;

function rand_string($length = 10) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $charactersLength = strlen($characters);
    $randomString = '';
    for ($i = 0; $i < $length; $i++) {
        $randomString .= $characters[rand(0, $charactersLength - 1)];
    }
    return $randomString;
}

function sql_question_marks($count, $sets = 1) {
    return substr(str_repeat(",(".substr(str_repeat(",?", $count), 1).")", $sets), 1);
}

function unsecure_concat($params) {
    return "('" . implode("','", $params) . "')";
}

$params = [];
$param_types = '';
for ($i = 0; $i < NUM_PARAMS; $i++) {
    $params[] = rand_string();
    $param_types .= 's';
}

$big_query = <<<SQL
    SELECT c.product, a.name, b.value
    FROM b
    INNER JOIN a ON b.a_id = a.id AND a.name IN ('1be6f9eb563f3bf85c78b4219bf09de9')
    INNER JOIN c on c.b_id = b.id and c.product IN
SQL . sql_question_marks(count($params));

$non_parameterized = <<<SQL
    SELECT c.product, a.name, b.value
    FROM b
    INNER JOIN a ON b.a_id = a.id AND a.name IN ('1be6f9eb563f3bf85c78b4219bf09de9')
    INNER JOIN c on c.b_id = b.id and c.product IN
SQL . unsecure_concat($params);

$connection = new mysqli(HOST, USER, PASS, DB_NAME);

$q = $connection->prepare($big_query);
$q->bind_param($param_types, ...$params);
$start_time = hrtime(true);
$q->execute(); // This one shows the issue...100-250 ms execution time in MySQL 5.7 and ~10 seconds with 8.0.
$end_time = hrtime(true);

$total_time = ($end_time - $start_time) / 1000000000; // convert to seconds

echo 'The total time for parameterized query is ' . $total_time . ' seconds.';

$q->get_result(); // not concerned with results.

$q = $connection->prepare($big_query . ' LIMIT 1');
$q->bind_param($param_types, ...$params);
$start_time = hrtime(true);
$q->execute(); // This one also shows the issue...100-250 ms execution time in MySQL 5.7 and ~10 seconds with 8.0.
$end_time = hrtime(true);

$total_time = ($end_time - $start_time) / 1000000000; // convert to seconds

echo '<br>The total time for parameterized query with limit 1 is ' . $total_time . ' seconds.';

$q->get_result(); // not concerned with results 

$q = $connection->prepare($non_parameterized);
$start_time = hrtime(true);
$q->execute(); // Same execution time in 5.7 and 8.0.
$end_time = hrtime(true);

$total_time = ($end_time - $start_time) / 1000000000; // convert to seconds

echo '<br>The total time for non-parameterized query is ' . $total_time . ' seconds.';

您可以在此处下载示例数据:https://drive.google.com/file/d/111T7g1NowfWO_uZ2AhT9jdj4LiSNck8u/view?usp=sharing https://drive.google.com/file/d/111T7g1NowfWO_uZ2AhT9jdj4LiSNck8u/view?usp=sharing

编辑:这是带有 7,000 个绑定参数的 JSON 解释。

{
    "EXPLAIN": {
        "query_block": {
            "select_id": 1,
            "cost_info": {
                "query_cost": "456.60"
            },
            "nested_loop": [
                {
                    "table": {
                        "table_name": "a",
                        "access_type": "const",
                        "possible_keys": [
                            "PRIMARY",
                            "name",
                            "name_id"
                        ],
                        "key": "name",
                        "used_key_parts": [
                            "name"
                        ],
                        "key_length": "257",
                        "ref": [
                            "const"
                        ],
                        "rows_examined_per_scan": 1,
                        "rows_produced_per_join": 1,
                        "filtered": "100.00",
                        "using_index": true,
                        "cost_info": {
                            "read_cost": "0.00",
                            "eval_cost": "0.10",
                            "prefix_cost": "0.00",
                            "data_read_per_join": "264"
                        },
                        "used_columns": [
                            "id",
                            "name"
                        ]
                    }
                },
                {
                    "table": {
                        "table_name": "b",
                        "access_type": "ref",
                        "possible_keys": [
                            "PRIMARY",
                            "uniquevalue",
                            "a_id"
                        ],
                        "key": "uniquevalue",
                        "used_key_parts": [
                            "a_id"
                        ],
                        "key_length": "4",
                        "ref": [
                            "const"
                        ],
                        "rows_examined_per_scan": 87,
                        "rows_produced_per_join": 87,
                        "filtered": "100.00",
                        "using_index": true,
                        "cost_info": {
                            "read_cost": "8.44",
                            "eval_cost": "8.70",
                            "prefix_cost": "17.14",
                            "data_read_per_join": "65K"
                        },
                        "used_columns": [
                            "id",
                            "a_id",
                            "value"
                        ]
                    }
                },
                {
                    "table": {
                        "table_name": "c",
                        "access_type": "ref",
                        "possible_keys": [
                            "PRIMARY",
                            "b_id",
                            "product"
                        ],
                        "key": "b_id",
                        "used_key_parts": [
                            "b_id"
                        ],
                        "key_length": "4",
                        "ref": [
                            "TestDatabase.b.id"
                        ],
                        "rows_examined_per_scan": 35,
                        "rows_produced_per_join": 564,
                        "filtered": "18.28",
                        "using_index": true,
                        "cost_info": {
                            "read_cost": "130.53",
                            "eval_cost": "56.47",
                            "prefix_cost": "456.60",
                            "data_read_per_join": "88K"
                        },
                        "used_columns": [
                            "product",
                            "b_id"
                        ],
                        "attached_condition": "" // i've omitted the condition since it breaks the SO char limit, it contains 7,000 random character strings at 10 length each
                    }
                }
            ]
        }
    }
}

正如另一位用户之前提到的,MySQL 8 中的默认字符集更改为 utf8mb4。由于您在查询谓词比较中存在问题的某些列上使用显式 utf8 字符集定义,您是否考虑过尝试简单地“设置名称 utf8” ;”在 PHP 中?对于准备好的语句,参数的强制转换可能与字符串文字的强制转换不同。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MySQL 8.0 升级后特定查询性能不佳 的相关文章

  • 为什么AES java解密返回额外的字符?

    请原谅我英语不好 我使用 mcrypt 我从这里得到它用于 php 和 java 的 MCrypt https snipt net raw ee573b6957b7416f28aa560ead71c3a2 nice 在我的android应用
  • PHP解析xml文件错误

    我正在尝试使用 simpleXML 来获取数据http rates fxcm com RatesXML http rates fxcm com RatesXML Using simplexml load file 我有时会遇到错误 因为这个
  • 我应该使用平面表还是标准化数据库?

    我目前正在开发一个使用 MySQL 数据库作为后端的 Web 应用程序 在继续下一步之前 我需要知道什么更适合我的情况 简而言之 在这个应用程序中 用户将能够使用任何数字字段 他们决定 构建自己的表单 现在我将其全部存储在通过外键链接的几个
  • SQL UPDATE 语句根据另一个现有行更新列

    基本上我有一个与下表具有相似格式的表格 我想做的是根据这个逻辑更新 Col4 如果 Col2 为空 则用 Col3 更新 Col4 如果 Col2 不为 null 则在 Col1 中查找与 Col2 中的值匹配的值 使用 col3 中的相应
  • 使用 php 更改白天和黑夜的背景?

    我正在制作一个 tumblr 页面 我的 html 页面有两种不同的背景 我希望白天背景从早上 7 点到晚上 8 点显示 夜间背景从晚上 8 点到早上 7 点显示 我决定用 php 来做这件事 但对于 php 来说我是个新手 我的朋友给我发
  • 如何从导出的 csv 文件中删除双引号

    我正在使用 Laravel 5 8 并且添加了 Maatwebsite 包 用于从数据库表导出 CSV 文件 这是我导出的类 class ConfirmedExport implements FromCollection WithHeadi
  • 删除表的重复项

    In my activity logs 它包含列 material name user id mod result 这标志着测试是否通过 失败 cert links 不知何故 用户生成了两倍的条目material name与cert lin
  • 当与“<”或“>”运算符一起使用时,MySQL 不使用 DATE 上的索引吗?

    我正在使用解释来测试这些查询 col 类型是 DATE 这使用索引 explain SELECT events FROM events WHERE events date 2010 06 11 这不 explain SELECT event
  • 如何将 javax.persistence.Column 定义为 Unsigned TINYINT?

    我正在基于 MySQL 数据库中的现有表创建 Java 持久性实体 Bean 使用 NetBeans IDE 8 0 1 我在这个表中遇到了一个字段 其类型为 无符号 TINYINT 3 我发现可以执行以下操作将列的类型定义为 unsign
  • CakePHP Xml 实用程序库触发 DOMDocument 警告

    我正在使用 CakePHP 在视图中生成 XMLXML核心库 http book cakephp org 2 0 en core utility libraries xml html xml Xml build data array ret
  • MySQL:如何获取每个分组的x个结果数[重复]

    这个问题在这里已经有答案了 可能的重复 mysql 在 GROUP BY 中使用 LIMIT 来获取每组 N 个结果 https stackoverflow com questions 2129693 mysql using limit w
  • PHP - hash_pbkdf2 函数

    我正在尝试使用此 php 函数执行一个函数来哈希密码 http be php net manual en function hash pbkdf2 php http be php net manual en function hash pb
  • PHP中如何识别服务器IP地址

    PHP中如何识别服务器IP地址 对于服务器 ip 来说是这样的 SERVER SERVER ADDR 这是港口的 SERVER SERVER PORT
  • 使用 DOJO 自动完成文本框

    我正在寻找一种使用 DOJO 进行文本框自动建议的简单方法 我将查询的数据库表 使用 PHP 脚本 以 JSON 形式返回 有超过 100 000 条记录 因此这确实不应该采用 FilteringSelect 或 ComboBox 的形式
  • 如何用javascript正确读取php cookies

    考虑这个 php 和 javascript 代码 然后我在控制台中看到的是 utma 111872281 291759993 1444771465 1445374822 1445436904 4 utmz 111872281 1444771
  • 如何在没有引用的情况下复制对象?

    PHP5 OOP 有据可查对象通过引用传递 http php net manual en language oop5 references php默认情况下 如果这是默认的 在我看来 有一种非默认的方式可以在没有参考的情况下进行复制 如何
  • 将 MySQL 结果作为 PHP 数组

    mysql 表 config name config value allow autologin 1 allow md5 0 当前的 php 代码 sth mysql query SELECT rows array while r mysq
  • 为什么 PHP 中不允许“传统”类型提示?

    刚刚发现类型提示 http php net manual en language oop5 typehinting phpPHP 中允许 但不适用于整数 字符串 布尔值或浮点数 为什么 PHP 不允许对整数 字符串等类型进行类型提示 从 P
  • 当复选框条件更改时,如何使用ajax更新mysql数据库?

    我有一个在客户端按行显示的文章表 每篇文章都有一个唯一的 ID 并包含一个复选框以指示该文章是否被选中为收藏夹 如果它是最喜欢的 则该复选框已被选中 如果没有 则未选中 现在 如果特定于每一行的复选框条件发生变化 我需要 js 或 jque
  • 文件修改时间检查的成本

    对于Linux下包含少量字节的文件 我只需要处理自上次处理以来发生更改的时间 我通过调用 PHP 检查文件是否被更改clearstatcache filemtime 定期 由于整个文件总是很小 因此删除对 filemtime 的调用并通过将

随机推荐

  • iOS9 上的 Admob 和插页式广告

    今天我注册了 adMob 并开始在我的应用程序中处理 adMob 广告 第一个是游戏 带有视图控制器 我想在游戏结束后立即显示插页式广告 所以我想在游戏结束后立即加载插页式广告游戏结束加载的视图 此视图仅在游戏结束后才可访问 并且游戏持续一
  • 对数组中的对象执行块操作并在全部完成时完成

    我有一个对象数组 我想对其执行块操作 我不确定执行此操作的最佳方法 我正在做类似下面代码的事情 但我认为这不是最佳实践 进行此类操作的最佳方法是什么 void performBlockOnAllObjects NSArray objects
  • 类不符合RequestRetrier协议

    我一直在将我的项目迁移到 swift3 并一直在努力让 Alamofire RequestRetrier 协议正常工作 我遵循 Alamofire 4 0 迁移指南 https github com Alamofire Alamofire
  • 使用 @OneToOne 和 @OneToMany 时如何使 Hibernate OUTER JOIN

    假设我有一张名为employee与领域employee id name and supervisor id and employee supervisor与领域supervisor id and name 存在外键关系employee an
  • Docker:如何对 docker Push 进行身份验证?

    嗨 我正在尝试docker push docker simple httpserver docker push myregistry simplehttpserver latest The push refers to a reposito
  • 活动管理日期过滤器日期格式自定义

    是否有简单的方法可以更改 ActiveAdmin 日期过滤器显示格式的默认 ISO 格式 yyyy mm dd 您可以向日期选择器提供额外的选项 而不是覆盖 js 如下所示 f input my date as datepicker dat
  • 禁用 Android WebView WebRTC

    Fresh Cromium WebView 支持 WebRTC 但我想禁用它 Chrome 应用程序可以允许在 Chrome 中执行此操作 因此这似乎并非不可能 我进行了很多研究 但没有找到合适的解决方案 任何帮助 将不胜感激 更新 仍未解
  • 快速显示所有 Firebase 用户的帖子

    我需要使用 firebase 作为后端创建一个 UISearchController 我目前在 firebase 中有两个用户 一名用户发布了一篇帖子 另一名用户发布了四篇帖子 我希望能够搜索我的数据库中所有书籍的标题 共有五本书 但是 到
  • 在node.js中使MySQL的ORDER BY动态化

    我想做ORDER BYNode js 中的 mysql 查询是动态的 但这不起作用 我console logmultiQuery 变量和一切看起来都很完美 但运行时它根本不起作用 这就是我所拥有的 var order multiQuery
  • Oracle SELECT - 双引号还是没有双引号? [复制]

    这个问题在这里已经有答案了 All 在为 Oracle 11i 数据库编写 SELECT 查询时 为什么 Oracle 中的某些表强制对所选字段应用引号 而其他表则不然 举个我最近遇到的例子 在 Aqua Data Studio 的查询分析
  • 如何在真实设备(移动设备)上开发Windows 10 UWP?

    我正在尝试在真实设备上调试我的应用程序 带有 Windows 10 Mobile 10 0 10581 0 的 lumia 830 开发者模式在两种设备 移动设备和 PC 上均处于开启状态 但是当我尝试在设备上模拟应用程序时 出现错误 DE
  • Rails ActionMailer 编码

    Setup Ruby 1 9 2 Rails 3 2 2 我的电子邮件编码遇到一些问题 注意 我对编码之类的东西不太熟悉 使用 ActionMailer 发送电子邮件时 电子邮件中的 html 出现了一些奇怪的情况 所有等号 都更改为 3D
  • 如何更改MySQL表的默认字符集?

    有一个MySQLtable这个定义取自SQLYog Enterprise Table Create Table etape prospection CREATE TABLE etape prospection etape prosp id
  • .h 是从 .h.in 生成的吗?

    我的库在构建后创建的 h 文件中有结构定义 但我在相应的 h in 中找不到这些定义 有人可以告诉我这一切是如何工作的以及它从哪里获取额外信息吗 具体来说 我正在构建 pth 用户空间线程库 它有 pth p h in 它不包含我正在寻找的
  • Pycharm-无法安装opencv

    我是 PyCharm 的新手 并尝试了解如何安装 openCV 包以及后来的 cv2 包 我在这里看到一个旧的讨论 建议首先从 internet 下载 openCV 然后更新系统解释器 我下载了 openCV 并将其放置在我管理项目的同一目
  • Fluentd 是否支持文件输出的日志轮换?

    我当前使用的设置是具有多个容器的 Docker 组合堆栈 这些容器将其日志记录信息发送到运行 Fluentd 守护进程的日志记录容器 在 compose 堆栈内 Fluentd 的配置包括一in forward收集日志并将其写入单独文件的源
  • 模拟/存根“super”调用

    我想模拟一下super调用 尤其是某些 ES6 类中的构造函数 例如 import Bar from bar class Foo extends Bar constructor opts super opts someFunc super
  • GitHub 上隐藏的 Markdown 文本

    GitHub 上有专门的 Markdown 语法来支持隐藏文本吗 我只是想把一些待办事项记在里面README md为了我自己 不让别人看到 EXTRAS 为了致敬 Tamas 的出色回答 以及人们多次提出的问题 下面是如何在 MD 文件中编
  • C++11 中缺少 std::u8string

    为什么 C 11 提供std u16string and std u32string并不是std u8string 我们需要实现utf 8编码还是使用额外的库 C 20 添加char8 t and std u8string http www
  • MySQL 8.0 升级后特定查询性能不佳

    编辑 我在 Python 中看到与 PHP 相同的行为 好像和MySQL有关 我们正在尝试从 MySQL 5 7 升级到 8 0 我们的代码库使用 PHP MySQLi 来查询 MySQL 服务器 在我们的测试设置中 我们发现绑定大量参数的