MySQL 条件下推与排序优化实例--MySQL8.035
❝开头还是介绍一下群,如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, OceanBase, Sql Server等有问题,有需求都可以加群群内有各大数据库行业大咖,可以解决你的问题。加群请联系 liuaustin3 ,(共3000人左右 1 + 2 + 3 + 4 +5 + 6 + 7 + 8 +9)(1 2 3 4 5 6 7群均已爆满,开8群350+ 9群 100+)
MySQL数据库衍生出很多兼容他的数据库产品,Mariadb、OceanBase (开源mysql兼容版本)、PolarDB for MySQL 等这些数据库产品都兼容MySQL.国产的项目不允许有MySQL的存在,导致大部分乙方的产品都在研究信创数据库,dump 完Oracle,继续dump MySQL.
所以这是一个系列,这里将使用MySQL,以及国产数据库产生同样的数据,进行他们查询规则的分析,看看是否有差异,为后续接受国产数据库,和新的项目做准备。本期是MySQL 8.035,针对这个版本在查询分析中的一些问题和特色进行分析和总结。
在进行总结前我们要产生测试数据。
CREATE DATABASE IF NOT EXISTS test;
USE test;
CREATE TABLE users (
user_id INT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50),
email VARCHAR(100),
signup_date DATE
);
CREATE TABLE products (
product_id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100),
price FLOAT,
created_at DATE
);
CREATE TABLE orders (
order_id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
product_id INT,
order_date DATE,
amount FLOAT,
note TEXT,
FOREIGN KEY (user_id) REFERENCES users(user_id),
FOREIGN KEY (product_id) REFERENCES products(product_id)
);
DELIMITER $$
CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255)
DETERMINISTIC
BEGIN
DECLARE chars VARCHAR(62) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
DECLARE result VARCHAR(255) DEFAULT '';
DECLARE i INT DEFAULT 0;
WHILE i < n DO
SET result = CONCAT(result, SUBSTRING(chars, FLOOR(1 + RAND() * 62), 1));
SET i = i + 1;
END WHILE;
RETURN result;
END$$
DELIMITER ;
DELIMITER $$
CREATE PROCEDURE insert_users(IN total INT)
BEGIN
DECLARE i INT DEFAULT 0;
WHILE i < total DO
INSERT INTO users (username, email, signup_date)
VALUES (
rand_string(8),
CONCAT(rand_string(5), '@example.com'),
DATE_SUB(CURDATE(), INTERVAL FLOOR(RAND() * 365 * 5) DAY)
);
SET i = i + 1;
END WHILE;
END$$
DELIMITER ;
DELIMITER $$
CREATE PROCEDURE insert_products(IN total INT)
BEGIN
DECLARE i INT DEFAULT 0;
WHILE i < total DO
INSERT INTO products (name, price, created_at)
VALUES (
rand_string(10),
ROUND(RAND() * 5000, 2),
DATE_SUB(CURDATE(), INTERVAL FLOOR(RAND() * 365 * 5) DAY)
);
SET i = i + 1;
END WHILE;
END$$
DELIMITER ;
DELIMITER $$
CREATE PROCEDURE insert_orders(IN total INT)
BEGIN
DECLARE i INT DEFAULT 0;
DECLARE uid INT;
DECLARE pid INT;
DECLARE user_count INT;
DECLARE product_count INT;
SELECT COUNT(*) INTO user_count FROM users;
SELECT COUNT(*) INTO product_count FROM products;
WHILE i < total DO
SET uid = FLOOR(1 + RAND() * user_count);
SET pid = FLOOR(1 + RAND() * product_count);
INSERT INTO orders (user_id, product_id, order_date, amount, note)
VALUES (
uid,
pid,
DATE_SUB(CURDATE(), INTERVAL FLOOR(RAND() * 365 * 5) DAY),
ROUND(RAND() * 100, 2),
rand_string(20)
);
SET i = i + 1;
END WHILE;
END$$
DELIMITER ;
-- 插入 10000 用户
CALL insert_users(10000);
-- 插入 1000 产品
CALL insert_products(1000);
-- 插入 100000 订单
CALL insert_orders(100000);
上面测试数据,产生3张表
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 14
Server version: 8.0.35 MySQL Community Server - GPL
Copyright (c) 2000, 2023, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h'forhelp. Type '\c' to clear the current input statement.
mysql> use test;
Database changed
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| orders |
| products |
| users |
+----------------+
3 rows inset (0.03 sec)
mysql>
这里这三张表之间的关系为
users 表 (用户表) 主键:user_id
表示一个注册用户
一个用户可以创建多个订单(1 对多)
products 表 (产品表) 主键:product_id
表示一个商品
一个产品可以被多个订单引用(1 对多)
orders 表 (订单表) 主键:order_id
外键:user_id -> users(user_id)
外键:product_id -> products(product_id)
表示某个用户下了某个产品的订单
user_id | users | ||
product_id | products |
users | orders.user_id | |
products | orders.product_id | |
orders | users / products |
问题1:过滤条件到底写在表链接中,还是写到where 条件中,我们看下面的图
两种查询的写法,一个是将order_date的时间过滤写到 inner join中的一行,二另一个写到了where条件中,在实际的应用中我们该怎么写。
1 先分清inner join 和 left join,在inner join 中是匹配两个表之间重合的数据,相当于交集,则可以把where的条件上推,写成图2的方式,如果是写成left join 而我们的where 条件中的过滤是左表,则还是可以把where条件写到上面,但如果过滤条件的表是右面的表,且使用的是left join,则不能把时间的条件写到 join后,而是需要写到where 条件中。我们可以用下面的表格来表达,简述中的意思。
ON 子句 | |
INNER JOIN 或 LEFT JOIN + WHERE |
在MySQL中,条件下推 是一种优化策略:将 WHERE 或 JOIN 的过滤条件尽可能早地应用在执行计划中,通常是在访问表数据前或刚开始访问时,从而减少不必要的数据访问和中间结果的行数。
在我们上图的执行中,可以多次运行两种语句观察具体的执行时间,在观察中我发现一些规律和问题。
一、在where条件中撰写条件,比在join后跟上过滤条件,运行的时间总是 where条件使用更多的时间。
二、如果将where条件上移,则需要建立更适合的索引,需要建立 user_id,order_date的联合索引在orders表中。
ON | WHERE | |
|---|---|---|
问题2 在多表查询中的排序问题和优化
在早期的MySQL数据库中,对于几个词老手都是敏感的
1 倒排 2 filesort 3 临时表
问题 1,什么情况可以避免使用filesort 在有排序的情况下
答:在查询的字段都在索引中,且排序的字段为这个索引的第一个字段的情况下,是无需进行filesort的,这里的原理为数据给付的时候已经是按照排序进行给付的,所以无需再获取数据后,在进行二次排序。
我们可以看下面的案例,第一个案例是有排序覆盖索引的情况下,查询没有进行usering filesort,第二个案例,我们删除了索引,则查询走了using filesort
这部分在MySQL有一些特殊的情况和优化的手段,一个字解释就是拆。
我们以上面的例子中,如果要进行大量返回值的排序后的查询,我们可以用这样的方法,返回的数据太多的情况下采用这样的方案有助于解耦和降低filesoft时的性能消耗。
SELECT user_id
FROM users
WHERE user_id in (1,...,1000) ORDER BY signup_date DESC LIMIT 900;
SELECT user_id,user_name FROM orders WHERE id IN (user_id);
在排序的写法中还有一种要不得就是随即函数,这样的写法中最大的问题为结果的每行都要进行一次rand()函数的计算。
SELECT user_id
FROM users
WHERE user_id in (1,.....,1000) ORDER BY rand() DESC LIMIT 1;
其实如果是想抽取一个随机的数据并给付结果,是可以通过下面的方案来进行的。也就是给一个确定的值。不要在ORDER BY 中进行计算。具体的方案是
explain SELECT user_id
FROM users
WHERE user_id in (1,999,34,23,56,564,1000) ORDER BY rand() DESC LIMIT 1;
可以撰写成下方的样子,数字7是可以改变的,在有多少值的情况下,可以改成多少。
explain SELECT user_id, username
FROM users
WHERE user_id = (
SELECT CAST(SUBSTRING_INDEX(SUBSTRING_INDEX('1,999,34,23,56,564,1000', ',', n), ',', -1) AS UNSIGNED)
FROM (
SELECT FLOOR(1 + RAND() * 7) AS n
) AS rand_pos
);
最后进行一个总结,MySQL在查询中由于本身数据处理引擎比较弱,很多复杂的语句需要进行拆分,同时在mysql中的条件下推 predicate Pushdown 要会使用,分清楚什么时候可以把条件写到 JOIN中,什么时候不可以会导致业务逻辑错误。
WHERE 条件下推到 JOIN、子查询、视图中提前执行 | ||
SELECT col1 场景 | ||
PostgreSQL 新版本就一定好--由培训现象让我做的实验
删除数据“八扇屏” 之 锦门英豪 --我去-BigData!
写了3750万字的我,在2000字的OB白皮书上了一课--记 《OceanBase 社区版在泛互场景的应用案例研究》
跟我学OceanBase4.0 --阅读白皮书 (OB分布式优化哪里了提高了速度)
跟我学OceanBase4.0 --阅读白皮书 (4.0优化的核心点是什么)
跟我学OceanBase4.0 --阅读白皮书 (0.5-4.0的架构与之前架构特点)
跟我学OceanBase4.0 --阅读白皮书 (旧的概念害死人呀,更新知识和理念)
MongoDB 相关文章
MongoDB “升级项目” 大型连续剧(4)-- 与开发和架构沟通与扫尾
MongoDB “升级项目” 大型连续剧(3)-- 自动校对代码与注意事项
MongoDB “升级项目” 大型连续剧(2)-- 到底谁是"der"
MongoDB “升级项目” 大型连续剧(1)-- 可“生”可不升
MongoDB 大俗大雅,上来问分片真三俗 -- 4 分什么分
MongoDB 大俗大雅,高端知识讲“庸俗” --3 奇葩数据更新方法
MongoDB 大俗大雅,高端的知识讲“通俗” -- 2 嵌套和引用
MongoDB 大俗大雅,高端的知识讲“低俗” -- 1 什么叫多模
MongoDB 合作考试报销活动 贴附属,MongoDB基础知识速通
MongoDB 使用网上妙招,直接DOWN机---清理表碎片导致的灾祸 (送书活动结束)
MongoDB 2023年度纽约 MongoDB 年度大会话题 -- MongoDB 数据模式与建模
“PostgreSQL” 高性能主从强一致读写分离,我行,你没戏!
POLARDB 添加字段 “卡” 住---这锅Polar不背
PolarDB 版本差异分析--外人不知道的秘密(谁是绵羊,谁是怪兽)
PolarDB 答题拿-- 飞刀总的书、同款卫衣、T恤,来自杭州的Package(活动结束了)
PolarDB for MySQL 三大核心之一POLARFS 今天扒开它--- 嘛是火
PostgreSQL 无服务 Neon and Aurora 新技术下的新经济模式 (翻译)
“PostgreSQL” 高性能主从强一致读写分离,我行,你没戏!
全世界都在“搞” PostgreSQL ,从Oracle 得到一个“馊主意”开始
PostgreSQL 加索引系统OOM 怨我了--- 不怨你怨谁
PostgreSQL “我怎么就连个数据库都不会建?” --- 你还真不会!
PostgreSQL 稳定性平台 PG中文社区大会--杭州来去匆匆
PostgreSQL 分组查询可以不进行全表扫描吗?速度提高上千倍?
POSTGRESQL --Austindatabaes 历年文章整理
PostgreSQL 查询语句开发写不好是必然,不是PG的锅
MySQL相关文章