如何优化多个ORDER BY?

5 postgresql optimization order-by

我在一个大约有 300,000 行且每列都有 B 树的表上遇到了缓慢的性能。

这适用于动态分页页面,其中查询是按需构建的,并且应用程序按照查询的指定顺序缓存主键。

对于此查询

explain analyze 
 SELECT supplier_management.buyer_purchase_order_id 
     FROM supplier_management 
     ORDER BY item_description DESC,
      item_number DESC,
      order_type ASC,
      possession_date DESC,
      shipment_type DESC,
      store_type DESC
Run Code Online (Sandbox Code Playgroud)

我得到这些结果:

排序(成本=51026.98..51750.35行=289348宽度=72)(实际时间=8229.280..12349.596行=289348循环=1)

排序键:商品描述、商品编号、订单类型、拥有日期、发货类型、商店类型

排序方式:外部合并磁盘:24744kB

-> 供应商管理上的顺序扫描(成本=0.00..10876.48行=289348宽度=72)(实际时间=0.015..187.426行=289348循环=1)

总运行时间:12407.064 毫秒

如何提高多列排序的性能?或者我应该用 C++ 来做?

表结构

buyer_purchase_order_id bigint
supplier_number bigint
supplier_name   character varying
purchase_order_number   bigint
store_number    integer
item_number bigint
item_description    character varying
project_type    character varying
order_date  integer
requested_arrival_date  integer
department  character varying
store_type  character varying
shipment_type   character varying
order_type  character varying
quantity_ordered    integer
quantity_allocation integer
quantity_staged integer
quantity_shipped    integer
quantity_received   integer
in_stock_date   integer
in_stock_date_visible_on    integer
show_red    boolean
requested_arrival_date_plus_four_business_days  integer
supplier_status character varying
notes_comments  character varying
requested_arrival_date_color    character varying
grand_opening_date  integer
possession_date integer
consolidator_name   character varying
real_requested_arrival_date_plus_four_business_days integer
Run Code Online (Sandbox Code Playgroud)

character varying超过索引的长度限制;然而,存在一些重复。将实际值放入另一个表中,进行规范化并连接到相关的整数列会更有效吗?

Cra*_*ger 4

您可以做一些事情:

尽可能使用按值或简单字段作为键的enums或查找,而不是 varchar 排序键。我会使用 an 因为您可以轻松控制排序顺序。integer"char"enum

枚举唯一严重的缺点是您当前无法从枚举类型中删除值。您可以添加它们(包括将它们插入排序顺序的中间),但不能删除它们。如果这是一个问题,您将需要使用查找表,或者仅声明"char"具有单字符代码的字段。

另外,如果您不需要正确的语言排序规则,请指定COLLATE "C"字符字段,例如

CREATE INDEX itemdesc_c ON supplier_management (item_description ASC COLLATE "C");
Run Code Online (Sandbox Code Playgroud)

进而:

ORDER BY ...
    itemdesc COLLATE "C",
    ...
Run Code Online (Sandbox Code Playgroud)

需要注意的重要事项:

  • WHEREPg 可以组合谓词(子句等)的索引,但不能排序。您不能使用位图索引扫描进行排序。因此它最多可以使用一个候选索引,然后它必须对每组内的行进行排序。

  • 低选择性索引是浪费时间。如果值分布不广,则不要对该列建立索引。

  • Pg 正在执行磁盘排序。在问题上投入更多的记忆 - 尝试SET work_mem = '20MB'开始。但请参阅下面我的评论,重新与 high 进行斗争max_connections。使用连接池。

  • 使用连接池。

  • 索引是有代价的——它们会减慢插入/更新/删除速度并增加真空工作。因此,如果索引没有被大量使用,请将其删除。

  • pg_catalog.pg_stat_user_indexes将帮助您判断使用了哪些索引。

  • pg_stat_statements(在 contrib 中)和pg_stat_plans(后者是外部模块)对于捕获有关查询模式、慢速查询等的数据非常有用。

  • 学会爱上这个auto_explain模块。


另外,如果您总是进行这种排序,创建一个复合索引来匹配它也会有所帮助。

CREATE INDEX bigindex ON supplier_management (
      item_description DESC,
      item_number DESC,
      order_type ASC,
      possession_date DESC,
      shipment_type DESC,
      store_type DESC
);
Run Code Online (Sandbox Code Playgroud)

...但请注意,它仅对这种特定的排序有用,并且它将是一个很大的索引,因此只有当您经常这样做时才值得拥有。事实上,您也可以添加supplier_management.buyer_purchase_order_id,这样它就可以进行仅索引扫描:

CREATE INDEX bigindex ON supplier_management (
      item_description DESC,
      item_number DESC,
      order_type ASC,
      possession_date DESC,
      shipment_type DESC,
      store_type DESC,
      buyer_purchase_order_id
);
Run Code Online (Sandbox Code Playgroud)