当前位置: 首页 > news >正文

6 mysql对order by group by join limit count的实现

排序 order by

索引是天然有序的,如果对索引排序,mysql只需要选择升序查询或降序查询即可,不需要专门的排序,效率非常高。
如果排序字段不是索引,mysql就需要将数据查出来再进行排序。

sort_buffer_size

在内存中排序,就肯定会消耗内存空间,mysql的sort_buffer_size就是设置这个空间的大小,sort_buffer_size并非全局共享,是每个线程独占,如果sort_buffer过大,而并发线程又多,就会内存不足。
当sort_buffer满了之后,就会写入临时文件。

调用方法
filesort()

估算排序总行数
estimate_rows_upper_bound()
总大小 = 总叶子节点数 * 页大小
行数 = 2 * (总大小 / 行大小)

总叶子节点数是stat值非实时数据
行大小是字段元数据设置的大小,非实际大小。
所以varcahr设置的值大小, 直接影响到排序的策略。

判断优先级队列是否可用
check_if_pq_applicable()
如果优先级队列可用,查出一条,就放到优先级队列里面排序,然后再查,不用全部查出来,然后排序,效率比较高。

如果不可用,那就用传统的排序方式,通常都是走的这个方式。
查出数据,存放到sort_buffer,直到查询结束或sort_buffer满了之后,save_index()对数据进行排序。
find_all_keys

参数:max_sort_length 默认1024

这个参数是限制每个记录最大排序值,超过这个记录,mysql就只取id和排序字段放到sort_buffer里面,排好序后,再回表查询。
没超过时,就整行放进去排序,排好序就返回,不用回表了。

varchar会按最大计算,并不是按实际大小计算
因为varchar的实际长度是存放在行记录中的,mysql不可能提前查询出行数据,然后在计算实际长度;
而是通过字段的元数据,直接计算大小。

group by

内存分组 ha_heap

通过HashMap数据结构分组
max_heap_table_size 内存表最大值 默认16M
tmp_table_size 临时表大小 默认 16M
取两个变量的最小值为内存临时的大小。

table->file ha_heap
ha_write_row()

写入行,写入到了ha_heap中,并非innodb
write_row ha_heap::write_row()
ha_heap 是一个内存临时表,一个hashMap结构

临时文件分组, innodb临时表

创建一个临时表,分组字段作为主键,主键具有唯一性,当插入重复主键时,就会发送数据合并。
最终临时表的数据就是一份不重复的数据。

mysql会优先使用内存表分组
如果数据量太大,超出最大值,就会在磁盘的临时文件进行分组。
这时候就会利用innodb进行临时表管理。
将内存中已插入的转移到磁盘,后续的直接插入到磁盘临时表。

create_innodb_tmp_table()
create_table()

排序

group by 默认会进行排序
分组后的数据,会经过排序逻辑

统计

在做分组插入临时表时,
会先查询一下临时表是否已有数据,
如果有,就取出来,和要插入的数据做个计算,然后更新(update_row)已有的数据。
如果没有,直接插入
相当于不断在做reduce操作

效率

内存临时表的效率要远高于磁盘临时表
如果某个sql group by 的时间很长, 就要考虑参与计算的数据是否超过了默认的16M.
如果超过了,要么优化sql,减少数据量, 要么增加内存表的大小。

join

join过程

join_buffer
默认256kb,每个线程独占

join过程会面临先查哪个表,再查哪个表,这个是mysql优化器决定的,没有固定。
可以通过执行计划explain看到先查询的哪个表。
通常mysql会选择先查询记录较少的表,这样可以先把它放到join buffer里面,太大可能就放不进去。

nested-loop

通过两层嵌套循环实现
不会使用到join_buffer,通过执行计划explain,如果没有using join buffer 那么就肯定是用的这个。
过程:
外层循环称之为驱动表,内层循环为被驱动表
当驱动表查询出一条后,被驱动表针对外层结果搜索一次。
被驱动表进行查询的条件也就是on的部分,如果所关联的内层表字段不是索引,那么每一次驱动表查询,就会进行一次被驱动表的全表扫描。若驱动表查询数量为n,那么被驱动表被全表扫描的次数为n,所以关联查询一定要建立索引。
如果两表没有on绑定,那么会产生卡迪尔乘积 n*m条结果
如果有外键关联,那么结果数量取决于驱动表。

block nested-loop

针对nested-loop的优化操作, 执行计划有显示using join buffer
上面nested-loop是逐个循环遍历的(for(i=0;i<n;i++),在无索引的情况下,有n次循环就有n次全表扫描,代价太大。
block nested-loop就进行了批量遍历(for(i=0;i<n;i+=m),驱动表每次循环,查询出m条数据,载入到内存(join buffer),然后进行一次被驱动表全表扫描,再进行数据关联。 那么被驱动表的总全表扫描次数为n/m次(m取决于BNL的内存设定大小,内存越大m就越大,次数也越少)。

子查询

简单子查询会被优化成join查询
select * from t1 where id in (select sid from t2 where type=1)
select * from t1 left join t2 on t2.sid=t1.id where t2.type=1

from 部分的子查询,需要建立临时表
内存临时表
磁盘临时表

limit

对innodb来说,还是全表扫描,但是在mysql层面会有一个计数器,用来计算limit,只返回满足次序的记录。
sql_class.cc :: send_data()
send_records, select_limit_cnt

mysql这么处理,会造成无论是取第几页,前面的记录都会被遍历,这就产生了深分页问题。
当页数非常大的时候,mysql就会很慢,mysql层面是无法处理的,只能在其他方面解决。
1 业务上避免深分页,只提供前100页数据,隐藏跳页操作,如电商商品的浏览
2 如果有递增的id或其他索引,可以利用递增索引,提前计算好目标页的索引,然后通过in查询
3 用一个较小的表记录次序,再使用2的方法。

count

count() = count(1) ≈ count(id) < count(字段)
count(
) count(1) 默认是会利用主键索引扫描所有记录,计算总数。
但如果该表有辅助索引,mysql将会扫描辅助索引计算总数。
由于辅助索引的叶子节点只有id,而且辅助索引的叶子数量必定和聚集索引的一样,所以扫描辅助索引的速度要更快。

count(id) 会遍历全表,取出id, 然后判断id是否为空,再count++
由于id是聚集索引,比较快
count(字段) 也是遍历全表,但字段是非索引,需要额外取出记录,消耗更多。

http://www.dtcms.com/a/545932.html

相关文章:

  • Rust:Trait 抽象与 unsafe 底层掌控力的深度实践
  • 安全员C证(全国版)模拟考试练习题答案解析
  • (huawei)最小栈
  • 四川建设网官网住房和城乡厅网站文字很少怎么做优化
  • apache 配置网站茶叶网站源码php
  • 南昌自主建站模板建设标准网站
  • PyTorch 基础详解:tensor.item() 方法
  • 外贸网站 php基于云平台网站群建设
  • 产品设计网站官网制作人是做什么的
  • 【每天一个知识点】“社区检测”(Community Detection)
  • 建站之星 discuz广州开发区东区
  • 04-函数与模块-练习
  • 网站seo教材中国建设银行校园招聘网站
  • 原型样网站做网站代理
  • 临海响应式网站设计wordpress移动应用
  • Rust生命周期与泛型的组合使用深度解析
  • 张家港网站建设服务全网营销公司排名前十
  • 网站建设廴金手指花总壹陆陈村九江网站建设
  • 合并两个排序的链表
  • 网站维护和建设工作范围昆明网站建设电话
  • 手机网站开发学习视频给wordpress权限
  • 广州五屏网站建设wordpress 上传到域名
  • 如何在uni-app中禁用iOS橡皮筋效果?
  • 安徽合肥做网站的公司免费企业网站程序
  • LangChain4j学习9:结构化输出
  • 公司介绍网站源码百度aipage智能建站系统
  • PyTorch快速搭建CV模型实战
  • 索引的数据结构
  • 仓颉语言中的协程调度机制深度解析
  • 对网站设计的建议长沙工商注册网上核名