博客

  • chnroute-linux — 中国大陆 IP 段每日自动更新 + 「仅允许国内访问」落地

    https://github.com/zhongdaiqi/ipfire

    chnroute-linux — 中国大陆 IP 段每日自动更新 + 「仅允许国内访问」落地

    在 Linux 上每天自动拉取中国大陆(CN)IPv4/IPv6 地址段,清洗聚合后原子写入内核, 配合入站白名单实现「仅允许国内 IP 访问」;同时可导出 ipset / nftables / RouterOS 三种格式, 用于多 WAN 出口策略路由(PBR)与国内外分流。

    本方案是 chnroute 思路的工程化落地版:chnroute 源自 Linux 社区,本质是 「中国大陆 CIDR 数据库」,用于在无 BGP 能力的边缘设备上实现近似 BGP 的按目标选路。


    一、实测结果(2026-09-30,国内家宽)

    APNIC 官方源         v4=8792 段  v6=2043 段      约 300 秒(国际出口慢)
    chnroutes2 聚合源    v4=3898 段                  1.7 秒
    china-operator-ip    v4=6207 段                  1.7 秒
    china-operator-ip6   v6=3414 段                  1.7 秒
    ...
    ─────────────────────────────────────────────────────────
    聚合结果             IPv4  5714 段 / 346,035,968 地址(约 3.46 亿,与 CNNIC 统计吻合)
                         IPv6  2247 段
    ISP 分组             电信 2843 段 / 移动 1003 段 / 联通 1733 段 / 教育网 85 段

    两个模式:

    模式 命令 耗时 说明
    全量(默认) --sources all ~300s APNIC 官方 + 聚合源,最权威
    快速 --sources aggregate ~74s 只用 jsdelivr 镜像聚合源,国内网络推荐

    GitHub 源可达性实测(这决定脚本必须做镜像回退):

    镜像 结果
    cdn.jsdelivr.net ✅ 96 KB / 1.7 s
    ghproxy.net ✅ 96 KB / 2.0 s
    raw.githubusercontent.com ❌ 超时(国内直连不通)
    raw.gitmirror.com ❌ DNS 解析失败

    二、文件清单

    文件 作用
    update_chnroute.py 主脚本:抓取 → 清洗 → 聚合 → 多格式导出 → 原子写入内核
    apply-whitelist.sh 启用「仅允许国内访问」入站白名单,自带防自锁自动回滚
    install.sh 一键部署(装文件、建目录、注册定时任务、首次运行)
    chnroute-update.service / .timer systemd 每日定时更新单元
    README.md 本文档

    三、快速开始

    # 0) 依赖(Debian/Ubuntu 示例)
    sudo apt update && sudo apt install -y python3 ipset iptables
    
    # 1) 一键安装(同时立即跑一次,只生成数据、不动内核)
    sudo ./install.sh --with-timer
    
    # 2) 确认数据合理(应为 4000~8000 段、覆盖 3.4 亿+ 地址)
    head -5 /var/lib/chnroute/chnroute-v4.txt
    cat /var/lib/chnroute/state.json
    
    # 3) 原子写入内核 ipset(可反复执行,更新过程不断流)
    sudo /usr/local/bin/update_chnroute.py --outdir /var/lib/chnroute --apply
    
    # 4) 启用「仅允许国内访问」
    echo "你的公网出口IP" | sudo tee -a /etc/chnroute/whitelist.txt
    sudo /usr/local/sbin/apply-whitelist.sh          # 应用,300 秒后自动回滚
    #   ↑ 立刻另开一个终端验证 SSH 是否还通
    sudo /usr/local/sbin/apply-whitelist.sh --commit # 确认无误,取消回滚,永久生效

    四、三个真正重要的工程细节

    1. 白名单不会把你锁在门外(防自锁三重保险)

    白名单一旦生效,境外 IP(包括你此刻的 SSH 跳板)会立刻被拒。所以 apply-whitelist.sh:

    1. 自动豁免:回环、内网(10/8、172.16/12、192.168/16)、本机全部公网 IP、 当前所有已建立 TCP 会话的对端(也就是你正在用的那条 SSH 连接)、 /etc/chnroute/whitelist.txt 自定义列表 —— 全部自动进豁免集合;
    2. 自动回滚:生效后立刻调度一个 300 秒的定时回滚(优先 systemd-run,退化到 at,再退化到后台进程)。 规则就算写错,最多断 300 秒,机器会自己恢复;
    3. 显式提交:你在窗口期内确认 SSH 正常,执行 --commit 取消回滚,策略才永久生效。

    2. 数据更新是原子的,不丢包不乱序

    用「影子集合 + swap」,而不是「删除重建」:

    ipset create cn4-tmp ...   ←  灌入新数据到影子集合(线上集合仍在正常工作)
    ipset restore -exist < chnroute-ipset.v4   (集合名改写为 cn4-tmp)
    ipset swap cn4-tmp cn4     ←  内核里一次指针交换,瞬时生效,零空窗
    ipset destroy cn4-tmp

    如果新数据抓取失败或校验不过,cn4 保持原样 —— 白名单永远不会变成空集合。

    3. 完整性守卫:防止数据源异常导致白名单塌陷

    白名单塌陷 = 灾难(所有国内用户被拒之门外)。所以每次落地前强制校验:

    • 绝对下限:IPv4 ≥ 2000 段、≥ 1 亿地址(实测约 5714 段 / 3.46 亿)
    • 绝对上限:IPv4 ≤ 15 亿地址(超过说明把非 CN 网段并进来了)
    • 相对变化:较上次条目数/IP 数骤降超 20% 直接拒绝;骤增 3 倍以上也拒绝
    • 命中即退出码 2,文件与内核都不改动,保留上一次的可用数据
    • 确认数据源无误时用 --force 跳过

    五、脚本能力速查

    # 常用
    python3 update_chnroute.py --outdir /var/lib/chnroute            # 只生成数据
    python3 update_chnroute.py --sources aggregate --apply           # 快速模式 + 写内核
    python3 update_chnroute.py --isp --ros-pbr --format all --apply  # 全格式 + ISP 分组 + ROS PBR 模板
    python3 update_chnroute.py --backend nft --nft-table 'inet filter' --apply
    python3 update_chnroute.py --dry-run --apply                     # 只演示不落地
    python3 update_chnroute.py --log-file /var/log/chnroute/update.log
    参数 说明
    --sources all\|apnic\|aggregate 数据源组合,默认 all
    --isp 追加电信/移动/联通/教育网/科技网/鹏博士分组
    --format txt,ipset,nft,ros 输出格式,all 表示全部
    --set-prefix cn ipset 集合前缀 → cn4 / cn6
    --ros-list CN / --ros-pbr RouterOS list 名 / 生成 PBR 模板
    --apply / --backend 原子写入内核 / 选 ipset 或 nft
    --force 跳过完整性守卫(危险)
    --quiet / --log-file 静默 / UTF-8 日志落盘

    输出文件(--format all):

    chnroute-v4.txt / chnroute-v6.txt     纯 CIDR(带元信息头),通用格式
    chnroute-ipset.v4 / .v6               ipset restore 格式(原子 swap 用)
    chnroute.nft                          nftables set 定义(nft -f 单事务加载)
    chnroute-ros.rsc                      RouterOS address-list 批量导入脚本
    chnroute-ros-pbr.rsc                  RouterOS 策略路由模板(国内直连 / 境外分流)
    chnroute-diff.txt                     与上次更新的差异(新增/移除各 200 条)
    state.json                            本次摘要:段数、地址数、各源状态
    cn-chinanet.txt / cn-cmcc.txt / ...   ISP 分组(--isp 时生成)

    六、三种落地方式

    A) ipset + iptables(推荐,兼容性最好)

    sudo update_chnroute.py --outdir /var/lib/chnroute --format all --apply
    sudo apply-whitelist.sh --commit
    sudo apply-whitelist.sh --status     # 查看链与集合
    sudo apply-whitelist.sh --rollback   # 随时撤销

    生成的 IPv4 链结构(IPv6 同理走 ip6tables):

    CHNROUTE-IN:
      1. -m conntrack --ctstate ESTABLISHED,RELATED  -j ACCEPT   ← 放行已建立连接(响应包)
      2. -i lo                                        -j ACCEPT   ← 回环
      3. -m set --match-set chnroute-wl4 src          -j ACCEPT   ← 豁免集合(内网/本机/活跃会话/自定义)
      4. -m set --match-set cn4 src                   -j ACCEPT   ← 中国大陆
      5. -j DROP                                                  ← 其余拒绝(MODE=loose 时改为 RETURN)

    挂到 INPUT 首位(-I INPUT 1),优先于既有规则。可用环境变量覆盖:

    sudo SET4=cn4 MODE=strict ROLLBACK_DELAY=600 WHITELIST_FILE=/etc/chnroute/whitelist.txt ./apply-whitelist.sh

    B) nftables

    sudo update_chnroute.py --backend nft --nft-table 'inet filter' --format nft --apply

    nft -f chnroute.nft 整文件是一个事务,同名 set 被原子替换。在你自己的规则里引用:

    nft add rule inet filter input ip saddr @cn4 accept

    注意:nftables 的 set 与引用它的规则必须在同一张表内,所以用 --nft-table 指定你的表名(默认 inet filter)。

    C) RouterOS(多 WAN 策略路由)

    python3 update_chnroute.py --outdir ./out --format ros --isp --ros-pbr
    # 把 chnroute-ros.rsc 上传到设备 /file,然后:
    #   /import file-name=chnroute-ros.rsc

    生成的 chnroute-ros-pbr.rsc 是可直接改用的模板(国内直连、境外走代理线路):

    /ip firewall mangle
    add chain=prerouting src-address-list=LAN dst-address-list=CN  action=mark-routing new-routing-mark=to-cn       passthrough=yes
    add chain=prerouting src-address-list=LAN dst-address-list=!CN action=mark-routing new-routing-mark=to-overseas passthrough=yes
    
    /ip route
    add dst-address=0.0.0.0/0 gateway=<国内WAN>   routing-mark=to-cn       distance=1 check-gateway=ping
    add dst-address=0.0.0.0/0 gateway=<海外WAN>   routing-mark=to-overseas distance=1 check-gateway=ping

    配合 --isp 生成的 CN-CHINANET / CN-CMCC / CN-UNICOM 列表,还能做运营商级精细分流 (三网互联互通瓶颈是实打实的,电信用户访问移动 IDC 绕行会显著抬高 RTT 与丢包)。 check-gateway=ping + distance 分级实现链路健康感知与自动备份。


    七、定时更新

    sudo ./install.sh --with-timer    # systemd:每天 04:30 + 随机 30 分钟,自动 --apply
    sudo ./install.sh --with-cron     # 无 systemd 时用 crontab

    systemd 单元要点:

    • RandomizedDelaySec=1800 打散触发时间,避免与其他定时任务叠加
    • Persistent=true 关机/休眠错过时间点后开机补跑
    • TimeoutStartSec=900 超时视为失败,旧 ipset 不受影响
    • 日志落 /var/log/chnroute/update.log

    检查:

    systemctl list-timers chnroute-update.timer
    journalctl -u chnroute-update.service -n 50
    tail -f /var/log/chnroute/update.log

    八、常见问题

    Q:APNIC 源太慢(约 5 分钟),能快吗? 用 --sources aggregate(~74 秒)。它走 jsdelivr 镜像上的 BGP 聚合数据,段数更少、覆盖一致。 也可以把 systemd 单元的 --format all 改成加 --sources aggregate。

    Q:用户访问国内 CDN 但 CDN 回源在境外,会不会被误封? 白名单只判源 IP。国内 CDN 边缘节点在国内,源 IP 就是国内的,不受影响。

    Q:白名单生效后,出站请求的响应包会被丢吗? 不会。链首放行 ESTABLISHED,RELATED,你主动发起的连接,响应包照常进来。

    Q:要不要同时放行 UDP / DNS? 白名单基于 address-list,协议无关。但如果只给国外用户开 DNS,那么他们连域名都解析不了 —— 若确有境外用户,请把他们加进 /etc/chnroute/whitelist.txt 而不是开放端口。

    Q:误封了自己怎么救?

    1. 等 300 秒自动回滚(如果还没 --commit);
    2. 云控制台 VNC/串口执行 sudo /usr/local/sbin/chnroute-rollback.sh;
    3. 事先把出口 IP 写进 /etc/chnroute/whitelist.txt 是唯一可靠的预防手段。

    Q:IPv6 要不要一起管? 要。只做 v4 白名单时,境外用户仍可经 IPv6 进来。脚本默认 v4/v6 一起生成; 若服务器没有 IPv6,加 --no-ipv6。

    Q:想撤掉全部策略? sudo /usr/local/sbin/apply-whitelist.sh --rollback;完全卸载 sudo ./install.sh --uninstall。


    九、数据源与合规说明

    • APNIC 官方统计文件:delegated-apnic-latest,含 CN 的 allocated/assigned 记录, 权威、只增不减。IPv4 记录以「地址个数」表示,脚本用 summarize_address_range 正确转 CIDR (例如 300 个地址 → /24 + /27 + /29 + /30,已单测验证)。
    • china-operator-ip:BGP 路由表聚合,附带 ISP(电信/移动/联通/教育网等)归属。
    • chnroutes2:已聚合的最小 CIDR 集,体积小、速度快。

    以上均为公开的 RIR/BGP 数据派生品,用于网络运维与访问控制;请遵守当地法律法规及所在单位的网络管理规定。

  • 记一次 Druid 连接 MySQL 报 Communications link failure 的排查过程

    # 记一次 Druid 连接 MySQL 报 Communications link failure 的排查过程
    
    ## 问题现象
    
    Druid 连接池启动时报错:
    

    com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure

    The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server. at com.mysql.cj.protocol.a.NativeProtocol.negotiateSSLConnection(NativeProtocol.java:361) … Caused by: javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are in

    
    连接地址是 `jdbc:mysql://127.0.0.1:3306/k_k`,MySQL 就在本机。
    
    ## 排查过程
    
    ### 1. 先排除基础问题
    
    确认 MySQL 服务在运行、端口 3306 可通,排除服务没启动、防火墙拦截等常见原因。
    
    ### 2. 定位到 SSL 握手阶段
    
    堆栈里最关键的一行是:
    

    at com.mysql.cj.protocol.a.NativeProtocol.negotiateSSLConnection(NativeProtocol.java:361)

    
    说明 TCP 连接已经建立,问题出在 **SSL 握手阶段**,而不是网络不通。
    
    ### 3. 检查 MySQL 的 TLS 配置
    
    ```sql
    SHOW VARIABLES LIKE 'tls_version';

    结果:

    TLSv1,TLSv1.1,TLSv1.2

    MySQL 支持 TLSv1.2,但同时也启用了已被淘汰的 TLSv1 和 TLSv1.1。

    4. 获取 CA 证书

    MySQL 启用 SSL 后,数据目录下会生成一组证书文件。先找到数据目录:

    SHOW VARIABLES LIKE 'datadir';

    在数据目录里找到 ca.pem,这就是服务端的 CA 证书。

    5. 将 CA 证书导入 Java 信任库

    使用 JDK 自带的 keytool:

    /www/server/java/jdk1.8.0_371/bin/keytool -importcert \
      -alias MySQLCACert \
      -file /www/server/data/ca.pem \
      -keystore truststore.jks \
      -storepass 你的密码

    生成的 truststore.jks 位于执行命令时的当前目录。

    6. 在 JDBC URL 中配置信任库

    jdbc:mysql://127.0.0.1:3306/k_k?sslMode=VERIFY_CA&trustCertificateKeyStoreUrl=file:/路径/truststore.jks&trustCertificateKeyStorePassword=你的密码

    重新启动后,仍然报同样的错。

    真正的原因

    这是 MySQL Connector/J 8.0.18 及更早版本的一个已知设计问题:

    • Connector/J 8.0.16 出于兼容旧版 MySQL(yaSSL 编译)的考虑,默认不启用 TLSv1.2,只尝试用 TLSv1/TLSv1.1 协商。
    • JDK 8u371 默认禁用了 TLSv1 和 TLSv1.1 这两个不安全的旧协议。

    双方在“能用哪个协议”上谈不拢,SSL 握手直接失败。

    解决方案

    在 JDBC URL 中显式指定 TLS 协议版本:

    jdbc:mysql://127.0.0.1:3306/k_k?sslMode=VERIFY_CA&trustCertificateKeyStoreUrl=file:/路径/truststore.jks&trustCertificateKeyStorePassword=你的密码&enabledTLSProtocols=TLSv1.2

    关键是 enabledTLSProtocols=TLSv1.2,它强制驱动使用 TLSv1.2,打破协议僵局。加上后连接正常。

    几个容易踩的坑

    • ssl_cipher 查询为空是正常的,MySQL 8.0 的密码套件由底层 OpenSSL 管理,不再通过该变量显式列出。
    • ssl_ca 只返回 ca.pem 而非完整路径也是正常的,该变量存储的是配置文件里填写的值,要拿绝对路径得查 datadir。
    • keytool 命令找不到,是因为它不在系统 PATH 里,需要用 JDK bin 目录下的绝对路径执行。

    后续可以改进的地方

    1. 升级驱动:Connector/J 8.0.19 及以上版本已修复默认不启用 TLSv1.2 的问题,升级后可以去掉 enabledTLSProtocols 参数。
    2. 清理 MySQL 旧协议:在 my.cnf 的 [mysqld] 段加上 tls_version=TLSv1.2,TLSv1.3,重启 MySQL,让服务端也彻底禁用旧协议,安全性更好。

    最终可用配置

    jdbc:mysql://127.0.0.1:3306/k_k?sslMode=VERIFY_CA&trustCertificateKeyStoreUrl=file:/路径/truststore.jks&trustCertificateKeyStorePassword=你的密码&enabledTLSProtocols=TLSv1.2

    版本信息:

    • MySQL Connector/J 8.0.16
    • Druid 1.1.18
    • JDK 1.8.0_371
  • 定制侠定制十二生肖龙主题适用于荣耀Magic9手机壳!TPU磨砂壳底壳透灰透白黑底壳磁吸壳!一个手机壳也能定制DIY!豆包AI生图提示词创意解锁无限个性二次元游戏动漫私人定制来图定制

    定制侠定制十二生肖龙主题适用于荣耀Magic9手机壳!
    定制手机壳操作界面

    定制流程

    1. 选择手机型号
    2. 上传定制图
    3. 调整布局
    4. 填写信息下单
    5. 等待收货

      定制活动

      填写邀请码yiyiyi,享受买一送一,联系客服出示邀请码

    豆包AI生成定制图

    8

    下单链接

    定制侠官网入口 www.dingzhixia.com

  • 湖仓一体(Lakehouse)知识全解

    从数据仓库 → 数据湖 → 湖仓一体的完整演进 · 核心技术 · 代码示例 · 选型指南

    标签:大数据架构 Iceberg Hudi Delta Lake 2026 整理

    目录

    1. 一句话理解湖仓一体
    2. 为什么需要湖仓一体:三种架构的演进
    3. 数仓 vs 数据湖 vs 湖仓一体 全面对比
    4. 湖仓一体的核心技术(六大能力)
    5. 关键技术深挖:开放表格式
    6. 主流技术栈全景图
    7. 动手示例:用 Iceberg 构建一个湖仓
    8. 典型业务场景与落地案例
    9. 常见坑与最佳实践
    10. 选型建议与学习路线
    11. 术语速查表

    1. 一句话理解湖仓一体

    通俗类比: 想象你要管理一家餐厅的食材仓库——

    • 数据仓库(数仓) 像一个"精品加工车间":食材进来必须先洗干净、切好、按规格装盘(严格的 ETL 和 Schema),取用方便、品质可控,但存不进"生的、形状怪"的食材(半结构化/非结构化数据),而且加工费很贵。
    • 数据湖 像一个"什么都往里扔的大冷库":生鲜、罐头、调料包、甚至不知道是什么的袋子全丢进去,便宜又能装,但时间一长没人知道哪个袋子是什么,变成"数据沼泽",想直接给客人上菜(分析查询)很难。
    • 湖仓一体(Lakehouse) = 把大冷库"装上货架、贴上标签、装上温控和出入库系统":既保留数据湖便宜、灵活、什么都能存的优点,又具备数仓的管理能力(事务、版本、高性能 SQL 查询)——一套存储,两种能力。

    正式定义: 湖仓一体是一种结合了数据湖的低成本灵活存储与数据仓库的数据管理及分析性能的新一代数据架构。它在对象存储(S3 / OSS / HDFS 等)上的开放文件格式(Parquet/ORC)之上,增加一层元数据与事务管理层(开放表格式),从而让同一个存储上同时支持 BI 报表、SQL 分析、机器学习、实时计算等多种负载。

    💡 核心公式(记住这个就够了):

    湖仓一体 = 对象存储(便宜) + 开放文件格式(Parquet) + 开放表格式(Iceberg/Hudi/Delta) + 统一计算引擎(Spark/Flink/Trino 等)

    2. 为什么需要湖仓一体:三种架构的演进

    2.1 第一阶段:数据仓库(1990s ~ 至今)

    典型代表:Oracle、Teradata、以及云上的 Snowflake、Redshift、ClickHouse、阿里云 MaxCompute/Hologres。

    • 流程: 业务库 → ETL(抽取/转换/加载)→ 数仓 → BI 报表。
    • 优点: 强 Schema、ACID 事务、查询性能好、数据质量高,适合财务、经营报表。
    • 缺点:
      • 只擅长结构化数据——日志、图片、视频、JSON 等存不进或成本极高;
      • 存储与计算绑定(尤其传统一体机),扩容要整体升级,价格昂贵;
      • 机器学习工程师拿不到原始数据,还得再导出到别处,形成多套副本。

    2.2 第二阶段:数据湖(2010s)

    典型形态:HDFS / S3 / OSS 上直接堆 CSV、JSON、Parquet 文件,用 Hive/Spark 跑批。

    • 优点: 存储便宜(对象存储约 ¥0.1/GB·月 级别)、格式不限、Schema-on-Read(读的时候才定义结构)、适合存原始数据和喂机器学习。
    • 缺点:
      • 没有事务: 两个任务同时改一个目录,数据直接写坏;
      • 没有版本: 写错了没法回滚;
      • 小文件灾难: 流式写入产生海量小文件,查询越跑越慢;
      • 一致性弱: 读到"写了一半"的数据;
      • 性能差: 做不了高并发 BI 报表。

    2.3 两套系统并行的痛苦(湖仓一体出现前的真实困境)

    2015~2020 年,很多公司的做法是"湖 + 仓"两套并行:原始数据进湖,ETL 加工后再进仓。结果是:

    • 同一份数据存 2~3 份(湖一份、仓一份、机器学习再一份),成本翻倍;
    • 湖到仓的同步链路又长又脆,数据一致性难保障;
    • 两个平台两套权限、两套元数据、两拨维护人员。

    2.4 第三阶段:湖仓一体(2020 至今)

    2020 年 Databricks 提出 Lakehouse 概念并发布论文《Lakehouse: A New Generation of Open Platforms that Unify Data Warehousing and Advanced Analytics》,核心思路是:不要两套系统了,直接在湖的存储上加一层"事务 + 元数据 + 高速读取"的管理层,让湖本身具备仓的能力。 此后 Iceberg、Hudi、Delta Lake 三大开放表格式快速成熟,湖仓一体成为主流架构。

    演进路径:

    ① 传统数仓                ② 湖 + 仓 两套并行            ③ 湖仓一体 Lakehouse
    ─────────────            ─────────────────            ─────────────────────
    业务库 → 数仓(贵)         数据湖(便宜) ⇄ 数仓            统一计算: SQL / BI / 流 / ML
    只存结构化 · 强事务       ↑ 同步链路复杂                  开放表格式: Iceberg/Hudi/Delta
    存不了日志/图片/ML数据    数据存多份 · 成本高             Parquet + 对象存储(便宜)
                                                            一套存储 · 事务 · 版本 · 全场景

    湖仓一体典型分层架构:

    ┌──────────────────────────────────────────────────────────────────────────┐
    │ 应用层:BI 报表 · 自助分析 · 数据 API · 机器学习/AI 应用                     │
    ├──────────────────────────────────────────────────────────────────────────┤
    │ 计算引擎层:Spark(批) · Flink(流) · Trino/Presto(交互查询)              │
    │            StarRocks/Doris(OLAP 加速)                                    │
    ├──────────────────────────────────────────────────────────────────────────┤
    │ 表格式/元数据层(核心):Iceberg / Hudi / Delta Lake                         │
    │                        —— 事务 · 版本 · Schema 演进 · 索引                  │
    ├──────────────────────────────────────────────────────────────────────────┤
    │ 存储层:S3 / OSS / COS / HDFS —— 开放文件格式 Parquet / ORC                 │
    └──────────────────────────────────────────────────────────────────────────┘

    3. 数仓 vs 数据湖 vs 湖仓一体 全面对比

    维度 数据仓库 数据湖 湖仓一体
    数据类型 仅结构化数据 结构化 + 半结构化 + 非结构化(全部) 全部类型
    存储成本 高(一体机/专用存储) 极低(对象存储) 低(对象存储)
    Schema Schema-on-Write(写入时强约束) Schema-on-Read(读取时解释) 两者兼有:写入有校验,且可平滑演进
    ACID 事务 ✅ 支持 ❌ 基本没有 ✅ 支持(表格式提供)
    数据版本/回滚 部分支持 ❌ ✅ Time Travel 时光旅行
    更新/删除 ✅ 原生支持 ❌ 只能整文件覆盖 ✅ 支持(行级更新/CDC/MOR)
    BI 查询性能 ✅ 很好 ❌ 差 ✅ 好(配合缓存/索引可接近数仓)
    机器学习 ❌ 不友好 ✅ 直接读原始数据 ✅ 直接在湖上训练,无需导出
    流批一体 困难 困难 ✅ 天然支持(Flink + 表格式)
    开放性 ❌ 常被厂商锁定 ✅ 开放文件 ✅ 完全开放(存储+格式+元数据都可迁移)
    典型产品 Snowflake、Redshift、MaxCompute、ClickHouse S3/HDFS + Hive + Spark 原始用法 Databricks、阿里云 EMR+OSS+Paimon/Iceberg、腾讯云 Oceanus+Iceberg、自建 Flink+Iceberg

    ⚠️ 常见误区:

    • ❌ "湖仓一体 = 数仓 + 数据湖两个产品拼一起" —— 错。它是一套存储上的统一架构,拼装反而是要被淘汰的旧模式。
    • ❌ "湖仓一体就是个产品" —— 它更像一种架构理念,可以用开源组件自建,也可以买一体化云产品。
    • ❌ "有了湖仓就不需要 OLAP 引擎了" —— 对超高并发的看板场景,仍常配 StarRocks/Doris/ClickHouse 做"查询加速层"(只是数据源变成了湖仓表,不再需要单独同步)。

    4. 湖仓一体的核心技术(六大能力)

    能力一:ACID 事务 —— 多个任务同时写也不会写坏

    基于乐观并发控制(OCC)+ 快照隔离:每次提交生成一个新快照(snapshot),提交时校验是否有冲突。就像 Git——大家基于同一版本改,提交时如果版本变了就先解决冲突再提交。

    能力二:Schema 演进 —— 表结构随业务平滑变化

    新增列、重命名列、修改列类型都不需要重写全表数据,也不用担心下游任务报错。旧数据文件里没有新列时读出来就是 NULL。

    能力三:Time Travel —— 查询/回滚到任意历史版本

    每次写入都是一个快照,可以查"昨天 10 点的表长什么样",也能一键回滚误操作。

    -- 查询 1 小时前的数据
    SELECT * FROM orders TIMESTAMP AS OF now() - interval 1 hours;
    -- 查询快照 id = 12345 时的数据
    SELECT * FROM orders VERSION AS OF 12345;
    -- 回滚到旧快照(撤销误操作)
    CALL catalog.system.rollback_to_snapshot('db.orders', 12345);

    能力四:行级更新与 CDC —— 湖也能像数据库一样 UPDATE/DELETE

    传统数据湖只能整文件重写;湖仓格式支持行级更新,可以直接消费 MySQL binlog(CDC 数据),把业务库的增删改实时同步进湖表。这是"实时数仓"的基础。

    能力五:高性能读取优化 —— 接近数仓的查询速度

    • 统计信息与数据裁剪: 每个数据文件记录每列的 min/max 统计,查询时直接跳过无关文件(分区裁剪 + 文件裁剪);
    • 索引: Hudi 支持 Bloom Filter 索引,Delta/Iceberg 支持删除向量(deletion vector);
    • 缓存与本地 SSD: Alluxio / 引擎内置缓存把热数据放到计算节点本地;
    • 小文件合并(Compaction)与排序(Clustering): 后台任务把小文件合并成大文件、按查询常用列重排,兼顾写入吞吐与查询速度。

    能力六:开放统一 —— 存储开放、引擎自由、一套数据全场景复用

    底层数据就是标准的 Parquet 文件 + 元数据文件,任何支持该表格式的引擎都能读:Spark 跑批、Flink 实时、Trino 即席查询、StarRocks 加速、Python 直接喂给模型训练。避免了厂商锁定和多份数据副本。


    5. 关键技术深挖:开放表格式(Open Table Format)

    开放表格式是湖仓一体的"心脏"。它本质上是一层元数据协议:规定"这个表由哪些数据文件组成、每个文件有哪些统计信息、表当前处于哪个快照",从而在"一堆文件"之上模拟出数据库表的能力。

    5.1 工作原理(以 Iceberg 为例)

    Catalog ──→ metadata.json ──→ manifest list ──→ manifest 文件
    (指向当前元数据)  (快照列表)      (本次快照的清单)   (列统计信息)
                                                        │
                                                        ▼
    data-1.parquet      data-2.parquet      data-0.parquet(虚线)
    含 min/max 统计      新写入的数据          旧文件(被更新后标记删除)

    写入 = 新增 parquet 文件 + 提交新快照(原子替换 Catalog 指针);查询 = 按当前快照扫描有效文件,用统计信息跳过无关数据。

    5.2 三大表格式对比

    对比项 Apache Iceberg Apache Hudi Delta Lake
    出生 Netflix 2018 开源,2020 进 Apache Uber 2017 开源(原名 Titan) Databricks 2019 开源
    设计初衷 为超大规模分析表设计,中立开放 为"数据库式"增量入湖/更新设计 为 Spark 生态深度集成设计
    更新模式 Copy-on-Write 为主(v2 支持位置删除) COW + MOR(Merge-on-Read)双模式,更新能力最强 COW 为主(新版支持 Deletion Vector 近似 MOR)
    擅长场景 海量离线分析、批处理、多引擎共存 高频 CDC 入湖、近实时更新、Flink 流场景 Spark 生态、与 Databricks 平台配合
    引擎支持 最广泛:Spark/Flink/Trino/StarRocks/Doris 等 Spark/Flink 为主 Spark 最佳;Flink/Trino 支持渐好
    隐藏分区/分区演进 ✅ 强项(按天分区可改为按小时,无需重写) 支持有限 需重写
    国内生态 极广(阿里/腾讯/网易等默认推荐之一) 广泛(Flink 社区常推荐) 多见于使用 Databricks 或 Azure 的公司
    后起之秀 Apache Paimon(原 Flink Table Store):流式场景新贵,更新性能好,是 Flink 官方生态主推的流式湖格式,2023 起在国内实时湖仓落地很多

    💡 一句话选型直觉(详细选型见第 10 章): 偏批/多引擎/中立 → Iceberg;偏 CDC 高频更新/流式 → Hudi 或 Paimon;已深度使用 Databricks → Delta Lake。三者都在快速演进,差距在缩小。


    6. 主流技术栈全景图

    层次 可选技术(开源为主)
    存储层 AWS S3 / 阿里云 OSS / 腾讯云 COS / Azure ADLS / HDFS / MinIO(自建对象存储)
    文件格式 Parquet(列存,最主流)、ORC、Avro(部分中间数据)
    表格式 Iceberg、Hudi、Delta Lake、Paimon
    Catalog(元数据服务) Hive Metastore(最常用)、AWS Glue、Nessie、Polaris、Rest Catalog
    批处理引擎 Spark(事实标准)、Hive MR(老系统)
    流处理引擎 Flink(事实标准)、Spark Structured Streaming、Kafka Connect
    交互查询/OLAP 加速 Trino/Presto、StarRocks、Doris、ClickHouse(做加速层)
    CDC 采集 Flink CDC、Debezium、Canal
    调度与治理 DolphinScheduler、Airflow、Dagster;数据质量:Griffin / dbt tests;血缘:OpenLineage
    一体化商业平台 Databricks、Snowflake(Iceberg Tables)、阿里云 EMR/实时计算 Flink 版 + OSS、腾讯云 Oceanus + DLC

    💡 国内最常见的开源组合("实时湖仓"标准栈):

    MySQL/业务库 → Flink CDC → Kafka → Flink 写入 Paimon/Iceberg(OSS 上)
    → Spark 离线加工 → StarRocks/Trino 加速查询 → BI 看板 / AI 应用

    一套链路同时覆盖实时、离线、机器学习三类需求。


    7. 动手示例:用 Iceberg 构建一个湖仓

    下面用最经典的 Spark + Iceberg 组合,演示从建表、写入、更新、时光旅行到回滚的完整流程。所有 SQL 都能在一个 Spark-SQL 会话里直接跑通。

    7.1 环境准备(Docker 一行起个环境)

    # 拉起含 Spark + Iceberg 的镜像(也可本机下载 spark + iceberg jar)
    docker run -it --rm tabulario/spark-iceberg bin/spark-sql \
      --packages org.apache.iceberg:iceberg-spark-runtime-3.5_2.12:1.5.2 \
      --conf spark.sql.catalog.demo=org.apache.iceberg.spark.SparkCatalog \
      --conf spark.sql.catalog.demo.type=hadoop \
      --conf spark.sql.catalog.demo.warehouse=s3://my-lakehouse/warehouse/

    7.2 建表与写入

    -- 建表:按天分区(分区只影响裁剪,改分区策略无需重写数据)
    CREATE TABLE demo.db.orders (
      order_id   BIGINT,
      user_id    BIGINT,
      amount     DECIMAL(10,2),
      status     STRING,
      pay_time   TIMESTAMP
    )
    USING iceberg
    PARTITIONED BY (days(pay_time));
    
    -- 插入数据(也可以直接写 Parquet 文件路径、或从 Kafka/CDC 流式写入)
    INSERT INTO demo.db.orders VALUES
      (1001, 88, 199.00, 'PAID',    '2026-09-19 10:30:00'),
      (1002, 91, 59.90,  'PAID',    '2026-09-19 11:05:00'),
      (1003, 88, 1299.00,'CREATED', '2026-09-20 09:12:00');

    7.3 像数据库一样更新与删除(数仓时代湖上做不到的事)

    -- 行级 UPDATE:把超时未支付的订单改为关闭
    UPDATE demo.db.orders
    SET status = 'CLOSED'
    WHERE status = 'CREATED'
      AND pay_time < current_timestamp() - interval 24 hours;
    
    -- 行级 DELETE:合规要求删除某用户数据(GDPR 场景)
    DELETE FROM demo.db.orders WHERE user_id = 88;

    7.4 Time Travel:查历史 & 回滚

    -- 查看提交历史
    SELECT made_current_at, snapshot_id, operation
    FROM demo.db.orders.snapshots
    ORDER BY made_current_at;
    
    -- 查询更新前的数据(回看历史)
    SELECT * FROM demo.db.orders
    VERSION AS OF 5281947119917518575;
    
    -- 回滚到旧快照,撤销刚才的误操作
    CALL demo.system.rollback_to_snapshot('demo.db.orders', 5281947119917518575);

    7.5 Schema 演进

    -- 新增一列:旧数据文件不用重写,读出为 NULL
    ALTER TABLE demo.db.orders
    ADD COLUMN coupon_id BIGINT;
    
    -- 把按天分区改成按小时分区?Iceberg 支持分区演进,无需重建表
    ALTER TABLE demo.db.orders
    SET PARTITION SPEC (hours(pay_time));

    7.6 运维:小文件合并与快照清理

    -- 把小文件合并成约 512MB 的大文件(流式写入后必做)
    CALL demo.system.rewrite_data_files(
      table => 'demo.db.orders',
      options => map('target-file-size-bytes','536870912')
    );
    
    -- 清理 7 天前的过期快照,释放存储(否则历史版本会一直占空间)
    CALL demo.system.expire_snapshots(
      table => 'demo.db.orders',
      older_than => now() - interval 7 days
    );

    7.7 Flink 实时写入(CDC 入湖示例)

    -- Flink SQL:MySQL 订单变更实时入湖(Flink CDC + Iceberg)
    CREATE TABLE mysql_orders (
      order_id BIGINT, amount DECIMAL(10,2), status STRING,
      PRIMARY KEY (order_id) NOT ENFORCED
    ) WITH (
      'connector' = 'mysql-cdc',
      'hostname' = 'mysql', 'database-name' = 'shop',
      'table-name' = 'orders'
    );
    
    INSERT INTO lake_orders SELECT * FROM mysql_orders;
    -- 业务库的 INSERT/UPDATE/DELETE 会持续以行级更新方式写入湖表
    -- 下游 StarRocks / Trino 查询湖表即可看到近实时的数据

    8. 典型业务场景与落地案例

    场景 1:电商实时数仓(最常见)

    痛点: 离线 T+1 报表 + 实时看板要维护两套链路(Lambda 架构),逻辑写两遍,口径还对不上。

    湖仓方案: Flink CDC 把订单库变更实时写入 Paimon/Iceberg 表,实时链路和离线链路读写同一张表(Kappa 思路),StarRocks 直查湖表供大屏使用。

    • 口径统一:实时与离线用同一份数据、同一套 SQL;
    • 链路简化:不再需要"实时结果 Kafka → 另存 HBase → 离线重跑 Spark"的复杂拼接;
    • 成本下降:历史数据全在对象存储上,比双写双存省 50%+ 存储。

    场景 2:机器学习 / AI 训练数据管理

    痛点: 特征数据在数仓,原始日志在湖,训练前要人工导出拼接,且无法追溯"模型是用哪个版本的数据训的"。

    湖仓方案: 特征表直接建在湖上(Spark/Flink 加工),PyTorch/ML 工程师用 iceberg-spark-runtime 或 pyiceberg 直接按快照读取训练集——每个模型都能记录"训练时用的 snapshot_id",实现数据可复现。

    # Python 侧读取湖表快照做训练(pyiceberg + pyarrow)
    from pyiceberg.catalog import load_catalog
    catalog = load_catalog("demo")
    tbl = catalog.load_table("db.user_features")
    df = tbl.scan(snapshot_id="5281947119917518575").to_arrow().to_pandas()
    # df 直接喂给 sklearn / PyTorch dataloader

    场景 3:多引擎自助分析平台

    痛点: 分析师用 SQL 即席查询,数据科学家跑 Spark,工程师写 Flink——以前数据要复制到三个系统。

    湖仓方案: 数据只存一份在 OSS+Iceberg,Trino/StarRocks 提供秒级 SQL,Spark 跑大作业,统一 Catalog + 统一权限(Ranger)管理。

    场景 4:日志与埋点的"全量归档 + 按需分析"

    App/前端埋点 JSON 直接以 Parquet 入湖(Schema-on-Write 宽表),平时只算聚合指标;某天想分析"新按钮点击率"时,直接用 SQL 扫原始明细——这是数仓做不到、纯数据湖又慢又乱的场景,湖仓刚好补上。

    💡 知名实践参考: Netflix(Iceberg 诞生地,EB 级表)、Uber(Hudi 诞生地,出行数据)、腾讯视频/美团/字节(大规模 Iceberg/Hudi/Paimon 落地)、Adobe/BM(Iceberg 多引擎 BI)。国内云厂商(阿里 EMR、腾讯 Oceanus/DLC、华为 FusionInsight)均已把湖仓一体作为默认推荐架构。


    9. 常见坑与最佳实践

    坑 现象 最佳实践
    小文件爆炸 Flink/高频写入后文件数百万级,查询越来越慢,元数据膨胀 定期 rewrite_data_files / 开启自动 Compaction;写入端攒批;分区粒度别太细
    快照只增不减 存储成本持续上涨,明明"删了"数据但磁盘没释放 按业务 SLA 定期 expire_snapshots(如保留 7 天);配合 remove_orphan_files
    MOR 读放大 Hudi/Paimon 高频更新后读查询变慢 控制 log 文件大小、及时 Compact;对读多写少的表用 COW
    分区设计照搬数仓 按"省份×日期"分区导致海量小分区 湖仓里分区只需支撑裁剪,避免过度分区;利用 Iceberg 隐藏分区和分区演进
    元数据/Catalog 单点 Hive Metastore 挂了全平台瘫痪 HMS 高可用部署;大型平台考虑 Nessie/Rest Catalog 多版本管理
    没有做数据质量卡点 垃圾数据进湖,下游全脏 入湖管道加校验(空值率、主键唯一、值域);用 dbt tests / Great Expectations 做质量门禁
    权限治理缺失 任何人都能扫全表,敏感数据裸奔 统一 Catalog + Apache Ranger/IAM 做表级/列级权限 + 脱敏
    以为湖仓能替代一切 直接拿湖表撑万级 QPS 看板,扛不住 超高并发场景加 StarRocks/Doris 查询加速层(湖仓内建或同步),或开缓存

    10. 选型建议与学习路线

    10.1 怎么选?(决策树)

    是否已深度绑定 Databricks / AWS 生态?
    ├─ 是 → Delta Lake(Databricks)/ Iceberg(Glue + Athena 也在大力推)
    └─ 否 → 看核心负载:
        ├─ 以离线批 + 多引擎分析为主 → Apache Iceberg(最中立、引擎支持最广)
        ├─ 以 CDC 高频更新 / 流式为主 → Apache Hudi 或 Apache Paimon(Flink 系首选 Paimon)
        ├─ 预算充足、想要开箱即用 → 商业平台:Databricks / 阿里云 / 腾讯云一体化湖仓
        └─ 不确定 → 先用 Iceberg 起步(社区最大、迁移成本最低),跑通后再演进

    10.2 个人/团队学习路线(由浅入深)

    1. 基础: SQL 进阶 + Hadoop 生态概念 + 对象存储(S3/OSS)基本操作;
    2. 第一站: Docker 起一个 Spark + Iceberg 环境,把第 7 章示例全部跑一遍;
    3. 进阶: 学 Flink SQL + Flink CDC,实现"MySQL → 湖表"实时同步;
    4. 查询层: 装一个 Trino 或 StarRocks,连湖表做秒级 SQL 查询;
    5. 工程化: 补齐 Compaction/快照清理的定时任务、Ranger 权限、数据质量检查;
    6. 深化: 读 Iceberg 官方 spec(表规范文档),理解 manifest/snapshot 机制;对比试用 Hudi 和 Paimon,理解 COW/MOR 权衡。

    11. 术语速查表

    术语 通俗解释
    OSS/S3/HDFS 对象存储/分布式文件系统,湖仓的"地面"——数据文件实际躺在这里,成本极低
    Parquet 列式存储文件格式:同一列数据放一起并压缩,分析查询只读需要的列,又小又快
    开放表格式 在文件之上定义"表"的元数据协议(Iceberg/Hudi/Delta/Paimon),提供事务、版本、统计信息
    Catalog "图书馆索引系统":记录每个表名指向哪份元数据,是事务提交的原子替换点
    快照 Snapshot 表在某一次提交后的完整状态(像 Git commit),Time Travel 的基础
    COW(Copy-on-Write) 更新时重写整个数据文件:读快、写贵,适合读多写少
    MOR(Merge-on-Read) 更新先追加到增量日志,读取时合并:写快、读略慢,适合高频更新
    CDC 变更数据捕获:把数据库的 INSERT/UPDATE/DELETE 以日志方式实时抽出来(如 Flink CDC/Debezium)
    Compaction / Clustering 后台把小文件合并成大文件 / 按常用查询列重排数据,是湖仓的"日常保养"
    Time Travel 按时间或快照 ID 查询/回滚历史数据
    Schema Evolution 表结构演进:加列/改名/改类型不重写数据
    Lambda 架构 旧方案:实时链路 + 离线链路两套并行(湖仓一体推动其向 Kappa/流批一体演进)

    湖仓一体知识全解 · 整理于 2026-09 · 内容涵盖概念演进、核心技术、Iceberg/Hudi/Delta/Paimon 对比、可运行示例与选型建议

  • 8GB内存的老电脑也能跑大模型:Ollama 本地部署完整踩坑记录

    谁说跑大模型必须有显卡?我在一台无GPU、8GB内存的老Windows电脑上跑通了 Ollama + Qwen2.5,本文记录从安装到推理的全过程和踩过的坑。

    一、为什么是 Ollama

    想在本地跑开源模型,Ollama 是目前最省心的方案:一条命令下载模型、自动量化、OpenAI 兼容 API 开箱即用。CPU 也能跑,只是慢点——对”问答、文本处理”这种非实时场景完全够用。

    二、安装踩坑实录

    1. 下载慢/失败:官网直连经常拉不动。实测用 PowerShell 的 Invoke-WebRequest 比 curl 稳,挂上镜像环境变量后正常。
    2. C盘空间:模型文件默认放 C 盘,一个 1.5B 模型约 1GB。C盘紧张的建议先迁移或清理。
    3. 环境变量大小写:Windows 下 HTTP_PROXY 和 http_proxy 同时存在会导致服务起不来(进程环境去重后才正常),这个坑非常隐蔽。
    4. 国内拉模型:设置 OLLAMA_HF_MIRROR 指向国内镜像源,速度直接起飞。

    三、跑通 Qwen2.5:1.5b

    ollama pull qwen2.5:1.5b
    ollama run qwen2.5:1.5b

    实测在纯 CPU、8GB 内存上:17-20 tokens/s,回答流畅度可接受。1.5B 量化模型内存占用约 1.2GB,8GB 机器毫无压力。

    四、API 调用示例

    curl http://localhost:11434/api/generate -d '{
      "model": "qwen2.5:1.5b",
      "prompt": "用一句话介绍你自己"
    }'

    兼容 OpenAI 格式,现有代码改个 base_url 就能接入。

    五、这类机器还能玩什么

    本地跑模型只是开始。我给这台老电脑的完整规划:Ollama 跑推理 + 定时任务挂脚本 + frp 内网穿透。如果你没有闲置电脑,也可以直接上云——腾讯云 2核2G 的新客价只要 99 元/年,跑 1.5B 模型问题不大,入口:

    (推广链接,价格与官网一致,走链接算支持我持续输出)

    六、下一步计划

    给 Ollama 接上 RAG、本地知识库,以及在 4G 内存限制下的多模型轮换策略,踩坑了继续来发。

    有问题评论区见,看到必回。

  • 个人网站0成本上CDN+HTTPS+WAF:腾讯云EdgeOne免费版保姆级白嫖指南

    做个人网站最烦的三件事:访问慢、没HTTPS被浏览器标”不安全”、被人扫来扫去。这三件事用腾讯云 EdgeOne 的免费版套餐能一次全解决,0元。

    一、EdgeOne是什么

    简单说就是”CDN + 安全防护”二合一:你的网站流量先过 EdgeOne 的边缘节点(缓存加速),再回源到你的服务器。顺带送WAF(防SQL注入、防刷)、免费HTTPS证书、DDoS基础防护。

    二、免费版能干什么

    • 每月免费流量额度,个人博客/小站完全够用
    • 免费TLS证书 + 一键强制HTTPS
    • 基础WAF规则
    • 边缘缓存加速,配置好缓存规则命中率能上90%
    • 国内节点加速(接入需备案域名)

    三、接入步骤(5分钟)

    1. 打开 EdgeOne 免费版领取页面(这是我的推广链接,价格与官网一致,白嫖套餐本身免费,走链接算是给我的支持)
    2. 注册/登录后选择免费版套餐开通
    3. 添加你的域名(域名需在腾讯云完成备案)
    4. 按提示到域名DNS处添加CNAME记录
    5. 开启HTTPS(申请免费证书一键搞定)
    6. 配置缓存规则:静态资源(图片/CSS/JS)缓存,动态请求回源

    四、几个实用配置建议

    • 缓存遵循源站 + 强制缓存静态后缀,别全站缓存,不然登录态会串
    • 开启”浏览器缓存遵从源站”,减少莫名刷新问题
    • WAF规则组选”中等”起步,误拦再调
    • 每月看一眼流量账单页,免费额度用超了会有提示,个人站基本不可能超

    五、和99元服务器的组合拳

    我的标准配置:2核2G轻量服务器(99元/年)跑源站 + EdgeOne免费版做加速防护,一年总成本99元,比很多域名钱还便宜。

    服务器买贵了的可以看看热卖套餐汇总(32元/月起)。

    有接入问题评论区聊,看到就回。