All you need to know about sorting in Postgres
按:之前看pg的执行计划,多次看到不同的排序方式,但不知何意。偶遇此篇讲解pg执行计划三种排序方式,备忘一下。
Sorting
Sorting is one of the most fundamental operations in database systems and understanding how they work inside is crucial in optimizing them.
This blog post mainly focuses on postgres(a kick ass relational database), but it can be translated to other databases in algorithmic terms.
- Setting the stage
- Disk merge sort - When data does not fit in memory
- Quick sort - Completely in memory
- Heap sort - Sorting with a limit
- Using indexes for sorting
- Optimization
Setting the stage
Below are the details of my setup.
Database version : 9.5 (Latest version at the time of this writing).
Operating system : Cent OS 7
Table structure is as below

Total Rows : 1 Million Rows.All values are unique via random data population.
Terminologies : If you are not familiar with any of the technical terms mentioned here, refer Postgres Guide.
Disk merge sort - When data does not fit in memory
If we consider a query like below,
Select * from users order by userid;
Running explain analyze, gives us the plan as follows (it actually runs the query).
performance_test=# explain analyze select * from users order by userid; Sort (cost=352825.84..355325.84 rows=1000000 width=219) (actual time=1601.031..2033.621 rows=1000000 loops=1)
Sort Key: userid
Sort Method: external merge Disk: 225872kB
-> Seq Scan on users (cost=0.00..41250.00 rows=1000000 width=219) (actual time=0.477..225.933 rows=1000000 loops=1)
Planning time: 0.298 ms
Execution time: 2170.056 ms
(6 rows)
Of course, in a real life situation nobody would use select * and select without a limit, but for demo purposes this should be fine.
External merge is much like Merge sort. It is used when the data does not fit in memory. This is probably the slowest sort method since there is lot of disk thrashing involved i.e it takes data from disk sorts it and then puts it back, it takes total table data/memory it can fit.
There are two different memory areas we can talk about mainly in postgres. shared_buffers is where all of the table data i.e the cached data from tables is stored. work_mem or worker memory is what is used by postgres for sorting, joins etc.,
So when we say that the data does not fit in memory, it means that work_mem is too low.
Quick sort - Completely in memory
If we bump up worker memory(say 300MB) and change the query slightly as follows.
Select userid from users order by userid;
We have a new query plan as below.
performance_test=# explain analyze select * from users order by userid; Sort (cost=140907.84..143407.84 rows=1000000 width=219) (actual time=719.208..879.849 rows=1000000 loops=1)
Sort Key: userid
Sort Method: quicksort Memory: 290202kB
-> Seq Scan on users (cost=0.00..41250.00 rows=1000000 width=219) (actual time=0.039..266.260 rows=1000000 loops=1)
Planning time: 0.079 ms
Execution time: 966.795 ms
(6 rows)
Postgres uses a well known sorting algorithm called Quick sort to accomplish in memory sorting. There are certain variations from a vanilla quick sort, you can lookup the source code to understand in much deeper detail.
This will definitely be faster than external merge, since all of the data is brought into memory and then the sorting is done in memory itself.
Heapsort - Sorting with a limit
Let’s look at a real life use case query.
Select userid from users order by userid limit 10;
Plan is changed as below.
performance_test=# explain analyze select userid from users order by userid limit 10; Limit (cost=62859.64..62859.67 rows=10 width=8) (actual time=483.900..483.901 rows=10 loops=1)
-> Sort (cost=62859.64..65359.64 rows=1000000 width=8) (actual time=483.898..483.899 rows=10 loops=1)
Sort Key: userid
Sort Method: top-N heapsort Memory: 25kB
-> Seq Scan on users (cost=0.00..41250.00 rows=1000000 width=8) (actual time=0.045..300.833 rows=1000000 loops=1)
Planning time: 0.078 ms
Execution time: 483.933 ms
(7 rows)
Underlying problem is the same, order all the rows by the column userid and select the top 10. The default sort order is ascending order in postgres.
You might wonder how does the plan change, it has to sort all of the data anyway to get the top 10/bottom 10 rows. The answer is yes, but what changes is the memory usage for sorting. Since we need only a limited set of rows, there is no need to sort all of them inside memory.
To achieve this, postgres maintains a heap with a bounded size. It consumes the input values in sequence. After it fills the heap up to the target number of tuples/rows it starts looking each new value to see if it’s larger than all current values/smaller than all current values, or fits within the heap.
If it’s larger than all current values (ascending sort in our case) it gets discarded, since we have enough values already. If it’s smaller than all current values or than some current values, it’s inserted at the appropriate point in the heap, everything gets shifted down by one, and it bumps the last entry off the heap.
The Algorithm used is heap sort which makes use of the heap data structure to sort things. This is also a classical well known algorithm.
Normally, unless you have very wide rows and you are selecting more rows that cannot fit it in memory, then heap sort is what will be used.
There won’t be much speed difference between a quicksort and a heap-sort, but what will be significant is the memory usage which can be seen clearly from both the query execution plans. When allocating memory is costly i.e when you have lesser free memory to allocate immediately, often happens when your machine is not scaling well to the memory needs, then there can be huge differences in speed.
Using indexes for sorting
All of the algorithms above involve a great deal of overhead that is common in all databases i.e the disk seek. No matter what algorithm is used, it has to first fetch all of the rows and then sort the data. Disk as we all know is probably the slowest and most time consuming component in a computing system.
We can avoid disk seek by using indexes. A B-Tree are commonly used to speed up sorting, where conditions, group by and a whole lot of other use cases.
Creating an index on the userid column is pretty straightforward as below.
create index on users(userid);
The table definition also lists that there is an index.

There is a great speed up in query execution time.
performance_test=# explain analyze select userid from users order by userid limit 10; Limit (cost=0.42..0.68 rows=10 width=8) (actual time=0.030..0.041 rows=10 loops=1)
-> Index Only Scan using users_userid_idx on users (cost=0.42..25980.42 rows=1000000 width=8) (actual time=0.029..0.034 rows=10 loops=1)
Heap Fetches: 0
Planning time: 0.096 ms
Execution time: 0.070 ms
(5 rows)
Since indexes are already ordered, it has to just lookup the values in the indexes which is much faster. After all a b-tree offers a lookup of O(logN) in asymptotic complexity time. A typical query that uses indexes will be in the order of milliseconds.
An index can be used in descending sort as well.
select userid from users order by userid desc limit 10;
performance_test=# explain analyze select userid from users order by userid desc limit 10; Limit (cost=0.42..0.68 rows=10 width=8) (actual time=0.085..0.092 rows=10 loops=1)
-> Index Only Scan Backward using users_userid_idx on users (cost=0.42..25980.42 rows=1000000 width=8) (actual time=0.084..0.089 rows=10 loops=1)
Heap Fetches: 0
Planning time: 0.142 ms
Execution time: 0.121 ms
(5 rows)
Indexes can also be cached entirely in memory, since they are smaller in size.
If we want to sort by multiple columns then we have to create an index appropriately.
create index on users(name desc,userid asc);
The above index called as a composite index, can be used for a query such as below,
Select userid,name from users order by name desc,userid asc limit 10;
performance_test=# explain analyze Select userid,name from users order by name desc,userid asc limit 10; Limit (cost=0.55..1.12 rows=10 width=59) (actual time=0.025..0.059 rows=10 loops=1)
-> Index Only Scan using users_name_userid_idx on users (cost=0.55..57240.55 rows=1000000 width=59) (actual time=0.024..0.030 rows=10 loops=1)
Heap Fetches: 0
Planning time: 0.114 ms
Execution time: 0.088 ms
(5 rows)
Optimization
I have listed four methods in which postgres does sorting.
- External merge (Slowest, because of increased disk I/O)
- Quick sort (Faster than external merge, best suited for sorting large data sets)
- Top-N heapsort (Faster than quicksort, stops when the limit of data is gathered)
- Sorting using indexes (Fastest, just lookup, no sorting)
It is important to understand that these algorithms by themselves are not slow. Postgres just uses the best algorithm for the job.
Optimizing Order by
- First step is to use an index. Almost always this is the rule of thumb.
- Check if your query has a limit clause. It makes no sense to order and select all of the rows in the table.
- Bump up worker memory for sorting. This has to be done carefully since each order by, sorting and other operations can use this individually, i.e if it is configured to be 128MB, then if there are 10 queries running, the total memory usage would 10 * 128 MB.
- In-memory tables. As said earlier, the slowest part of a database operation is disk access. An in-memory table can be used when it is not feasible to create indexes on all columns. This is usually done when a user does sorting on the application UI. Postgres does not have anything natively that supports/keeps the entire table in memory but there is a way to do this via IMCS. But you do not need to do this, since indexes are optimized to fit in memory and there is no need to cache/store the whole table in memory.
注:
- External merge (Slowest, because of increased disk I/O)
- Quick sort (Faster than external merge, best suited for sorting large data sets)
- Top-N heapsort (Faster than quicksort, stops when the limit of data is gathered)
- Sorting using indexes (Fastest, just lookup, no sorting)
参考:
https://madusudanan.com/blog/all-you-need-to-know-about-sorting-in-postgres/
All you need to know about sorting in Postgres的更多相关文章
- HDU Cow Sorting (树状数组)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2838 Cow Sorting Problem Description Sherlock's N (1 ...
- 1306. Sorting Algorithm 2016 12 30
1306. Sorting Algorithm Constraints Time Limit: 1 secs, Memory Limit: 32 MB Description One of the f ...
- 算法:POJ1007 DNA sorting
这题比较简单,重点应该在如何减少循环次数. package practice; import java.io.BufferedInputStream; import java.util.Map; im ...
- U3D sorting layer, sort order, order in layer, layer深入辨析
1,layer是对游戏中所有物体的分类别划分,如UIlayer, waterlayer, 3DModelLayer, smallAssetsLayer, effectLayer等.将不同类的物体划分到 ...
- WebGrid with filtering, paging and sorting 【转】
WebGrid with filtering, paging and sorting by Jose M. Aguilar on April 24, 2012 in Web Development A ...
- ASP.NET MVC WebGrid – Performing true AJAX pagination and sorting 【转】
ASP.NET MVC WebGrid – Performing true AJAX pagination and sorting FEBRUARY 27, 2012 14 COMMENTS WebG ...
- poj 1007:DNA Sorting(水题,字符串逆序数排序)
DNA Sorting Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 80832 Accepted: 32533 Des ...
- ural 1252. Sorting the Tombstones
1252. Sorting the Tombstones Time limit: 1.0 secondMemory limit: 64 MB There is time to throw stones ...
- CF#335 Sorting Railway Cars
Sorting Railway Cars time limit per test 2 seconds memory limit per test 256 megabytes input standar ...
随机推荐
- (C#)原型模式—深复制与浅复制
1.原型模式 用原型实例指定创建对象的实例,并且通过拷贝这些原型创建新的对象. *原型模式隐藏了创建对象的细节,提高了性能. *浅复制:被复制对象的所有变量都含有与原来对象相同的值,而且所有对其他对象 ...
- TW实习日记:第19天
今天一早上改完信息门户的代码之后,发现接口又出了问题,查了半天都不知道,原来又是网端的问题...真是心累啊,调整了一些细节样式,以及终于把企业微信的消息推送功能做完了.关键就在于有个表存放微信id的字 ...
- IntelliJ IDEA 2018 for MAC安装及破解
---------------------说在前面-------------------------- IntelliJ IDEA 2018 版本为2018.1.4 教程按照下载安装sdk.破解两部分 ...
- TCP系列40—拥塞控制—3、慢启动和拥塞避免概述
本篇中先介绍一下慢启动和拥塞避免的大概过程,下一篇中将会给出多个linux下reno拥塞控制算法的wireshark示例,并详细解释慢启动和拥塞避免的过程. 一.慢启动(slow start) 一个T ...
- TCP系列34—窗口管理&流控—8、缓存自动调整
一.概述 我们之前介绍过一种具有大的带宽时延乘积(band-delay product.BDP)的网络,这种网络称为长肥网络(LongFatNetwork,即LFN).我们想象一种简单的场景,假设发送 ...
- Spring Boot(七)扩展分析
前面的章节在分析SpringBoot启动过程中,我们发现SpringBoot使用Spring框架提供的SpringFactoriesLoader这个类,实现检索META-INF/spring.fact ...
- ejabberd学习2
1.ejabberd监听多个端口 每个网络连接进来,ejabberd都会使用一个进程来负责这个连接的数据处理.原理跟Joe Armstrong的<Erlang程序设计>中的并行服务器一样, ...
- 【alpha】Scrum站立会议第2次....10.17
小组名称:nice! 小组成员:李权 于淼 杨柳 刘芳芳 项目内容:约跑app 1.任务进度 成员 已完成 今日完成 李权 数据库设计 消息发送代码实现 于淼 注册.登录界面,以及登录界面后台代码.发 ...
- 【week2】 词频统计效能分析
效能统计工具:Jprofiler License Key:L-Larry_Lau@163.com#23874-hrwpdp1sh1wrn#0620 该性能分析工具对服务器进行监听,图一是线程变化图,当 ...
- 【PHP】- PHPStorm+XDebug进行调试图文教程
转载:https://www.cnblogs.com/LWMLWM/p/8251905.html 这篇文章主要为大家详细介绍了PHPStorm+XDebug进行调试图文教程,内容很丰富,具有一定的 ...