news 2026/2/21 11:57:34

PostgreSQL如何能存储一亿条链接信息?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
PostgreSQL如何能存储一亿条链接信息?

文章目录

    • 前言
      • 1、直接存储一亿数据会遇到的问题是什么?
      • 2、为什么PostgreSQL是合适的选择?
      • 3、用PostgreSQL进行存储大体思路
    • 一、基础前提:链接信息表结构设计
      • 1.1 基础表结构
      • 1.2 存储空间测算(一亿条数据)
    • 二、核心调优:PostgreSQL 配置优化(postgresql.conf)
      • 2.1 内存相关配置(核心)
      • 2.2 IO 相关配置(提升写入/读取速度)
      • 2.3 连接与并发配置(支撑批量写入)
    • 三、索引设计:高效查询+去重(亿级数据的核心)
      • 3.1 必建索引(核心)
      • 3.2 索引优化原则
    • 四、数据写入:高效导入一亿条链接(避免写入瓶颈)
      • 4.1 批量写入(比单条INSERT快100倍)
      • 4.2 写入优化技巧
    • 五、亿级数据的进阶方案:分表/分区(解决单表查询慢)
      • 5.1 分区表(推荐,PostgreSQL 10+ 原生支持)
      • 5.2 分表(按哈希分表,适合无时间维度的场景)
    • 六、维护与优化:亿级数据的长期管理
      • 6.1 定期 Vacuum(清理死元组)
      • 6.2 数据归档
      • 6.3 监控性能

前言

1、直接存储一亿数据会遇到的问题是什么?

直接存储一亿条数据会遇到以下几个核心挑战:

  1. 写入性能瓶颈:一亿条数据的插入本身就是个巨大的工程。单条插入会非常慢,事务日志会膨胀,I/O会成为主要瓶颈。
  2. 查询性能急剧下降:没有合适的索引,一个简单的WHERE查询(如WHERE url = '...')可能需要扫描整个表,耗时从毫秒级上升到分钟甚至小时级。
  3. 存储空间和维护成本:一亿条数据会占用大量磁盘空间。索引同样会占用空间,并且会降低写入速度。VACUUM等维护操作会变得非常缓慢。
  4. 内存压力:如果查询需要处理大量数据,而服务器的work_mem等参数配置不当,可能会导致磁盘交换,使性能雪崩。

2、为什么PostgreSQL是合适的选择?

  1. 数据量上限:PostgreSQL本身对表大小的限制非常大(理论上是32TB),一亿条记录对于它来说完全在能力范围内。很多公司在生产环境中用PostgreSQL管理数十亿甚至更多的数据。
  2. 数据完整性:链接信息通常包含URL、标题、描述、状态码等。PostgreSQL提供强大的数据类型(如TEXTVARCHARJSONBTIMESTAMPTZ)和约束,能保证数据的完整性和一致性。
  3. ACID事务:在高并发写入和更新的场景下,ACID特性可以确保数据不会因为并发操作而损坏。
  4. 高级功能:PostgreSQL的索引(特别是GIN和GiST)、分区表、全文搜索等功能,对于管理和查询海量链接至关重要。

3、用PostgreSQL进行存储大体思路

场景推荐方案优势
写入频率低、查询简单单表 + 压缩 + 优化索引实现简单,维护成本低
高并发写入、按时间查询分区表(按时间)查询时仅扫描目标分区,效率高
无时间维度、高频去重单表 + MD5唯一索引去重效率最高,写入速度快
超大规模(10亿+)分表 + 分区 + 读写分离水平扩展,支撑更高数据量

PostgreSQL 存储亿级链接信息的核心是:精简表结构减少空间、优化配置提升IO、合理索引加速查询、分区/分表分散压力,配合批量写入和定期维护,可稳定支撑亿级数据的存储和查询。

一、基础前提:链接信息表结构设计

链接信息通常包含url创建时间来源状态备注等字段,先设计高效的表结构(避免冗余、减少存储空间):

1.1 基础表结构

单表存储,适合查询简单、写入频率中等的场景

-- 创建链接信息表(优化字段类型,减少单条记录体积)CREATETABLEurl_info(id BIGSERIALPRIMARYKEY,-- 自增主键(BIGINT 适配亿级数据)urlVARCHAR(2048)NOTNULL,-- 链接最大长度2048(覆盖99%场景)sourceVARCHAR(64)NOTNULL,-- 来源(如"爬虫-百度"、"手动录入")statusSMALLINTNOTNULLDEFAULT0,-- 状态(0-未验证,1-有效,2-失效)create_timeTIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMP,update_timeTIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMP,md5_hashCHAR(32)UNIQUE-- URL的MD5哈希(用于去重,比直接索引URL高效));-- 关键优化:字段类型精简-- 1. 状态用 SMALLINT(2字节)而非 INT(4字节),节省50%空间;-- 2. 来源用 VARCHAR(64) 而非 TEXT,避免变长字段过度占用空间;-- 3. MD5哈希用 CHAR(32)(固定长度),比 VARCHAR 索引效率高。

1.2 存储空间测算(一亿条数据)

字段类型单条占用一亿条总占用(未压缩)压缩后(pg_compress)
idBIGSERIAL8字节800MB~400MB
urlVARCHAR(2048)平均64字节6.4GB~2.5GB
sourceVARCHAR(64)平均8字节800MB~300MB
statusSMALLINT2字节200MB~100MB
create_timeTIMESTAMP8字节800MB~400MB
update_timeTIMESTAMP8字节800MB~400MB
md5_hashCHAR(32)32字节3.2GB~1.2GB
总计-~130字节13GB~5.4GB

结论:一亿条链接信息,单表存储(含压缩)仅需约 5-8GB 存储空间,PostgreSQL 完全支撑。

二、核心调优:PostgreSQL 配置优化(postgresql.conf)

默认配置无法支撑亿级数据的高效写入和查询,需调整postgresql.conf关键参数,适配亿级数据写入/查询(以 PostgreSQL 16 为例)。postgresql.conf中的几个关键参数需要根据你的服务器硬件(特别是内存和磁盘)进行调整。

  • shared_buffers:PostgreSQL用于缓存数据的共享内存。通常设置为系统总内存的25%。
  • effective_cache_size:PostgreSQL估算的操作系统可用磁盘缓存。通常设置为系统总内存的50%-75%。
  • work_mem:单个查询排序、连接等操作可用的内存。对于复杂查询,适当增大此值(如64MB)可以避免磁盘排序。
  • maintenance_work_memVACUUMCREATE INDEX等维护操作使用的内存。可以设置得比work_mem大很多(如512MB或1GB)。
  • checkpoint_completion_target:控制检查点完成的平缓程度,提高此值(如0.9)可以平滑I/O负载。

2.1 内存相关配置(核心)

# 共享内存(建议为物理内存的1/4,如64GB内存设为16GB) shared_buffers = 16GB # 单查询内存(避免排序/哈希时频繁写临时文件) work_mem = 64MB # 维护操作内存(建索引、Vacuum时使用) maintenance_work_mem = 2GB # 临时缓冲区(减少临时文件IO) temp_buffers = 1GB

2.2 IO 相关配置(提升写入/读取速度)

# 写入策略:先写内存,批量刷盘(适合高并发写入) wal_buffers = 128MB # WAL刷盘触发阈值(减少IO次数) max_wal_size = 64GB min_wal_size = 16GB # 开启异步提交(提升写入性能,允许少量数据丢失,业务不敏感时开启) async_commit = on # 禁用同步刷盘(生产环境建议配SSD,再开启) fsync = off # 预读块数(提升顺序读取效率) effective_io_concurrency = 32

2.3 连接与并发配置(支撑批量写入)

# 最大连接数(根据业务调整,避免连接数过多) max_connections = 500 # 并行查询数(提升复杂查询效率) max_parallel_workers_per_gather = 8 max_parallel_workers = 16

三、索引设计:高效查询+去重(亿级数据的核心)

链接信息的常见查询场景:按URL去重按来源筛选按状态查询按时间范围筛选,索引设计需兼顾效率和空间:

3.1 必建索引(核心)

-- 1. URL去重:基于MD5哈希的唯一索引(比直接索引URL快10倍+)CREATEUNIQUEINDEXidx_url_md5ONurl_info(md5_hash);-- 2. 来源+状态组合索引(高频查询:如"来源为爬虫且状态失效的链接")CREATEINDEXidx_source_statusONurl_info(source,status);-- 3. 时间范围索引(按创建时间筛选)CREATEINDEXidx_create_timeONurl_info(create_time);-- 4. 可选:URL前缀索引(如需按URL开头模糊查询)CREATEINDEXidx_url_prefixONurl_info(url varchar_pattern_ops);

3.2 索引优化原则

  • 避免对url字段建全值索引:URL 长度长,索引体积大,查询效率低;
  • 优先用组合索引:覆盖高频查询条件(如source+status+create_time),减少回表;
  • 定期清理无效索引:用pg_stat_user_indexes查看索引使用情况,删除未使用的索引。

四、数据写入:高效导入一亿条链接(避免写入瓶颈)

4.1 批量写入(比单条INSERT快100倍)

# Python 示例:批量插入(每次1万条,减少网络交互)importpsycopg2importhashlibdefbatch_insert_urls(url_list,batch_size=10000):conn=psycopg2.connect(dbname="your_db",user="postgres",password="your_pwd",host="localhost",port="5432")cur=conn.cursor()# 禁用自动提交,批量提交conn.autocommit=Falseforiinrange(0,len(url_list),batch_size):batch=url_list[i:i+batch_size]values=[]forurl,source,statusinbatch:# 计算URL的MD5哈希(去重)md5=hashlib.md5(url.encode('utf-8')).hexdigest()values.append(f"('{url}', '{source}',{status}, '{md5}')")# 批量插入SQLsql=f""" INSERT INTO url_info (url, source, status, md5_hash) VALUES{','.join(values)}ON CONFLICT (md5_hash) DO NOTHING; -- 去重:已存在则跳过 """cur.execute(sql)conn.commit()print(f"已插入{i+batch_size}条")cur.close()conn.close()# 调用示例(假设url_list是一亿条链接的列表)# batch_insert_urls(url_list)

4.2 写入优化技巧

  • 禁用触发器/约束:写入前临时禁用非必要的触发器、外键约束,写入后恢复;
  • 关闭索引:批量写入前删除非主键索引,写入完成后重建(比边写边建快);
  • 使用 COPY 命令:若数据在文件中,用COPY url_info FROM '/path/urls.csv'导入(最快方式)。

五、亿级数据的进阶方案:分表/分区(解决单表查询慢)

若单表存储一亿条数据后查询效率下降(如复杂筛选、排序),可采用分区表分表策略:

5.1 分区表(推荐,PostgreSQL 10+ 原生支持)

create_time分区(如按月份),将一亿条数据分散到多个分区,查询时仅扫描目标分区:

-- 1. 创建分区表(主表)CREATETABLEurl_info_part(id BIGSERIAL,urlVARCHAR(2048)NOTNULL,sourceVARCHAR(64)NOTNULL,statusSMALLINTNOTNULLDEFAULT0,create_timeTIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMP,update_timeTIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMP,md5_hashCHAR(32)UNIQUE)PARTITIONBYRANGE(create_time);-- 2. 创建分区(按月份,示例:2025年1月-12月)CREATETABLEurl_info_202501PARTITIONOFurl_info_partFORVALUESFROM('2025-01-01')TO('2025-02-01');CREATETABLEurl_info_202502PARTITIONOFurl_info_partFORVALUESFROM('2025-02-01')TO('2025-03-01');-- ... 依次创建其他月份分区-- 3. 为每个分区建索引(仅在分区上建,减少索引体积)CREATEINDEXidx_202501_source_statusONurl_info_202501(source,status);

5.2 分表(按哈希分表,适合无时间维度的场景)

md5_hash的哈希值分表,将数据分散到 10 个分表:

-- 创建分表(url_info_0 到 url_info_9)CREATETABLEurl_info_0(LIKEurl_info INCLUDINGALL);CREATETABLEurl_info_1(LIKEurl_info INCLUDINGALL);-- ... 直到 url_info_9-- 写入时按MD5哈希取模分表INSERTINTOurl_info_{md5_hash%10}(url,source,status,md5_hash)VALUES(...);

六、维护与优化:亿级数据的长期管理

6.1 定期 Vacuum(清理死元组)

-- 自动Vacuum配置(postgresql.conf)autovacuum=onautovacuum_vacuum_scale_factor=0.05-- 数据变化5%触发Vacuumautovacuum_analyze_scale_factor=0.02-- 数据变化2%触发Analyze-- 手动Vacuum(批量写入后执行)VACUUMANALYZEurl_info;

6.2 数据归档

将过期的链接数据(如1年前的)归档到冷表/低成本存储(如PostgreSQL的TOAST表、外部存储):

-- 创建归档表CREATETABLEurl_info_archive(LIKEurl_info INCLUDINGALL);-- 迁移过期数据INSERTINTOurl_info_archiveSELECT*FROMurl_infoWHEREcreate_time<'2024-01-01';-- 删除原表过期数据DELETEFROMurl_infoWHEREcreate_time<'2024-01-01';

6.3 监控性能

用 PostgreSQL 自带工具监控:

-- 查看表大小SELECTpg_size_pretty(pg_total_relation_size('url_info'));-- 查看索引使用情况SELECTindexrelname,idx_scanFROMpg_stat_user_indexesWHERErelname='url_info';-- 查看慢查询SELECT*FROMpg_stat_statementsORDERBYtotal_timeDESCLIMIT10;
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/22 8:50:55

每日一练:流星雨

题目描述贝西听说一场非凡的流星雨即将来临&#xff1b;报告称这些流星将撞击地球并摧毁它们所碰到的任何东西。为了安全&#xff0c;她发誓要找到一个安全的位置&#xff08;一个从未被流星摧毁的地方&#xff09;。她目前在坐标平面的原点放牧&#xff0c;想要移动到一个新的…

作者头像 李华
网站建设 2026/2/20 9:18:27

21、SNMP网络管理与数据中心发现实战

SNMP网络管理与数据中心发现实战 1. 配置Net - SNMP 当你要在想要监控的客户端上安装Net - SNMP时,应使用主机资源MIB(Management Information Base,管理信息库)来编译Net - SNMP。具体操作步骤如下: ./configure -with-mib-modules=host运行 configure 时,它会尝试…

作者头像 李华
网站建设 2026/2/21 8:05:12

25、技术探索:数据查询、服务器管理与Python包管理

技术探索:数据查询、服务器管理与Python包管理 数据查询代码分析 在数据处理中,我们常常需要从数据存储中获取特定的记录。以下是一段相关代码: collection = [] #grab last 10 records from datastore query = ChangeModel.all().order(-date) records = query.fetch(l…

作者头像 李华
网站建设 2026/2/20 17:04:55

中国独立开发者创业实战指南:从技术到商业的变现路径

中国独立开发者创业实战指南&#xff1a;从技术到商业的变现路径 【免费下载链接】chinese-independent-developer 分享中国独立开发者们正在进行的工作和项目的列表。 项目地址: https://gitcode.com/GitHub_Trending/ch/chinese-independent-developer 在当今技术创业…

作者头像 李华
网站建设 2026/2/15 10:00:25

从零构建大模型智能体:OpenAI Function Calling智能体实战

引言 随着大语言模型逐步具备“理解—推理—行动”的能力&#xff0c;如何让模型稳定、可控地调用外部工具&#xff0c;已成为构建智能体&#xff08;Agent&#xff09;系统的关键一环。相比早期基于文本协议的工具调用方式&#xff0c;OpenAI 推出的 Function Calling&#x…

作者头像 李华
网站建设 2026/2/22 3:44:32

‘‘空字符串有索引和没索引怎么存储?

1.如果有索引&#xff0c;那么存储在二级索引中,例如:(,id1)(,id2) 2.如果没有索引,那么存储在主键索引行数据中,例如:(id1,name,pwd123),(id2,name,pwd456)

作者头像 李华