orderby工作原理 + 最小代价取随机数

orderby是如何工作的

  • 场景例子:假设你要查询城市是“杭州”的所有人名字,并且按照姓名排序返回 前1000个人的姓名、年龄。

    • 表结构:

? SQL语句:select city,name,age from t where city="杭州" order by name limit 1000;

  • 全字段排序

      • Extra字段中Using filesort表示需要排序,mysql会给每个线程分配一块内存用于排序
  • 执行流程
    1. 初始化sort_buffffer,确定放入name、city、age这三个字段;
    2. 从索引city找到第一个满足city=‘杭州’条件的主键id
    3. 到主键id索引取出整行,取name、city、age三个字段的值,存入sort_buffffer中;
    4. 从索引city取下一个记录的主键id;
    5. 重复步骤3、4直到city的值不满足查询条件为止,对应的主键id也就是图中的ID_Y;
    6. 对sort_buffffer中的数据按照字段name做快速排序;
    7. 按照排序结果取前1000行返回给客户端。
  • sort_buffffer_size,就是MySQL为排序开辟的内存(sort_buffffer)的大小。如果要排序的数据量 小于sort_buffffer_size,排序就在内存中完成。但如果排序数据量太大,内存放不下,则不得不 利用磁盘临时文件辅助排序。
  • 这个算法过程里面,只对原表的数据读了一遍,剩下的操作都是在sort_buffffer和临时文件中执行的。但这个算法有一个问题,就是如果查询要返回的字段很多的话,那么sort_buffffer里面 要放的字段数太多,这样内存里能够同时放下的行数很少,要分成很多个临时文件,排序的性能会很差。 所以如果单行很大,这个方法效率不够好。
  • rowid
    • max_length_for_sort_data,是MySQL中专门控制用于排序的行数据的长度的一个参数。
    • city、name、age 这三个字段的定义总长度是36,我把max_length_for_sort_data设置为16, 我们再来看看计算过程有什么改变。
    • 新的算法放入sort_buffffer的字段,只有要排序的列(即name字段)和主键id。但这时,排序的结果就因为少了city和age字段的值,不能直接返回了,整个执行流程就变成如下所示的样子:
      1. 初始化sort_buffffer,确定放入两个字段,即name和id;
      2. 从索引city找到第一个满足city=‘杭州’条件的主键id,也就是图中的ID_X;
      3. 到主键id索引取出整行,取name、id这两个字段,存入sort_buffffer中;
      4. 从索引city取下一个记录的主键id;
      5. 重复步骤3、4直到不满足city=‘杭州’条件为止,也就是图中的ID_Y;
      6. 对sort_buffffer中的数据按照字段name进行排序;
      7. 遍历排序结果,取前1000行,并按照id的值回到原表中取出city、name和age三个字段返回 给客户端。
    • 实际上MySQL服务端从排序后的sort_buffffer 中依次取出id,然后到原表查到city、name和age这三个字段的结果,不需要在服务端再耗费内 存存储结果,是直接返回给客户端的。
  • 全字段排序 VS rowid排序结论
    • 如果MySQL实在是担心排序内存太小,会影响排序效率,才会采用rowid排序算法,这样排序过 程中一次可以排序更多行,但是需要再回到原表去取数据。

      如果MySQL认为内存足够大,会优先选择全字段排序,把需要的字段都放到sort_buffffer中,这 样排序后就会直接从内存里面返回查询结果了,不用再回到原表去取数据。

      这也就体现了MySQL的一个设计思想:如果内存够,就要多利用内存,尽量减少磁盘访问。 对于InnoDB表来说,rowid排序会要求回表多造成磁盘读,因此不会被优先选择。

  • 你可以设想下,如果能够保证从city这个索引上取出来的行,天然就是按照name递增排序的话, 是不是就可以不用再排序了呢?
    • 所以,我们可以在这个市民表上创建一个city和name的联合索引
    • 在这个索引里面,我们依然可以用树搜索的方式定位到第一个满足city=‘杭州’的记录,并且额外 确保了,接下来按顺序取“下一条记录”的遍历过程中,只要city的值是杭州,name的值一定是有序的。
      1. 从索引(city,name)找到第一个满足city=‘杭州’条件的主键id;
      2. 到主键id索引取出整行,取name、city、age三个字段的值,作为结果集的一部分直接返 回;
      3. 从索引(city,name)取下一个记录主键id;
      4. 重复步骤2、3,直到查到第1000条记录,或者是不满足city=‘杭州’条件时循环结束。
      • 这个查询过程不需要临时表,也不需要排序。

最小代价取随机数

  • 场景例子:某个英语学习app,每次用户打开都会取随机3个单词。
  • 表结构,假设有10000条数据。

  • 内存临时表
    1. 用sql语句:select word from words order by rand() limit 3;

      • extra字段中显示不仅需要临时表还需要执行排序,即表示需要在临时表上排序。
      • 对于innodb表来说,会执行全字段排序来减少磁盘访问,优先选择,对于内存表来说,仅仅是简单的根据数据行的位置没直接访问内存得到数据,用于排序的行越少越好,所以mysql会选择rowid排序。
      • 以上这条语句这姓流程如下:
        1. 创建一个临时表,使用的是memory引擎,第一个字段是double,暂记为字段R,第二个字段是varchar,记为字段W,并且此表无索引。
        2. 从words表中,按主键顺序取出所有的word值,对于每一个word值,调用rand()函数生成一个大于0或小于1的随机小数,并且把随机小数和word分别存入临时表字段R和W中,到此扫描行10000.
        3. 在临时表的10000行数据上按照字段R排序
        4. 初始化sort_buffer(用来排序的内存空间),有两个字段,一个是double类型,一个是整型。
        5. 从内存表逐行的取出R值和位置信息,分别存入sort_buffer这两个字段里,这个过程要对临时表做全表扫描,此时扫描10000
        6. 在sortbuffer中根据R值进行排序,这个过程不涉及表的操作,不增加扫描行
        7. 排序完成后,取前三个位置信息,依次到内存临时表取word值,返回给客户端,访问了3行数据,总扫描行20003
    • 每个引擎唯一标识数据行的信息:rowid,6字节。对于有主键地表来说就是主键id,对于没主键的表就是系统生成的。
  • 磁盘临时表
    • tmp_table_size限制了内存临时表的大小,默认16M,如果临时表大小超过了这个数值,就会转化成磁盘临时表。
  • 对于select word from words order by rand() limit 3语句,其实没有用到临时文件,而是使用优先队列排序算法,只取前3,后面的顺序并不管,节省了很多计算量。
    • 即使如此也需要扫描20003行数据,有什么办法可以随机排序呢?

      • 取得整个表的行数记为C,扫描了C行
      • 取Y=floor(c*rand()),floor表示取整
      • 再用limit Y,1
    • 整个语句扫描了C+Y+1行,代价要小于order by rand()

原文地址:https://www.cnblogs.com/jimmyhe/p/11085326.html

时间: 2024-10-12 04:53:41

orderby工作原理 + 最小代价取随机数的相关文章

How Javascript works (Javascript工作原理) (十四) 解析,语法抽象树及最小化解析时间的 5 条小技巧

个人总结:读完这篇文章需要15分钟,文章介绍了抽象语法树与js引擎解析这些语法树的过程,提到了懒解析--即转换为AST的过程中不直接进入函数体解析,当这个函数体需要执行的时候才进行相应转换.(因为有的函数体只是声明了,并没有实际被调用) 解析,语法抽象树及最小化解析时间的 5 条小技巧 这是 JavaScript 工作原理的第十四章. 概述 我们都知道运行一大段 JavaScript 代码性能会变得很糟糕.代码不仅仅需要在网络中传输而且还需要解析,编译为字节码,最后运行.之前的文章讨论了诸如 J

SQL索引工作原理

SQL 当一个新表被创建之时,系统将在磁盘中分配一段以8K为单位的连续空间,当字段的值从内存写入磁盘时,就在这一既定空间随机保存,当一个8K用完的时候, SQLS指针会自动分配一个8K的空间.这里,每个8K空间被称为一个数据页(Page),又名页面或数据页面,并分配从0-7的页号,每个文件的第0页记录引导信息,叫文件头(File header):每8个数据页(64K)的组合形成扩展区(Extent),称为扩展.全部数据页的组合形成堆(Heap). SQLS 规定行不能跨越数据页,所以,每行记录的

Nginx 工作原理和优化、漏洞

1.  Nginx的模块与工作原理 Nginx由内核和模块组成,其中,内核的设计非常微小和简洁,完成的工作也非常简单,仅仅通过查找配置文件将客户端请求映射到一个location block(location是Nginx配置中的一个指令,用于URL匹配),而在这个location中所配置的每个指令将会启动不同的模块去完成相应的工作. Nginx的模块从结构上分为核心模块.基础模块和第三方模块: 核心模块:HTTP模块.EVENT模块和MAIL模块 基础模块:HTTP Access模块.HTTP F

Nginx工作原理和优化、漏洞(转)

查看安装了哪些模块命令: [[email protected] xcache]# nginx/sbin/nginx -Vnginx version: nginx/1.2.3built by gcc 4.4.7 20120313 (Red Hat 4.4.7-3) (GCC) TLS SNI support enabledconfigure arguments: --prefix=/opt/xcache/nginx --with-ipv6 --with-http_ssl_module --with

java gc的工作原理、如何优化GC的性能、如何和GC进行有效的交互

java gc的工作原理.如何优化GC的性能.如何和GC进行有效的交互 一个优秀的Java 程序员必须了解GC 的工作原理.如何优化GC的性能.如何和GC进行有效的交互,因为有一些应用程序对性能要求较高,例如嵌入式系统.实时系统等.只有全面提升内存的管理效 率,才能提高整个应用程序的性能. 本篇文章首先简单介绍GC的工作原理,然后再对GC的几个关键问题进行深入探讨,最后提出一些Java程序设计建议,从GC角度提高Java程序的性能. GC的基本原理     Java 的内存管理实际上就是对象的管

JPG的工作原理

转载请注明出处:(http://www.jianshu.com/p/934269064bfb) 本篇文章翻译自谷歌出的优化视频里面的光头佬(Colt McAnlis),原文地址需翻墙, 以下正文: JPG格式是1992年出现的最先进的图片压缩技术之一.此后,它就成为互联网图片的主力.这当然和JPG背后的技术有关,JPG的工作原理异常复杂,它需要深入理解人眼是如何调整对色彩和图像边缘的感知. 在研究这些东西前(你也一样,如果你在阅读此文),我想把JPG的编码原理分解成几部分,这样我们就可以更好的理

路由器开发(二)—— 路由器工作原理

当信息需要在两个网络之间传输时,常用路由器这种互连设备来负责数据的传输.路由器的主要工作是:路径的决定和数据包的转发(从路由器一个接口输入,然后选择合适接口输出):维护路由表. 路由器工作的方式非常简洁明了,从接收报文中抽取目的地址,并确定地址中的网络号,查找路由选择表以获得与目标网络相匹配的表项.在路由选择表中的匹配表项中包括下一站.目的地.输出接口和其它与路由有关的参数.报文被封装在适合输出接口的帧中,并由输出接口输出. 下面具体分析路由器两种工作的工作原理. 一.路由的概念  路由是将对象

nginx 工作原理总结

1.  Nginx的模块与工作原理 Nginx由内核和模块组成,其中,内核的设计非常微小和简洁,完成的工作也非常简单,仅仅通过查找配置文件将客户端请求映射到一个location block(location是Nginx配置中的一个指令,用于URL匹配),而在这个location中所配置的每个指令将会启动不同的模块去完成相应的工作. Nginx的模块从结构上分为核心模块.基础模块和第三方模块: 核心模块:HTTP模块.EVENT模块和MAIL模块 基础模块:HTTP Access模块.HTTP F

LVS集群之工作原理

  首先我们要了解LVS的工作机制: LVS里Director本身不响应请求,只是接受转发请求到后方,Realservers才是后台真正响应请求. LVS 工作原理基本类似DNAT,又不完全相像,它是一种四层交换,默认情况下通过用户请求的地址和端口来判断用户的请求,从而转发到后台真正提供服务的主机,而判断这种请求的是通过套接字来实现,所以四层就可以实现. 而且这个转发的过程对用户而言是透明的(简单的讲,就是用户访问DR的IP,而DR转发给RSS,而用户不知道这个过程) LVS的工作模式: 1.D