PostgreSQL 当有多个索引可选时,优化器如何选择

本文涉及的产品
云原生数据库 PolarDB 分布式版,标准版 2核8GB
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
简介: 标签PostgreSQL , 索引 , 复合索引 , 选择 , 成本 , 优化器背景当一个表有很多索引时,并且一个QUERY可以使用到其中的多个索引时,数据库会如何做出选择?最终选择哪个,或者哪几个索引呢?《PostgreSQL 多查询条件,多个索引的选择算法与问题诊断方法》选择单个索引时,PATH可以选择index scan , index only scan, bitmap scan。

标签

PostgreSQL , 索引 , 复合索引 , 选择 , 成本 , 优化器


背景

当一个表有很多索引时,并且一个QUERY可以使用到其中的多个索引时,数据库会如何做出选择?最终选择哪个,或者哪几个索引呢?

《PostgreSQL 多查询条件,多个索引的选择算法与问题诊断方法》

选择单个索引时,PATH可以选择index scan , index only scan, bitmap scan。

选择多个索引时,PATH可以选择bitmap scan。

那么优化器会选择哪个,或者哪几个索引,又会选择index scan , index only scan还是 bitmap scan呢?

例子

1、创建4张表,5个字段,分别使用int2,int4,int8,text类型。

create table t1 (c1 int2, c2 int2, c3 int2, c4 int2, c5 int2);  
  
create table t2 (c1 int4, c2 int4, c3 int4, c4 int4, c5 int4);  
  
create table t3 (c1 int8, c2 int8, c3 int8, c4 int8, c5 int8);  
  
create table t4 (c1 text, c2 text, c3 text, c4 text, c5 text);  

2、写入测试数据1000万条,其中c3字段为常量

insert into t1 select random()*32767,random()*32767,random()*32767,1,random()*32767 from generate_series(1,10000000);  
  
insert into t2 select random()*2000000000,random()*2000000000,random()*2000000000,1,random()*2000000000 from generate_series(1,10000000);  
  
insert into t3 select random()*2000000000,random()*2000000000,random()*2000000000,1,random()*2000000000 from generate_series(1,10000000);  
  
insert into t4 select md5(random()::text),md5(random()::text),md5(random()::text),'a',md5(random()::text) from generate_series(1,10000000);  

3、创建若干索引

create index idx_t1_1 on t1(c1);  
create index idx_t1_2 on t1(c1,c2);  
create index idx_t1_3 on t1(c1,c2,c3);  
create index idx_t1_4 on t1(c4);  
  
create index idx_t2_1 on t2(c1);  
create index idx_t2_2 on t2(c1,c2);  
create index idx_t2_3 on t2(c1,c2,c3);  
create index idx_t2_4 on t2(c4);  
  
create index idx_t3_1 on t3(c1);  
create index idx_t3_2 on t3(c1,c2);  
create index idx_t3_3 on t3(c1,c2,c3);  
create index idx_t3_4 on t3(c4);  
  
create index idx_t4_1 on t4(c1);  
create index idx_t4_2 on t4(c1,c2);  
create index idx_t4_3 on t4(c1,c2,c3);  
create index idx_t4_4 on t4(c4);  

4、强制收集统计信息

vacuum analyze t1;  
vacuum analyze t2;  
vacuum analyze t3;  
vacuum analyze t4;  

数据库如何选择索引? - 现象

1、输入c1,c2,c3三个字段的等值过滤,作为条件,t1,t2,t3,t4分别选择了哪个索引?

t1选择了c1,c2,c3的合并索引,没有任何多余的filter

postgres=# explain select * from t1 where c1=1 and c2=1 and c3=1;  
                             QUERY PLAN                               
--------------------------------------------------------------------  
 Index Scan using idx_t1_3 on t1  (cost=0.43..2.66 rows=1 width=10)  
   Index Cond: ((c1 = 1) AND (c2 = 1) AND (c3 = 1))  
(2 rows)  

t2也选择了c1,c2,c3的合并索引,没有任何多余的filter

postgres=# explain select * from t2 where c1=1 and c2=1 and c3=1;  
                             QUERY PLAN                               
--------------------------------------------------------------------  
 Index Scan using idx_t2_3 on t2  (cost=0.43..2.66 rows=1 width=20)  
   Index Cond: ((c1 = 1) AND (c2 = 1) AND (c3 = 1))  
(2 rows)  

t3选择了c1,c2的合并索引,有多余的c3的filter

postgres=# explain select * from t3 where c1=1 and c2=1 and c3=1;  
                             QUERY PLAN                               
--------------------------------------------------------------------  
 Index Scan using idx_t3_2 on t3  (cost=0.43..2.66 rows=1 width=40)  
   Index Cond: ((c1 = 1) AND (c2 = 1))  
   Filter: (c3 = 1)  
(3 rows)  

t4选择了c1,c2的合并索引,有多余的c3的filter

postgres=# explain select * from t4 where c1='1' and c2='1' and c3='a';  
                             QUERY PLAN                                
---------------------------------------------------------------------  
 Index Scan using idx_t4_2 on t4  (cost=0.56..2.78 rows=1 width=134)  
   Index Cond: ((c1 = '1'::text) AND (c2 = '1'::text))  
   Filter: (c3 = 'a'::text)  
(3 rows)  

t1,t2,t3,t4的数据记录数,数据分布,SQL、索引都一样,为什么会有以上的选择性差异呢?

数据库如何选择索引? - 分析

1 索引大小分析

postgres=# \di+ idx_t*  
                          List of relations  
 Schema |   Name   | Type  |  Owner   | Table |  Size   | Description   
--------+----------+-------+----------+-------+---------+-------------  
 public | idx_t1_1 | index | postgres | t1    | 214 MB  |   
 public | idx_t1_2 | index | postgres | t1    | 214 MB  |   
 public | idx_t1_3 | index | postgres | t1    | 214 MB  |   
 public | idx_t1_4 | index | postgres | t1    | 214 MB  |   
 public | idx_t2_1 | index | postgres | t2    | 214 MB  |   
 public | idx_t2_2 | index | postgres | t2    | 214 MB  |   
 public | idx_t2_3 | index | postgres | t2    | 301 MB  |   
 public | idx_t2_4 | index | postgres | t2    | 214 MB  |   
 public | idx_t3_1 | index | postgres | t3    | 214 MB  |   
 public | idx_t3_2 | index | postgres | t3    | 301 MB  |   
 public | idx_t3_3 | index | postgres | t3    | 387 MB  |   
 public | idx_t3_4 | index | postgres | t3    | 214 MB  |   
 public | idx_t4_1 | index | postgres | t4    | 563 MB  |   
 public | idx_t4_2 | index | postgres | t4    | 911 MB  |   
 public | idx_t4_3 | index | postgres | t4    | 1266 MB |   
 public | idx_t4_4 | index | postgres | t4    | 214 MB  |   
(16 rows)  

有没有觉得很奇怪呢?

t1表,从1个字段到3个字段的合并索引,索引大小都一样!!!!!!!而且都与t3表单个字段一样大。

t2表,一个字段与2个字段的索引大小一样大!!!!!2个字段的与T3表的1个字段一样大。3个字段的与T3表的2个字段一样大。

t3表,容量方面看起来很正常。

t4表,c4单个字段的索引,与T3表的单个字段的索引一样大。

看起来好像是这样的:

INDEX TUPLE占用8个字节的倍数。(可能是对齐的考虑)

2 索引内部结构分析

《深入浅出PostgreSQL B-Tree索引结构》

1、采用pageinspect插件,可以看到索引内部的内容

create extension pageinspect;  

2、查看索引叶子节点的内容

postgres=# select * from bt_metap('idx_t1_1');  
 magic  | version | root | level | fastroot | fastlevel   
--------+---------+------+-------+----------+-----------  
 340322 |       2 |  290 |     2 |      290 |         2  
(1 row)  
  
postgres=# select * from bt_page_items('idx_t1_1',290) limit 5;  
 itemoffset |   ctid   | itemlen | nulls | vars |          data             
------------+----------+---------+-------+------+-------------------------  
          1 | (3,1)    |       8 | f     | f    |   
          2 | (289,1)  |      16 | f     | f    | 54 01 00 00 00 00 00 00  
          3 | (575,1)  |      16 | f     | f    | a9 02 00 00 00 00 00 00  
          4 | (860,1)  |      16 | f     | f    | fe 03 00 00 00 00 00 00  
          5 | (1145,1) |      16 | f     | f    | 52 05 00 00 00 00 00 00  
(5 rows)  
  
postgres=# select * from bt_page_items('idx_t1_1',289) limit 5;  
 itemoffset |  ctid   | itemlen | nulls | vars |          data             
------------+---------+---------+-------+------+-------------------------  
          1 | (572,1) |      16 | f     | f    | a9 02 00 00 00 00 00 00  
          2 | (286,1) |       8 | f     | f    |   
          3 | (287,1) |      16 | f     | f    | 55 01 00 00 00 00 00 00  
          4 | (288,1) |      16 | f     | f    | 56 01 00 00 00 00 00 00  
          5 | (291,1) |      16 | f     | f    | 57 01 00 00 00 00 00 00  
(5 rows)  
  
postgres=# select * from bt_page_items('idx_t1_1',287) limit 5;  
 itemoffset |    ctid     | itemlen | nulls | vars |          data             
------------+-------------+---------+-------+------+-------------------------  
          1 | (38155,112) |      16 | f     | f    | 56 01 00 00 00 00 00 00  
          2 | (20034,36)  |      16 | f     | f    | 55 01 00 00 00 00 00 00  
          3 | (20183,9)   |      16 | f     | f    | 55 01 00 00 00 00 00 00  
          4 | (20250,124) |      16 | f     | f    | 55 01 00 00 00 00 00 00  
          5 | (20957,123) |      16 | f     | f    | 55 01 00 00 00 00 00 00  
(5 rows)  
  
postgres=# select * from t1 where ctid='(38155,112)';  
 c1  |  c2   |  c3   | c4 |  c5     
-----+-------+-------+----+-------  
 342 | 18698 | 24394 |  1 | 27763  
(1 row)  
  
postgres=# select to_hex(342);  
 to_hex   
--------  
 156  
(1 row)  
  
对应56 01 00 00 00 00 00 00  
  
倒过来看 01 56。  

确实有一个现象和前面的描述一样: INDEX TUPLE占用8个字节的倍数。

虽然是INT2的类型,本质上只占用2字节,但是实际上INDEX TUPLE占用了8个字节。

3、查看T2表,1个字段的索引。现象也和前面的描述一样: INDEX TUPLE占用8个字节的倍数。

postgres=# select * from bt_metap('idx_t2_1');  
 magic  | version | root | level | fastroot | fastlevel   
--------+---------+------+-------+----------+-----------  
 340322 |       2 |  290 |     2 |      290 |         2  
(1 row)  
  
postgres=# select * from bt_page_items('idx_t2_1',290) limit 5;  
 itemoffset |   ctid   | itemlen | nulls | vars |          data             
------------+----------+---------+-------+------+-------------------------  
          1 | (3,1)    |       8 | f     | f    |   
          2 | (289,1)  |      16 | f     | f    | 0f 5c 3c 01 00 00 00 00  
          3 | (575,1)  |      16 | f     | f    | 8f 8e 7a 02 00 00 00 00  
          4 | (860,1)  |      16 | f     | f    | c1 f8 b7 03 00 00 00 00  
          5 | (1145,1) |      16 | f     | f    | 46 12 f7 04 00 00 00 00  
(5 rows)  
  
postgres=# select * from bt_page_items('idx_t2_1',289) limit 5;  
 itemoffset |  ctid   | itemlen | nulls | vars |          data             
------------+---------+---------+-------+------+-------------------------  
          1 | (572,1) |      16 | f     | f    | 8f 8e 7a 02 00 00 00 00  
          2 | (286,1) |       8 | f     | f    |   
          3 | (287,1) |      16 | f     | f    | 5b 8a 3d 01 00 00 00 00  
          4 | (288,1) |      16 | f     | f    | 36 a2 3e 01 00 00 00 00  
          5 | (291,1) |      16 | f     | f    | 4b ca 3f 01 00 00 00 00  
(5 rows)  
  
postgres=# select * from bt_page_items('idx_t2_1',572) limit 5;  
 itemoffset |    ctid    | itemlen | nulls | vars |          data             
------------+------------+---------+-------+------+-------------------------  
          1 | (3798,12)  |      16 | f     | f    | 3d b3 7b 02 00 00 00 00  
          2 | (48744,46) |      16 | f     | f    | 8f 8e 7a 02 00 00 00 00  
          3 | (40279,76) |      16 | f     | f    | 1a 8f 7a 02 00 00 00 00  
          4 | (53268,39) |      16 | f     | f    | a9 8f 7a 02 00 00 00 00  
          5 | (16540,92) |      16 | f     | f    | 35 90 7a 02 00 00 00 00  
(5 rows)  
  
postgres=# select * from t2 where ctid='(3798,12)';  
    c1    |    c2     |    c3    | c4 |     c5       
----------+-----------+----------+----+------------  
 41661245 | 940376658 | 41196565 |  1 | 1467443120  
(1 row)  
  
postgres=# select to_hex(41661245);  
 to_hex    
---------  
 27bb33d  
(1 row)  
  
对应3d b3 7b 02 00 00 00 00  
  
倒过来看 02 7b b3 3d。  

4、查看T2表,3个字段的索引。现象也和前面的描述一样: INDEX TUPLE占用8个字节的倍数。

postgres=# select * from bt_metap('idx_t2_3');  
 magic  | version | root | level | fastroot | fastlevel   
--------+---------+------+-------+----------+-----------  
 340322 |       2 |  209 |     2 |      209 |         2  
(1 row)  
  
postgres=# select * from bt_page_items('idx_t2_3',209) limit 5;  
 itemoffset |  ctid   | itemlen | nulls | vars |                      data                         
------------+---------+---------+-------+------+-------------------------------------------------  
          1 | (3,1)   |       8 | f     | f    |   
          2 | (208,1) |      24 | f     | f    | a8 2c a1 00 78 82 d4 3d 55 1b a6 67 00 00 00 00  
          3 | (413,1) |      24 | f     | f    | a8 9d 42 01 3c fe ab 6b e7 69 81 1c 00 00 00 00  
          4 | (617,1) |      24 | f     | f    | c8 f7 e5 01 eb cd b6 31 49 90 14 6f 00 00 00 00  
          5 | (821,1) |      24 | f     | f    | e7 b6 86 02 9c 1e 63 65 c4 c3 06 48 00 00 00 00  
(5 rows)  
  
postgres=# select * from bt_page_items('idx_t2_3',208) limit 5;  
 itemoffset |  ctid   | itemlen | nulls | vars |                      data                         
------------+---------+---------+-------+------+-------------------------------------------------  
          1 | (410,1) |      24 | f     | f    | a8 9d 42 01 3c fe ab 6b e7 69 81 1c 00 00 00 00  
          2 | (205,1) |       8 | f     | f    |   
          3 | (206,1) |      24 | f     | f    | b5 ef a1 00 bf b0 c5 14 94 4f ae 63 00 00 00 00  
          4 | (207,1) |      24 | f     | f    | 1b b1 a2 00 0a f5 c6 33 c4 09 e3 12 00 00 00 00  
          5 | (210,1) |      24 | f     | f    | 34 81 a3 00 82 74 a7 42 fe d6 33 06 00 00 00 00  
(5 rows)  
  
postgres=# select * from bt_page_items('idx_t2_3',410) limit 5;  
 itemoffset |    ctid     | itemlen | nulls | vars |                      data                         
------------+-------------+---------+-------+------+-------------------------------------------------  
          1 | (61769,131) |      24 | f     | f    | a6 6b 43 01 83 ab a4 04 0c a4 9e 29 00 00 00 00  
          2 | (44927,154) |      24 | f     | f    | a8 9d 42 01 3c fe ab 6b e7 69 81 1c 00 00 00 00  
          3 | (24587,3)   |      24 | f     | f    | e3 9d 42 01 4a 9f 46 25 b9 d2 8e 47 00 00 00 00  
          4 | (29996,51)  |      24 | f     | f    | be 9e 42 01 66 33 fd 03 8c 8c 39 2c 00 00 00 00  
          5 | (4032,73)   |      24 | f     | f    | 5c 9f 42 01 ea 4d b1 01 1d af 88 32 00 00 00 00  
(5 rows)  
  
postgres=# select * from t2 where ctid='(61769,131)';  
    c1    |    c2    |    c3     | c4 |     c5       
----------+----------+-----------+----+------------  
 21195686 | 77900675 | 698262540 |  1 | 1522991839  
(1 row)  
  
postgres=# select to_hex(21195686);  
 to_hex    
---------  
 1436ba6  
(1 row)  
  
postgres=# select to_hex(77900675);  
 to_hex    
---------  
 4a4ab83  
(1 row)  
  
postgres=# select to_hex(698262540);  
  to_hex    
----------  
 299ea40c  
(1 row)  
  
对应a6 6b 43 01 83 ab a4 04 0c a4 9e 29 00 00 00 00  
  
每个字段占用4字节,倒过来看 01 43 6b a6。04 a4 ab 83。29 9e a4 0c。   

但是这些个索引选择有什么关系吗?

本身没什么关系,但是由于这样出现了一个问题,索引本身占用的大小,在评估成本时也在成本计算环节之一。

HINT , 成本对比

最后数据库选什么索引,其实还是取决于COST。

每个PATH的COST都会算一遍,哪个最低选哪个。

使用hint来强制使用某个索引,这样就能对比出COST到底差多少?

postgres=# set pg_hint_plan.debug_print =on;  
SET  
postgres=# set pg_hint_plan.enable_hint=on;  
SET  
  
postgres=# set pg_hint_plan.message_level =log;  
SET  
postgres=# set client_min_messages =log;  
SET  

以t4表为例

postgres=# explain (analyze,verbose,timing,costs,buffers) /*+ IndexScan(t4 idx_t4_3) */ select * from t4 where c1='1' and c2='1' and c3='a';  
LOG:  available indexes for IndexScan(t4): idx_t4_3  
LOG:  pg_hint_plan:  
used hint:  
IndexScan(t4 idx_t4_3)  
not used hint:  
duplication hint:  
error hint:  
  
                                                      QUERY PLAN                                                        
----------------------------------------------------------------------------------------------------------------------  
 Index Scan using idx_t4_3 on public.t4  (cost=0.69..2.91 rows=1 width=134) (actual time=0.012..0.012 rows=0 loops=1)  
   Output: c1, c2, c3, c4, c5  
   Index Cond: ((t4.c1 = '1'::text) AND (t4.c2 = '1'::text) AND (t4.c3 = 'a'::text))  
   Buffers: shared hit=5  
 Planning time: 0.188 ms  
 Execution time: 0.027 ms  
(6 rows)  
postgres=# explain (analyze,verbose,timing,costs,buffers) /*+ IndexScan(t4 idx_t4_2) */ select * from t4 where c1='1' and c2='1' and c3='a';  
LOG:  available indexes for IndexScan(t4): idx_t4_2  
LOG:  pg_hint_plan:  
used hint:  
IndexScan(t4 idx_t4_2)  
not used hint:  
duplication hint:  
error hint:  
  
                                                      QUERY PLAN                                                        
----------------------------------------------------------------------------------------------------------------------  
 Index Scan using idx_t4_2 on public.t4  (cost=0.56..2.78 rows=1 width=134) (actual time=0.012..0.012 rows=0 loops=1)  
   Output: c1, c2, c3, c4, c5  
   Index Cond: ((t4.c1 = '1'::text) AND (t4.c2 = '1'::text))  
   Filter: (t4.c3 = 'a'::text)  
   Buffers: shared hit=5  
 Planning time: 0.212 ms  
 Execution time: 0.027 ms  
(7 rows)  

很显然,数据库评估出来选择idx_t4_2的成本为2.78,使用idx_t4_3的成本为2.91。

理所当然,选择了idx_t4_2。虽然它多了一次filter。

那么为什么idx_t4_2的成本更低呢?

 public | idx_t4_2 | index | postgres | t4    | 911 MB  |   
 public | idx_t4_3 | index | postgres | t4    | 1266 MB |   

1、走idx_t4_2索引时,评估出来扫描的数据块比idx_t4_3更少,虽然它多了filter,但是filter引入的开销在 RANDOM PAGE SCAN面前小得多(并且实际上这里没有filter的成本,因为c1,c2两个条件下去后评估出来的ROWS=0 select * from t4 where c1='1' and c2='1';)。

2、走idx_t4_3索引时,评估出来不需要filter,但是这个索引比idx_t4_2占用的空间大,按比例划分时,算了更多的random page SCAN 成本。即使没有额外的filter,成本也比idx_t4_2更高。

PS:

这样的评估算法应该还有优化的空间,因为实际上两个索引的LEVEL是一样的。

postgres=# select * from bt_metap('idx_t2_2');  
 magic  | version | root | level | fastroot | fastlevel   
--------+---------+------+-------+----------+-----------  
 340322 |       2 |  290 |     2 |      290 |         2  
(1 row)  
  
postgres=# select * from bt_metap('idx_t2_3');  
 magic  | version | root | level | fastroot | fastlevel   
--------+---------+------+-------+----------+-----------  
 340322 |       2 |  209 |     2 |      209 |         2  
(1 row)  

以上,使用idx_t2_2和idx_t2_3扫描的INDEX PAGE数实际上是一样的。

小结

PostgreSQL 采用CBO的优化器模型,哪个PATH成本低,就使用哪个PATH。

在有多个索引可以使用时,实际上数据库会计算每一种的成本,哪种成本低,选择哪个。

影响成本的因素很多:

算子设置,选择性,扫描成本,INDEX TUPLE过滤成本,HEAP TUPLE过滤成本。

成本是综合的结果。

思考:

select * from tbl where c1=1 and c2=1 and c3=1 and c4=1;  
  
idx(c1,c2)  
  
idx(c2,c3)  
  
idx(c4,c3,c1)  

选哪个索引?需要考虑什么因素?

提示:

多列统计信息,统计信息,多列条件合并选择性、索引大小、RANDOM SCAN成本,SEQ SCAN成本,FILTER的成本,成本算子参数,优化器开关等。

参考

《深入浅出PostgreSQL B-Tree索引结构》

《PostgreSQL 10 黑科技 - 自定义统计信息》

src/backend/optimizer/path/costsize.c

《PostgreSQL 多查询条件,多个索引的选择算法与问题诊断方法》

《PostgreSQL 自定义函数表达式选择性评估算法 - Statistics, Cardinality, Selectivity, Estimate》

《PostgreSQL 优化器行评估算法》

《关键时刻HINT出彩 - PG优化器的参数优化、执行计划固化CASE》

《优化器成本因子校对 - PostgreSQL explain cost constants alignment to timestamp》

《优化器成本因子校对(disk,ssd,memory IO开销精算) - PostgreSQL real seq_page_cost & random_page_cost in disks,ssd,memory》

https://www.postgresql.org/docs/11/static/planner-stats-details.html

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
目录
相关文章
|
4月前
|
监控 关系型数据库 数据库
PostgreSQL的索引优化策略?
【8月更文挑战第26天】PostgreSQL的索引优化策略?
100 1
|
4月前
|
SQL 关系型数据库 MySQL
SQL Server、MySQL、PostgreSQL:主流数据库SQL语法异同比较——深入探讨数据类型、分页查询、表创建与数据插入、函数和索引等关键语法差异,为跨数据库开发提供实用指导
【8月更文挑战第31天】SQL Server、MySQL和PostgreSQL是当今最流行的关系型数据库管理系统,均使用SQL作为查询语言,但在语法和功能实现上存在差异。本文将比较它们在数据类型、分页查询、创建和插入数据以及函数和索引等方面的异同,帮助开发者更好地理解和使用这些数据库。尽管它们共用SQL语言,但每个系统都有独特的语法规则,了解这些差异有助于提升开发效率和项目成功率。
415 0
|
4月前
|
关系型数据库 数据库 PostgreSQL
PostgreSQL索引维护看完这篇就够了
PostgreSQL索引维护看完这篇就够了
302 0
|
6月前
|
关系型数据库 分布式数据库 数据库
PolarDB产品使用合集之优化器对索引的阈值一般是多少
PolarDB是阿里云推出的一种云原生数据库服务,专为云设计,提供兼容MySQL、PostgreSQL的高性能、低成本、弹性可扩展的数据库解决方案,可以有效地管理和优化PolarDB实例,确保数据库服务的稳定、高效运行。以下是使用PolarDB产品的一些建议和最佳实践合集。
|
Oracle 关系型数据库 数据库
PostgreSQL和Oracle两种数据库有啥区别?如何选择?
PostgreSQL和Oracle两种数据库有啥区别?如何选择?
525 0
|
存储 关系型数据库 MySQL
如何选择最适合你的数据库解决方案:PostgreSQL VS MySQL 技术选型对比
如何选择最适合你的数据库解决方案:PostgreSQL VS MySQL 技术选型对比
315 1
|
存储 关系型数据库 数据库
PostgreSQL技术大讲堂 - 第28讲:索引内部结构
从零开始学PostgreSQL技术大讲堂 - 第28讲:索引内部结构
755 2
|
关系型数据库 Go 数据库
《提高查询速度:PostgreSQL索引实用指南》
《提高查询速度:PostgreSQL索引实用指南》
592 0
|
存储 自然语言处理 关系型数据库
MySQL学习笔记-优化器选索引的因素
MySQL学习笔记-优化器选索引的因素
159 0
|
关系型数据库 分布式数据库 数据库
PolarDB for PostgreSQL 14:全局索引
PolarDB for PostgreSQL 14 相较于 PostgreSQL 14,提供了更多企业级数据库的特性。本实验将体验其中的全局索引功能。

相关产品

  • 云原生数据库 PolarDB
  • 云数据库 RDS PostgreSQL 版