PostgreSQL 索引虚拟列 - 表达式索引 - JOIN提速

本文涉及的产品
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介:

标签

PostgreSQL , join , 表达式索引 , 虚拟列索引 , 静态数据 , immutable函数


背景

CASE: 使用虚拟索引,响应时间从2.3秒下降到0.3毫秒

业务系统在设计时,为了减少数据冗余,提升可读性,通常需要将不同的数据放到不同的表。

在查询时,通过多表JOIN来补齐需要查询或在过滤的内容。

比如这样的例子:

有两张表,分别有1千万和100万数据,当用户查询时,需要补齐那100万表中的某个字段进行过滤。

create table a (id int, bid int, c1 int, c2 int, c3 int);  
  
CREATE TABLE b (id int primary key, path text);  
  
insert into a select id, random()*1000000 , random()*10000000, random()*10000000 , random()*10000000 from generate_series(1,10000000) t(id);  
  
insert into b select id, md5(random()::text) from generate_series(1,1000000) t(id);  
  
create index idx_b_1 on b(path text_pattern_ops);  
  
-- 查询  
select a.* from a left join b on (a.bid=b.id and b.path like 'abc%');  

那么它的性能如何呢?

postgres=# explain select a.* from a left join b on (a.bid=b.id) where b.path like 'abcde%';  
                                     QUERY PLAN                                       
------------------------------------------------------------------------------------  
 Hash Join  (cost=9.70..289954.61 rows=1000 width=20)  
   Hash Cond: (a.bid = b.id)  
   ->  Seq Scan on a  (cost=0.00..163695.00 rows=10000000 width=20)  
   ->  Hash  (cost=8.45..8.45 rows=100 width=4)  
         ->  Index Scan using idx_b_1 on b  (cost=0.42..8.45 rows=100 width=4)  
               Index Cond: ((path ~>=~ 'abcde'::text) AND (path ~<~ 'abcdf'::text))  
               Filter: (path ~~ 'abcde%'::text)  
(7 rows)  
  
Time: 0.777 ms  
postgres=# select a.* from a left join b on (a.bid=b.id) where b.path like 'abcde%';  
   id    |  bid   |   c1    |   c2    |   c3      
---------+--------+---------+---------+---------  
 2423577 | 633740 |  846719 | 1720744 |  416608  
 2433286 | 633740 | 9797626 | 6737349 | 5669893  
 3851817 | 633740 | 8764393 | 3779499 | 2830950  
 4889541 | 633740 | 3892055 | 9470525 |  611262  
 5004634 | 633740 | 5420943 | 2448245 | 5719976  
 5372019 | 633740 | 5402891 | 3441462 | 8194368  
 6051251 | 633740 | 8691218 | 7184625 | 5940346  
 6344344 | 633740 | 5869018 | 9352883 |  636112  
 9751456 | 633740 | 3797867 | 1934900 | 2511398  
(9 rows)  
  
Time: 2348.506 ms (00:02.349)  

条件越多,性能会越差。

这样的查询,并发一高,性能影响会比较大。

当b表是静态的时(没有DML),可以用虚拟列索引来实现优化。

表达式索引 - 虚拟列索引

假设B表不会发生DML,是一个静态表。

1、创建一个获取path的函数

create or replace function get_path(int) returns text as $$  
  select path from b where id=$1;  
$$ language sql strict immutable;  

这个函数用于从B表获取path,假设B表静态(不会有增删改),那么这个函数就是immutable的,无论什么时候输入一个ID,返回的都是同一个path。

2、在a表直接创建表达式索引(虚拟列索引)

create index idx_a_1 on a(get_path(bid) text_pattern_ops);  

3、修改SQL语句如下

select * from a where get_path(bid) like 'abc%';  

4、响应时间从2.3秒下降到0.3毫秒。

postgres=# select * from a where get_path(bid) like 'abcde%';  
   id    |  bid   |   c1    |   c2    |   c3      
---------+--------+---------+---------+---------  
 2423577 | 633740 |  846719 | 1720744 |  416608  
 2433286 | 633740 | 9797626 | 6737349 | 5669893  
 3851817 | 633740 | 8764393 | 3779499 | 2830950  
 4889541 | 633740 | 3892055 | 9470525 |  611262  
 5004634 | 633740 | 5420943 | 2448245 | 5719976  
 5372019 | 633740 | 5402891 | 3441462 | 8194368  
 6051251 | 633740 | 8691218 | 7184625 | 5940346  
 6344344 | 633740 | 5869018 | 9352883 |  636112  
 9751456 | 633740 | 3797867 | 1934900 | 2511398  
(9 rows)  
  
postgres=# select * from b where path like 'abcde%';  
   id   |               path                 
--------+----------------------------------  
 633740 | abcde980c8568a9a6a140885d92fcebe  
(1 row)  
  
  
postgres=# explain (analyze,verbose,timing,costs,buffers) select * from a where get_path(bid) like 'abcde%';  
                                                         QUERY PLAN                                                           
----------------------------------------------------------------------------------------------------------------------------  
 Index Scan using idx_a_1 on public.a  (cost=0.56..158697.56 rows=50000 width=20) (actual time=0.151..0.276 rows=9 loops=1)  
   Output: id, bid, c1, c2, c3  
   Index Cond: ((get_path(a.bid) ~>=~ 'abcde'::text) AND (get_path(a.bid) ~<~ 'abcdf'::text))  
   Filter: (get_path(a.bid) ~~ 'abcde%'::text)  
   Buffers: shared hit=50  
 Planning time: 0.092 ms  
 Execution time: 0.300 ms  
(7 rows)  

即使没有虚拟索引,也可以PostgreSQL的使用并行计算

32个并行,耗时454毫秒,依旧不如使用虚拟列索引的效果。

postgres=# set parallel_tuple_cost =0;  
SET  
postgres=# set parallel_setup_cost =0;  
SET  
postgres=# set max_parallel_workers_per_gather =32;  
SET  
postgres=# alter table a set (parallel_workers =32);  
ALTER TABLE  
  
postgres=# explain select a.* from a left join b on (a.bid=b.id) where b.path like 'abcde%';  
                                   QUERY PLAN                                     
--------------------------------------------------------------------------------  
 Gather  (cost=20835.25..91600.56 rows=1000 width=20)  
   Workers Planned: 32  
   ->  Hash Join  (cost=20835.25..91600.56 rows=31 width=20)  
         Hash Cond: (a.bid = b.id)  
         ->  Parallel Seq Scan on a  (cost=0.00..66820.00 rows=312500 width=20)  
         ->  Hash  (cost=20834.00..20834.00 rows=100 width=4)  
               ->  Seq Scan on b  (cost=0.00..20834.00 rows=100 width=4)  
                     Filter: (path ~~ 'abcde%'::text)  
(8 rows)  
  
Time: 0.685 ms  
  
postgres=# select a.* from a left join b on (a.bid=b.id) where b.path like 'abcde%';  
   id    |  bid   |   c1    |   c2    |   c3      
---------+--------+---------+---------+---------  
 5004634 | 633740 | 5420943 | 2448245 | 5719976  
 9751456 | 633740 | 3797867 | 1934900 | 2511398  
 3851817 | 633740 | 8764393 | 3779499 | 2830950  
 4889541 | 633740 | 3892055 | 9470525 |  611262  
 6344344 | 633740 | 5869018 | 9352883 |  636112  
 2433286 | 633740 | 9797626 | 6737349 | 5669893  
 2423577 | 633740 |  846719 | 1720744 |  416608  
 6051251 | 633740 | 8691218 | 7184625 | 5940346  
 5372019 | 633740 | 5402891 | 3441462 | 8194368  
(9 rows)  
  
Time: 454.405 ms  
相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
5月前
|
监控 关系型数据库 数据库
PostgreSQL的索引优化策略?
【8月更文挑战第26天】PostgreSQL的索引优化策略?
127 1
|
7月前
|
DataWorks 关系型数据库 MySQL
DataWorks产品使用合集之用脚本的方式同步数据到MySQL,怎么指定列作为目标表为唯一行
DataWorks作为一站式的数据开发与治理平台,提供了从数据采集、清洗、开发、调度、服务化、质量监控到安全管理的全套解决方案,帮助企业构建高效、规范、安全的大数据处理体系。以下是对DataWorks产品使用合集的概述,涵盖数据处理的各个环节。
|
8月前
|
关系型数据库 MySQL
Mysql语句_查询数据百分比、人员年龄、数据排序、添加查询时的列属性、合并查询结果
Mysql语句_查询数据百分比、人员年龄、数据排序、添加查询时的列属性、合并查询结果
58 0
|
8月前
|
缓存 关系型数据库 MySQL
MySQL查询优化:提速查询效率的13大秘籍(合理使用索引合并、优化配置参数、使用分区优化性能、避免不必要的排序和group by操作)(下)
MySQL查询优化:提速查询效率的13大秘籍(合理使用索引合并、优化配置参数、使用分区优化性能、避免不必要的排序和group by操作)(下)
357 0
|
8月前
|
缓存 关系型数据库 MySQL
MySQL 查询优化:提速查询效率的13大秘籍(索引设计、查询优化、缓存策略、子查询优化以及定期表分析和优化)(中)
MySQL 查询优化:提速查询效率的13大秘籍(索引设计、查询优化、缓存策略、子查询优化以及定期表分析和优化)(中)
1293 0
|
5月前
|
SQL 关系型数据库 MySQL
SQL Server、MySQL、PostgreSQL:主流数据库SQL语法异同比较——深入探讨数据类型、分页查询、表创建与数据插入、函数和索引等关键语法差异,为跨数据库开发提供实用指导
【8月更文挑战第31天】SQL Server、MySQL和PostgreSQL是当今最流行的关系型数据库管理系统,均使用SQL作为查询语言,但在语法和功能实现上存在差异。本文将比较它们在数据类型、分页查询、创建和插入数据以及函数和索引等方面的异同,帮助开发者更好地理解和使用这些数据库。尽管它们共用SQL语言,但每个系统都有独特的语法规则,了解这些差异有助于提升开发效率和项目成功率。
588 0
|
5月前
|
关系型数据库 数据库 PostgreSQL
PostgreSQL索引维护看完这篇就够了
PostgreSQL索引维护看完这篇就够了
380 0
|
6月前
|
关系型数据库 MySQL 数据库
实时计算 Flink版产品使用问题之如何排除某个列进行同步MySQL数据
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
6月前
|
数据库 索引 关系型数据库
MySQL设计规约问题之为什么在插入数据时,必须显示指定插入的列属性
MySQL设计规约问题之为什么在插入数据时,必须显示指定插入的列属性
|
7月前
|
存储 关系型数据库 MySQL
深入探索MySQL的虚拟列:发展、原理与应用
深入探索MySQL的虚拟列:发展、原理与应用

相关产品

  • 云原生数据库 PolarDB
  • 云数据库 RDS PostgreSQL 版
  • 下一篇
    开通oss服务