Postgres基于非索引列从大表中提取数据

如何解决Postgres基于非索引列从大表中提取数据

我们有一张关于生产的表,它已经存在了很长一段时间,并且该表的容量很大(接近 3 TB),因为该表中的大部分数据都是陈旧且未使用的,我们计划将其删除没有任何参考的历史数据。 有一个类型为 boolean 的“active”列,我们可以使用它来删除这些数据,但是该列未编入索引。 考虑到表的体积,我不太确定创建新索引是否会有所帮助,我尝试一次增量删除 100K 的非活动行,但体积仍然如此之大,以至于需要数月才能清除向上。 表的主键是 UUID 类型,我想创建一个新表并只插入 active="true" as

insert
    into
    mytable_active
        select
            *
        from
            mytable
        where
            is_active = true;   

但正如预期的那样,这种方法也因体积而失败,并且永远运行。

欢迎任何建议方法。

解决方法

当你需要快速删除很多行时,分区很好.......当表已经分区时。

如果您需要的列上没有索引,那么至少需要进行一次全表扫描,除非您可以使用另一个索引(例如“日期”或其他索引)来缩小范围。

我的意思是,你可以创建一个索引“WHERE active”,但这也需要你试图避免的全表扫描,所以......嗯。

首先,删除。只是不要,即使是 LIMIT 的一小部分也不行。它不仅会写入大部分表(3TB 写入),还会将其写入 WAL(另外 3 TB),还会更新索引,并将其也写入 WAL。这将永远持续下去,并且来自索引更新的随机 IO 会影响您的性能。如果它完成了,你仍然会有一个 3TB 的文件,其中大部分是未分配的。加上索引。

所以,没有删除。呃,等等。

  • 使用 DELETE 的场景:

用视图“SELECT * FROM humongous WHERE active=true”交换表,并在视图上添加触发器或规则以将更新/插入/删除重定向到基础表。确保触发器将所有新行设置为 active=true。

重新创建除主键之外的每个索引(同时),添加“WHERE active=true”。这将需要对第一个索引进行全表扫描,即使您在“活动”上创建索引,因为 CREATE INDEX WHERE 在指定 WHERE 时似乎无法使用另一个索引来加速。

删除旧索引

注意视图的目的只是为了确保绝对所有查询在 WHERE 中都有“active=true”,否则,它们将无法使用我们刚刚创建的条件索引,因此每个查询都将是一个全表扫描,这是不可取的。

现在,您可以使用 delete from mytable where id in ( select id from mytable where active = false limit 100000);

一点一点地删除

这是一个权衡,你将有大量的表扫描来重新创建索引,但你会避免由于大量删除而导致索引更新的随机 IO,这就是你说这需要几个月的真正原因.

  • INSERT INTO new_table SELECT 的场景...

如果你在这个巨大的表上运行了插入和更新,那么你就有问题了,因为这些在操作过程中不会被转移到新表中。因此,解决方案是:

  • 关闭所有运行长查询的脚本和服务
  • 锁定一切
  • 创建新表
  • 将huge_table 重命名为huge_old
  • 创建一个由huge_table 和huge_old 组成的UNION ALL 视图。从应用的角度来看,这个视图替代了huge_table。它必须处理优先级,即如果新表中存在一行,则应忽略旧表中存在的具有相同 id 的行......因此它必须有一个 JOIN。应事先仔细测试此步骤。
  • 解锁

然后,让它运行一段时间,看看视图是否会破坏你的性能。在这一点上,如果它坏了,您可以通过删除视图并将表重命名回原来的自己来轻松返回。我说过要关闭所有运行长查询的脚本和服务,因为它们可能会因视图而失败,并且您不希望在一个长查询运行时使用大锁,因为这将停止一切,直到它完成。

  • 在视图上添加插入/更新/删除触发器以将写入重定向到 new_table。插入直接进入新表,更新必须传输行,删除必须命中两个表,UNIQUE 约束将......很有趣。这会有点复杂。

现在传输数据。

即使需要一段时间,谁在乎呢?它最终会完成。我想如果你有一个 3TB 的表,你必须有一些不错的存储空间,即使这是我们用来放置数据的这些旧的旋转东西,如果 IO 不是随机的,它应该不会超过几个小时 . 所以这个想法是只使用线性 IO。

手指交叉,希望该表没有存储在单独的 TOAST 表中的大文本列,该列需要每行随机访问一次。你检查了吗?

现在,您实际上可能希望它运行更长的时间,以便它使用更少的 IO 带宽,用于读取和写入,尤其是 WAL 写入。只要查询不会降低其他用户的性能,查询运行多长时间并不重要。

Postgres 可能会进行并行表扫描以使用框中的所有内核和所有 IO,因此可能先禁用它。

那么我认为你应该尽量避免这种搞笑的(对于旁观者来说)场景,它从表中读取了半天,没有找到任何匹配的行,所以磁盘可以很好地处理读取,然后它会找到所有最后匹配的行并继续将 300GB 写入 WAL 和目标表,导致巨大的写入争用,当您知道时必须按 Ctrl-C,您只是在直觉中知道它已接近完成.

所以:

 create bogus_table just like mytable but without indices;
 insert into bogus_table select * from mytable;

10% 的“活动”行仍然是 300GB,所以最好检查一下服务器是否可以处理写入 300GB 的表而不会减慢速度。观察 vmstat 并检查 iowait 是否发疯,观察每秒事务数、查询延迟、Web 服务器响应能力,以及通常的数据库健康状况。如果电话响了,请按 Ctrl-C 并说“已修复!”

完成几个检查点后,Ctrl-C。是时候做真正的事情了。

现在要使此查询花费更长的时间(因此破坏更少的 IO 带宽),您可以将其添加到选择中的列中:

pg_sleep((random()<0.000001)::INTEGER * 0.1)

这将使其平均每百万行休眠 0.1 秒。在查看 vmstat 时根据口味进行调整。

您还可以使用 hacks 监控查询进度。

它应该可以正常工作。

一旦从被诅咒的表中提取了有趣的行,您就可以将旧数据移动到数据仓库或其他地方,或者移动到冷存储中,或者如果您想运行一些分析,就可以将其加载到 clickhouse 中。

>

也许在新表增长回 3TB 之前对其进行分区也是一个好主意。或者定期移动旧行。

现在,我想知道你如何备份这个东西...

-- 编辑

好吧,我有另一个想法,也许更简单,但你需要一个盒子。

获取具有快速存储和设置逻辑复制功能的第二台服务器。在这个副本服务器上,创建一个大表的空 UNLOGGED 副本,主键上只有一个索引。逻辑复制会复制整个表,所以需要一段时间。原始服务器中的第二块网卡或一些 QoS 调整将有助于不破坏您实际用于提供查询服务的以太网连接。

逻辑复制是基于行的,并通过主键识别行,因此您绝对需要在从属设备上手动创建该 PK 索引。

我现在已经在我的家用盒子上进行了测试,效果很好。初始数据传输有点慢,但这可能是我的网络。暂停然后恢复复制传输在暂停期间在主服务器上插入或更新的行。但是,重命名表似乎会破坏它,因此您将无法执行 INSERT INTO SELECT,您必须在副本上执行 DELETE。使用 SSD,只有一个 PK 索引,将表设置为 UNLOGGED,应该不会永远占用。也许使用 btrfs 会将随机索引写入 IO 转换为线性 IO,因为它具有写入时复制的性质。或者,如果 PK 索引适合 shared_buffers,只需 YOLO 并将 checkpoint_timeout 设置为“7 天”,这样它实际上不会写入任何内容。您可能需要分块进行删除,以便复制的更新跟上。

当我删除 PK 索引以加快删除速度,然后在重新启用复制之前重新创建它时,它没有赶上更新。所以你不能删除索引。

但是有没有一种方法可以只传输您想要保留的行而不是传输所有内容并删除,同时让副本跟上主服务器的更新?...可以为插入执行此操作(只需禁用初始数据副本)但不幸的是不适用于更新。您需要一个整数主键,以便您可以在副本上生成虚假行,然后在复制期间更新这些行……但您不能使用 UUID PK 来做到这一点。

无论如何。完成此操作后,将要保留在主服务器上的 WAL 段数设置为非常高的值,以便稍后恢复复制而不会丢失更新。

现在你可以在副本上运行你的大 DELETE。完成后,清空,也许是 CLUSTER,重新创建所有索引等,并将表设置为 LOGGED。

然后您可以故障转移到新服务器。或者,如果您喜欢冒险,您可以将副本的表复制回主服务器上,因为它在另一个架构中应该具有相同的名称。

因为所有更新都是复制的,所以应该允许非常短的停机时间,副本将始终是最新的。

,

我建议:

  1. 将活动记录复制到临时表
  2. 删除主表
  3. 将临时表重命名为主表名

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-