### 一. Postgresql简介
#### 1.1 PostgreSQL概述
- PostgreSQL数据库是目前功能最强大的开源数据库,支持丰富的数据类型(如JSON和JSONB类型、数组类型)和自定义类型。而且他提供了丰富的接口,可以很容易的扩展它的功能,如可以再GiST框架下实现自己的索引类型等。PostgreSQL是完全的事务安全性数据库,完整地支持外键、联合、视图、触发器和存储过程(并支持多种语言开发存储过程)。它支持了大多数的SQL:2008标准的数据类型,包括整型、数值值、布尔型、字节型、字符型、日期型、时间间隔型和时间型,它也支持存储二进制的大对像,包括图片、声音和视频。PostgreSQL对很多高级开发语言有原生的编程接口,如C/C++、Java、.Net、Perl、Python、Ruby、Tcl 和ODBC以及其他语言等,也包含各种文档。
#### 1.2 PostgreSQL数据库与其他数据库的对比
- PostgresSQL与MySQL数据库的对比
|维度| PostgreSQL| Mysql|
| ------------- |:-------------:| -----:|
|功能| 支持所有主流的夺标连接查询方式,支持绝大多数的SQL语法,对正则表达式支持强,内置函数丰富,字段支持数组|多表连接查询方式只支持有限,SQL语法支持有限,子查询性能比较低,不支持sequence|
| 性能与度量 | 大量的性能视图,有物理IO和表扫描索引扫描方面的性能数据,支持同步复制 | 性能数据少,问题难定位,复制是异步的,无法做到数据零丢失 |
| 在线操作 | 增加列,本质上是在系统表上把列定义上,无须更新物理结构;支持在线并发创建索引而不锁定表更新操作 | 功能较弱:增加列,基本上是新建一张表;建索引会锁表,不能做任何操作 |
| 其他 | 适合做数据仓库,支持空间数据存储,PostGIS插件 | 不适合做数据仓库 |
- PostgreSQL与Oracle对比
|维度| PostgreSQL| Oracle|
| ------------- |:-------------:| -----:|
|功能| 不如Oracle,功能最强大的开源数据库 | 功能最强大的商业数据库 |
|其他| 更多的互联网特征功能,支持网络地址类型、XML类型、JSON类型、UUID类型,以及数组类型,有强大的正则表达式函数,where条件可以使用正则表达式匹配,可以使用Python、Perl等写存储过程 | |
#### 1.3 PostgreSQL数据类型
| 分类名称 | 说明 |
| ------------- |:-------------:|
布尔类型|支持SQL标准的Boolean数据类型|
数值类型|数值类型有2字节的smallint、4字节的int、8字节的bigint,十进制精确类型有numeric,浮点类型有real和double precision,还有8字节的money类型|
字符类型|有varchar(n)、char(n)、text三种类型,varchar最大可以存储1GB|
二进制数据类型|只有一种bytea|
位串类型|位串就是一串1和0的字符串,有bit(n)、bit varying(n)两种,其他数据库没有此数据类型|
日期和时间类型|有date、time、timestamp,而time和timestamp又分是否包括时区的两种类型|
枚举类型|枚举类型是一种包含一系列有序静态值集合的数据类型,等于某些编程语言中的enum类型,使用前需要使用create type创建这个类型|
几何类型|包括了点(point)、直线(line)、线段(lseg)、路径(path)、多边形(polygon)、圆(cycle)等类型,特有|
网络地址类型|有cidr、inet、macaddr三种类型,特有|
数组类型|可以存储一个数组,特有|
复合类型|可以把已有的简单类型组合成用户自定义的类型,就如C语言中的结构体一样,对应其他数据库中的自定义类型|
xml类型|可以存储XML数据的类型|
json类型|可以存储json类型的数据|
range类型|范围类型,可以存储范围数据,特有|
对象标识符类型|postgres内部标识对象的类型,如oid类型、regproc类型、regclass类型等|
伪类型|伪类型不能作为字段的数据类型,但是它可以用于声明一个函数的参数或者结果类型。有any、anyarray、anyelement、cstring、internal、language_handler、record、trigger、void、opaque|
其他类型|一些不好分类的类型,如UUID类型、pg_lsn类型|
-
数据类型的转换
- 使用“类型名”加上单引号转换
- select bit '11110011';
- select int '1' + int '2';
- 支持标准SQL类型转换函数CAST进行类型转换
- select CAST('5' as int),CAST('2014-07-17',date);
- 更简洁的类型转换方式,双冒号的方式
- select '5'::int,'2014-07-17'::date;
- 复合类型的使用
在PostgreSQL中可以如C语言中的结构体一样定义一个符合类型。
- 复合类型的定义
- CREATE TYPE complex AS (
r double precision,
i double precision
);
- 用复合类型创建表
- CREATE TABLE test_table (
id int,
coltage complex,
current complex,
remark text
);
- 用复合类型做函数的参数
```
CREATE FUNCTION complex_multi(complex, complex) RETURNS complex
AS $$ SELECT ROW($1.r*2.r - $1.i*$2.i - $1.i*$2.r)::complex $$
LANGUAGE SOL;
```
- 新增表数据
- insert into test_table(1,ROW(11.213,11.345),ROW(22.213,22.345),'测试1');
- insert into test_table(2,(33.213,33.345),(44.213,44.345),'测试2');
- insert into test_table(id, coltage.r, coltage.i, current.r, current.i,remark)
values(3,333.213,333.345,444.213,444.345,'测试3');
- 访问复合类型
- select coltage.r from test_table;
- 修改复合类型
- update test_table set coltage=(55.213,55.345) where id = 1;
- Range类型的使用
特有的数据类型:Range类型,可以进行范围快速搜索。
Range类型的subtype。
- pg支持范围类型
- int4range — Range of integer
- int8range — Range of bigint
- numrange — Range of numeric
- tsrange — Range of timestamp without time zone
- tstzrange — Range of timestamp with time zone
- daterange — Range of date
- 自定义创建:使用“CREATE TYPE”创建一些Range类型,预发如下:
CREATE TYPE name AS RANGE(
SUBTYPE = subtype
[,SUBTYPE_OPCLASS = subtype_operator_class]
...
)
举例:
CREATE TYPE floatrange AS RANGE(
SUBTYPE = float8,
SUBTYPE_DIFF = float8mi
);
- 使用场景举例:IP地址查询
CREATE TABLE ipdb1(
ip_begin inet,
ip_end inet,
area text,
sp text
);
查询某个IP所属区域:select * from ipdb1 where ip_begin <= '115.195.180.105'::inet and ip_end >= '115.195.180.105'::inet;
- 使用Range类型
CREATE TYPE inetrange AS RANGE (subtype = inet);
CREATE TABLE ipdb2(
ip_range inetrange,
area text,
sp text
);
insert into ipdb2 select ('['||ip_begin||','||ip_end||']')::inetrange,area,sp from ipdb1;
创建gist索引:
CREATE INDEX idx_ipdb2_ip_range ON ipdb2 USING gist(ip_range);
查询:
select * from ipdb2 where ip_range @> '115.195.180.105'::inet;
- 数组类型的使用
支持表的字段使用定长或可变长度的一维或多维数组,数组的类型可以是任何数据库内建的类型、用户自定义的类型、枚举类型,以及组合类型。
- 数组类型的声明
CREATE TABLE testtab04(id int, col1 int[], col2 int[10], col3 text[][]);
- 输入数组值
CREATE TABLE testtab05(id int, col1 int[]);
insert into testtab05 values(1, '{1,2,3}');
insert into testtab05 values(2, '{4,5,6}');
CREATE TABLE testtab06(id int, col1 text[]);
insert into testtab06 values(1, '{how,howe,howl}');
insert into testtab05 values(2,'[2:4]={1,2,3}');指定数组下标
- 访问数组
select id,col1[1] from testtab05;
select id,col1[1:2] from testtab05;指定数组1、2小标数据
- 修改数组
update testtab05 set col1 = '{7,8,9}' where id = 1;
update testtab05 set col1[1] = 10 where id = 1;
- 数组聚合函数array_agg
- 附数组操作符:
|Operator
|Description
|Example
|Result|
| ---- |:-------------:| ---------------:|----------:|
|=|
equal|
ARRAY[1.1,2.1,3.1]::int[] = ARRAY[1,2,3]|t|
|<>|
not equal|
ARRAY[1,2,3] <> ARRAY[1,2,4]|t|
|<|
less than|
ARRAY[1,2,3] < ARRAY[1,2,4]|
t|
|>|
greater than|
ARRAY[1,4,3] > ARRAY[1,2,4]|
t|
|<=|
less than| or equal
ARRAY[1,2,3] <= ARRAY[1,2,3]|
t|
|>=
|greater than| or equal
ARRAY[1,4,3] >= ARRAY[1,4,3]|
t|
|@>|
contains|
ARRAY[1,4,3] @> ARRAY[3,1]|
t|
|<@|
is contained by|
ARRAY[2,7] <@ ARRAY[1,7,4,2,6]|
t|
|&&|
overlap (have elements in common)|
ARRAY[1,4,3] && ARRAY[2,1]|
t|
|\|\||
array-to-array concatenation|
ARRAY[1,2,3] \|\| ARRAY[4,5,6]|
{1,2,3,4,5,6}|
|\|\||
array-to-array concatenation|
ARRAY[1,2,3] \|\| ARRAY[[4,5,6],[7,8,9]]|
{{1,2,3},{4,5,6},{7,8,9}}|
|\|\||
element-to-array concatenation|
3 \|\| ARRAY[4,5,6]|
{3,4,5,6}|
|\|\||
array-to-element concatenation|
ARRAY[4,5,6] \|\| 7|
{4,5,6,7}|
#### 1.4 模式匹配和正则表达式
提供三种实现模式匹配的方法:传统SQL的LIKE操作符、SQL99新增的SIMILAR TO操作符、POSIX风格的正则表达式
模式匹配函数substring
- 传统SQL的LIKE操作符
还提供了标准SQL中没有的ILIKE操作符,用于忽略大小写的模式匹配。
LIKE、ILIKE、NOT LIKE、NOT ILIKE
- SIMILAR TO正则表达式
SIMILAR TO除下划线和百分号的使用与LIKE相同,还支持正则表达式查询。
| 表示选择(二选一,如a|b,类似or)
\* 表示重复前面项0次或多次,如'6\*1','(66)\*1'
\+ 表示重复前面项1次或多次,如'6+1','(66)+1'
[] 表示方括号内字符集中的任意一个字符,如[123]表示1或2或3中的1个,可以是1,也可以是2,还可以是3,但是只能是单个字符。
- 查询c字段值是'abc'或'ABc'的行 select * from tbl_insert where c similar to '(ab|AB)c';
- 查询c字段中以'1'结尾,前面有0个或多个'6'的行 select * from tbl_insert where c similar to '6*1';
- 查询字段c中以'1'结尾,前面是0到9之间任意一个数字的行 select * from tbl_insert where c similar to '[0-9]1';
- POSIX正则表达式
POSIX 正则表达式提供了比 LIKE 和 SIMILAR TO 操作符 更强大的模式匹配的方法。许多 Unix 工具,比如 egrep, sed,或 awk 使用一种与我们这里描述的类似的模式匹配语言。
- 正则表达式匹配操作符
|操作符|
描述|
例子|
| -------- |:-------------:| ------------------:|
|~|
匹配正则表达式,大小写相关|
'thomas' ~ '.*thomas.*'|
|~*|
匹配正则表达式,大小写无关
|'thomas' ~* '.*Thomas.*'|
|!~
|不匹配正则表达式,大小写相关|
'thomas' !~ '.*Thomas.*'|
|!~*|
不匹配正则表达式,大小写无关|
'thomas' !~* '.*vadim.*'|
- 模式匹配函数substring
- substring有三种用法:
- 1.substring(<字符串>, <数字>,[数字])
后两个参数为数字,则这个函数与substr是一样的,也其它语言中的substr意义一样。
select substring('osdba',2);
sdba
(1 row)
- 2.substring(<字符串>,<字符串>)
有两个参数,都是字符串,这是一种使用正则式的方式。
而只有两个参数的的substring中的正则表达式,就是使用POSIX的正则表达式。
见示例:
osdba=# select substring('osdba-5-osdba',E'(\\d+)');
5
(1 row)
这种方式的substring函数返回正则表达式中“()”中匹配的部分。
- 3.substring(<字符串>,<字符串>,<字符串)或substring(<字符串> from <字符串> for <字符串)
这种形式的substring使用SQL正则表达式。第三个参数为指定一个转义字符。如下示例:
osdba=# select substring('osdba-5-osdba','%#"[0-9]+#"%','#');
5
(1 row)
模式必须出现后跟双引号(")的两个转义字符。匹配“#"”这两个标记之间的模式的字符串将被返回。
#### 1.5 PostgreSQL索引
- 索引分类
| 索引类型 | 应用场景 | 例子 |
| -------- |:-------------:| ------------------:|
| btree| 支持所有的数据类型,适合处理等值查询和范围查询,支持排序,支持大于、小于、等于、大于或等于、小于或等于的搜索| |
| hash|只能处理简单的等值查询,例如很长的字符串,并且用户只需要等值搜索,建议使用hash index| |
| gin|反转索引,1、当需要搜索多值类型内的VALUE时,适合多值类型,例如数组、全文检索、TOKEN。(根据不同的类型,支持相交、包含、大于、在左边、在右边等搜索)
2、当用户的数据比较稀疏时,如果要搜索某个VALUE的值,可以适应btree_gin支持普通btree支持的类型。(支持btree的操作符)
3、当用户需要按任意列进行搜索时,gin支持多列展开单独建立索引域,同时支持内部多域索引的bitmapAnd, bitmapOr合并,快速的返回按任意列搜索请求的数据。| |
| gist| GiST是一个通用的索引接口,可以使用GiST实现b-tree, r-tree等索引结构。不同的类型,支持的索引检索也各不一样。例如:
1、几何类型,支持位置搜索(包含、相交、在上下左右等),按距离排序。
2、范围类型,支持位置搜索(包含、相交、在左右等)。
3、IP类型,支持位置搜索(包含、相交、在左右等)。4、空间类型(PostGIS),支持位置搜索(包含、相交、在上下左右等),按距离排序。
5、标量类型,支持按距离排序。 | |
| sp-gist| SP-GiST类似GiST,是一个通用的索引接口,但是SP-GIST使用了空间分区的方法,使得SP-GiST可以更好的支持非平衡数据结构,例如quad-trees, k-d tree, radis tree.
1、几何类型,支持位置搜索(包含、相交、在上下左右等),按距离排序。
2、范围类型,支持位置搜索(包含、相交、在左右等)。
3、IP类型,支持位置搜索(包含、相交、在左右等)。| |
- 索引创建
举例:CREATE TABLE contacts(
id int primary key,
name varchar(40),
phone varchar(32)[],
address text
);
按name快速查询,新建btree索引
CREATE INDEX idx_contacts_name on contacts(name);
按phone快速查询,由于是该字段是数组,btree索引不起作用,可以创建一个GIN索引
CREATE INDEX idx_contscts_phone on contacts using gin(phone);
查询:select * from contacts where phone @> array['13333333333'::varchar(32)];
测试:
- 并发索引创建
通常,创建索引的时候会锁定表以防止写入,然后对表做全面扫描,从而完成创建索引操作。在此过程中用户仍然可以读取表,但是写操作是会被阻塞的。如果数据量巨大,则创建索引可能需要持续几十分钟,在一般业务场景中是不可接受的。
PostgreSQL支持不阻塞创建索引方式,即通过在CREATE INDEX中加CONCURRENTLY选项来实现的。
举例:
CREATE TABLE testtab01(id int primary key, note text, test varchar(32));
常规索引,创建索引过程中会阻塞表写操作:
CREATE INDEX idx_testtab01_note on testtab01(note);
并发索引,创建索引的过程中可以对表进行写操作:
DROP INDEX idx_testtab01_note;
CREATE INDEX CONCURRENTLY idx_testtab01_note on testtab01(note);
- 索引的特色
- 表达式上的索引
PostgreSQL支持函数索引,索引的键可以是一个函数,还可以是从一个或多个字段甲酸出来的标量表达式。
举例:CREATE TABLE indextest(id int, note text);
select * from indextest where lower(note) = 'hello world';
因为使用了函数,无法利用到note字段上的普通索引,所以这时需要建一个函数索引,如下:
CREATE INDEX idx_ indextest_note ON indextest(lower(note));
表达式上的索引不是在进行索引查找的时候计算表达式的,而是在插入数据行或更新数据行时进行计算的。
如果把表达式上的索引声明为UNIQUE,如下:
CREATE UNIQUE INDEX indextest_note ON indextest(lower(note));
那么会禁止往note列中插入只有大小写区别而内容相同的数据行。因此,在表达式上的索引可以实现简单唯一约束无法实现的一些约束。
- 部分索引
只在自己感兴趣的那部分数据上创建索引,而不是对每一行数据都创建索引,此种方式创建索引就需要使用WHERE条件了。
举例:create index idx_tbl_partial_index1_level on tbl_partial_index1(level) where level = 'red';
select * from tbl_partial_index1 where level = 'red';
- GiST索引
几何类型检索,内置的SP-GiST索引操作类:box,circle,inet,point,range,tsquery,tsvector
- SP-GiST索引
内置的SP-GiST索引操作类:point,range,text
- GIN索引
GIN所以通常用于全文检索。PostgreSQL数据库已经对一些内置的数组类型实现了GIN索引操作类。插入更新是,GIN索引比较慢,如果要向一张表中插入大量的数据,最好把GIN索引删除掉,插入好之后再重建索引。
- 关于索引的扩展 https://yq.aliyun.com/articles/111793
#### 1.6 分区表
PostgresSQL分区的意思是把逻辑上的一个大表分割成物理上的几块儿。分区不仅能带来访问速度的提升,关键的是,它能带来管理和维护上的方便。
分区的具体好处是:
某些类型的查询性能可以得到极大提升。
更新的性能也可以得到提升,因为表的每块的索引要比在整个数据集上的索引要小。如果索引不能全部放在内存里,那么在索引上的读和写都会产生更多的磁盘访问。
批量删除可以用简单的删除某个分区来实现。
可以将很少用的数据移动到便宜的、转速慢的存储介质上。
在PG里表分区是通过表继承来实现的,一般都是建立一个主表,里面是空,然后每个分区都去继承它。无论何时,都应保证主表里面是空的。
小表分区不实际,表在多大情况下才考虑分区呢?PostgresSQL官方给出的建议是:当表本身大小超过了机器物理内存的实际大小时(the size of the table should exceed the physical memory of the database server),可以考虑分区。
- 建分区表的步骤:
1)创建父表,所有分区都从它集成。这个表中没有数据,不要再这个表上定义任何检查约束,除非你希望月所所有分区,也不要在其上定义任何索引。
2)创建几个子表,每个都是从主表上集成的。这些表与父表字段一直,子表称作分区,实际上他们就是普通的表。inherits
3) 给分区表增加约束,定义每个分区允许的键值。
4)对每个分区,在关键字字段上创建索引。
5)定义一个规则或者触发器,把对主表的数据插入重定向到对应的分区表。
6)确保constraint_excelusion里的配置参数postgresql.conf是打开的。
- 如何创建传统的hash分区
1、创建父表 create table tbl (id int, info text, crt_time timestamp);
2、创建分区表,增加约束
```
do language plpgsql $$
declare
parts int := 5;
begin
for i in 0..parts-1 loop
execute format('create table tbl%s (like tbl including all) inherits (tbl)', i);
execute format('alter table tbl%s add constraint ck check(mod(id,%s)=%s)', i, parts, i);
end loop;
end;
$$;
```
3、创建触发器函数,内容为数据路由,路由后返回NULL(即不写本地父表)
```
create or replace function ins_tbl() returns trigger as $$
declare
begin
case abs(mod(NEW.id,4))
when 0 then
insert into tbl0 values (NEW.*);
when 1 then
insert into tbl1 values (NEW.*);
when 2 then
insert into tbl2 values (NEW.*);
when 3 then
insert into tbl3 values (NEW.*);
else
return NEW; -- if NULL insert into father table
end case;
return null;
end;
$$ language plpgsql strict;
```
4、创建before触发器
create trigger tg1 before insert on tbl for each row when (NEW.id is not null) execute procedure ins_tbl();
5、验证
postgres=# insert into tbl values (1);
INSERT 0 0
postgres=# insert into tbl values (null);
INSERT 0 1
postgres=# insert into tbl values (0);
INSERT 0 0
postgres=# insert into tbl values (1);
INSERT 0 0
postgres=# insert into tbl values (2);
INSERT 0 0
postgres=# insert into tbl values (3);
INSERT 0 0
postgres=# insert into tbl values (4);
INSERT 0 0
postgres=# select tableoid::regclass, * from tbl;
tableoid | id | info | crt_time
----------+----+------+----------
tbl | | |
tbl0 | 0 | |
tbl0 | 4 | |
tbl1 | 1 | |
tbl1 | 1 | |
tbl2 | 2 | |
tbl3 | 3 | |
(7 rows)
6、查询时,只要提供了约束条件,会自动过滤到子表,不会扫描不符合约束条件的其他子表。
postgres=# explain select * from tbl where abs(mod(id,4)) = abs(mod(1,4)) and id=1;
--------------------------------------------------------------------------
Append (cost=0.00..979127.84 rows=3 width=45)
-> Seq Scan on tbl (cost=0.00..840377.67 rows=2 width=45)
Filter: ((id = 1) AND (abs(mod(id, 4)) = 1))
-> Seq Scan on tbl1 (cost=0.00..138750.17 rows=1 width=45)
Filter: ((id = 1) AND (abs(mod(id, 4)) = 1))
(5 rows)
#### 1.7 性能测试
- 单表10亿
create table test01 (id bigint primary key, info text, create_time timestamp);
insert into test01 select generate_series(1,1000000000), 'text'||generate_series(1,1000000000),now();
create index idx_test01_id on test01(id);
create index idx_test01_info on test01(info);
explain select * from test01 where info = 'text53';
| Seq Scan on test01 (cost=0.00..2669989.50 rows=603650 width=48) |
| Filter: (info = 'text53'::text) |
+------------------------------------------------------------------+
共返回 2 行记录,花费 5.00 ms.
- 分区表20亿,分10个表
创建父表:create table fenqu(id int,info text,crt_time timestamp);
创建子表:
create table fenqu01() inherits(fenqu);
create table fenqu02() inherits(fenqu);
create table fenqu03() inherits(fenqu);
create table fenqu04() inherits(fenqu);
create table fenqu05() inherits(fenqu);
create table fenqu06() inherits(fenqu);
create table fenqu07() inherits(fenqu);
create table fenqu08() inherits(fenqu);
create table fenqu09() inherits(fenqu);
create table fenqu10() inherits(fenqu);
插入测试数据:
insert into fenqu01 select generate\_series(1,20000000), 'text'||generate\_series(1,20000000),now();
insert into fenqu02 select generate\_series(20000001,40000000), 'text'||generate\_series(20000001,40000000),now();
insert into fenqu03 select generate\_series(40000001,60000000), 'text'||generate\_series(40000001,60000000),now();
insert into fenqu04 select generate\_series(60000001,80000000), 'text'||generate\_series(60000001,80000000),now();
insert into fenqu05 select generate\_series(80000001,100000000), 'text'||generate\_series(80000001,100000000),now();
insert into fenqu06 select generate\_series(100000001,120000000), 'text'||generate\_series(100000001,120000000),now();
insert into fenqu07 select generate\_series(120000001,140000000), 'text'||generate\_series(120000001,140000000),now();
insert into fenqu08 select generate\_series(140000001,160000000), 'text'||generate\_series(140000001,160000000),now();
insert into fenqu09 select generate\_series(160000001,180000000), 'text'||generate\_series(160000001,180000000),now();
insert into fenqu10 select generate\_series(180000001,200000000), 'text'||generate\_series(180000001,200000000),now();
分表创建索引:
create index idx_fenqu01_id on fenqu01(id);
create index idx_fenqu02_id on fenqu02(id);
create index idx_fenqu03_id on fenqu03(id);
create index idx_fenqu04_id on fenqu04(id);
create index idx_fenqu05_id on fenqu05(id);
create index idx_fenqu06_id on fenqu06(id);
create index idx_fenqu07_id on fenqu07(id);
create index idx_fenqu08_id on fenqu08(id);
create index idx_fenqu09_id on fenqu09(id);
create index idx_fenqu10_id on fenqu10(id);
create index idx_fenqu01_info on fenqu01(info);
create index idx_fenqu02_info on fenqu02(info);
create index idx_fenqu03_info on fenqu03(info);
create index idx_fenqu04_info on fenqu04(info);
create index idx_fenqu05_info on fenqu05(info);
create index idx_fenqu06_info on fenqu06(info);
create index idx_fenqu07_info on fenqu07(info);
create index idx_fenqu08_info on fenqu08(info);
create index idx_fenqu09_info on fenqu09(info);
create index idx_fenqu10_info on fenqu10(info);
数据查询:
explain analyze select * from test01 where info = 'text53';
| Index Scan using idx_test01_text on test01 (cost=0.57..8.59 rows=1 width=28) (actual time=0.707..0.708 rows=1 loops=1) |
| Index Cond: (info = 'text53'::text)
| Planning time: 0.826 ms
| Execution time: 0.724 ms
共返回 4 行记录,花费 5.00 ms.
- 数组测试
建测试数组表:
create table arraytest(id int, namestr text, phone text[]);
建索引:
create index idx_arraytest_id on arraytest(id);
create index idx_arraytest_namestr on arraytest(namestr);
create index idx_arraytest_phone on arraytest using gin(phone);
导入测试数据:
insert into arraytest select generate_series(1,10000000), 'name'||generate_series(1,10000000), regexp_split_to_array(generate_series(1,10000000)||','||(random()\*(2\*10^9))::integer,E'\\\,')
查询测试:
explain analyze select * from arraytest where id = 234234;
explain analyze select * from arraytest where phone @> array[1191838269]::text[];
psql=#select * from arraytest where phone @> array[1191838269]::text[];
+--------------+-------------------+---------------------+
| ID | NAMESTR | PHONE |
+--------------+-------------------+---------------------+
| 234234 | name234234 | {234234,1191838269} |
+--------------+-------------------+---------------------+
共返回 1 行记录,花费 3.00 ms.
explain analyze select * from arraytest where phone[2] = '1191838269';
psql=#select * from arraytest where phone[2] = '1191838269'
+--------------+-------------------+---------------------+
| ID | NAMESTR | PHONE |
+--------------+-------------------+---------------------+
| 234234 | name234234 | {234234,1191838269} |
+--------------+-------------------+---------------------+
共返回 1 行记录,花费 8934.00 ms.
GIN索引用=号性能就很差了。
- 其他 https://yq.aliyun.com/articles/272112?utm_content=m_35570