索引膨胀

对于索引,随着业务不断的增删改,会造成膨胀,尤其Btree索引,也会涉及索引分裂、合并等,导致索引访问效率降低、维护成本增加。另外,索引页的复用与HEAP PAGE不一样,因为索引的内容是有序结构,只有符合顺序的ITEM才能插入对应的PAGE中,不像HEAP TUPLE,只要有空间就可以插入。index page无论在任何位置,都不能从磁盘删除,因此索引变大后,不能回收空间,除非vacuum full。所以索引膨胀后,通常需要重建索引来回收索引空间。

此外,对于B树索引,新构建的索引比多次更新的索引访问速度稍快,因为逻辑上相邻的页面通常在新构建索引中也是物理上相邻的。为了提高访问速度,定期重新B_Tree 索引可能是值得的。

对于重建索引,REINDEX在任何情况下都可以安全方便地使用。默认情况下,该命令需要ACCESS EXCLUSIVE锁。可以使用CONCURRENTLY选项创建索引,该选项只需要SHARE UPDATE EXCLUSIV锁,不阻塞读写。

索引膨胀的原因:

1.大量删除发生后,导致索引页面稀疏,降低了索引使用效率。

2.长时间运行的事务,阻止了vacuum对表的清理工作,因而导致页面稀疏状态一直保持。

3.索引字段乱序写入,导致索引频繁分裂,使得索引页并不是百分百填满,所以膨胀。

查询获取每个表的行数、索引以及有关这些索引的一些信息:


  1. SELECT
  2. pg_class.relname,
  3. pg_size_pretty(pg_class.reltuples::bigint) AS rows_in_bytes,
  4. pg_class.reltuples AS num_rows,
  5. COUNT(*) AS total_indexes,
  6. COUNT(*) FILTER ( WHERE indisunique) AS unique_indexes,
  7. COUNT(*) FILTER ( WHERE indnatts = 1 ) AS single_column_indexes,
  8. COUNT(*) FILTER ( WHERE indnatts IS DISTINCT FROM 1 ) AS multi_column_indexes
  9. FROM
  10. pg_namespace
  11. LEFT JOIN pg_class ON pg_namespace.oid = pg_class.relnamespace
  12. LEFT JOIN pg_index ON pg_class.oid = pg_index.indrelid
  13. WHERE
  14. pg_namespace.nspname = 'public' AND
  15. pg_class.relkind = 'r'
  16. GROUP BY pg_class.relname, pg_class.reltuples
  17. ORDER BY pg_class.reltuples DESC;

  1. SELECT
  2. t.schemaname,
  3. t.tablename,
  4. c.reltuples::bigint AS num_rows,
  5. pg_size_pretty(pg_relation_size(c.oid)) AS table_size,
  6. psai.indexrelname AS index_name,
  7. pg_size_pretty(pg_relation_size(i.indexrelid)) AS index_size,
  8. CASE WHEN i.indisunique THEN 'Y' ELSE 'N' END AS "unique",
  9. psai.idx_scan AS number_of_scans,
  10. psai.idx_tup_read AS tuples_read,
  11. psai.idx_tup_fetch AS tuples_fetched
  12. FROM
  13. pg_tables t
  14. LEFT JOIN pg_class c ON t.tablename = c.relname
  15. LEFT JOIN pg_index i ON c.oid = i.indrelid
  16. LEFT JOIN pg_stat_all_indexes psai ON i.indexrelid = psai.indexrelid
  17. WHERE
  18. t.schemaname NOT IN ('pg_catalog', 'information_schema')
  19. ORDER BY 1, 2;

查找重复索引,查找具有相同列集、相同操作类、表达式和谓词的多个索引,但需要人为判断需要删除的重复项:


  1. SELECT pg_size_pretty(sum(pg_relation_size(idx))::bigint) as size,
  2. (array_agg(idx))[1] as idx1, (array_agg(idx))[2] as idx2,
  3. (array_agg(idx))[3] as idx3, (array_agg(idx))[4] as idx4
  4. FROM (
  5. SELECT indexrelid::regclass as idx, (indrelid::text ||E'\n'|| indclass::text ||E'\n'|| indkey::text ||E'\n'||coalesce(indexprs::text,'')||E'\n' || coalesce(indpred::text,'')) as key
  6. FROM pg_index) sub
  7. GROUP BY key HAVING count(*)>1
  8. ORDER BY sum(pg_relation_size(idx)) DESC;

查找未使用的索引:


  1. select
  2. indexrelid::regclass as index, relid::regclass as table
  3. from
  4. pg_stat_user_indexes
  5. JOIN pg_index USING (indexrelid)
  6. where
  7. idx_scan = 0 and indisunique is false;

有些场景,重建索引后,索引就变小了。通常这种情况是索引字段乱序写入,导致索引频繁分裂,使得索引页并不是百分百填满,密度低,索引页面浪费。

索引碎片模拟


  1. 乱序写入:
  2. test=# create table t_split(id int);
  3. CREATE TABLE
  4. test=# create index idx_split on t_split (id);
  5. CREATE INDEX
  6. test=# insert into t_split select random()*1000000 from generate_series(1,1000000);
  7. INSERT 0 1000000
  8. test=# \di+ idx_split
  9. List of relations
  10. Schema | Name | Type | Owner | Table | Size | Description
  11. --------+-----------+-------+--------+---------+-------+-------------
  12. public | idx_split | index | system | t_split | 30 MB |
  13. (1 row)
  14. 顺序写入:
  15. test=# truncate t_split ;
  16. TRUNCATE TABLE
  17. test=# \di+ idx_split
  18. List of relations
  19. Schema | Name | Type | Owner | Table | Size | Description
  20. --------+-----------+-------+--------+---------+------------+-------------
  21. public | idx_split | index | system | t_split | 8192 bytes |
  22. (1 row)
  23. test=# insert into t_split select generate_series(1,1000000);
  24. INSERT 0 1000000
  25. test=# \di+ idx_split
  26. List of relations
  27. Schema | Name | Type | Owner | Table | Size | Description
  28. --------+-----------+-------+--------+---------+-------+-------------
  29. public | idx_split | index | system | t_split | 22 MB |
  30. (1 row)
  31. 先写入数据,后建索引:
  32. test=# drop index idx_split ;
  33. DROP INDEX
  34. test=# create index idx_split on t_split (id);
  35. CREATE INDEX
  36. test=# \di+ idx_split
  37. List of relations
  38. Schema | Name | Type | Owner | Table | Size | Description
  39. --------+-----------+-------+--------+---------+-------+-------------
  40. public | idx_split | index | system | t_split | 22 MB |
  41. (1 row)
  42. 业务运行久了,不断的增删改,也会导致索引碎片:
  43. test=# create table test(id int);
  44. CREATE TABLE
  45. test=# insert into test values(generate_series(1,1000000));
  46. INSERT 0 1000000
  47. test=# create index idx_fragmented on test(id);
  48. CREATE INDEX
  49. CREATE EXTENSION kbstattuple;
  50. 刚刚创建的索引没有碎片:
  51. test=# \x
  52. Expanded display is on.
  53. test=# SELECT * FROM pgstatindex('idx_fragmented');
  54. -[ RECORD 1 ]------+---------
  55. version | 4
  56. tree_level | 2
  57. index_size | 22609920
  58. root_block_no | 289
  59. internal_pages | 11
  60. leaf_pages | 2748
  61. empty_pages | 0
  62. deleted_pages | 0
  63. avg_leaf_density | 89.93
  64. leaf_fragmentation | 0
  65. leaf_fragmentation的碎片率是33.33%:
  66. test=# insert into test values(generate_series(1,1000000));
  67. INSERT 0 1000000
  68. test=# SELECT * FROM pgstatindex('idx_fragmented');
  69. -[ RECORD 1 ]------+---------
  70. version | 4
  71. tree_level | 2
  72. index_size | 67846144
  73. root_block_no | 289
  74. internal_pages | 39
  75. leaf_pages | 8242
  76. empty_pages | 0
  77. deleted_pages | 0
  78. avg_leaf_density | 60.06
  79. leaf_fragmentation | 33.33
  80. reindex之后,即可回收空间,减少碎片。
  81. test=# reindex index idx_fragmented;
  82. REINDEX
  83. test=# \di+ idx_fragmented
  84. List of relations
  85. -[ RECORD 1 ]---------------
  86. Schema | public
  87. Name | idx_fragmented
  88. Type | index
  89. Owner | system
  90. Table | test
  91. Size | 43 MB
  92. Description |
  93. test=# SELECT * FROM pgstatindex('idx_fragmented');
  94. -[ RECORD 1 ]------+---------
  95. version | 4
  96. tree_level | 2
  97. index_size | 45236224
  98. root_block_no | 208
  99. internal_pages | 26
  100. leaf_pages | 5495
  101. empty_pages | 0
  102. deleted_pages | 0
  103. avg_leaf_density | 90.01
  104. leaf_fragmentation | 0

总结

通过以上方法监控索引膨胀,以及索引碎片情况,及时对索引reindex 进行碎片优化,建议不要在一个表上建太多索引,准确评估经常update的列和经常select的列,以便创建合适的索引。

文章知识点与官方知识档案匹配,可进一步学习相关知识
CS入门技能树Linux入门初识Linux32604 人正在系统学习中

[转帖]金仓数据库KingbaseES V8R6 索引膨胀的更多相关文章

  1. 通过ODBC接口访问人大金仓数据库

      国产化软件和国产化芯片的窘境一样,一方面市场已经存在性能优越的同类软件,成本很低,但小众的国产化软件不仅需要高价买入版权,并且软件开发维护成本高:另一方面,国产软件目前普遍难用,性能不稳定,Bug ...

  2. QT 之 ODBC连接人大金仓数据库

    QT 之 使用 ODBC 驱动连接人大金仓数据库 获取数据库驱动和依赖动态库 此操作可在人大金仓官网下载与系统匹配的接口动态库,或者从架构数据库的源码中获取驱动和依赖动态库 分别为: 驱动动态库:kd ...

  3. 通过jmeter连接人大金仓数据库

    某项目用的人大金仓数据库,做性能测试,需要用jmeter来连接数据库处理一批数据.jmeter连接人大金仓,做个记录. 1. 概要 在"配置元件"中添加"JDBC Con ...

  4. linux安装国产数据库(金仓数据库,达梦数据库,南大通用数据库)

    今天在公司做的任务是,在Linux的环境下安装三种数据库,结果一种数据库也没有安装好,首先遇到的问题是安装南大通用数据库遇到安装的第五步,就出现问题了,问题是Gbase SDK没有安装成功,以及Gba ...

  5. Rocky4.2下安装金仓v7数据库(KingbaseES)

    1.准备操作系统 1.1 系统登录界面 1.2 操作系统版本信息 jdbh:~ # uname -ra Linux jdbh -x86_64 # SMP Fri Dec :: CST x86_64 G ...

  6. 润乾配置连接kingbase(金仓)数据库

     问题背景 客户根据项目的不同,使用润乾连接的数据库类型各种各样,此文针对前几日使用润乾设计器连接kingbase金仓数据库做一个说明. kingbase金仓数据库是一款国产数据库,操作方式和配置 ...

  7. 金仓Kingbase数据库网页数据维护分析工具

    金仓Kingbase是优秀的国产数据库产品,在能源,政务,国防等领域广泛使用, 现在TreeSoft数据库管理系统已支持Kingbase了,直接在浏览器中就可以操作查看Kingbase数据了,十分方便 ...

  8. DBeaver连接达梦|虚谷|人大金仓等国产数据库

    前言 工作中有些项目可能会接触到「达梦.虚谷.人大金仓」等国产数据库,但通常这些数据库自带的连接工具使用并不方便,所以这篇文章记录一下 DBeaver 连接国产数据库的通用模版,下文以达梦为例(其他国 ...

  9. KingbaseES V8R6备份恢复案例之---同一数据库创建不同stanza备份

    案例说明: 在生产环境,有的应用需要调用数据库的sys_rman做备份,为了区分数据库自身的sys_rman备份和应用的备份,可以使用不同的stanza name创建备份.本案例介绍了,如何在King ...

  10. KingbaseES V8R6备份恢复案例之--删除test数据库后sys_backup.sh备份

    案例说明: KingbaseES V8R6通过sys_backup.sh执行物理备份,默认sys_backup.sh执行备份初始化时,需要连接test数据库进行身份的认证:在一些生产环境为了安全需求, ...

随机推荐

  1. MoE:LLM终身学习的可能性

    本文分享自华为云社区<DTSE Tech Talk | 第47期:MoE:LLM终身学习的可能性>,作者:华为云社区精选. 在DTSE Tech Talk的第47期直播<MoE:LL ...

  2. 超详细API插件使用教程,教你开发AI垃圾分类机器人

    本文分享自华为云社区[案例教学]华为云API对话机器人的魅力-体验AI垃圾分类机器人,作者:华为云PaaS服务小智. 体验用Huawei Cloud API开发AI垃圾分类机器人,并学习AI自然语言的 ...

  3. 跟着B站UP主小姐姐去华为坂田基地采访扫地僧

    摘要:谁说程序员就只能写代码呢!华为扫地僧的才艺是完全可以solo出道的那种. 忍不住想要和你们分享下我9月份的快乐呀!Mark下最近完成的一件超了不起的事情!我去你们口中别人家的公司-华为啦!这次采 ...

  4. 8种图数据库对 NULL 属性值支持情况

    摘要:在语义网等图模型中,遵循开放世界假设,对于数据中未包含的事实,都认为是未知的而非假的. 本文分享自华为云社区<图数据库对 NULL 属性值支持情况>,原文作者:你好_TT . NUL ...

  5. 可视大盘 + 健康分机制,火山引擎 DataLeap 为企业降低资源优化门槛!

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 随着数仓及研发技术团队维护的数据量大.资源使用量大.成本越高.优化压力越大.如何主动发现无效或低效使用的资源,并且 ...

  6. 1个案例读懂——游戏产品如何用 A/B 测试做增长

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 随着国内游戏用户数量趋于饱和,中国游戏产业也从高速成长期逐渐转型,市场成熟度提升,竞争趋于精细化. 随着游戏出海以 ...

  7. Kubernetes(K8S) yaml 介绍

    使用空格做为缩进 缩进的空格数目不重要, 只要相同层级的元素左侧对齐即可 低版本缩进时不允许使用 Tab 键, 只允许使用空格 使用#标识注释, 从这个字符一直到行尾, 都会被解释器忽略 --- 使用 ...

  8. Python 数组比较

    a = [1, 2, 3, 5, 6, 5, 7, 8] b = [1, 3, 4, 5, 6, 3, 8, 7] print('A => %s' % a) print('B => %s' ...

  9. 01-什么是 Java:Java 初学者指南

    什么是Java? Java 是一种用于互联网分布式环境的面向对象编程语言.它是一种高级语言,也易于阅读和理解.有了它,开发人员可以"编写一次,随处运行"(WORA),这意味着编译后 ...

  10. Mysql--JOIN连表查询

    一.Join查询原理 MySQL内部采用了一种叫做 nested loop join(嵌套循环连接)的算法:通过驱动表的结果集作为循环基础数据,然后一条一条的通过该结果集中的数据作为过滤条件到下一个表 ...