mysql 排序、查询执行流程、幻读

文章目录

  • MySQL的 ORDER BY 执行流程
    • 示例表和查询语句
    • 执行流程
      • 全字段排序
      • Rowid 排序
      • 全字段排序 VS rowid排序
      • 联合索引优化
      • 覆盖索引优化
    • 小结
    • 思考题
      • 问题
      • 执行过程中是否需要排序?
      • 如何在数据库端实现不排序?
      • 实现分页需求
  • 使用`ORDER BY RAND()`
    • 内存临时表与磁盘临时表
    • 随机选择算法的优化
    • 实际应用和进一步优化
    • 小结
    • 思考题
    • 回答
      • 示例优化:
  • 误区
      • 案例一:条件字段函数操作
      • 案例二:隐式类型转换
      • 案例三:隐式字符编码转换
      • 总结优化策略
      • 行动建议
  • 为什么单行查询在MySQL中执行缓慢
      • 背景
      • 第一类:查询长时间不返回
        • 等待MDL锁
        • 等待表Flush
        • 等待行锁
      • 第二类:查询执行慢
        • 全表扫描
        • 一致性读导致慢查询
      • 解决查询慢的策略
      • 小结
      • 思考题
  • 幻读
      • 幻读的定义
      • 幻读的问题
      • 解决幻读的策略
      • 幻读的业务影响
      • 总结

MySQL的 ORDER BY 执行流程

在开发应用时,经常需要根据指定字段排序来显示结果。以下是关于 MySQL 中 ORDER BY 的执行流程及影响因素的总结。

示例表和查询语句

假设有一个市民表,定义如下:

CREATE TABLE `t` (`id` int(11) NOT NULL,`city` varchar(16) NOT NULL,`name` varchar(16) NOT NULL,`age` int(11) NOT NULL,`addr` varchar(128) DEFAULT NULL,PRIMARY KEY (`id`),KEY `city` (`city`)
) ENGINE=InnoDB;

查询城市为“杭州”的市民名字,并按姓名排序返回前1000个结果的SQL语句:

SELECT city, name, age FROM t WHERE city='杭州' ORDER BY name LIMIT 1000;

执行流程

全字段排序

当执行一个包含ORDER BY的查询时,如果不使用适当的索引,MySQL会使用全字段排序:

  1. 初始化排序缓冲区sort_buffer被用来存储需要排序的列。
  2. 从索引中检索数据:基于city索引检索满足条件的记录,然后加载相应的字段到sort_buffer
  3. 执行排序:在sort_buffer中对数据进行排序。
  4. 输出结果:根据排序结果输出前1000条数据。

Rowid 排序

当单行数据量过大时,MySQL可能采用rowid排序以节约内存:

  1. 排序缓冲区较小:只存储关键的排序字段和主键id。
  2. 数据检索和排序:检索并排序关键字段,使用主键再次查询以获取完整数据。
  3. 结果输出:根据排序后的id顺序输出结果。

全字段排序 VS rowid排序

在内存足够的情况下优先选择全字段排序,否则rowid排序

体现了MySQL的一个设计思想: 如果内存够,就要多利用内存,尽量减少磁盘访问。

联合索引优化

  1. 创建联合索引
ALTER TABLE t ADD INDEX city_user(city, name);
  1. 查询流程简化:利用联合索引 city_user(city, name),天然保证按 name 递增排序,避免排序操作,流程如下:
    • 从索引(city, name)中找到第一个满足 city=‘杭州’ 条件的记录。
    • 取出该记录中的 city、name、age 直接返回。
    • 重复上述步骤,直到取到1000条记录或不满足条件时停止。

覆盖索引优化

  1. 创建覆盖索引
ALTER TABLE t ADD INDEX city_user_age(city, name, age);
  1. 查询流程进一步简化:利用覆盖索引避免回表操作,流程如下:
    • 从索引(city, name, age)中找到第一个满足 city=‘杭州’ 条件的记录,直接取出并返回。
    • 重复上述步骤,直到取到1000条记录或不满足条件时停止。

小结

MySQL 的排序操作有多种实现方式,具体选择取决于查询条件和索引情况。为了提高性能,建议合理利用联合索引和覆盖索引,减少排序操作和磁盘访问。

思考题

问题

假设表中已有联合索引 city_name(city, name),查询杭州和苏州两个城市的市民名字,并按名字排序,显示前100条记录:

SELECT * FROM t WHERE city IN ('杭州', '苏州') ORDER BY name LIMIT 100;

执行过程中是否需要排序?

由于查询条件包含多个城市,合并后可能无序,无法利用单一的联合索引顺序,MySQL 需要对结果集进行排序。

如何在数据库端实现不排序?

每个城市单独查询

可以创建联合索引 city_name(city, name) 并结合分区查询:

(SELECT * FROM t WHERE city = '杭州' ORDER BY name LIMIT 100)
UNION ALL
(SELECT * FROM t WHERE city = '苏州' ORDER BY name LIMIT 100)
ORDER BY name LIMIT 100;

实现分页需求

对于第101页,即查询第10000至10099条记录:

(SELECT * FROM t WHERE city = '杭州' ORDER BY name LIMIT 10000, 100)
UNION ALL
(SELECT * FROM t WHERE city = '苏州' ORDER BY name LIMIT 10000, 100)
ORDER BY name LIMIT 10000, 100;

这种方式避免了单次大数据量排序,提高查询效率。

本文主要讲述了如何在MySQL中实现随机选择单词的功能,并分析了使用ORDER BY RAND()导致的性能问题,提出了几种优化方法。

使用ORDER BY RAND()

在初步尝试中,可以使用SELECT word FROM words ORDER BY RAND() LIMIT 3;来随机选择三个单词。这种方法虽简单,但随着数据量的增大,性能问题逐渐显现:

  • MySQL需要在内存中创建临时表并对其进行随机排序,这一过程涉及大量的数据读取和排序操作。
  • 根据EXPLAIN命令的输出,此查询需要使用临时表和文件排序,显著增加了查询的资源消耗。

内存临时表与磁盘临时表

文章进一步解释了MySQL在处理ORDER BY RAND()时可能使用的两种临时表:

  1. 内存临时表:如果临时表的大小未超过tmp_table_size配置的限制,则使用内存中的临时表。
  2. 磁盘临时表:如果临时表大小超过限制,则转为在磁盘上创建临时表,这会进一步降低性能,由internal\_tmp\_disk\_storage\_engine控制。

随机选择算法的优化

文章提出了两种优化随机选择单词的方法:

  1. 随机算法1

    • 查询表的主键ID的最大和最小值。
    • 生成一个介于最大和最小ID之间的随机数。
    • 查询ID大于或等于该随机数的第一条记录。
    • 这种方法虽然快速,但不够均匀,特别是当ID存在空洞时。
  2. 随机算法2

    • 计算表中的总行数。
    • 生成一个随机数来选择行号。
    • 使用LIMIT语句直接跳过前N行,返回第N+1行。
    • 这种方法提供了更均匀的随机性,但性能开销大于算法1,尽管依然优于ORDER BY RAND()

实际应用和进一步优化

对于实际应用,尤其是行数较多的表,推荐使用随机算法2,并在应用层进行SQL语句的构建和执行。此外,对于随机选择多个单词的需求,可以通过生成多个随机行号并分别查询来实现,尽管这会增加数据库的访问次数。

小结

在设计数据库交互时,考虑查询的性能影响是非常关键的。直接使用ORDER BY RAND()可能引起严重的性能问题,尤其是在数据量较大的情况下。通过理解MySQL的内部工作原理和利用优化的查询方法,可以显著提高应用的响应速度和整体性能。

思考题

  • 如何进一步减少随机算法3的扫描行数?
  • 是否有可能通过改进SQL结构或使用特定的数据库功能(如更复杂的索引策略)来优化随机选择的效率?

回答

要减少随机算法3的扫描行数,并优化随机选择的效率,可以采取以下措施:

  1. 使用单查询优化

    • 在一次查询中获取多个随机行。例如:
      SELECT * FROM words WHERE id >= @X ORDER BY id LIMIT 3;
      
    • 这样可以减少多次扫描。
  2. 利用表统计信息

    • 获取表的总行数并计算随机偏移量。
      SELECT COUNT(*) INTO @C FROM words;
      SET @Y = FLOOR(@C * RAND());
      SELECT * FROM words LIMIT @Y, 1;
      
    • 更准确地生成随机数以减少空洞。
  3. 索引优化

    • 创建复合索引,提高查询速度。
  4. 应用层优化

    • 缓存部分单词表,应用层随机选择,减少数据库访问。
    • 预加载单词列表到应用层进行随机选择。
  5. 利用数据库特性

    • 使用数据库特定的优化功能,如随机选择函数。

示例优化:

使用单查询获取多个随机行,并利用表统计信息:

SELECT COUNT(*) INTO @C FROM words;
SET @Y1 = FLOOR(@C * RAND());
SET @Y2 = FLOOR(@C * RAND());
SET @Y3 = FLOOR(@C * RAND());
SELECT * FROM words WHERE id >= @Y1 LIMIT 1;
SELECT * FROM words WHERE id >= @Y2 LIMIT 1;
SELECT * FROM words WHERE id >= @Y3 LIMIT 1;

这样可以减少扫描行数,提高效率。

误区

案例一:条件字段函数操作

  • 问题描述:在使用函数处理索引字段(如month(t_modified))时,可能会破坏值的有序性,优化器会放弃走树搜索。MySQL无法利用索引进行快速查找,导致性能下降。
  • 解决方案:避免在查询条件中对索引字段使用函数。改用基于字段本身的范围查询,例如使用t_modified BETWEEN '2016-7-1' AND '2016-7-31'

案例二:隐式类型转换

  • 问题描述:当查询条件中的数据类型不匹配字段类型时(如整数与字符串比较tradeid = 110717),会触发类型转换,从而导致索引失效。
  • 解决方案:确保查询条件中的数据类型与数据库字段的类型一致,避免隐式类型转换。

案例三:隐式字符编码转换

  • 问题描述:不同表之间的字段字符编码不一致(如utf8utf8mb4),在进行连接查询时无法使用索引。

这个设定很好理解,utf8mb4是utf8的超集。类似地,在程序设计语言里面,做自动类型转换的时候,为了避免数据在转换过程中由于截断导致数据错误,也都是“按数据长度增加的方向”进行转换的。

  • 解决方案
    • 调整编码:调整相关字段的字符编码,使其一致。
    • 显式转换:在查询时使用CONVERT函数显式进行字符编码转换。

总结优化策略

  1. 避免在索引字段上使用函数:使用索引字段本身而非经过函数处理的结果作为查询条件。
  2. 保持数据类型一致:在查询中使用与数据库字段相同的数据类型。
  3. 统一字符编码:确保关联查询中涉及的字段具有相同的字符编码,或在查询中指定正确的字符编码转换。

行动建议

  • 在业务代码升级时,对可能出现的新SQL语句进行EXPLAIN分析,是一个避免性能问题的好习惯。
  • 共享实际遇到的性能问题及其解决方案,可以帮助团队成员学习并提高处理类似问题的能力。

为什么单行查询在MySQL中执行缓慢

背景

  • 常见的理解是,复杂查询或大数据量返回会导致性能问题,但某些单行查询同样会非常缓慢。
  • 排除数据库整体负载高的情况,本文专注于单行查询的特定性能问题。

第一类:查询长时间不返回

等待MDL锁
  • 问题:查询被表的元数据锁(MDL)阻塞。
  • 诊断:使用 SHOW PROCESSLIST 查看状态为 Waiting for table metadata lock
  • 解决:通过 sys.schema_table_lock_waits 找到阻塞的线程ID并 KILL 掉。
等待表Flush
  • 问题:查询被 FLUSH TABLES 操作阻塞。
  • 诊断SHOW PROCESSLIST 显示状态为 Waiting for table flush
  • 解决:找到并终止执行 FLUSH TABLES 的会话。
等待行锁
  • 问题:查询被另一个事务持有的行锁阻塞。
  • 诊断SHOW PROCESSLIST 显示状态为 Waiting for lock.
  • 解决:使用 sys.innodb_lock_waits 查找持有锁的事务,并 KILL 掉。

第二类:查询执行慢

全表扫描
  • 问题:查询没有使用索引,导致全表扫描。
  • 示例SELECT * FROM t WHERE c=50000 LIMIT 1;
  • 解决:添加适当的索引。
一致性读导致慢查询
  • 问题:一致性读导致扫描大量回滚日志(undo log)。
  • 示例SELECT * FROM t WHERE id=1;
  • 复现:一个事务进行大量更新,导致查询需要回滚大量操作。
  • 解决:优化事务设计,避免生成大量的undo log。

解决查询慢的策略

  1. 使用索引:确保查询条件使用适当的索引。
  2. 优化事务设计:避免长事务和大事务,减少锁争用。
  3. 监控和诊断工具:使用 SHOW PROCESSLISTsys.innodb_lock_waits 等工具诊断问题。

小结

即使是简单的单行查询也可能因多种原因导致性能问题。了解和识别这些问题的原因是关键,合理使用工具和策略可以显著提升查询效率。

思考题

考虑查询SELECT * FROM t WHERE c = 5 FOR UPDATE;,分析它如何加锁以及锁释放的时机,讨论可能的性能影响。

幻读

幻读的定义

幻读是在数据库事务处理中遇到的一种现象,它发生在一个事务(Transaction)在执行过程中进行两次相同的查询,却得到了不同的结果。这种情况通常是由于在这两次查询间有其他事务插入或修改了数据所致。

幻读在“当前读”下才会出现

幻读的问题

幻读主要问题在于它违反了事务的隔离性,特别是在可重复读(Repeatable Read)隔离级别下。在这个级别,预期事务能够多次读取同样的数据行并得到相同的结果,但幻读破坏了这一期望。

在上文中提到的例子中,尽管在事务开始时锁定了d=5的行(id=5),事务中途对其他行(如id=0或id=1)的修改和新增行(id=1新增),导致了幻读,因为这些修改和添加的数据行在后续的查询中被看到了,违反了预期的事务隔离性。

解决幻读的策略

为了解决幻读问题,MySQL的InnoDB存储引擎引入了间隙锁(Gap Locks)的概念,除了对查询到的数据行加锁外,还对数据行之间的间隙加锁。这种锁策略不仅锁定数据行,也锁定行之间的间隙,防止其他事务在这些间隙中插入新的行。间隙锁和行锁合称next-key lock,每个next-key lock是前开后闭区间。

例如,如果事务A查询了所有d=5的数据行并对其加锁,通过间隙锁机制,事务B将无法插入一个新的d=5的行,因为这将需要在已锁定的间隙中插入数据,而这是被禁止的。

幻读的业务影响

幻读可能会导致数据不一致性,影响业务逻辑的正确执行。例如,如果一个事务基于查询结果执行操作(如计算总和或更新数据),由于幻读导致的数据变化可能会使得最终结果不正确或事务失败。

总结

在处理数据库事务时,了解和应对幻读是确保数据一致性和事务隔离性的关键。通过使用适当的隔离级别和理解数据库的锁机制,可以有效地管理和减少幻读带来的问题。在设计数据库操作和事务逻辑时,合理选择隔离级别和理解其对应的锁策略对于开发稳定可靠的应用程序至关重要。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/17717.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ANDROID OLLVM 混淆配置

安装环境 MacOSGITCMAKENDK - 21.1.6352462 步骤 1. 编译项目 此项目版本较低 https://github.com/obfuscator-llvm/obfuscator ,我们使用 https://github.com/heroims/obfuscator 进行编译 git clone https://github.com/heroims/obfuscator.gitcd obfuscator…

曼城四连冠,剑南春与万千球迷共同见证“荣耀时刻”

执笔 | 洪大大 编辑 | 扬 灵 5月19日,英超2023-2024赛季第38轮比赛全面开打,凭借队员的出色发挥,曼城最终以3-1战胜西汉姆联,成功捧起了英超联赛的奖杯,成为英格兰足球顶级联赛100多年历史上第一支成就四连冠的豪门…

事务报错没有显示回滚导致DDL阻塞引发的问题

在业务开发过程中,显示的开启事务并且在事务处理过程中对不同的情况进行显示的COMMIT或ROLLBACK,这是一个完整数据库事务处理的闭环过程。 这种在应用开发逻辑层面去handle的事务执行的结果,既确保了事务操作的数据完整性,又遵循了…

简单句语法

简单句是指包含一个主语和一个谓语的句子,它表达一个完整的思想。简单句是构成更复杂句子的基础。 简单句的两种基本结构 简单句可以分为两种基本结构: 主谓结构: 描述主语所做的动作或行为,也就是 “做什么”。 主系结构: 描述主语的状态…

Python2和Python3对utf8的实现方式有什么区别?

# -*- coding: utf8 -*- 是一个特殊的文件头部注释,通常出现在Python 2的源代码文件的开头。这个注释告诉Python解释器,该源文件使用的是UTF-8编码。这对于包含非ASCII字符(例如中文字符、特殊符号等)的Python源代码文件来说非常重…

探索未来设计新境界,PSAI插件 艺术创作神器来袭!

想象一下,如果有一个工具,能够让你的设计工作变得既简单又高效,那会是怎样的体验?现在,梦想成真了! 这是一款革命性的PSAI设计插件,专为创意人士打造。它将彻底改变你的设计流程,让你…

【OpenCV】像素信息统计

介绍了计算像素均值、方差的API,以及统计像素信息的方法。相关API: minMaxLoc()mean()meanStdDev() 代码: #include "iostream" #include "opencv2/opencv.hpp"using namespace std; using namespace cv;int main(int…

谈谈如何建立可落地的数字化转型战略

数字化转型战略是指将数字技术集成到企业或组织的所有领域,从根本上改变其运营方式以及为客户提供价值的方式。它涉及采用新技术并重新思考现有业务流程,以提高效率、生产力和客户满意度。 成功的数字化转型战略需要采用涉及人员、流程和技术的整体方法。…

【全开源】JAVA同城搬家系统源码小程序APP源码

JAVA同城搬家系统源码 特色功能: 强大的数据处理能力:JAVA提供了丰富的数据结构和算法,以及强大的并发处理能力,使得系统能够快速地处理大量的货物信息、司机信息、订单信息等,满足大规模物流的需求。智能路径规划&a…

香橙派 AIPro开发板上手测评

前言 最近拿到了一个新玩具:香橙派 AIPro。一个只比银行卡大一点点的开发板能带给我们多少惊喜呢?接下来就跟我一起来体验下这块开发板的魅力。 一、硬件配置 CPU:配备了4核64位ARM处理器,其中默认预留1个给AI处理器使用 NPU&am…

SpringBoot和Apache Doris实现实时广告推荐系统

本专题旨在向读者深度解读Apache Doris技术,探讨其与SpringBoot框架结合在各类实际应用场景中的角色与作用。本专题包括十篇文章,每篇文章都概述了一个特定应用领域,如大数据分析、实时报告系统、电商数据分析等,并通过对需求的解析、解决方案的设计、实际应用示例的展示以…

【Python实战】你还在冲会员看电影电视剧吗?Python带你实现各大资源免费看!

前言 halo,包子们下午好 今天给大家实现一个视频播放器,可以看任何电影,电视剧,不要再为以后看电视看电影而烦恼,今天是福利文章,相信我绝对有用! 开发工具 Python版本:3.7.8 相…

Java Lambda 会影响性能吗?

# 测试代码LamdaTest.java import java.util.*;class LamdaTest {static volatile List<Integer> integers new ArrayList<Integer>();// 普通 for 循环测试public static int forLoopInteger() {int total 0;for (int i 0; i < integers.size(); i) {total…

驱动未来:IT行业的现状与发展趋势

前言 随着技术的不断进步&#xff0c;IT行业已成为推动全球经济和社会发展的关键力量。从云计算、大数据、人工智能到物联网、5G通信和区块链&#xff0c;这些技术正在重塑我们的生活和工作方式。本文将探讨IT行业的现状和未来发展趋势&#xff0c;并邀请行业领袖、技术专家和…

Follow Your Pose: Pose-Guided Text-to-Video Generation using Pose-Free Videos

清华深&港科&深先进&Tencent AAAI24https://github.com/mayuelala/FollowYourPose 问题引入 本文的任务是根据文本来生成高质量的角色视频&#xff0c;并且可以通过pose来控制任务的姿势&#xff1b;当前缺少video-pose caption数据集&#xff0c;所以提出一个两…

Java的上下转型与多态

上下转型 首先&#xff0c;定义一个父类Person // 父类 class Person {public void run(){System.out.println("person 中的 run");}public void eat(){System.out.println("Person 中的 eat");}}接着定义一个继承自父类的子类Student: // 子类 class S…

拿捏数据结构- 链式二叉树

链式二叉树的概念&#xff1a; 链式二叉树解决的是非完全二叉树解决不了的问题 什么意思呢&#xff0c;简单的说就是&#xff0c;链式二叉树 可以是下面三种二叉树 但是非链式二叉树只能是前两种 链式二叉树的存储 节点结构&#xff1a;首先定义一个结构体或类来表示二叉树的节…

机器学习-7-机器学习中常用的可视化方式总结

参考通透!!监督学习和无监督学习全总结! 参考机器学习中的可视化 1 监督学习和无监督学习 监督学习和无监督学习,它们之间的主要区别在于训练数据的标签信息是否提供。 1.1 概述 一、监督学习(Supervised Learning): (1)标签信息: 监督学习使用带有标签的训练数据。这…

单元测试的实现方式

单元测试的实现方式包括&#xff1a;人工静态检查、动态执行跟踪 人工静态检查 人工静态检查是一种单元测试实现方式&#xff0c;它主要依赖开发人员的人工代码审查和静态分析工具来识别潜在的代码问题。 代码审查&#xff1a;开发人员通过仔细检查代码来发现潜在的问题。他…

不怕YOLOv10高歌猛进,我有YOLOv8稳扎稳打

YOLOv10 出来有几天时间了&#xff0c;这次我没有选择第一时间出文章解析&#xff0c;如此频繁的发布数字版本的 YOLO 着实让人头疼&#xff0c;虽然数字的更新并非旧版技术的过时&#xff0c; 但是这肯定会让很多在校同学增加很多焦虑情绪。这里还是请大家辩证看待。 v10 这次…