导读
大家可以想看看这条SQL语句思考一下:
SELECT * FROM user WHERE user_name LIKE "%am%" AND age >= 18 AND age <= 24 AND sex = 0 ORDER BY age, user_name LIMIT 0, 50
复制代码
这条SQL使用了快速排序对 age,username 排序,有没有更好的办法,提升排序的性能?
你可能已经发现,这条SQL其实只需要取前50个排好序的用户,但是,上面的执行过程确对表中的1000条记录都进行了排序,如果我只对50记录进行排序,保证这50条记录就是排好序的前50条,从排序算法来看,是不是代价就小很多了呢?那么, 我们怎么做到只对50记录进行排序,同时,这50条记录又刚好是排好序的前50条呢?
学过排序算法的同学可能已经猜到了, 堆排序! 是的,MySQL5.6开始引入了一个新的排序算法,叫做 优先级队列排序 ,该队列使用的就是堆排序,保证只对有限数量n条的记录排序,同时,排序后的结果就是前n条记录。
关于堆排序,计算机相关专业的同学应该比较熟悉,在此,我就不详细讲述了。
PTMalloc
这里,我主要结合《单表数据规模达到多大时进行分表最佳?》这一章中《内存分配》这部分,来详细看一下上面排序过程中,MySQL申请内存的过程:MySQL默认使用ptmalloc内存分配器给进程分配内存。
数据结构
因此,我们先来看一下ptmalloc管理内存块所使用的数据结构:
分配区
ptmalloc使用一个主分配区和多个动态分配区来管理内存,主分配区与非 主分配区用环形链表进行管理。如上图中的 Main Arena 表示一个主分配区, Dynamic Arena 表示3个动态分配区,它们之间首尾相连,形成环形状。ptmalloc 根据系统对分配区 的争用情况动态增加非主分配区的数量,分配区的数量一旦增加,就不会再减少了。
在《单表数据规模达到多大时进行分表最佳?》中《内存分配》部分,我讲过内存分配器在自身没有可分配内存时,会向Linux系统申请内存,可以申请堆内存,也可以申请文件映射区的内存,所以,对ptmalloc来说,主分配区可以申请堆和文件映射的内存,而动态分配区只能申请文件映射区的内存。
当某一线程需要调用malloc()分配内存空间时,该线程先查看线程私有变量中是否已经 存在一个分配区,如果存在,尝试对该分配区加锁,如果加锁成功,使用该分配区分配内存, 如果失败,该线程搜索循环链表试图获得一个没有加锁的分配区。如果所有的分配区都已经 加锁,那么 malloc()会开辟一个新的分配区,把该分配区加入到全局分配区循环链表并加锁, 然后使用该分配区进行分配内存操作。在释放操作中,线程同样试图获得待释放内存块所在 分配区的锁,如果该分配区正在被别的线程使用,则需要等待直到其他线程释放该分配区的 互斥锁之后才可以进行释放操作。
chunk
ptmalloc 会统一管理分配区空闲的内存块,当用户进行下一次分配请