重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
在数据库运维工作中,经常会有数据目录使用率较高需要调整的情况,通常会给数据库建立多个表空间,
10多年的阜南网站建设经验,针对设计、前端、开发、售后、文案、推广等六对一服务,响应快,48小时及时工作处理。网络营销推广的优势是能够根据用户设备显示端的尺寸不同,自动调整阜南建站的显示方式,使网站能够适用不同显示终端,在浏览器中调整网站的宽度,无论在任何一种浏览器上浏览网站,都能展现优雅布局与设计,从而大程度地提升浏览体验。创新互联公司从事“阜南网站设计”,“阜南网站推广”以来,每个客户项目都认真落实执行。
并分别位于不同的盘上,这时需要做的工作就是调整库中现有表和索引的表空间,下面简单总结下这块维护
工作的内容,以下都是基于 PostgreSQL 9.0.1 做的测试。
一 查询某个表所在表空间的简单方法
PostgreSQL 提供类似" \ "命令很方便得到相关信息,命令如下:
skytf= \d test_2
Table "skytf.test_2"
Column | Type | Modifiers
--------+-----------------------+-----------
id | integer |
obj_id | integer | not null
name | character varying(64) |
Indexes:
"idx_hash_name" hash (name)
"idx_test_2" btree (id, obj_id)
Tablespace: "tbs_skytf_idx"
备注:如果这个表的表空间为当前数据库的默认表空间,那么上面则不会显示 Tablespace 信息,
相反,则会显示这张有的表空间,例如上面的表 test_2 的表空间为 tbs_skytf_idx,而
表空间 "tbs_skytf_idx" 不是数据库 skytf 的默认表空间, 那么如何查询数据库的默认
表空间呢,可以通过以下命令查询。
--1.1 查询数据库的默认表空间
skytf= select datname,dattablespace from pg_database where datname='skytf';
datname | dattablespace
---------+---------------
skytf | 14203070
(1 row)
skytf= select oid,spcname from pg_tablespace where oid=14203070;
oid | spcname
----------+-----------
14203070 | tbs_skytf
(1 row)
备注:通过以上查出数据库 skytf 的默认表空间为 tbs_skytf。
二 批量查询数据库表和索引的表空间
--2.1 查询表和索引所在的表空间
select relname, relkind, relpages,pg_size_pretty(pg_relation_size(a.oid)), tb.spcname
from pg_class a, pg_tablespace tb
where a.reltablespace = tb.oid
and a.relkind in ('r', 'i')
order by a.relpages desc;
备注:上面只取了部分结果,这个查询能够查询表和索引所处的表空间,但是有一点需要注意,这个查询
仅显示表空间不是数据库默认表空间的数据库对像,而我们通常需要查出位于数据库默认表空间的
对像,显然上面的查询不是我们想要的,接下来看另一个查询。
--2.2 查询位于默认数据库表空间的对像
select relname, relkind, relpages,pg_size_pretty(pg_relation_size(a.oid)),reltablespace,relowner
from pg_class a
where a.relkind in ('r', 'i')
and reltablespace='0'
order by a.relpages desc;
备注:这个查询加入限制条件 reltablespace='0',即可查找出位于当前数据库默认表空间的
数据库表和索引。 通常这才是我们想要的结果,接下来可以把部分表转移到其它表空间上去,转移
的方法可以用 "ALTER TABLE move tablespace "或者重建索引移表空间等方法,这里不详细介绍。
--2.3 查询在某个表空间上的对像
select relname, relkind, relpages,pg_size_pretty(pg_relation_size(a.oid)),reltablespace,relowner
from pg_class a, pg_tablespace tb
where a.relkind in ('r', 'i')
and a.reltablespace=tb.oid
and tb.spcname='tablespace_name'
order by a.relpages desc;
--2.4 手册上对于 pgclass 视图的 reltablespace 字段解释
The tablespace in which this relation is stored. If zero, the database is default tablespace is
implied. (Not meaningful if the relation has no on-disk file.)
可能的原因:
1) 如果查询的目标表中数据量很少的情况下,PostgreSQL不会走索引查询的,而是直接顺序查找;
2) 查询的条件中并未直接使用索引的字段,或者字段不完整;
一.gin索引需要安装第三方插件
yum install postgresql96-contrib -- 安装插件
find / -name extension --可以看到btree_gin.control存在
create extension btree_gin; -- 添加索引
二.测试数据基本属性介绍
总共使用3个表,表结构和数据量完全一致。
表数据量:10522369
表字段:id ,basic_acc_no,id_card,name,sex,telephone,json_t
1)索引的配置情况:
basic_account_info_al -- btree
basic_account_info_al2 --gin
basic_account_info_al3 -- btree multi
basic_account_info_al 单列索引 id,basic_acc_no,name,json_t
basic_account_info_al2 gin索引 (id,basic_acc_no,id_card,name),(json_t)
basic_account_info_al3 复合索引 (id,basic_acc_no),(name,id)(json_t,id)
basic_account_info_al 表达式索引 (json_t-id)
basic_account_info_al2表达式索引 ((json_t-'id'))
三.测试结果
1.唯一值属性:索引字段都是唯一 id,basic_acc_no
查询语句
explain analyse select * from basic_account_info_al2 where id = 29699221 ;
explain analyse select * from basic_account_info_al where id = 29699221 ;
explain analyse select * from basic_account_info_al3 where id = 29699221 ;
explain analyse select * from basic_account_info_al2 where basic_acc_no = 'XFK2990134' ;
explain analyse select * from basic_account_info_al where basic_acc_no = 'XFK2990134' ;
explain analyse select * from basic_account_info_al3 where basic_acc_no = 'XFK2990134' ;
explain analyse select * from basic_account_info_al2 where basic_acc_no = 'XFK9780134' and id = 29699221;
explain analyse select * from basic_account_info_al where basic_acc_no = 'XFK9780134' and id = 29699221;
explain analyse select * from basic_account_info_al3 where basic_acc_no = 'XFK9780134' and id = 29699221;
explain analyse select * from basic_account_info_al2 where id = 29699221 and basic_acc_no = 'XFK9780134' ;
explain analyse select * from basic_account_info_al where id = 29699221 and basic_acc_no = 'XFK9780134' ;
explain analyse select * from basic_account_info_al3 where id = 29699221 and basic_acc_no = 'XFK9780134' ;
2.重复值属性: name是有重复值的。
explain analyse select * from basic_account_info_al where name ='张燕洪';
explain analyse select * from basic_account_info_al3 where name ='张燕洪';
explain analyse select *from basic_account_info_al2 where name ='张燕洪';
explain analyse select * from basic_account_info_al2 where id = 24426014 and name = '周杨' ;
explain analyse select * from basic_account_info_al where id = 24426014 and name = '周杨' ;
explain analyse select * from basic_account_info_al3 where id = 24426014 and name = '周杨' ;
explain analyse select * from basic_account_info_al2 where name = '周杨' and id = 24426014 ;
explain analyse select * from basic_account_info_al where name = '周杨' and id = 24426014 ;
explain analyse select * from basic_account_info_al3 where name = '周杨' and id = 24426014 ;
3.jsonb属性
create index inx_gin_json on basic_account_info_al2 using gin (json_t);
create index inx_btree_json on basic_account_info_al (json_t);
create index inx_btree_2_js on basic_account_info_al3 (json_t,id );
explain analyse select * from basic_account_info_al where json_t ='{"id": 21782879, "sex": 0, "name": "刘乐典"}';
explain analyse select * from basic_account_info_al2 where json_t ='{"id": 21782879, "sex": 0, "name": "刘乐典"}';
explain analyse select * from basic_account_info_al3 where json_t ='{"id": 21782879, "sex": 0, "name": "刘乐典"}';
explain analyse select * from basic_account_info_al WHERE json_t @ '{"id": 21782879}';
explain analyse select * from basic_account_info_al2 WHERE json_t @ '{"id": 21782879}';
explain analyse select * from basic_account_info_al3 WHERE json_t @ '{"id": 21782879}';
explain analyse select * from basic_account_info_al where (json_t-id)= '24426014' ;
explain analyse select * from basic_account_info_al2 where (json_t-id)= '24426014' ;
explain analyse select * from basic_account_info_al3 where (json_t-id)='24426014' ;
4.jsonb表达式索引
查询条件 表名 查询时使用的索引名称 查询时间(5次平均)/ms
(json_t-id)= '24426014' basic_account_info_al inx_json_id 0.040
basic_account_info_al3 inx_json_id_2 0.039
explain analyse select * from basic_account_info_al where (json_t-id)= '24426014' ;
explain analyse select * from basic_account_info_al2 where (json_t-id)= '24426014' ;
四.获相同的结果使用Jsonb与btree对比
jsonb支持两种特有的GIN索引jsonb_ops和jsonb_path_ops。 jsonb_ops调用gin_extract_jsonb函数生成key,每个键和值都作为一个单独的索引项。而jsonb_path_ops使用函数gin_extract_jsonb_path抽取:只为每个值创建一个索引项。{“foo”:{“bar”,”baz”}}, jsonb_ops生成3个索引项,jsonb_path_ops由foo,bar,baz组合一个hash值作为一个索引项。jsonb_path_ops索引要比jsonb_ops的小很多,性能上也会有所提升。
create index inx_gin_patn_json ON public.basic_account_info_al4 USING gin (json_t jsonb_path_ops); -- jsonb_path_ops
create index inx_gin_json on basic_account_info_al2 using gin (json_t); --jsonb_ops
1.精确查询
2.范围查询
下表显示了gin索引对于jsonb数据类型可使用的操作符。
名称 索引数据类型 可索引操作符
jsonb_ops jsonb ? ? ?| @
json_path_ops jsonb @
注:? ? ?| 索引key是否包含在jsonb中
对于范围(json_t-'id') 20000079,这样的条件 gin索引不起作用, 这里采用表达式索引方式,查询条件的两边数据类型相同才可以做索引查询,否则全表扫描。
CREATE INDEX inx_json_id_2 ON public.basic_account_info_al2 USING btree (((json_t-'id')::int));
总结: 当仅有一个条件查询时,gin索引与btree索引的性能差异不大,但有多个条件查询时,gin,btree单
列索引没有btree复合索引的性能高。jsonb是以二进制格式存储且不保证键的顺序。可以使用表达式索引指定到jsonb的具体键值,但是如果不能提前知道查询数据中的哪个键,确定定义GIN索引和使用@(或者其他有利于索引的操作符)查询。
五.jsonb添加数据属性
例如:
{"id":20000241,"name":"陈敏","sex":1} - {"age":"18","id":20000241,"name":"陈敏","sex":1}
一旦创建了索引,就不需要进一步的干预:当表被修改时,系统将更新索引,当执行计划认为使用索引比顺序的表扫描更有效的时候,它会使用索引。
UPDATE basic_account_info_al4 SET json_t = json_t || '{"age":"18"}'::jsonb; -- 更新语句
gin索引名称 索引方式 修改前大小 修改后大小 带索引更新时间
inx_gin_patn_json jsonb_path_ops 574M 615M 643561.004 ms
inx_gin_json jsonb_ops 665M 695M 时间过长超过1h
jsonb_ops方式建立的索引大量更新时,执行时间太长。当插入更新时gin索引比较慢,如果要向一张大表中插入大量数据时,最好先把gin索引删除,插入数据后再重建索引。
当json_t为{"id":20000241,"name":"陈敏","sex":1} 数据量为10522369 创建gin索引时间
130372.955 ms
当json_t为{"age":"18","id":20000241,"name":"陈敏","sex":1} 数据量为10522369 创建gin索引时间
148971.011 ms
一、索引的类型:
PostgreSQL提供了多种索引类型:B-Tree、Hash、GiST和GIN,由于它们使用了不同的算法,因此每种索引类型都有其适合的查询类型,缺省时,CREATE INDEX命令将创建B-Tree索引。
1. B-Tree:
CREATE TABLE test1 (
id integer,
content varchar
);
CREATE INDEX test1_id_index ON test1 (id);
B-Tree索引主要用于等于和范围查询,特别是当索引列包含操作符" 、=和"作为查询条件时,PostgreSQL的查询规划器都会考虑使用B-Tree索引。在使用BETWEEN、IN、IS NULL和IS NOT NULL的查询中,PostgreSQL也可以使用B-Tree索引。然而对于基于模式匹配操作符的查询,如LIKE、ILIKE、~和 ~*,仅当模式存在一个常量,且该常量位于模式字符串的开头时,如col LIKE 'foo%'或col ~ '^foo',索引才会生效,否则将会执行全表扫描,如:col LIKE '%bar'。
2. Hash:
CREATE INDEX name ON table USING hash (column);
散列(Hash)索引只能处理简单的等于比较。当索引列使用等于操作符进行比较时,查询规划器会考虑使用散列索引。
这里需要额外说明的是,PostgreSQL散列索引的性能不比B-Tree索引强,但是散列索引的尺寸和构造时间则更差。另外,由于散列索引操作目前没有记录WAL日志,因此一旦发生了数据库崩溃,我们将不得不用REINDEX重建散列索引。
3. GiST:
GiST索引不是一种单独的索引类型,而是一种架构,可以在该架构上实现很多不同的索引策略。从而可以使GiST索引根据不同的索引策略,而使用特定的操作符类型。
4. GIN:
GIN索引是反转索引,它可以处理包含多个键的值(比如数组)。与GiST类似,GIN同样支持用户定义的索引策略,从而可以使GIN索引根据不同的索引策略,而使用特定的操作符类型。作为示例,PostgreSQL的标准发布中包含了用于一维数组的GIN操作符类型,如:、=、等。
二、复合索引:
PostgreSQL中的索引可以定义在数据表的多个字段上,如:
CREATE TABLE test2 (
major int,
minor int,
name varchar
}
CREATE INDEX test2_mm_idx ON test2 (major, minor);
1. B-Tree类型的复合索引:
在B-Tree类型的复合索引中,该索引字段的任意子集均可用于查询条件,不过,只有当复合索引中的第一个索引字段(最左边)被包含其中时,才可以获得最高效率。
2. GiST类型的复合索引:
在GiST类型的复合索引中,只有当第一个索引字段被包含在查询条件中时,才能决定该查询会扫描多少索引数据,而其他索引字段上的条件只是会限制索引返回的条目。假如第一个索引字段上的大多数数据都有相同的键值,那么此时应用GiST索引就会比较低效。
3. GIN类型的复合索引:
与B-Tree和GiST索引不同的是,GIN复合索引不会受到查询条件中使用了哪些索引字段子集的影响,无论是哪种组合,都会得到相同的效率。
使用复合索引应该谨慎。在大多数情况下,单一字段上的索引就已经足够了,并且还节约时间和空间。除非表的使用模式非常固定,否则超过三个字段的索引几乎没什么用处。
三、组合多个索引:
PostgreSQL可以在查询时组合多个索引(包括同一索引的多次使用),来处理单个索引扫描不能实现的场合。与此同时,系统还可以在多个索引扫描之间组成AND和OR的条件。比如,一个类似WHERE x = 42 OR x = 47 OR x = 53 OR x = 99的查询,可以被分解成四个独立的基于x字段索引的扫描,每个扫描使用一个查询子句,之后再将这些扫描结果OR在一起并生成最终的结果。另外一个例子是,如果我们在x和y上分别存在独立的索引,那么一个类似WHERE x = 5 AND y = 6的查询,就会分别基于这两个字段的索引进行扫描,之后再将各自扫描的结果进行AND操作并生成最终的结果行。
为了组合多个索引,系统扫描每个需要的索引,然后在内存里组织一个BITMAP,它将给出索引扫描出的数据在数据表中的物理位置。然后,再根据查询的需要,把这些位图进行AND或者OR的操作并得出最终的BITMAP。最后,检索数据表并返回数据行。表的数据行是按照物理顺序进行访问的,因为这是位图的布局,这就意味着任何原来的索引的排序都将消失。如果查询中有ORDER BY子句,那么还将会有一个额外的排序步骤。因为这个原因,以及每个额外的索引扫描都会增加额外的时间,这样规划器有时候就会选择使用简单的索引扫描,即使有多个索引可用也会如此。
四、唯一索引:
CREATE UNIQUE INDEX name ON table (column [, ...]);
五、表达式索引:
表达式索引主要用于在查询条件中存在基于某个字段的函数或表达式的结果与其他值进行比较的情况,如:
SELECT * FROM test1 WHERE lower(col1) = 'value';
此时,如果我们仅仅是在col1字段上建立索引,那么该查询在执行时一定不会使用该索引,而是直接进行全表扫描。如果该表的数据量较大,那么执行该查询也将会需要很长时间。解决该问题的办法非常简单,在test1表上建立基于col1字段的表达式索引,如:
CREATE INDEX test1_lower_col1_idx ON test1 (lower(col1));
SELECT * FROM people WHERE (first_name || ' ' || last_name) = 'John Smith';
和上面的例子一样,尽管我们可能会为first_name和last_name分别创建独立索引,或者是基于这两个字段的复合索引,在执行该查询语句时,这些索引均不会被使用,该查询能够使用的索引只有我们下面创建的表达式索引。
CREATE INDEX people_names ON people ((first_name || ' ' || last_name));
CREATE INDEX命令的语法通常要求在索引表达式周围书写圆括弧,就像我们在第二个例子里显示的那样。如果表达式只是一个函数调用,那么可以省略,就像我们在第一个例子里显示的那样。
从索引维护的角度来看,索引表达式要相对低效一些,因为在插入数据或者更新数据的时候,都必须为该行计算表达式的结果,并将该结果直接存储到索引里。然而在查询时,PostgreSQL就会把它们看做WHERE idxcol = 'constant',因此搜索的速度等效于基于简单索引的查询。通常而言,我们只是应该在检索速度比插入和更新速度更重要的场景下使用表达式索引。
六、部分索引:
部分索引(partial index)是建立在一个表的子集上的索引,而该子集是由一个条件表达式定义的(叫做部分索引的谓词)。该索引只包含表中那些满足这个谓词的行。
由于不是在所有的情况下都需要更新索引,因此部分索引会提高数据插入和数据更新的效率。然而又因为部分索引比普通索引要小,因此可以更好的提高确实需要索引部分的查询效率。见以下三个示例:
1. 索引字段和谓词条件字段一致:
CREATE INDEX access_log_client_ip_ix ON access_log(client_ip)
WHERE NOT (client_ip inet '192.168.100.0' AND client_ip inet '192.168.100.255');
下面的查询将会用到该部分索引:
SELECT * FROM access_log WHERE url = '/index.html' AND client_ip = inet '212.78.10.32';
下面的查询将不会用该部分索引:
一个不能使用这个索引的查询可以是
SELECT * FROM access_log WHERE client_ip = inet '192.168.100.23';
2. 索引字段和谓词条件字段不一致:
PostgreSQL支持带任意谓词的部分索引,唯一的约束是谓词的字段也要来自于同样的数据表。注意,如果你希望你的查询语句能够用到部分索引,那么就要求该查询语句的条件部分必须和部分索引的谓词完全匹配。 准确说,只有在PostgreSQL能够识别出该查询的WHERE条件在数学上涵盖了该索引的谓词时,这个部分索引才能被用于该查询。
CREATE INDEX orders_unbilled_index ON orders(order_nr) WHERE billed is not true;
下面的查询一定会用到该部分索引:
SELECT * FROM orders WHERE billed is not true AND order_nr 10000;
那么对于如下查询呢?
SELECT * FROM orders WHERE billed is not true AND amount 5000.00;
这个查询将不像上面那个查询这么高效,毕竟查询的条件语句中没有用到索引字段,然而查询条件"billed is not true"却和部分索引的谓词完全匹配,因此PostgreSQL将扫描整个索引。这样只有在索引数据相对较少的情况下,该查询才能更有效一些。
下面的查询将不会用到部分索引。
SELECT * FROM orders WHERE order_nr = 3501;
3. 数据表子集的唯一性约束:
CREATE TABLE tests (
subject text,
target text,
success boolean,
...
);
CREATE UNIQUE INDEX tests_success_constraint ON tests(subject, target) WHERE success;
该部分索引将只会对success字段值为true的数据进行唯一性约束。在实际的应用中,如果成功的数据较少,而不成功的数据较多时,该实现方法将会非常高效。
七、检查索引的使用:
见以下四条建议:
1. 总是先运行ANALYZE。
该命令将会收集表中数值分布状况的统计。在估算一个查询返回的行数时需要这个信息,而规划器则需要这个行数以便给每个可能的查询规划赋予真实的开销值。如果缺乏任何真实的统计信息,那么就会使用一些缺省数值,这样肯定是不准确的。因此,如果还没有运行ANALYZE就检查一个索引的使用状况,那将会是一次失败的检查。
2. 使用真实的数据做实验。
用测试数据填充数据表,那么该表的索引将只会基于测试数据来评估该如何使用索引,而不是对所有的数据都如此使用。比如从100000行中选1000行,规划器可能会考虑使用索引,那么如果从100行中选1行就很难说也会使用索引了。因为100行的数据很可能是存储在一个磁盘页面中,然而没有任何查询规划能比通过顺序访问一个磁盘页面更加高效了。与此同时,在模拟测试数据时也要注意,如果这些数据是非常相似的数据、完全随机的数据,或按照排序顺序插入的数据,都会令统计信息偏离实际数据应该具有的特征。
3. 如果索引没有得到使用,那么在测试中强制它的使用也许会有些价值。有一些运行时参数可以关闭各种各样的查询规划。
4. 强制使用索引用法将会导致两种可能:一是系统选择是正确的,使用索引实际上并不合适,二是查询计划的开销计算并不能反映现实情况。这样你就应该对使用和不使用索引的查询进行计时,这个时候EXPLAIN ANALYZE命令就很有用了。