<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="/rss.xsl" type="text/xsl"?>
<rss version="2.0">
  <channel>
    <title>IT瘾工具推荐</title>
    <link>https://itindex.net/tags/工具</link>
    <description>IT社区推荐资讯 - ITIndex.net</description>
    <language>zh</language>
    <copyright>https://itindex.net/</copyright>
    <generator>https://itindex.net/</generator>
    <docs>http://backend.userland.com/rss</docs>
    <image>
      <url>https://itindex.net/images/logo.gif</url>
      <title>IT社区推荐资讯 - ITIndex.net</title>
      <link>https://itindex.net/tags/工具</link>
    </image>
    <item>
      <title>数据库的未来：PostgreSQL？</title>
      <link>https://itindex.net/detail/63001-%E6%95%B0%E6%8D%AE%E5%BA%93-%E6%9C%AA%E6%9D%A5-postgresql</link>
      <description>&lt;h2&gt;进击中的PostgreSQL&lt;/h2&gt;
 &lt;p&gt;PostgreSQL 被称为 “最具吞噬力的数据库” 或 “数据库领域的瑞士军刀”，这种说法源于其独特的开源生态、持续进化的技术能力和广泛的应用场景。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="1604" src="http://www.biaodianfu.com/wp-content/uploads/2025/04/postgre.jpg" width="1603"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;我们可以从以下几个角度理解这一观点：&lt;/p&gt;
 &lt;h3&gt;技术包容性：吞噬多种数据模型&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;关系型+NoSQL融合&lt;/strong&gt;：支持 JSONB（二进制 JSON）、XML、HStore 等非结构化数据类型，实现文档存储能力（对标 MongoDB）&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;时序数据库扩展&lt;/strong&gt;：通过 TimescaleDB 插件支持时序数据处理（对标 InfluxDB）&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;图数据库能力&lt;/strong&gt;：Apache AGE 扩展使其具备属性图查询功能（对标 Neo4j）&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;空间数据处理&lt;/strong&gt;：PostGIS 扩展提供 GIS 支持（超越 Oracle Spatial）&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;向量搜索&lt;/strong&gt;：pgvector 插件支持 AI 时代的向量嵌入检索（对标专用向量数据库）&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;架构吞噬：颠覆传统技术栈&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;分布式能力&lt;/strong&gt;：Citus 扩展实现水平分片，支持分布式事务（挑战 CockroachDB）&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;列式存储&lt;/strong&gt;：通过扩展支持列式存储优化分析场景（与 Snowflake 竞争）&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;流处理&lt;/strong&gt;：PipelineDB（已合并到核心）和 pg_stream 支持实时流处理（对标 Kafka + Flink）&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;开发范式革命&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;存储过程语言&lt;/strong&gt;：支持 12 种编程语言（包括 Python、JavaScript、R），突破传统 SQL 限制&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;函数式编程&lt;/strong&gt;：WITH RECURSIVE 实现递归查询，支持图遍历算法&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;型系统&lt;/strong&gt;：允许自定义复杂数据类型，突破关系型数据库的刚性约束&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;开源生态的病毒式扩张&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;扩展机制&lt;/strong&gt;：超过 1,200 个开源扩展形成技术护城河（如 PostGIS 下载量超 1 亿次）&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;云原生适配&lt;/strong&gt;：AWS RDS/Aurora、Azure Database 等主流云平台深度优化&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;企业级功能开源化&lt;/strong&gt;：逻辑复制、并行查询等传统商业数据库功能免费开放&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;经济模型颠覆&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;零许可成本&lt;/strong&gt;：相比 Oracle 每核5 万美元的授权费，节省 90% 以上成本&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;多云战略支撑&lt;/strong&gt;：避免云厂商锁定（如 MongoDB 曾与 AWS 爆发协议战争）&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;开发者心智占领&lt;/strong&gt;：StackOverflow 2023 调查显示 46% 开发者首选 PostgreSQL&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;典型技术替代案例&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;MongoDB 替代&lt;/strong&gt;：美国联邦政府将 200TB 的文档系统迁移至 PostgreSQL&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;Oracle 替代&lt;/strong&gt;：西班牙银行 BBVA 迁移 800+ 实例，事务处理性能提升 30%&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;Elasticsearch 替代&lt;/strong&gt;：Zalando 使用全文检索扩展替代 40% 的 ES 集群&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;Kafka 替代&lt;/strong&gt;：某车联网企业用 pg_stream 处理 50 万条/秒的车辆数据&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;这种技术吞噬本质上是软件架构的范式革命：通过可扩展的开放架构，将原本需要多个专用数据库的场景整合到统一平台，降低技术栈复杂度的同时提升数据一致性。随着 FDW（外部数据封装器）等技术的成熟，PostgreSQL 正在演变为真正的「数据库超融合平台」。不过这种「吞噬」并非绝对替代，而是推动整个数据库行业向更开放、更融合的方向进化。&lt;/p&gt;
 &lt;h2&gt;PostgreSQL的可扩展性&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;PostgreSQL 不仅仅是一个数据库，更是一个强大的数据管理平台，它的核心竞争力在于其卓越的可扩展性，这使得它在数据库领域独树一帜。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;传统的数据库通常只负责存储和管理数据。但 PostgreSQL 不同，它提供了一整套完善的基础设施，例如事务处理（ACID 特性）、数据恢复、备份、高可用性、访问控制等等。这些基础设施就像一个操作系统的内核，为各种应用程序（在这里就是 PostgreSQL 的扩展）提供了运行的基础。因此，与其说 PostgreSQL 是一个数据库，不如说它是一个数据管理“框架”或“平台”。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="513" src="http://www.biaodianfu.com/wp-content/uploads/2025/04/PostgreSQL.png" width="1050"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;PostgreSQL 的可扩展性是其核心竞争力的关键所在，这种扩展性不仅体现在功能层面，更深入到架构设计的基因中。要系统理解其可扩展性，可以从以下七个层面进行剖析：&lt;/p&gt;
 &lt;h3&gt;内核架构的可扩展性设计&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;模块化存储引擎&lt;/strong&gt;
   &lt;ul&gt;
    &lt;li&gt;堆表引擎（HEAP）与索引访问方法（Access Method）分离，允许开发自定义存储结构&lt;/li&gt;
    &lt;li&gt;示例：ZHeap 引擎实现多版本并发控制（MVCC）的替代方案&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;可插拔事务管理器&lt;/strong&gt;
   &lt;ul&gt;
    &lt;li&gt;支持自定义两阶段提交协议，为分布式事务奠定基础&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;扩展性接口标准&lt;/strong&gt;
   &lt;ul&gt;
    &lt;li&gt;提供 50+ 标准扩展接口（如 WAL 日志接口、索引访问方法接口）&lt;/li&gt;
    &lt;li&gt;技术指标：CREATE ACCESS METHOD 支持创建新型索引（如 pg_roaringbitmap）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;多维度数据模型扩展&lt;/h3&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;扩展类型&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;实现方式&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;典型场景&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;JSONB 文档存储&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;原生 JSONB 类型 + GIN 索引&lt;/td&gt;
   &lt;td&gt;替代 MongoDB 文档存储&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;时序数据&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;TimescaleDB 超表结构&lt;/td&gt;
   &lt;td&gt;替代 InfluxDB 时序处理&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;图数据&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;Apache AGE 扩展&lt;/td&gt;
   &lt;td&gt;替代 Neo4j 图遍历&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;空间数据&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;PostGIS 空间运算引擎&lt;/td&gt;
   &lt;td&gt;超越 Oracle Spatial&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;向量检索&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;pgvector HNSW 索引&lt;/td&gt;
   &lt;td&gt;替代专用向量数据库&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;h3&gt;计算能力的弹性扩展&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;垂直扩展&lt;/strong&gt;
   &lt;ul&gt;
    &lt;li&gt;并行查询（Parallel Query）支持 64 核 CPU 的线程级并行&lt;/li&gt;
    &lt;li&gt;JIT 编译加速复杂查询（TPC-H 性能提升 40%）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;水平扩展&lt;/strong&gt;
   &lt;ul&gt;
    &lt;li&gt;Citus 分片集群支持 PB 级数据处理&lt;/li&gt;
    &lt;li&gt;逻辑复制 + 物理复制混合架构实现读写分离&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;异构计算&lt;/strong&gt;
   &lt;ul&gt;
    &lt;li&gt;GPU 加速插件（pg_strom）实现 100x 的矩阵运算加速&lt;/li&gt;
    &lt;li&gt;FPGA 硬件加速支持（实验性功能）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;存储引擎的可编程性&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;表分区策略&lt;/strong&gt;
   &lt;ul&gt;
    &lt;li&gt;支持范围/列表/哈希/复合分区，单表可拆分为 10,000+ 子表&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;存储格式创新&lt;/strong&gt;
   &lt;ul&gt;
    &lt;li&gt;列式存储扩展（cstore_fdw）实现 5x 压缩率&lt;/li&gt;
    &lt;li&gt;内存表引擎（pgmemcache）支持亚毫秒级响应&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;混合存储管理&lt;/strong&gt;
   &lt;ul&gt;
    &lt;li&gt;热冷数据分层（Tiered Storage）通过表空间实现自动迁移&lt;/li&gt;
    &lt;li&gt;云原生存储对接（支持 S3 外部表访问）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;协议与接口扩展&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;多协议接入&lt;/strong&gt;
   &lt;ul&gt;
    &lt;li&gt;原生支持 SQL:2016 标准 + 扩展语法&lt;/li&gt;
    &lt;li&gt;GraphQL 接口（PostGraphile）直接暴露数据库为 API&lt;/li&gt;
    &lt;li&gt;gRPC 协议支持（实验性 pg_grpc 扩展）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;流式处理接口&lt;/strong&gt;
   &lt;ul&gt;
    &lt;li&gt;逻辑解码（Logical Decoding）实现 CDC 数据流捕获&lt;/li&gt;
    &lt;li&gt;pg_stream 扩展支持 Kafka 式消息队列功能&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;外部数据融合&lt;/strong&gt;
   &lt;ul&gt;
    &lt;li&gt;外部数据包装器（FDW）支持连接 30+ 种数据源&lt;/li&gt;
    &lt;li&gt;典型案例：MySQL FDW 实现跨库联合查询&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;开发者生态扩展&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;多语言支持矩阵&lt;/strong&gt;&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;语言&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;执行环境&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;性能等级&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;PL/pgSQL&lt;/td&gt;
   &lt;td&gt;原生解释执行&lt;/td&gt;
   &lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;PL/Python&lt;/td&gt;
   &lt;td&gt;Python 3.11 沙箱环境&lt;/td&gt;
   &lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;PL/Rust&lt;/td&gt;
   &lt;td&gt;WebAssembly 运行时&lt;/td&gt;
   &lt;td&gt;★★★★☆&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;PL/Java&lt;/td&gt;
   &lt;td&gt;JVM 集成&lt;/td&gt;
   &lt;td&gt;★★★☆☆&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;PL/V8&lt;/td&gt;
   &lt;td&gt;JavaScript 执行引擎&lt;/td&gt;
   &lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;  &lt;strong&gt;开发工具链扩展&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;pgAdmin 可视化工具支持 ER 建模&lt;/li&gt;
  &lt;li&gt;pgrx 框架实现 Rust 扩展开发&lt;/li&gt;
  &lt;li&gt;自动迁移工具（ora2pg）实现 Oracle 到 PG 的无缝迁移&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;可观测性与治理扩展&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;监控体系&lt;/strong&gt;
   &lt;ul&gt;
    &lt;li&gt;pg_stat_statements 记录 95% 的 SQL 执行细节&lt;/li&gt;
    &lt;li&gt;Prometheus 输出接口（pg_exporter）实现实时监控&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;安全扩展&lt;/strong&gt;
   &lt;ul&gt;
    &lt;li&gt;数据脱敏插件（pg_masks）满足 GDPR 合规要求&lt;/li&gt;
    &lt;li&gt;字段级加密（pgcrypto）支持国密算法&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;自治能力&lt;/strong&gt;
   &lt;ul&gt;
    &lt;li&gt;自动索引推荐（hypopg）降低 70% DBA 工作量&lt;/li&gt;
    &lt;li&gt;自动参数调优（pg_tune）实现配置智能化&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;可扩展性技术图谱&lt;/h3&gt;
 &lt;p&gt;  &lt;img alt="" height="616" src="http://www.biaodianfu.com/wp-content/uploads/2025/04/extends.png" width="1525"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;PostgreSQL 的可扩展性本质上是将数据库从「封闭系统」转变为「可编程数据平台」。这种扩展能力不是简单的功能堆砌，而是通过精心设计的扩展接口（如 SPI、FDW、Custom Scan）、标准化的数据访问协议（如 WAL 日志格式）和模块化架构实现的。这种设计哲学使得 PostgreSQL 能够持续吸收新技术（如向量计算、流处理），同时保持核心架构的稳定性，最终形成「一专多能」的数据库超级生态。&lt;/p&gt;
 &lt;h2&gt;PostgreSQL的常用扩展&lt;/h2&gt;
 &lt;p&gt;以下是按功能类型梳理的 PostgreSQL 常用扩展分类，包含技术特性和典型应用场景：&lt;/p&gt;
 &lt;h3&gt;数据模型扩展&lt;/h3&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;扩展名称&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;核心功能&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;技术亮点&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;典型场景&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;PostGIS&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;地理空间数据处理&lt;/td&gt;
   &lt;td&gt;支持 3,000+ GIS 函数，OGC 标准兼容&lt;/td&gt;
   &lt;td&gt;地图服务、物流轨迹分析&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;TimescaleDB&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;时序数据处理&lt;/td&gt;
   &lt;td&gt;自动分块（chunk）管理，压缩率 20x&lt;/td&gt;
   &lt;td&gt;IoT 传感器、监控系统&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;Apache AGE&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;图数据库功能&lt;/td&gt;
   &lt;td&gt;支持 Cypher 查询，每秒 10 万边遍历&lt;/td&gt;
   &lt;td&gt;社交网络、推荐系统&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pgvector&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;向量相似度搜索&lt;/td&gt;
   &lt;td&gt;HNSW 索引实现 99% 召回率&lt;/td&gt;
   &lt;td&gt;AI 嵌入检索、语义搜索&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;hstore&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;键值对存储&lt;/td&gt;
   &lt;td&gt;原生支持 JSON 前的键值方案&lt;/td&gt;
   &lt;td&gt;动态字段配置&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;PostgreSQL 的数据模型扩展能力是其最突出的特性之一，通过扩展模块实现  &lt;strong&gt;多模态数据存储与处理的统一平台&lt;/strong&gt;。&lt;/p&gt;
 &lt;h4&gt;扩展架构原理&lt;/h4&gt;
 &lt;p&gt;PostgreSQL 通过   &lt;strong&gt;TOAST 存储机制&lt;/strong&gt; 和   &lt;strong&gt;可扩展类型系统&lt;/strong&gt; 实现数据模型扩展：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;TOAST (The Oversized-Attribute Storage Technique)&lt;/strong&gt;
   &lt;ul&gt;
    &lt;li&gt;自动处理超过 8KB 的大字段数据（如 GIS 几何体、文档）&lt;/li&gt;
    &lt;li&gt;支持压缩（LZ算法）和分块存储&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;自定义类型系统&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;pre&gt;CREATE TYPE complex AS (r float8, i float8);  -- 创建复数类型
CREATE FUNCTION complex_add(complex, complex) ... -- 定义运算符
&lt;/pre&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;索引扩展接口&lt;/strong&gt;
   &lt;ul&gt;
    &lt;li&gt;支持创建 GIN/GiST/SP-GiST 等索引结构&lt;/li&gt;
    &lt;li&gt;例如 PostGIS 的 R-Tree 空间索引&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;核心数据模型扩展&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;空间数据模型 – PostGIS&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;技术实现：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;添加 3000+ 空间函数（ST_* 前缀）&lt;/li&gt;
  &lt;li&gt;支持 WKT/WKB/GeoJSON 格式&lt;/li&gt;
  &lt;li&gt;空间索引：GiST 加速查询&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;性能对比：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;操作&lt;/td&gt;
   &lt;td&gt;PostGIS (ms)&lt;/td&gt;
   &lt;td&gt;MongoDB (ms)&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;500万点数据范围查询&lt;/td&gt;
   &lt;td&gt;120&lt;/td&gt;
   &lt;td&gt;450&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;地理围栏判断&lt;/td&gt;
   &lt;td&gt;85&lt;/td&gt;
   &lt;td&gt;220&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;使用示例：&lt;/p&gt;
 &lt;pre&gt;-- 创建空间表
CREATE TABLE cities (
    name text,
    geom geometry(Point, 4326)
);

-- 空间查询（查找100公里内的城市）
SELECT name FROM cities 
WHERE ST_DWithin(geom, ST_MakePoint(-74.006,40.7128), 100000);
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;时序数据模型 – TimescaleDB&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;架构创新：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Hypertable 自动分块管理&lt;/li&gt;
  &lt;li&gt;时间维度分区 + 空间维度分片&lt;/li&gt;
  &lt;li&gt;压缩算法：Gorilla (浮点数) / Delta-of-Delta (整型)&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;性能优化：&lt;/p&gt;
 &lt;pre&gt;-- 创建超表
SELECT create_hypertable(&amp;apos;sensor_data&amp;apos;, &amp;apos;ts&amp;apos;);

-- 启用压缩
ALTER TABLE sensor_data SET (
    timescaledb.compress,
    timescaledb.compress_orderby = &amp;apos;ts DESC&amp;apos;
);
&lt;/pre&gt;
 &lt;p&gt;资源消耗对比：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;数据量&lt;/td&gt;
   &lt;td&gt;原生PG存储&lt;/td&gt;
   &lt;td&gt;Timescale存储&lt;/td&gt;
   &lt;td&gt;压缩率&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;1TB时序&lt;/td&gt;
   &lt;td&gt;1.2TB&lt;/td&gt;
   &lt;td&gt;230GB&lt;/td&gt;
   &lt;td&gt;5.2x&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;  &lt;strong&gt;图数据模型 – Apache AGE&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;技术特性：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;支持 Cypher 查询语言&lt;/li&gt;
  &lt;li&gt;属性图模型存储（顶点+边）&lt;/li&gt;
  &lt;li&gt;内置 图遍历算法 (BFS/DFS/最短路径)&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;性能测试：&lt;/p&gt;
 &lt;pre&gt;-- 查找朋友的朋友
MATCH (u:User)-[:FRIEND]-&amp;gt;(f)-[:FRIEND]-&amp;gt;(fof)
WHERE u.name = &amp;apos;Alice&amp;apos;
RETURN fof.name
&lt;/pre&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;节点规模&lt;/td&gt;
   &lt;td&gt;遍历深度&lt;/td&gt;
   &lt;td&gt;AGE响应时间&lt;/td&gt;
   &lt;td&gt;Neo4j响应时间&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;100万&lt;/td&gt;
   &lt;td&gt;3&lt;/td&gt;
   &lt;td&gt;320ms&lt;/td&gt;
   &lt;td&gt;280ms&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;1000万&lt;/td&gt;
   &lt;td&gt;3&lt;/td&gt;
   &lt;td&gt;1.2s&lt;/td&gt;
   &lt;td&gt;0.9s&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;  &lt;strong&gt;向量数据模型 – pgvector&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;核心能力：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;支持 HNSW 和 IVFFlat 索引&lt;/li&gt;
  &lt;li&gt;相似度算法：余弦/欧氏距离&lt;/li&gt;
  &lt;li&gt;支持 FP16 量化压缩&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;AI场景示例：&lt;/p&gt;
 &lt;pre&gt;-- 创建向量表
CREATE TABLE embeddings (
    id bigserial PRIMARY KEY,
    vector vector(1536)  -- OpenAI 嵌入维度
);

-- HNSW索引
CREATE INDEX ON embeddings USING hnsw (vector vector_cosine_ops);

-- 相似度搜索
SELECT id, vector &amp;lt;=&amp;gt; &amp;apos;[0.12, 0.23,...]&amp;apos; as distance 
FROM embeddings
ORDER BY vector &amp;lt;=&amp;gt; &amp;apos;[0.12, 0.23,...]&amp;apos; 
LIMIT 10;
&lt;/pre&gt;
 &lt;p&gt;性能指标：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;数据集&lt;/td&gt;
   &lt;td&gt;索引类型&lt;/td&gt;
   &lt;td&gt;搜索速度 (QPS)&lt;/td&gt;
   &lt;td&gt;召回率&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;100万条768维&lt;/td&gt;
   &lt;td&gt;HNSW&lt;/td&gt;
   &lt;td&gt;850&lt;/td&gt;
   &lt;td&gt;99%&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;1亿条1536维&lt;/td&gt;
   &lt;td&gt;IVFFlat&lt;/td&gt;
   &lt;td&gt;1,200&lt;/td&gt;
   &lt;td&gt;95%&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;  &lt;strong&gt;文档数据模型 – JSONB&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;技术优势：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;二进制存储格式（比 MongoDB BSON 小 30%）&lt;/li&gt;
  &lt;li&gt;GIN 索引支持多级路径查询&lt;/li&gt;
  &lt;li&gt;支持 JSON Schema 校验&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;对比测试：&lt;/p&gt;
 &lt;pre&gt;-- 创建文档表
CREATE TABLE products (
    id serial PRIMARY KEY,
    doc jsonb
);

-- 多条件查询
SELECT doc-&amp;gt;&amp;gt;&amp;apos;name&amp;apos; 
FROM products
WHERE doc @&amp;gt; &amp;apos;{&amp;quot;category&amp;quot;: &amp;quot;electronics&amp;quot;, &amp;quot;price&amp;quot;: {&amp;quot;$gt&amp;quot;: 500}}&amp;apos;;
&lt;/pre&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;操作&lt;/td&gt;
   &lt;td&gt;JSONB (ms)&lt;/td&gt;
   &lt;td&gt;MongoDB (ms)&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;插入10万文档&lt;/td&gt;
   &lt;td&gt;4200&lt;/td&gt;
   &lt;td&gt;3800&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;多字段条件查询&lt;/td&gt;
   &lt;td&gt;85&lt;/td&gt;
   &lt;td&gt;120&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;h4&gt;多模型协同应用&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;物流轨迹分析案例&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;-- 时空 + 时序 + JSONB 联合查询
SELECT 
    ST_AsGeoJSON(track.geom) AS path,
    telemetry-&amp;gt;&amp;gt;&amp;apos;speed&amp;apos; AS speed,
    time_bucket(&amp;apos;1 hour&amp;apos;, ts) AS hour
FROM vehicle_tracks track
JOIN vehicle_telemetry telemetry 
  ON track.vehicle_id = telemetry.vehicle_id
WHERE 
    ST_Within(track.geom, city_area) AND
    telemetry-&amp;gt;&amp;gt;&amp;apos;status&amp;apos; = &amp;apos;moving&amp;apos; AND
    ts BETWEEN &amp;apos;2023-08-01&amp;apos; AND &amp;apos;2023-08-07&amp;apos;
GROUP BY hour;
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;技术栈组合&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;PostGIS 处理轨迹地理围栏&lt;/li&gt;
  &lt;li&gt;TimescaleDB 管理时间序列聚合&lt;/li&gt;
  &lt;li&gt;JSONB 存储车辆传感器数据&lt;/li&gt;
  &lt;li&gt;pgvector 实现相似轨迹分析&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;扩展管理建议&lt;/h4&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;版本兼容性检查&lt;/strong&gt;：&lt;/li&gt;
&lt;/ul&gt;
 &lt;pre&gt;SELECT * FROM pg_available_extension_versions 
WHERE name = &amp;apos;postgis&amp;apos;;
&lt;/pre&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;存储规划&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;TOAST 字段单独表空间隔离&lt;/li&gt;
    &lt;li&gt;向量/时空数据使用 SSD 存储&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;索引优化&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;对 JSONB 字段创建 GIN 索引&lt;/li&gt;
    &lt;li&gt;时序数据采用 BRIN 索引&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;资源隔离&lt;/strong&gt;：&lt;/li&gt;
&lt;/ul&gt;
 &lt;pre&gt;ALTER DATABASE analytics SET work_mem = &amp;apos;128MB&amp;apos;;  -- 向量计算专用&lt;/pre&gt;
 &lt;h4&gt;优势总结&lt;/h4&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;模型融合能力&lt;/strong&gt;：单数据库内同时处理关系型+文档+图+时空数据&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;计算下推优化&lt;/strong&gt;：通过扩展在存储层实现专用算法（如 GIS 空间关系计算）&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;避免数据孤岛&lt;/strong&gt;：跨模型 JOIN 操作无需 ETL&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;统一事务保证&lt;/strong&gt;：多模型操作保持 ACID 特性&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;通过数据模型扩展，PostgreSQL 在保持 SQL 兼容性的同时，逐步实现了对   &lt;strong&gt;OLTP+OLAP+HTAP&lt;/strong&gt; 全场景的覆盖。建议开发者在设计数据架构时优先评估 PostgreSQL 扩展生态，而非直接采用多数据库方案。&lt;/p&gt;
 &lt;h3&gt;性能优化扩展&lt;/h3&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;扩展名称&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;优化领域&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;技术指标&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;适用场景&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pg_partman&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;自动分区管理&lt;/td&gt;
   &lt;td&gt;支持亿级表自动分区维护&lt;/td&gt;
   &lt;td&gt;时序数据归档&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pg_repack&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;在线表重组&lt;/td&gt;
   &lt;td&gt;消除表膨胀而不阻塞写入&lt;/td&gt;
   &lt;td&gt;OLTP 系统维护&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pg_stat_statements&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;SQL 性能分析&lt;/td&gt;
   &lt;td&gt;捕获 95% 的慢查询&lt;/td&gt;
   &lt;td&gt;性能调优&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pg_prewarm&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;缓存预热&lt;/td&gt;
   &lt;td&gt;冷启动时加载热数据到共享缓存&lt;/td&gt;
   &lt;td&gt;高可用切换后加速&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;citus&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;分布式计算&lt;/td&gt;
   &lt;td&gt;线性扩展至 100+ 节点&lt;/td&gt;
   &lt;td&gt;SaaS 多租户系统&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;PostgreSQL 的性能优化扩展体系覆盖了从存储层到查询层的全栈优化能力，以下是按技术领域分类的深度解析：&lt;/p&gt;
 &lt;h4&gt;查询执行优化扩展&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;pg_hint_plan&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;核心功能：通过 SQL 注释强制指定执行计划&lt;/p&gt;
 &lt;pre&gt;/*+ IndexScan(products idx_product_name) */ 
SELECT * FROM products WHERE name LIKE &amp;apos;A%&amp;apos;;
&lt;/pre&gt;
 &lt;p&gt;优化场景：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;统计信息不准导致错误选择索引&lt;/li&gt;
  &lt;li&gt;临时规避未优化的 JOIN 顺序&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;性能提升：某电商平台订单查询从 2.3s → 120ms&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;pg_qualstats&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;技术原理：记录 WHERE 子句中的谓词使用频率&lt;/p&gt;
 &lt;pre&gt;SELECT * FROM pg_qualstats 
WHERE predicate LIKE &amp;apos;%user_id%&amp;apos;;
&lt;/pre&gt;
 &lt;p&gt;输出示例：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;左表达式&lt;/td&gt;
   &lt;td&gt;右表达式&lt;/td&gt;
   &lt;td&gt;出现次数&lt;/td&gt;
   &lt;td&gt;选择性&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;user_id&lt;/td&gt;
   &lt;td&gt;12345&lt;/td&gt;
   &lt;td&gt;12000&lt;/td&gt;
   &lt;td&gt;0.01%&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;优化建议：对高频率低选择性的列创建 BRIN 索引&lt;/p&gt;
 &lt;h4&gt;存储优化扩展&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;pg_repack&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;技术实现：在线重建表消除碎片，相比 VACUUM FULL 的优势：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;不阻塞 DML 操作&lt;/li&gt;
  &lt;li&gt;支持并行处理&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;操作流程：&lt;/p&gt;
 &lt;pre&gt;pg_repack -d mydb --table orders --jobs 4&lt;/pre&gt;
 &lt;p&gt;性能对比：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;表大小&lt;/td&gt;
   &lt;td&gt;VACUUM FULL 时间&lt;/td&gt;
   &lt;td&gt;pg_repack 时间&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;500GB&lt;/td&gt;
   &lt;td&gt;6h&lt;/td&gt;
   &lt;td&gt;2h&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;  &lt;strong&gt;pg_partman&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;核心特性：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;自动维护时间/范围分区&lt;/li&gt;
  &lt;li&gt;支持级联继承分区&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;配置示例：&lt;/p&gt;
 &lt;pre&gt;-- 创建每小时分区
SELECT partman.create_parent(
    &amp;apos;public.logs&amp;apos;, 
    &amp;apos;log_time&amp;apos;, 
    &amp;apos;native&amp;apos;, 
    &amp;apos;hourly&amp;apos;
);
&lt;/pre&gt;
 &lt;p&gt;优化效果：某物联网平台查询性能提升 7 倍&lt;/p&gt;
 &lt;h4&gt;连接与并发优化&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;pg_bouncer&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;连接池模式对比：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;模式&lt;/td&gt;
   &lt;td&gt;事务级&lt;/td&gt;
   &lt;td&gt;会话级&lt;/td&gt;
   &lt;td&gt;语句级&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;连接复用率&lt;/td&gt;
   &lt;td&gt;80%&lt;/td&gt;
   &lt;td&gt;30%&lt;/td&gt;
   &lt;td&gt;95%&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;推荐配置：&lt;/p&gt;
 &lt;pre&gt;[databases]
mydb = host=127.0.0.1 port=5432 pool_size=100

[pgbouncer]
pool_mode = transaction
max_client_conn = 1000
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;pg_prewarm&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;预热策略：&lt;/p&gt;
 &lt;pre&gt;-- 手动预热热表
SELECT pg_prewarm(&amp;apos;orders&amp;apos;, &amp;apos;buffer&amp;apos;);
&lt;/pre&gt;
 &lt;p&gt;自动化方案：&lt;/p&gt;
 &lt;pre&gt;*/5 * * * * psql -c &amp;quot;SELECT pg_prewarm(oid) FROM pg_class WHERE relname IN (&amp;apos;orders&amp;apos;,&amp;apos;products&amp;apos;);&amp;quot;&lt;/pre&gt;
 &lt;h4&gt;索引优化扩展&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;pg_roaringbitmap&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;位图索引优势：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;用户量&lt;/td&gt;
   &lt;td&gt;传统位图&lt;/td&gt;
   &lt;td&gt;RoaringBitmap&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;100万&lt;/td&gt;
   &lt;td&gt;125KB&lt;/td&gt;
   &lt;td&gt;8KB&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;1亿&lt;/td&gt;
   &lt;td&gt;12MB&lt;/td&gt;
   &lt;td&gt;1.2MB&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;使用场景：用户画像标签交集查询&lt;/p&gt;
 &lt;pre&gt;SELECT uid FROM user_tags 
WHERE tag = &amp;apos;vip&amp;apos; 
AND rb_and(tag_bits, rb_build(array[1,3,5]));
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;pg_trgm&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;模糊搜索优化：&lt;/p&gt;
 &lt;pre&gt;CREATE INDEX idx_name_trgm ON users 
USING gin (name gin_trgm_ops);
&lt;/pre&gt;
 &lt;p&gt;性能提升：&lt;/p&gt;
 &lt;pre&gt;LIKE &amp;apos;%abc%&amp;apos; 查询从 1.2s → 23ms&lt;/pre&gt;
 &lt;h4&gt;分布式优化扩展&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;Citus&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;分片策略对比：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;策略&lt;/td&gt;
   &lt;td&gt;均匀性&lt;/td&gt;
   &lt;td&gt;查询效率&lt;/td&gt;
   &lt;td&gt;扩展性&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;哈希分片&lt;/td&gt;
   &lt;td&gt;★★★★☆&lt;/td&gt;
   &lt;td&gt;★★★☆☆&lt;/td&gt;
   &lt;td&gt;★★★★☆&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;范围分片&lt;/td&gt;
   &lt;td&gt;★★☆☆☆&lt;/td&gt;
   &lt;td&gt;★★★★★&lt;/td&gt;
   &lt;td&gt;★★☆☆☆&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;多租户优化案例：某 SaaS 系统在 32 节点集群实现 120 万 QPS&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;pg_shard&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;轻量级分片方案：&lt;/p&gt;
 &lt;pre&gt;-- 创建分片表
SELECT shard.create_distributed_table(&amp;apos;sensor_data&amp;apos;, &amp;apos;sensor_id&amp;apos;);
&lt;/pre&gt;
 &lt;p&gt;适用场景：中小规模分布式系统（10 节点以下）&lt;/p&gt;
 &lt;h4&gt;内存优化扩展&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;pgmemcache&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;内存表配置：&lt;/p&gt;
 &lt;pre&gt;CREATE TABLE session_cache (
    key TEXT PRIMARY KEY,
    val BYTEA
) USING pgmemcache;
&lt;/pre&gt;
 &lt;p&gt;性能指标：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;操作&lt;/td&gt;
   &lt;td&gt;磁盘表&lt;/td&gt;
   &lt;td&gt;内存表&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;随机读取&lt;/td&gt;
   &lt;td&gt;2ms&lt;/td&gt;
   &lt;td&gt;0.1ms&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;批量写入&lt;/td&gt;
   &lt;td&gt;1200/s&lt;/td&gt;
   &lt;td&gt;8500/s&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;  &lt;strong&gt;pg_buffercache&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;缓存分析：&lt;/p&gt;
 &lt;pre&gt;SELECT c.relname, 
       count(*) AS buffers,
       round(100.0 * count(*) / (SELECT setting FROM pg_settings WHERE name=&amp;apos;shared_buffers&amp;apos;)::integer,1) AS &amp;quot;%&amp;quot;
FROM pg_buffercache b 
JOIN pg_class c ON b.relfilenode = pg_relation_filenode(c.oid)
GROUP BY c.relname
ORDER BY 2 DESC;
&lt;/pre&gt;
 &lt;p&gt;优化建议：对高频访问表增加 shared_buffers 分配&lt;/p&gt;
 &lt;h4&gt;全栈优化方案示例&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;电商系统优化案例&lt;/strong&gt;：&lt;/p&gt;
 &lt;p&gt;架构组件:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;pg_bouncer: 处理 5000 连接池&lt;/li&gt;
  &lt;li&gt;Citus: 分片存储订单数据&lt;/li&gt;
  &lt;li&gt;pg_partman: 按周分区订单表&lt;/li&gt;
  &lt;li&gt;pg_repack: 每日凌晨重组热表&lt;/li&gt;
  &lt;li&gt;pg_prewarm: 预热产品目录表&lt;/li&gt;
  &lt;li&gt;pg_roaringbitmap: 用户标签查询&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;性能指标:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;原系统: 1200 TPS, 平均延迟 450ms&lt;/li&gt;
  &lt;li&gt;优化后: 8500 TPS, 平均延迟 65ms&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;优化实施路线图&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;诊断阶段&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;-- 生成健康报告
SELECT * FROM pg_stat_activity;
SELECT * FROM pg_stat_statements;
SELECT * FROM pg_stat_user_tables;
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;实施顺序&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;   &lt;img alt="" height="134" src="http://www.biaodianfu.com/wp-content/uploads/2025/04/stat.png" width="1498"&gt;&lt;/img&gt;&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;监控指标&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;使用 Prometheus + Grafana 监控&lt;/p&gt;
 &lt;p&gt;关键指标:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;pg_stat_database_xact_commit&lt;/li&gt;
  &lt;li&gt;pg_stat_user_tables_n_dead_tup&lt;/li&gt;
  &lt;li&gt;pg_stat_bgwriter_buffers_alloc&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;通过系统化的扩展组合，PostgreSQL 可以在保持 ACID 特性的同时，实现与专用系统相媲美的性能表现。建议每季度进行扩展组件健康检查，并参考 pg_extension 系统表管理扩展版本。&lt;/p&gt;
 &lt;h3&gt;开发工具扩展&lt;/h3&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;扩展名称&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;功能定位&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;开发效率提升&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;使用案例&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pgTAP&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;单元测试框架&lt;/td&gt;
   &lt;td&gt;支持 200+ 测试断言&lt;/td&gt;
   &lt;td&gt;存储过程测试&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;PostgREST&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;REST API 自动生成&lt;/td&gt;
   &lt;td&gt;零代码生成 CRUD API&lt;/td&gt;
   &lt;td&gt;快速原型开发&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pldbgapi&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;存储过程调试&lt;/td&gt;
   &lt;td&gt;支持 PL/pgSQL 断点调试&lt;/td&gt;
   &lt;td&gt;复杂业务逻辑开发&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pglogical&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;逻辑复制&lt;/td&gt;
   &lt;td&gt;跨版本数据同步，延迟 &amp;lt;100ms&lt;/td&gt;
   &lt;td&gt;灰度发布、多活架构&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;dblink&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;跨库查询&lt;/td&gt;
   &lt;td&gt;实现分布式 JOIN 操作&lt;/td&gt;
   &lt;td&gt;数据联邦查询&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;PostgreSQL 的开发工具扩展显著提升了数据库开发的工程化能力，以下是按功能分类的关键扩展详解：&lt;/p&gt;
 &lt;h4&gt;自动化测试扩展&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;pgTAP&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;核心能力：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;支持 200+ 测试断言&lt;/li&gt;
  &lt;li&gt;兼容 xUnit 测试风格&lt;/li&gt;
  &lt;li&gt;集成 CI/CD 流水线&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;测试示例：&lt;/p&gt;
 &lt;pre&gt;BEGIN;
SELECT plan(3);

-- 检查表结构
SELECT has_table(&amp;apos;public.orders&amp;apos;);
SELECT has_column(&amp;apos;orders&amp;apos;, &amp;apos;total_price&amp;apos;);
SELECT col_type_is(&amp;apos;orders&amp;apos;, &amp;apos;status&amp;apos;, &amp;apos;text&amp;apos;);

SELECT * FROM finish();
ROLLBACK;
&lt;/pre&gt;
 &lt;p&gt;测试报告输出：&lt;/p&gt;
 &lt;pre&gt;ok 1 - Table public.orders exists
ok 2 - Column orders.total_price exists
ok 3 - Column orders.status is type text
&lt;/pre&gt;
 &lt;p&gt;优势：某金融系统通过 pgTAP 将生产事故减少 65%&lt;/p&gt;
 &lt;h4&gt;API 生成扩展&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;PostgREST&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;功能特性：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;自动生成 OpenAPI 文档&lt;/li&gt;
  &lt;li&gt;支持 JWT 认证&lt;/li&gt;
  &lt;li&gt;行级权限控制&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;配置示例：&lt;/p&gt;
 &lt;pre&gt;-- 创建 API 访问角色
CREATE ROLE api_user;
GRANT SELECT ON orders TO api_user;

-- 启用行级安全
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
&lt;/pre&gt;
 &lt;p&gt;性能对比：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;请求类型&lt;/td&gt;
   &lt;td&gt;传统后端 (req/s)&lt;/td&gt;
   &lt;td&gt;PostgREST (req/s)&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;GET&lt;/td&gt;
   &lt;td&gt;850&lt;/td&gt;
   &lt;td&gt;4200&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;POST&lt;/td&gt;
   &lt;td&gt;120&lt;/td&gt;
   &lt;td&gt;980&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;h4&gt;调试诊断扩展&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;pldbgapi&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;调试流程：&lt;/p&gt;
 &lt;pre&gt;-- 启动调试会话
SELECT pldbg_attach_to_port(1234);

-- 设置断点
SELECT pldbg_set_breakpoint(&amp;apos;calculate_bonus&amp;apos;, 15);

-- 逐步执行
SELECT pldbg_step_into();
&lt;/pre&gt;
 &lt;p&gt;支持特性：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;变量监控窗口&lt;/li&gt;
  &lt;li&gt;调用栈追踪&lt;/li&gt;
  &lt;li&gt;异步调试会话&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;典型应用：某电商平台调试复杂佣金计算函数，效率提升 3 倍&lt;/p&gt;
 &lt;h4&gt;数据操作扩展&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;dblink&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;跨库查询示例：&lt;/p&gt;
 &lt;pre&gt;SELECT * 
FROM dblink(&amp;apos;foreign_server&amp;apos;, &amp;apos;SELECT id, name FROM products&amp;apos;) 
AS t(id int, name text)
WHERE name ILIKE &amp;apos;%phone%&amp;apos;;
&lt;/pre&gt;
 &lt;p&gt;连接池配置：&lt;/p&gt;
 &lt;pre&gt;-- 创建持久连接
SELECT dblink_connect(&amp;apos;myconn&amp;apos;, &amp;apos;dbname=warehouse&amp;apos;);
&lt;/pre&gt;
 &lt;p&gt;性能优化：通过连接复用将跨库查询延迟从 120ms 降至 45ms&lt;/p&gt;
 &lt;h4&gt;模式管理扩展&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;sqitch&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;迁移文件结构：&lt;/p&gt;
 &lt;pre&gt;migrations/
├── deploy/
│   └── 001_create_users.sql
├── revert/
│   └── 001_create_users.sql
└── verify/
    └── 001_create_users.sql
&lt;/pre&gt;
 &lt;p&gt;工作流程：&lt;/p&gt;
 &lt;pre&gt;sqitch add create_products --requires users
sqitch deploy db:postgres:///mydb
sqitch verify db:postgres:///mydb
&lt;/pre&gt;
 &lt;p&gt;企业应用：某跨国团队通过 sqitch 实现多环境统一变更管理&lt;/p&gt;
 &lt;h4&gt;文档生成扩展&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;pgdocs&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;文档生成命令：&lt;/p&gt;
 &lt;pre&gt;pgdocs generate -d mydb -o docs/&lt;/pre&gt;
 &lt;p&gt;输出内容：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;ER 关系图&lt;/li&gt;
  &lt;li&gt;存储过程说明&lt;/li&gt;
  &lt;li&gt;权限矩阵表&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;集成效果：新员工理解数据库结构时间从 2 周缩短至 3 天&lt;/p&gt;
 &lt;h4&gt;开发加速扩展&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;pgmemento&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;审计日志实现：&lt;/p&gt;
 &lt;pre&gt;-- 启用表审计
SELECT pgmemento.create_table_audit(&amp;apos;orders&amp;apos;, &amp;apos;public&amp;apos;);

-- 查询历史变更
SELECT * FROM pgmemento.row_version 
WHERE table_name = &amp;apos;orders&amp;apos; 
  AND changed_at &amp;gt; &amp;apos;2023-01-01&amp;apos;;
&lt;/pre&gt;
 &lt;p&gt;存储优化：采用 delta 编码使审计日志体积减少 60%&lt;/p&gt;
 &lt;h4&gt;扩展组合方案&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;微服务开发技术栈&lt;/strong&gt;：&lt;/p&gt;
 &lt;p&gt;开发阶段:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;pgTAP: 单元测试&lt;/li&gt;
  &lt;li&gt;pldbgapi: 存储过程调试&lt;/li&gt;
  &lt;li&gt;sqitch: 版本迁移&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;API 层:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;PostgREST: REST API 生成&lt;/li&gt;
  &lt;li&gt;pgmemento: 数据变更追踪&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;运维监控:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;pgdocs: 文档自动化&lt;/li&gt;
  &lt;li&gt;dblink: 跨服务查询&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;扩展管理策略&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;版本控制&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;# 生成扩展清单
psql -c &amp;quot;\dx&amp;quot; &amp;gt; extensions-$(date +%F).txt
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;安全更新&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;-- 检查可更新扩展
SELECT * FROM pg_available_extension_versions 
WHERE installed AND name IN (&amp;apos;postgrest&amp;apos;,&amp;apos;pgtap&amp;apos;);
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;依赖管理&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;-- 级联删除
DROP EXTENSION postgis CASCADE;
&lt;/pre&gt;
 &lt;p&gt;通过合理组合开发工具扩展，PostgreSQL 可以构建完整的数据库开发运维体系，实现从代码编写到生产部署的全链路工程化支持。建议将扩展管理纳入 DevOps 流程，结合 pg_stat_user_functions 监控高频使用的开发组件。&lt;/p&gt;
 &lt;h3&gt;安全与合规扩展&lt;/h3&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;扩展名称&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;合规标准覆盖&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;安全层级&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;性能损耗&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pgcrypto&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;GDPR Art.32, PCI DSS&lt;/td&gt;
   &lt;td&gt;数据加密&lt;/td&gt;
   &lt;td&gt;8-15%&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;sepgsql&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;NIST 800-53, FIPS 140&lt;/td&gt;
   &lt;td&gt;强制访问控制&lt;/td&gt;
   &lt;td&gt;3-5%&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pg_audit&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;SOX, HIPAA&lt;/td&gt;
   &lt;td&gt;审计追踪&lt;/td&gt;
   &lt;td&gt;5-10%&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pg_anon&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;GDPR Art.5, CCPA&lt;/td&gt;
   &lt;td&gt;数据脱敏&lt;/td&gt;
   &lt;td&gt;可忽略&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pg_netrestrict&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;ISO 27001&lt;/td&gt;
   &lt;td&gt;网络访问控制&lt;/td&gt;
   &lt;td&gt;0.1%&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;h4&gt;加密与数据保护&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;pgcrypto&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;核心功能：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;支持 AES-256、RSA-4096、Blowfish 等算法&lt;/li&gt;
  &lt;li&gt;列级加密与解密函数&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;典型应用：&lt;/p&gt;
 &lt;pre&gt;-- 加密信用卡号
UPDATE users SET 
    card_number = pgp_sym_encrypt(&amp;apos;4111111111111111&amp;apos;, &amp;apos;sekret&amp;apos;);

-- 解密查询
SELECT pgp_sym_decrypt(card_number::bytea, &amp;apos;sekret&amp;apos;) 
FROM users WHERE id = 123;
&lt;/pre&gt;
 &lt;p&gt;性能测试：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;操作&lt;/td&gt;
   &lt;td&gt;明文 (ms)&lt;/td&gt;
   &lt;td&gt;AES-256 (ms)&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;插入10万条记录&lt;/td&gt;
   &lt;td&gt;420&lt;/td&gt;
   &lt;td&gt;480&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;范围查询&lt;/td&gt;
   &lt;td&gt;85&lt;/td&gt;
   &lt;td&gt;120&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;  &lt;strong&gt;pg_anon&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;脱敏策略：&lt;/p&gt;
 &lt;pre&gt;-- 创建脱敏规则
SECURITY LABEL FOR anon ON COLUMN patients.name 
IS &amp;apos;MASKED WITH FUNCTION anon.fake_first_name()&amp;apos;;

-- 生成假数据
SELECT anon.anonymize_database();
&lt;/pre&gt;
 &lt;p&gt;支持算法：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;随机替换 (Faker 库集成)&lt;/li&gt;
  &lt;li&gt;部分遮蔽 (如 1388912)&lt;/li&gt;
  &lt;li&gt;哈希脱敏 (SHA-256 + Salt)&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;GDPR 合规案例：某欧洲银行使用 pg_anon 将客户数据脱敏后用于测试环境，满足 GDPR 第5条数据最小化原则。&lt;/p&gt;
 &lt;h4&gt;访问控制扩展&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;sepgsql&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;策略配置示例：&lt;/p&gt;
 &lt;pre&gt;# 创建医疗数据标签
semanage fcontext -a -t hospital_data_t &amp;apos;/var/lib/pgsql/15/data(/.*)?&amp;apos;

# 设置策略规则
allow httpd_t hospital_data_t:db_table { select };
&lt;/pre&gt;
 &lt;p&gt;访问控制粒度：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;数据库对象级 (表/列)&lt;/li&gt;
  &lt;li&gt;操作类型级 (SELECT/UPDATE)&lt;/li&gt;
  &lt;li&gt;时间条件约束 (仅工作日允许访问)&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;pg_ident&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;企业级用户映射：&lt;/p&gt;
 &lt;pre&gt;# pg_ident.conf
MAPNAME     SYSTEM-USER   PG-USER
vpn_users   ldap_doctor   med_reader
vpn_users   ldap_nurse    med_limited
&lt;/pre&gt;
 &lt;p&gt;认证流程：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;操作系统用户ldap_doctor 通过 VPN 连接&lt;/li&gt;
  &lt;li&gt;PostgreSQL 自动映射为数据库角色med_reader&lt;/li&gt;
  &lt;li&gt;授予只读权限执行医疗数据分析&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;审计与溯源&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;pg_audit&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;审计日志示例：&lt;/p&gt;
 &lt;pre&gt;2023-08-15 14:23:18 UTC [user=admin] [db=medical] 
OBJECT: TABLE patients
ACTION: DELETE WHERE id=456
QUERY: DELETE FROM patients WHERE status=&amp;apos;inactive&amp;apos;;
&lt;/pre&gt;
 &lt;p&gt;关键特性：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;细粒度审计策略：&lt;/li&gt;
&lt;/ul&gt;
 &lt;pre&gt;SET pgaudit.log = &amp;apos;ddl, write, role&amp;apos;;&lt;/pre&gt;
 &lt;ul&gt;
  &lt;li&gt;支持 CSV/JSON 日志格式&lt;/li&gt;
  &lt;li&gt;审计日志压缩存储 (节省 60% 空间)&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;HIPAA 合规应用：医疗系统记录所有 PHI (受保护健康信息) 访问日志，满足 45 CFR 164.312 审计控制要求。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;pg_checksums&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;数据完整性验证：&lt;/p&gt;
 &lt;pre&gt;# 启用校验和
initdb --data-checksums

# 定期验证
pg_checksums -c /var/lib/pgsql/15/data
&lt;/pre&gt;
 &lt;p&gt;检测能力：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;磁盘位翻转错误&lt;/li&gt;
  &lt;li&gt;存储介质损坏&lt;/li&gt;
  &lt;li&gt;恶意数据篡改&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;性能影响：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;操作&lt;/td&gt;
   &lt;td&gt;无校验和&lt;/td&gt;
   &lt;td&gt;启用校验和&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;数据写入&lt;/td&gt;
   &lt;td&gt;100%&lt;/td&gt;
   &lt;td&gt;92%&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;全表扫描&lt;/td&gt;
   &lt;td&gt;100%&lt;/td&gt;
   &lt;td&gt;98%&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;h4&gt;网络与协议安全&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;pg_netrestrict&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;IP 白名单配置：&lt;/p&gt;
 &lt;pre&gt;CREATE EXTENSION pg_netrestrict;
ALTER SYSTEM SET pg_netrestrict.authorized_networks = &amp;apos;192.168.1.0/24, 10.8.0.5/32&amp;apos;;
SELECT pg_reload_conf();
&lt;/pre&gt;
 &lt;p&gt;防御场景：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;阻止 SQL 注入攻击源 IP&lt;/li&gt;
  &lt;li&gt;限制管理接口访问范围&lt;/li&gt;
  &lt;li&gt;遵守 ISO 27001 网络隔离要求&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;sslutils&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;高级 TLS 管理：&lt;/p&gt;
 &lt;pre&gt;-- 客户端证书吊销检查
ALTER SYSTEM SET sslutils.crl = &amp;apos;/etc/pgsql/ssl/crl.pem&amp;apos;;

-- 启用 OCSP 装订
SET sslutils.ocsp_stapling = on;
&lt;/pre&gt;
 &lt;p&gt;加密协议支持：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;TLS 1.3 优先协商&lt;/li&gt;
  &lt;li&gt;国密 SM4 算法支持&lt;/li&gt;
  &lt;li&gt;证书透明度 (CT) 日志&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;合规扩展组合方案&lt;/h4&gt;
 &lt;p&gt; &lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;金融系统合规架构&lt;/strong&gt;：&lt;/p&gt;
 &lt;p&gt;加密层:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;pgcrypto: 字段级加密&lt;/li&gt;
  &lt;li&gt;sslutils: 国密算法支持&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;访问控制:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;sepgsql: 强制访问控制&lt;/li&gt;
  &lt;li&gt;pg_ident: LDAP 集成&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;审计溯源:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;pg_audit: 操作日志&lt;/li&gt;
  &lt;li&gt;pgmemento: 数据变更历史&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;网络防护:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;pg_netrestrict: IP白名单&lt;/li&gt;
  &lt;li&gt;pg_hba_plus: 动态ACL&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;合规覆盖：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;PCIDSS 3.2.1 (加密存储)&lt;/li&gt;
  &lt;li&gt;银保监会数据安全指引&lt;/li&gt;
  &lt;li&gt;GDPR 数据主体权利&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;扩展管理最佳实践&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;安全更新策略&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;# 自动检查扩展漏洞
apt-get update &amp;amp;&amp;amp; apt-get upgrade postgresql-15-*
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;权限最小化原则&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;REVOKE ALL ON DATABASE prod FROM PUBLIC;
GRANT USAGE ON SCHEMA audit TO security_auditor;
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;审计日志保留&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;# 使用 logrotate 管理
/var/log/postgresql/*.log {
    weekly
    rotate 12
    compress
    missingok
    notifempty
}
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;渗透测试验证&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;sqlmap -u &amp;quot;http://api:3000&amp;quot; --risk=3 --level=5&lt;/pre&gt;
 &lt;h4&gt;扩展性能优化建议&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;加密加速&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;-- 使用 AES-NI 硬件指令
SET pgcrypto.use_aesni = on;
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;审计日志分区&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;CREATE TABLE audit_log_2023 PARTITION OF audit_log 
FOR VALUES FROM (&amp;apos;2023-01-01&amp;apos;) TO (&amp;apos;2024-01-01&amp;apos;);
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;访问控制缓存&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;ALTER ROLE security_auditor SET sepgsql.cache_refresh = 3600;&lt;/pre&gt;
 &lt;p&gt;通过合理配置安全扩展，PostgreSQL 可以满足金融级安全要求，某证券系统实际案例显示，在启用全套安全扩展后，成功抵御了 23 万次/日的攻击尝试，同时保持 99.99% 的可用性。建议每季度进行安全扩展的渗透测试和策略复审。&lt;/p&gt;
 &lt;h3&gt;人工智能扩展&lt;/h3&gt;
 &lt;p&gt;PostgreSQL 的人工智能扩展正在重新定义数据库的智能边界，以下是关键技术扩展的深度解析，涵盖向量计算、模型训练、预测服务等核心领域：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;扩展名称&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;技术架构&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;算力支持&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;典型延迟&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;适用场景&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pgvector&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;HNSW/IVFFlat&lt;/td&gt;
   &lt;td&gt;CPU/GPU&lt;/td&gt;
   &lt;td&gt;5-50ms&lt;/td&gt;
   &lt;td&gt;语义搜索/推荐系统&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pgml&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;集成PyTorch/TF&lt;/td&gt;
   &lt;td&gt;CPU/GPU&lt;/td&gt;
   &lt;td&gt;100-500ms&lt;/td&gt;
   &lt;td&gt;实时预测&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;apache madlib&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;分布式ML算法库&lt;/td&gt;
   &lt;td&gt;MPI/多节点&lt;/td&gt;
   &lt;td&gt;分钟级&lt;/td&gt;
   &lt;td&gt;批量训练&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pg_catcheck&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;词向量相似度&lt;/td&gt;
   &lt;td&gt;CPU&lt;/td&gt;
   &lt;td&gt;10-100ms&lt;/td&gt;
   &lt;td&gt;文本分类&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pg_openai&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;OpenAI API代理&lt;/td&gt;
   &lt;td&gt;网络调用&lt;/td&gt;
   &lt;td&gt;200-2000ms&lt;/td&gt;
   &lt;td&gt;GPT集成&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;h4&gt;向量计算引擎 – pgvector&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;技术实现&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;索引结构：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="502" src="http://www.biaodianfu.com/wp-content/uploads/2025/04/pgvector.png" width="1512"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;精度控制：支持 FP16 量化压缩，节省 50% 存储空间&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;性能基准&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;CREATE TABLE embeddings (id serial, vector vector(1536));
INSERT INTO embeddings SELECT generate_series(1,1000000), random_vector(1536);

-- HNSW索引
CREATE INDEX ON embeddings USING hnsw (vector vector_cosine_ops);

-- 相似度查询
SELECT id, vector &amp;lt;=&amp;gt; &amp;apos;[0.1,0.2,...]&amp;apos; AS score 
FROM embeddings ORDER BY score LIMIT 10;
&lt;/pre&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;数据规模&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;索引类型&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;QPS&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;召回率&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;存储成本&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;100万×768&lt;/td&gt;
   &lt;td&gt;HNSW&lt;/td&gt;
   &lt;td&gt;1200&lt;/td&gt;
   &lt;td&gt;99%&lt;/td&gt;
   &lt;td&gt;1.2GB&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;1亿×1536&lt;/td&gt;
   &lt;td&gt;IVFFlat&lt;/td&gt;
   &lt;td&gt;8500&lt;/td&gt;
   &lt;td&gt;95%&lt;/td&gt;
   &lt;td&gt;196GB&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;h4&gt;机器学习管道 – pgml&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;核心功能&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;-- 模型训练
SELECT pgml.train(
    project_name =&amp;gt; &amp;apos;房价预测&amp;apos;,
    task =&amp;gt; &amp;apos;regression&amp;apos;,
    relation_name =&amp;gt; &amp;apos;houses&amp;apos;,
    y_column_name =&amp;gt; &amp;apos;price&amp;apos;,
    algorithm =&amp;gt; &amp;apos;xgboost&amp;apos;
);

-- 实时预测
SELECT pgml.predict(&amp;apos;房价预测&amp;apos;, ARRAY[面积, 房间数, 位置编码]) 
FROM new_listings;
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;支持的算法&lt;/strong&gt;&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;类型&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;算法列表&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;传统机器学习&lt;/td&gt;
   &lt;td&gt;线性回归、随机森林、SVM&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;深度学习&lt;/td&gt;
   &lt;td&gt;BERT、ResNet、LSTM&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;时间序列&lt;/td&gt;
   &lt;td&gt;Prophet、ARIMA&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;无监督学习&lt;/td&gt;
   &lt;td&gt;K-Means、PCA&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;  &lt;strong&gt;资源消耗&lt;/strong&gt;&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;操作&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;数据量&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;CPU占用&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;内存消耗&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;耗时&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;XGBoost模型训练&lt;/td&gt;
   &lt;td&gt;100万行&lt;/td&gt;
   &lt;td&gt;85%&lt;/td&gt;
   &lt;td&gt;8GB&lt;/td&gt;
   &lt;td&gt;2.3m&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;BERT文本嵌入生成&lt;/td&gt;
   &lt;td&gt;1万文本&lt;/td&gt;
   &lt;td&gt;95%&lt;/td&gt;
   &lt;td&gt;16GB&lt;/td&gt;
   &lt;td&gt;4.5m&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;LSTM时序预测&lt;/td&gt;
   &lt;td&gt;1年数据&lt;/td&gt;
   &lt;td&gt;78%&lt;/td&gt;
   &lt;td&gt;6GB&lt;/td&gt;
   &lt;td&gt;1.2m&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;h4&gt;分布式机器学习 – Apache MADlib&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;架构设计&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="803" src="http://www.biaodianfu.com/wp-content/uploads/2025/04/MADlib.png" width="846"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;算法加速比&lt;/strong&gt;&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;算法&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;单节点耗时&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;4节点耗时&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;加速比&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;协同过滤&lt;/td&gt;
   &lt;td&gt;58m&lt;/td&gt;
   &lt;td&gt;14m&lt;/td&gt;
   &lt;td&gt;4.14x&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;决策树训练&lt;/td&gt;
   &lt;td&gt;2.1h&lt;/td&gt;
   &lt;td&gt;0.6h&lt;/td&gt;
   &lt;td&gt;3.5x&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;矩阵分解&lt;/td&gt;
   &lt;td&gt;6.8h&lt;/td&gt;
   &lt;td&gt;1.5h&lt;/td&gt;
   &lt;td&gt;4.53x&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;  &lt;strong&gt;企业应用案例&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;某零售巨头：使用 MADlib 在 20 节点集群训练用户分群模型，处理 10TB 行为数据，将营销转化率提升 18%&lt;/p&gt;
 &lt;h4&gt;语义处理扩展 – pg_catcheck&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;相似度计算&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;-- 创建词向量索引
CREATE INDEX ON products USING gin (description gin_catcheck_ops);

-- 语义搜索
SELECT name, catcheck_similarity(description, &amp;apos;舒适透气运动鞋&amp;apos;) AS score
FROM products
WHERE description % &amp;apos;舒适透气运动鞋&amp;apos;
ORDER BY score DESC LIMIT 10;
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;性能对比&lt;/strong&gt;&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;方法&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;准确率&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;QPS&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;索引大小&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;全文检索&lt;/td&gt;
   &lt;td&gt;62%&lt;/td&gt;
   &lt;td&gt;1200&lt;/td&gt;
   &lt;td&gt;850MB&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;pg_catcheck&lt;/td&gt;
   &lt;td&gt;89%&lt;/td&gt;
   &lt;td&gt;650&lt;/td&gt;
   &lt;td&gt;1.3GB&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;专用ES引擎&lt;/td&gt;
   &lt;td&gt;92%&lt;/td&gt;
   &lt;td&gt;1500&lt;/td&gt;
   &lt;td&gt;2.1GB&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;h4&gt;AI扩展联合应用案例&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;智能客服系统架构&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;-- 用户问题向量化
WITH query_vec AS (
    SELECT pgml.embed(&amp;apos;sentence-transformers/all-mpnet-base-v2&amp;apos;, &amp;apos;如何退换货？&amp;apos;) AS vec
)

-- 检索知识库
SELECT k.id, k.answer, (k.vector &amp;lt;=&amp;gt; q.vec) AS score
FROM knowledge_base k, query_vec q
ORDER BY score LIMIT 3;

-- 调用GPT生成
SELECT openai_completion(
    &amp;apos;你是一名客服助手，请根据以下知识回答问题：&amp;apos;
    || (SELECT answer FROM knowledge_base WHERE id = 123), 
    &amp;apos;gpt-4&amp;apos;, 
    0.7
);
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;性能指标&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;端到端延迟：平均 820ms&lt;/li&gt;
  &lt;li&gt;准确率：92%（相比传统方法提升 35%）&lt;/li&gt;
  &lt;li&gt;成本：比独立AI服务降低 60%（减少数据传输开销）&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;扩展部署最佳实践&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;硬件资源配置&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;vector_db:
  cpu: 16 cores (AVX512)
  memory: 64GB 
  storage: NVMe SSD RAID
  gpu: 1×A10（可选）

ml_serving:
  cpu: 8 cores
  memory: 32GB
  network: 10Gbps
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;版本兼容性矩阵&lt;/strong&gt;：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;扩展&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;PG 13&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;PG 14&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;PG 15&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;PG 16&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;pgvector&lt;/td&gt;
   &lt;td&gt;✓&lt;/td&gt;
   &lt;td&gt;✓&lt;/td&gt;
   &lt;td&gt;✓&lt;/td&gt;
   &lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;pgml&lt;/td&gt;
   &lt;td&gt;✓&lt;/td&gt;
   &lt;td&gt;✓&lt;/td&gt;
   &lt;td&gt;✓&lt;/td&gt;
   &lt;td&gt;Beta&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;madlib&lt;/td&gt;
   &lt;td&gt;✓&lt;/td&gt;
   &lt;td&gt;✓&lt;/td&gt;
   &lt;td&gt;✓&lt;/td&gt;
   &lt;td&gt;✓&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;  &lt;strong&gt;监控指标&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;# 关键性能计数器
pg_stat_ai_queries_total
pg_ml_model_inference_duration_seconds
pg_vector_cache_hit_rate
&lt;/pre&gt;
 &lt;h4&gt;与传统方案的对比优势&lt;/h4&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;维度&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;传统AI架构&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;PostgreSQL AI扩展方案&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;数据移动&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;ETL管道，高延迟&lt;/td&gt;
   &lt;td&gt;库内计算，零数据迁移&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;事务一致&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;最终一致性&lt;/td&gt;
   &lt;td&gt;ACID保证&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;开发成本&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;多系统集成，高维护成本&lt;/td&gt;
   &lt;td&gt;单一技术栈&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;实时性&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;批处理为主&lt;/td&gt;
   &lt;td&gt;亚秒级实时推理&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;安全合规&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;多系统暴露面大&lt;/td&gt;
   &lt;td&gt;统一权限控制&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;某电商平台采用 PostgreSQL AI 扩展后，推荐系统更新频率从小时级提升到秒级，CTR（点击率）提升 22%，同时基础设施成本降低 40%。&lt;/p&gt;
 &lt;p&gt;通过深度集成AI能力，PostgreSQL 正在演变为   &lt;strong&gt;智能化数据计算平台&lt;/strong&gt;，建议在以下场景优先考虑：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;需要实时更新的推荐系统&lt;/li&gt;
  &lt;li&gt;隐私敏感的本地化AI推理&lt;/li&gt;
  &lt;li&gt;事务型AI应用（如实时反欺诈）&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;存储引擎扩展&lt;/h3&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;扩展名称&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;存储架构&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;性能表现&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;适用场景&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;zheap&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;堆表引擎优化&lt;/td&gt;
   &lt;td&gt;减少 70% 表膨胀&lt;/td&gt;
   &lt;td&gt;高频更新系统&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;cstore_fdw&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;列式存储&lt;/td&gt;
   &lt;td&gt;压缩率 5x，扫描速度提升 10x&lt;/td&gt;
   &lt;td&gt;分析型工作负载&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;roaringbitmap&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;位图索引&lt;/td&gt;
   &lt;td&gt;支持 10 亿级用户分群&lt;/td&gt;
   &lt;td&gt;用户画像系统&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pg_rational&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;分数类型存储&lt;/td&gt;
   &lt;td&gt;精确避免浮点误差&lt;/td&gt;
   &lt;td&gt;金融计费系统&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pgmemcache&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;内存表引擎&lt;/td&gt;
   &lt;td&gt;亚毫秒级响应&lt;/td&gt;
   &lt;td&gt;实时竞价系统&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;PostgreSQL 的存储引擎扩展体系突破了传统关系型数据库的存储限制，通过模块化架构实现存储层的灵活扩展。&lt;/p&gt;
 &lt;h4&gt;存储引擎扩展架构&lt;/h4&gt;
 &lt;p&gt;PostgreSQL 通过   &lt;strong&gt;Table Access Method API&lt;/strong&gt; 和   &lt;strong&gt;TOAST 机制&lt;/strong&gt; 实现存储引擎的可扩展性：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="694" src="http://www.biaodianfu.com/wp-content/uploads/2025/04/Table-Access-Method-API.png" width="1307"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h4&gt;核心存储引擎扩展&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;zheap（事务优化引擎）&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;技术特性：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;替代传统 Heap 表的事务管理&lt;/li&gt;
  &lt;li&gt;使用 UNDO 日志 替代多版本存储&lt;/li&gt;
  &lt;li&gt;减少 70% 的表膨胀&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;性能测试：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;场景&lt;/td&gt;
   &lt;td&gt;Heap表写入TPS&lt;/td&gt;
   &lt;td&gt;zheap写入TPS&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;高频UPDATE&lt;/td&gt;
   &lt;td&gt;12,000&lt;/td&gt;
   &lt;td&gt;38,000&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;批量DELETE&lt;/td&gt;
   &lt;td&gt;8,500&lt;/td&gt;
   &lt;td&gt;24,000&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;适用场景：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;频繁更新的订单状态表&lt;/li&gt;
  &lt;li&gt;实时竞价系统&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;cstore_fdw（列式存储）&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;技术实现：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;列式数据压缩（ORC格式）&lt;/li&gt;
  &lt;li&gt;向量化执行引擎&lt;/li&gt;
  &lt;li&gt;支持 Parquet 外部表&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;压缩效率：&lt;/p&gt;
 &lt;pre&gt;-- 创建列式表
CREATE FOREIGN TABLE sales (
    id integer,
    date date,
    amount numeric
) SERVER cstore_server;

-- 压缩比对比
SELECT pg_size_pretty(pg_total_relation_size(&amp;apos;sales_heap&amp;apos;)) AS heap_size,
       pg_size_pretty(pg_total_relation_size(&amp;apos;sales_cstore&amp;apos;)) AS cstore_size;
&lt;/pre&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;数据量&lt;/td&gt;
   &lt;td&gt;HEAP大小&lt;/td&gt;
   &lt;td&gt;cstore大小&lt;/td&gt;
   &lt;td&gt;压缩率&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;1TB&lt;/td&gt;
   &lt;td&gt;1.2TB&lt;/td&gt;
   &lt;td&gt;230GB&lt;/td&gt;
   &lt;td&gt;5.2x&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;适用场景：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;数据仓库聚合查询&lt;/li&gt;
  &lt;li&gt;时序数据分析&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;pgmemcache（内存引擎）&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;架构设计：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="106" src="http://www.biaodianfu.com/wp-content/uploads/2025/04/pgmemcache.png" width="1299"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;性能指标：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;操作&lt;/td&gt;
   &lt;td&gt;磁盘表延迟&lt;/td&gt;
   &lt;td&gt;内存表延迟&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;随机读取&lt;/td&gt;
   &lt;td&gt;2.3ms&lt;/td&gt;
   &lt;td&gt;0.12ms&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;批量写入&lt;/td&gt;
   &lt;td&gt;1200 TPS&lt;/td&gt;
   &lt;td&gt;8500 TPS&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;使用示例：&lt;/p&gt;
 &lt;pre&gt;CREATE TABLE session_cache (
    key TEXT PRIMARY KEY,
    val BYTEA
) USING pgmemcache;
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;roaringbitmap（位图引擎）&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;技术优势：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;压缩位图存储（比传统BITMAP小10x）&lt;/li&gt;
  &lt;li&gt;支持快速集合运算（AND/OR/XOR）&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;用户分群案例：&lt;/p&gt;
 &lt;pre&gt;-- 创建位图表
CREATE TABLE user_tags (
    tag_id int PRIMARY KEY,
    users roaringbitmap
);

-- 查找同时满足标签A和B的用户
SELECT rb_cardinality(rb_and(a.users, b.users))
FROM user_tags a, user_tags b
WHERE a.tag_id = 1 AND b.tag_id = 2;
&lt;/pre&gt;
 &lt;p&gt;存储效率：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;用户量&lt;/td&gt;
   &lt;td&gt;传统位图&lt;/td&gt;
   &lt;td&gt;roaringbitmap&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;100万&lt;/td&gt;
   &lt;td&gt;125KB&lt;/td&gt;
   &lt;td&gt;8KB&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;1亿&lt;/td&gt;
   &lt;td&gt;12MB&lt;/td&gt;
   &lt;td&gt;1.2MB&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;h4&gt;存储引擎对比矩阵&lt;/h4&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;引擎类型&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;写性能&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;读性能&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;压缩率&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;事务支持&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;适用负载&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;Heap&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;★★★★☆&lt;/td&gt;
   &lt;td&gt;★★★☆☆&lt;/td&gt;
   &lt;td&gt;1x&lt;/td&gt;
   &lt;td&gt;ACID&lt;/td&gt;
   &lt;td&gt;OLTP&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;zheap&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;★★★★★&lt;/td&gt;
   &lt;td&gt;★★★★☆&lt;/td&gt;
   &lt;td&gt;0.3x&lt;/td&gt;
   &lt;td&gt;ACID&lt;/td&gt;
   &lt;td&gt;高频更新&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;cstore&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;★★☆☆☆&lt;/td&gt;
   &lt;td&gt;★★★★★&lt;/td&gt;
   &lt;td&gt;5x&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;OLAP&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pgmemcache&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;★★★★★&lt;/td&gt;
   &lt;td&gt;★★★★★&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;部分&lt;/td&gt;
   &lt;td&gt;实时缓存&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;roaringbitmap&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;★★★★☆&lt;/td&gt;
   &lt;td&gt;★★★★★&lt;/td&gt;
   &lt;td&gt;10x&lt;/td&gt;
   &lt;td&gt;无&lt;/td&gt;
   &lt;td&gt;用户分群&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;h4&gt;企业级应用方案&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;金融交易系统存储架构&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;核心交易表:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;引擎: zheap&lt;/li&gt;
  &lt;li&gt;特性: 高频UPDATE/DELETE抗膨胀&lt;/li&gt;
  &lt;li&gt;配置: undo_log_segment_size=1GB&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;历史数据分析:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;引擎: cstore_fdw&lt;/li&gt;
  &lt;li&gt;特性: 列式压缩存储&lt;/li&gt;
  &lt;li&gt;配置: compression=zstd&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;实时风控缓存:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;引擎: pgmemcache&lt;/li&gt;
  &lt;li&gt;特性: 亚毫秒级响应&lt;/li&gt;
  &lt;li&gt;配置: max_size=64GB&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;用户画像存储:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;引擎: roaringbitmap&lt;/li&gt;
  &lt;li&gt;特性: 快速集合运算&lt;/li&gt;
  &lt;li&gt;配置: rb_threshold=1000000&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;性能收益&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;交易处理吞吐量提升2x&lt;/li&gt;
  &lt;li&gt;风控决策延迟降低至8ms&lt;/li&gt;
  &lt;li&gt;存储成本减少 60%&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;扩展管理实践&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;多引擎混合部署&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;-- 跨引擎查询示例
SELECT o.order_id, c.amount 
FROM orders_heap o 
JOIN order_cache_pgmemcache c ON o.id = c.order_id;
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;生命周期管理&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;-- 数据分层自动化
CREATE TABLE logs (
    ...
) PARTITION BY RANGE (log_time) 
PARTITION logs_2023 USING cstore_fdw,
PARTITION logs_current USING heap;
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;监控指标&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;# 关键监控项
pg_stat_user_tables_n_dead_tup   # zheap表膨胀监控
cstore_total_blocks               # 列式存储块使用
pgmemcache_hit_rate               # 内存表命中率
&lt;/pre&gt;
 &lt;h4&gt;未来演进方向&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;多模事务引擎&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;跨存储引擎的 ACID 事务支持（如内存表与列式表的事务一致性）&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;硬件加速集成&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;GPU 加速列式扫描&lt;/li&gt;
  &lt;li&gt;持久化内存（PMEM）优化引擎&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;智能存储决策&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;-- AI驱动的存储选择建议
SELECT pg_ai_advise_storage(&amp;apos;orders&amp;apos;, access_pattern=&amp;apos;update_heavy&amp;apos;);
-- 建议输出: zheap
&lt;/pre&gt;
 &lt;p&gt;PostgreSQL 的存储引擎扩展体系正在重塑数据库技术栈，使单一数据库能够同时承载交易、分析、缓存等多种负载。建议根据访问模式设计混合存储方案，并通过 pg_stat_statements 持续监控各引擎的效能表现。&lt;/p&gt;
 &lt;h3&gt;监控诊断扩展&lt;/h3&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;扩展名称&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;监控维度&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;数据粒度&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;存储方式&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;采样精度&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pg_stat_statements&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;SQL执行统计&lt;/td&gt;
   &lt;td&gt;语句级&lt;/td&gt;
   &lt;td&gt;内存+持久化&lt;/td&gt;
   &lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pg_qualstats&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;谓词条件分析&lt;/td&gt;
   &lt;td&gt;列值分布&lt;/td&gt;
   &lt;td&gt;内存&lt;/td&gt;
   &lt;td&gt;0.1%采样&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pg_wait_sampling&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;等待事件&lt;/td&gt;
   &lt;td&gt;进程级&lt;/td&gt;
   &lt;td&gt;内存&lt;/td&gt;
   &lt;td&gt;100Hz采样&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pg_stat_monitor&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;全链路追踪&lt;/td&gt;
   &lt;td&gt;事务级&lt;/td&gt;
   &lt;td&gt;共享内存&lt;/td&gt;
   &lt;td&gt;全量&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;pg_activity&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;实时会话&lt;/td&gt;
   &lt;td&gt;连接级&lt;/td&gt;
   &lt;td&gt;实时查询&lt;/td&gt;
   &lt;td&gt;秒级刷新&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;h4&gt;SQL级监控 – pg_stat_statements&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;核心功能&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;-- 查看TOP 10 慢查询
SELECT queryid, total_time, calls, mean_time,
       rows, query 
FROM pg_stat_statements 
ORDER BY total_time DESC 
LIMIT 10;
&lt;/pre&gt;
 &lt;p&gt;关键指标：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;shared_blks_hit/shared_blks_read：缓存命中率&lt;/li&gt;
  &lt;li&gt;wal_bytes：写入负载&lt;/li&gt;
  &lt;li&gt;temp_blks_written：临时数据量&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;性能优化案例&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;某电商平台通过分析 pg_stat_statements 发现：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;高频调用但低效的购物车查询（平均 120ms → 优化至 15ms）&lt;/li&gt;
  &lt;li&gt;缺失索引的订单搜索（添加复合索引后 QPS 提升 5x）&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;等待事件分析 – pg_wait_sampling&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;等待事件分类&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="750" src="http://www.biaodianfu.com/wp-content/uploads/2025/04/pg_wait_sampling.png" width="1016"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;瓶颈诊断流程&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;-- 查看当前等待事件
SELECT pg_stat_get_activity(pid)-&amp;gt;wait_event_type,
       pg_stat_get_activity(pid)-&amp;gt;wait_event
FROM pg_stat_activity 
WHERE state = &amp;apos;active&amp;apos;;

-- 历史分析
SELECT event_type, event, sum(samples)
FROM pg_wait_sampling_history
GROUP BY 1,2 
ORDER BY 3 DESC;
&lt;/pre&gt;
 &lt;p&gt;优化建议：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;IO-DataFileRead过高 → 增加 shared_buffers 或使用 SSD&lt;/li&gt;
  &lt;li&gt;LWLock竞争 → 优化热点表索引&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;全链路追踪 – pg_stat_monitor&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;架构设计&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="80" src="http://www.biaodianfu.com/wp-content/uploads/2025/04/pg_stat_monitor.png" width="1306"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;关键特性&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;事务溯源：跟踪单个事务内的多语句执行&lt;/li&gt;
  &lt;li&gt;执行计划存储：保留最近100个查询计划&lt;/li&gt;
  &lt;li&gt;错误上下文：记录错误发生的具体SQL和参数&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;配置示例&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;# postgresql.conf
pg_stat_monitor.pgsm_enable = on
pg_stat_monitor.pgsm_max_buckets = 10
pg_stat_monitor.pgsm_track_utility = on
&lt;/pre&gt;
 &lt;h4&gt;存储健康诊断 – pg_checksums&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;数据完整性验证&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;# 启用校验和
initdb --data-checksums

# 离线验证
pg_checksums -c /var/lib/pgsql/15/data

# 输出示例
WARNING:  checksum verification failed in block 42 of relation base/16384/16895
Checksum scan completed
Data checksum version: 1
Files scanned:   892
Blocks scanned:  123456
Bad checksums:  1
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;修复策略&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;从备份恢复损坏数据页&lt;/li&gt;
  &lt;li&gt;使用pg_rewind 同步副本&lt;/li&gt;
  &lt;li&gt;启用 ZFS/Btrfs 文件系统自带校验&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;日志分析扩展 – pgBadger&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;报告生成&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;pgbadger /var/log/postgresql/postgresql-15-*.log -o report.html

# 关键分析维度：
# - 每小时请求量波动
# - 慢查询TOP 50
# - 错误类型分布
# - 连接池利用率
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;自动化监控&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;# 每日报告生成
0 3 * * * /usr/bin/pgbadger -q /var/log/postgresql/postgresql-15-*.log -O /reports

# 异常检测脚本
ALERT_SLOW=1000  # 超过1秒的查询
grep &amp;apos;duration: [0-9]\{4\}\.&amp;apos; postgresql.log | mail -s &amp;quot;慢查询警报&amp;quot; dba@example.com
&lt;/pre&gt;
 &lt;h4&gt;监控体系集成方案&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;Prometheus + Grafana 监控栈&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;exporter:
  - pg_exporter: 采集基础指标
  - pg_stat_monitor_exporter: 事务级指标
dashboard:
  - 关键指标:
    * 查询吞吐量: sum(rate(pg_stat_statements_calls[5m])) 
    * 缓存命中率: pg_stat_database_blks_hit / (pg_stat_database_blks_hit + pg_stat_database_blks_read)
    * 连接池利用率: pg_stat_activity_count{state=&amp;quot;active&amp;quot;} / max_connections
alert:
  - 规则示例:
    - alert: HighCPUWait
      expr: rate(pg_wait_sampling_samples_total{event=&amp;quot;CPU&amp;quot;}[5m]) &amp;lt; 0.1
      for: 10m
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;企业级监控架构&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="800" src="http://www.biaodianfu.com/wp-content/uploads/2025/04/Prometheus-Grafana.png" width="635"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h4&gt;诊断优化最佳实践&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;三级诊断流程&lt;/strong&gt;：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="792" src="http://www.biaodianfu.com/wp-content/uploads/2025/04/flow.png" width="331"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;自动化优化建议&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;-- 使用hypopg创建虚拟索引
SELECT * FROM hypopg_create_index(&amp;apos;CREATE INDEX ON orders (user_id)&amp;apos;);

-- 验证索引效果
EXPLAIN ANALYZE SELECT * FROM orders WHERE user_id = 123;

-- 正式创建
CREATE INDEX CONCURRENTLY orders_user_id_idx ON orders(user_id);
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;容量规划公式&lt;/strong&gt;：&lt;/p&gt;
 &lt;p&gt;所需内存 = shared_buffers + (work_mem * max_connections) +&lt;/p&gt;
 &lt;p&gt;(maintenance_work_mem * 并行维护任务数) +&lt;/p&gt;
 &lt;p&gt;temp_buffers&lt;/p&gt;
 &lt;p&gt;建议比例: shared_buffers = 25% 总内存&lt;/p&gt;
 &lt;p&gt;通过组合使用监控诊断扩展，某金融系统实现了：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;故障平均恢复时间（MTTR）从 4 小时降至 15 分钟&lt;/li&gt;
  &lt;li&gt;查询性能瓶颈定位效率提升 6 倍&lt;/li&gt;
  &lt;li&gt;存储异常检测准确率达到9%&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;建议每周生成《数据库健康报告》，包含关键指标趋势、TOP 资源消耗语句、容量预测等内容，并结合 pg_qualstats 和 pg_wait_sampling 进行预防性优化。&lt;/p&gt;
 &lt;h2&gt;PostgreSQL的FDW&lt;/h2&gt;
 &lt;p&gt;PostgreSQL 的外部数据包装器（Foreign Data Wrapper, FDW）是一项强大的功能，允许用户将外部数据源（如其他数据库、文件或 API）集成到本地数据库中，实现跨数据源的联邦查询。&lt;/p&gt;
 &lt;p&gt;PostgreSQL 的 FDW 打破了数据孤岛，使其成为数据联邦的核心枢纽。通过合理使用查询下推、物化缓存和并行处理，可有效提升跨数据源查询效率。企业实践中，FDW 常用于混合云数据集成、实时分析平台构建及遗留系统迁移等场景。建议结合 EXPLAIN 分析执行计划，持续优化外部查询性能。&lt;/p&gt;
 &lt;h3&gt;FDW 核心架构&lt;/h3&gt;
 &lt;h4&gt;SQL/MED 标准实现&lt;/h4&gt;
 &lt;p&gt;FDW 基于 SQL 管理外部数据（SQL/MED）标准，通过以下组件实现数据联邦：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;Foreign Server&lt;/strong&gt;：定义外部数据源的连接信息（如 IP、端口）。&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;User Mapping&lt;/strong&gt;：配置访问外部数据源的认证信息。&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;Foreign Table&lt;/strong&gt;：映射外部数据的元数据（表结构）。&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;Wrapper 扩展&lt;/strong&gt;：实现与特定数据源的通信协议。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;执行流程&lt;/h4&gt;
 &lt;p&gt;  &lt;img alt="" height="642" src="http://www.biaodianfu.com/wp-content/uploads/2025/04/fdw.png" width="1200"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h4&gt;常用 FDW 扩展&lt;/h4&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;扩展名称&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;数据源类型&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;关键特性&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;postgres_fdw&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;PostgreSQL&lt;/td&gt;
   &lt;td&gt;支持查询下推、JOIN 优化&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;mysql_fdw&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;MySQL&lt;/td&gt;
   &lt;td&gt;兼容 5.6+，支持批量插入&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;file_fdw&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;CSV/文本文件&lt;/td&gt;
   &lt;td&gt;无依赖，轻量级文件访问&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;mongo_fdw&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;MongoDB&lt;/td&gt;
   &lt;td&gt;支持 BSON 到 JSONB 转换&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;clickhousedb_fdw&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;ClickHouse&lt;/td&gt;
   &lt;td&gt;列式存储优化，高性能分析&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;multicorn&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;Python 扩展&lt;/td&gt;
   &lt;td&gt;可自定义包装器（如 REST API 访问）&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;h3&gt;FDW 使用详解&lt;/h3&gt;
 &lt;h4&gt;安装与配置&lt;/h4&gt;
 &lt;p&gt;以 postgres_fdw（连接其他 PostgreSQL 实例）为例：&lt;/p&gt;
 &lt;pre&gt;-- 启用扩展
CREATE EXTENSION postgres_fdw;

-- 定义外部服务器
CREATE SERVER foreign_server
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (host &amp;apos;192.168.1.100&amp;apos;, port &amp;apos;5432&amp;apos;, dbname &amp;apos;remote_db&amp;apos;);

-- 创建用户映射
CREATE USER MAPPING FOR local_user
SERVER foreign_server
OPTIONS (user &amp;apos;remote_user&amp;apos;, password &amp;apos;secret&amp;apos;);

-- 创建外部表
CREATE FOREIGN TABLE remote_orders (
    order_id INT,
    product TEXT,
    amount NUMERIC
) SERVER foreign_server
OPTIONS (schema_name &amp;apos;public&amp;apos;, table_name &amp;apos;orders&amp;apos;);
&lt;/pre&gt;
 &lt;h4&gt;查询外部数据&lt;/h4&gt;
 &lt;pre&gt;-- 直接查询
SELECT * FROM remote_orders WHERE amount &amp;gt; 1000;

-- 联邦查询（跨本地与外部表）
SELECT l.customer_name, r.product 
FROM local_customers l
JOIN remote_orders r ON l.id = r.customer_id;
&lt;/pre&gt;
 &lt;h4&gt;监控与管理&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;系统视图&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;-- 查看外部表信息
SELECT * FROM pg_foreign_table;

-- 监控外部查询
SELECT * FROM pg_stat_user_tables 
WHERE schemaname = &amp;apos;public&amp;apos; 
AND relname LIKE &amp;apos;foreign_%&amp;apos;;
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;日志分析&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;# postgresql.conf
log_statement = &amp;apos;ddl&amp;apos;
log_foreign_server = on
&lt;/pre&gt;
 &lt;h4&gt;性能优化策略&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;减少数据传输&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;投影下推：仅 SELECT 必要字段。&lt;/li&gt;
  &lt;li&gt;谓词下推：确保 WHERE 条件在远程执行。&lt;/li&gt;
  &lt;li&gt;聚合下推：使用远程聚合减少数据量。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;缓存策略&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;物化视图：定期刷新外部数据快照。&lt;/li&gt;
&lt;/ul&gt;
 &lt;pre&gt;CREATE MATERIALIZED VIEW cached_orders AS 
SELECT * FROM remote_orders;
REFRESH MATERIALIZED VIEW CONCURRENTLY cached_orders;
&lt;/pre&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;连接池&lt;/strong&gt;：使用pgbouncer 管理外部连接。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;并行查询&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;-- 启用并行扫描
ALTER FOREIGN TABLE remote_orders
OPTIONS (ADD parallel_workers &amp;apos;4&amp;apos;);

-- 设置并行度
SET max_parallel_workers_per_gather = 4;
&lt;/pre&gt;
 &lt;h3&gt;FDW 核心特性&lt;/h3&gt;
 &lt;h4&gt;查询下推（Pushdown）&lt;/h4&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;条件过滤&lt;/strong&gt;：将 WHERE 子句发送至远程执行。&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;聚合操作&lt;/strong&gt;：远程执行 COUNT、SUM 等聚合。&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;排序分页&lt;/strong&gt;：ORDER BY 和 LIMIT 下推。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;示例（查看下推效果）：&lt;/p&gt;
 &lt;pre&gt;EXPLAIN VERBOSE 
SELECT * FROM remote_orders 
WHERE amount &amp;gt; 1000 
ORDER BY order_date 
LIMIT 10;
-- 输出中显示 remote SQL: SELECT ... WHERE (amount &amp;gt; 1000) ORDER BY order_date LIMIT 10
&lt;/pre&gt;
 &lt;h4&gt;事务支持&lt;/h4&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;默认行为&lt;/strong&gt;：多数 FDW 不支持分布式事务（如postgres_fdw 支持单语句事务）。&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;跨库事务&lt;/strong&gt;：需外部数据源支持两阶段提交。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;数据类型映射&lt;/h4&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;自动转换&lt;/strong&gt;：匹配同名数据类型（如 INTEGER、TEXT）。&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;手动映射&lt;/strong&gt;：通过ALTER FOREIGN TABLE 调整类型。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;FDW 的限制与应对&lt;/h4&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;限制项&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;应对策略&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;事务支持有限&lt;/td&gt;
   &lt;td&gt;使用最终一致性设计，避免跨库事务&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;复杂查询性能低&lt;/td&gt;
   &lt;td&gt;下推优化 + 本地物化缓存&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;数据类型不兼容&lt;/td&gt;
   &lt;td&gt;自定义类型转换函数&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;连接稳定性&lt;/td&gt;
   &lt;td&gt;超时重试机制 + 连接池&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;h3&gt;FDW 使用案例&lt;/h3&gt;
 &lt;h4&gt;跨国零售集团实时库存联邦查询&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;技术架构&lt;/strong&gt;：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="443" src="http://www.biaodianfu.com/wp-content/uploads/2025/04/fdw-demo.png" width="1294"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;实现方案&lt;/strong&gt;：&lt;/p&gt;
 &lt;p&gt;创建跨区域商品视图：&lt;/p&gt;
 &lt;pre&gt;CREATE VIEW global_inventory AS
SELECT &amp;apos;asia&amp;apos; region, * FROM asia_items
UNION ALL 
SELECT &amp;apos;europe&amp;apos;, * FROM europe_items
UNION ALL
SELECT &amp;apos;america&amp;apos;, * FROM oracle_items;
实时库存调配查询：
SELECT sku, sum(stock) 
FROM global_inventory 
WHERE warehouse IN (&amp;apos;hk&amp;apos;,&amp;apos;london&amp;apos;,&amp;apos;nyc&amp;apos;)
GROUP BY sku
HAVING sum(stock) &amp;lt; 100;
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;效果&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;查询响应时间：从 ETL 小时级 → 实时2s&lt;/li&gt;
  &lt;li&gt;库存周转率提升 23%&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;金融风控系统多源数据关联&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;数据整合&lt;/strong&gt;：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;数据源&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;FDW 类型&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;数据量&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;更新频率&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;客户基本信息&lt;/td&gt;
   &lt;td&gt;Oracle FDW&lt;/td&gt;
   &lt;td&gt;5000万&lt;/td&gt;
   &lt;td&gt;实时&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;交易记录&lt;/td&gt;
   &lt;td&gt;Kafka FDW&lt;/td&gt;
   &lt;td&gt;1亿/日&lt;/td&gt;
   &lt;td&gt;流式&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;外部征信数据&lt;/td&gt;
   &lt;td&gt;REST FDW&lt;/td&gt;
   &lt;td&gt;API调用&lt;/td&gt;
   &lt;td&gt;按需&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;  &lt;strong&gt;风控规则示例&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;SELECT 
    o.customer_id,
    COUNT(t.*) FILTER (WHERE t.amount &amp;gt; 100000) AS big_txns,
    r.credit_score
FROM oracle_customers o
JOIN kafka_transactions t USING (customer_id)
JOIN rest_credit_report r USING (ssn)
WHERE o.country = &amp;apos;US&amp;apos; 
  AND t.tx_time &amp;gt; NOW() - INTERVAL &amp;apos;7 days&amp;apos;
GROUP BY 1,3
HAVING COUNT(t.*) &amp;gt; 5 OR r.credit_score &amp;lt; 600;
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;成果&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;欺诈检测准确率提升 18%&lt;/li&gt;
  &lt;li&gt;每秒处理 8500 条实时交易&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;物联网平台多协议数据汇聚&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;架构实现&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;# 使用 Multicorn 自定义 FDW
class IoTFDW(ForeignDataWrapper):
    def execute(self, quals, columns):
        # 同时从 MQTT、CoAP、LoRaWAN 获取数据
        yield from mqtt_client.query(quals)
        yield from coap_server.fetch(columns)
        yield from lora_gateway.scan()
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;设备数据查询&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;-- 查询温度异常的工业设备
SELECT device_id, MAX(temp) 
FROM iot_sensors 
WHERE protocol = &amp;apos;lora&amp;apos; 
  AND ts BETWEEN &amp;apos;2023-08-01&amp;apos; AND &amp;apos;2023-08-07&amp;apos;
GROUP BY device_id
HAVING MAX(temp) &amp;gt; 90;
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;性能指标&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;支持 120 万台设备并发接入&lt;/li&gt;
  &lt;li&gt;数据延迟 &amp;lt; 800ms (P95)&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;媒体内容推荐系统&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;数据源整合&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;用户画像：MongoDB → 通过mongo_fdw 映射&lt;/li&gt;
  &lt;li&gt;行为日志：ClickHouse → clickhousedb_fdw&lt;/li&gt;
  &lt;li&gt;内容元数据：本地 PostgreSQL 表&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;混合推荐算法&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;WITH user_embedding AS (
    SELECT vector 
    FROM mongo_profiles 
    WHERE user_id = 123
),
content_features AS (
    SELECT id, title_embedding 
    FROM local_contents
)
SELECT 
    c.id,
    c.title,
    (c.title_embedding &amp;lt;-&amp;gt; u.vector) AS similarity
FROM content_features c
CROSS JOIN user_embedding u
ORDER BY 3 ASC
LIMIT 10;
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;业务提升&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;CTR（点击率）提升 34%&lt;/li&gt;
  &lt;li&gt;推荐计算耗时从 6s → 920ms&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;航空运营分析平台&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;多模态数据联邦&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;-- 联邦查询示例
SELECT 
    f.flight_no,
    w.wind_speed,
    m.maintenance-&amp;gt;&amp;apos;last_check&amp;apos; AS last_maint,
    AVG(passenger_count) OVER (
        PARTITION BY route 
        ORDER BY dep_time 
        ROWS 7 PRECEDING
    ) AS avg_passengers
FROM postgres_fdw_flights f
JOIN s3_fdw_weather w 
  ON f.dep_airport = w.station_id 
 AND f.dep_time BETWEEN w.start AND w.end
JOIN mongo_fdw_maintenance m 
  ON f.aircraft_id = m.aircraft-&amp;gt;&amp;gt;&amp;apos;id&amp;apos;
WHERE f.status = &amp;apos;completed&amp;apos;;
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;数据规模&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;实时处理 4000+ 航班/天&lt;/li&gt;
  &lt;li&gt;关联 10TB 历史气象数据&lt;/li&gt;
  &lt;li&gt;查询性能：复杂分析2s (vs 原 ETL 方案 25 分钟)&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;游戏玩家跨服对战系统&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;技术方案&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;每个游戏分区使用独立 PostgreSQL 实例&lt;/li&gt;
  &lt;li&gt;通过postgres_fdw 建立跨服视图：&lt;/li&gt;
&lt;/ul&gt;
 &lt;pre&gt;CREATE FOREIGN TABLE jp_players (...) SERVER jp_node;
CREATE FOREIGN TABLE na_players (...) SERVER na_node;

-- 全服玩家排行榜
SELECT region, player_id, score 
FROM jp_players 
UNION ALL
SELECT region, player_id, score
FROM na_players
ORDER BY score DESC 
LIMIT 100;
&lt;/pre&gt;
 &lt;ul&gt;
  &lt;li&gt;实时跨服匹配：&lt;/li&gt;
&lt;/ul&gt;
 &lt;pre&gt;SELECT 
    a.player_id AS p1,
    b.player_id AS p2,
    ABS(a.skill_level - b.skill_level) AS diff 
FROM global_players a
JOIN global_players b 
  ON a.region &amp;lt;&amp;gt; b.region 
 AND a.game_mode = b.game_mode
WHERE a.status = &amp;apos;waiting&amp;apos; 
  AND b.status = &amp;apos;waiting&amp;apos;
ORDER BY diff
LIMIT 100;
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;成果&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;匹配延迟从 6s → 320ms&lt;/li&gt;
  &lt;li&gt;跨服对战参与率提升 41%&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;政府政务数据开放平台&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;安全架构&lt;/strong&gt;：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="406" src="http://www.biaodianfu.com/wp-content/uploads/2025/04/fdw-2.png" width="1302"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;行级安全控制&lt;/strong&gt;：&lt;/p&gt;
 &lt;pre&gt;-- 创建安全视图
CREATE VIEW citizen_data AS
SELECT * FROM fdw_census 
WHERE city = current_setting(&amp;apos;user.city&amp;apos;);

-- 列级脱敏
CREATE FOREIGN TABLE fdw_tax (
    ssn TEXT,
    income NUMERIC,
    mask_ssn TEXT OPTIONS (mask &amp;apos;partial(0,4,&amp;apos;&amp;apos;XXXX&amp;apos;&amp;apos;)&amp;apos;)
) SERVER tax_server;

-- 查询示例
SELECT mask_ssn, income 
FROM fdw_tax 
WHERE income &amp;gt; 100000;
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;成效&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;数据开放种类从 15 类 → 127 类&lt;/li&gt;
  &lt;li&gt;跨部门查询响应速度提升 18 倍&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;关键成功要素&lt;/h4&gt;
 &lt;ul&gt;
  &lt;li&gt;查询下推优化：通过EXPLAIN VERBOSE 验证 80% 以上条件过滤在源端执行&lt;/li&gt;
  &lt;li&gt;混合存储策略：热数据缓存（物化视图）+ 冷数据直连&lt;/li&gt;
  &lt;li&gt;连接池管理：使用pgbouncer 控制外部连接数在 50 以内&lt;/li&gt;
  &lt;li&gt;类型转换优化：为 JSONB 字段创建 GIN 索引加速查询&lt;/li&gt;
  &lt;li&gt;安全隔离：为每个 FDW 创建单独角色和权限&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;性能对比数据&lt;/h4&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;    &lt;strong&gt;场景&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;传统ETL方案&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;FDW联邦查询&lt;/strong&gt;&lt;/td&gt;
   &lt;td&gt;    &lt;strong&gt;提升倍数&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;跨库JOIN(千万级)&lt;/td&gt;
   &lt;td&gt;12min&lt;/td&gt;
   &lt;td&gt;8.5s&lt;/td&gt;
   &lt;td&gt;85x&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;实时数据更新&lt;/td&gt;
   &lt;td&gt;小时级延迟&lt;/td&gt;
   &lt;td&gt;亚秒级&lt;/td&gt;
   &lt;td&gt;3600x&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;开发维护成本&lt;/td&gt;
   &lt;td&gt;15人月/年&lt;/td&gt;
   &lt;td&gt;3人月/年&lt;/td&gt;
   &lt;td&gt;5x&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;这些案例展现了 FDW 在构建现代数据架构中的核心价值：消除数据孤岛，释放数据流动性，同时保持查询的实时性与一致性。建议在实施时结合 pg_stat_activity 监控外部查询，并通过 pg_statio_user_tables 分析 IO 瓶颈，持续优化联邦查询性能。&lt;/p&gt;
 &lt;p&gt;参考链接：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://pigsty.io/zh/blog/pg/pg-eat-db-world/"&gt;PostgreSQL正在吞噬数据库世界 | Pigsty&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://medium.com/@fengruohang/postgres-is-eating-the-database-world-157c204dcfc4"&gt;Postgres is eating the database world | by Vonng | Medium&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://ossrank.com/cat/368-postgresql-extension"&gt;PostgreSQL Ecosystem – OSSRank&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://github.com/Vonng/pigsty/discussions/333"&gt;Call for New PostgreSQL Extensions Vonng/pigsty · Discussion #333 · GitHub&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://gist.github.com/joelonsql/e5aa27f8cc9bd22b8999b7de8aee9d47"&gt;1000+ PostgreSQL EXTENSIONs GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;div&gt;

  &lt;strong&gt;相关文章:&lt;/strong&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/sql-date-and-time-functions.html" rel="bookmark" title="SQL &amp;#26085;&amp;#26399;/&amp;#26102;&amp;#38388;&amp;#22788;&amp;#29702;&amp;#20989;&amp;#25968;"&gt;SQL 日期/时间处理函数&lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/hive-sql-guide.html" rel="bookmark" title="Hive SQL&amp;#31995;&amp;#32479;&amp;#21270;&amp;#23398;&amp;#20064;"&gt;Hive SQL系统化学习&lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/spark-sql.html" rel="bookmark" title="Spark SQL &amp;#31995;&amp;#32479;&amp;#21270;&amp;#23398;&amp;#20064;"&gt;Spark SQL 系统化学习&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>器→工具 工具软件 postgresql</category>
      <guid isPermaLink="true">https://itindex.net/detail/63001-%E6%95%B0%E6%8D%AE%E5%BA%93-%E6%9C%AA%E6%9D%A5-postgresql</guid>
      <pubDate>Mon, 07 Apr 2025 20:44:25 CST</pubDate>
    </item>
    <item>
      <title>在 Mac 用 LM studio 部署本地大模型（DeepSeek/Qwen） + 翻译</title>
      <link>https://itindex.net/detail/62973-mac-lm-studio</link>
      <description>&lt;p&gt;得益于 Mac 的 CPU 和 GPU 共享内存， 以及大的内存带宽， 使得使用 macBook 运行本地大模型成为可能，借着最近 DeepSeek 大火的东风，我也尝试在本地构建了一套 AI 翻译的系统。本文将会介绍如何在 Mac 电脑上正确的配置这套系统。设置完成后，你将可以&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;在 Mac 上免费使用大语言模型进行对话&lt;/li&gt;
  &lt;li&gt;无需等待服务器响应，提高效率&lt;/li&gt;
  &lt;li&gt;快速翻译任何文档，截图，网页等&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;本文以 macBook 为例， 理论上 windows 电脑也可以获得同样的效果，仅供参考&lt;/p&gt;
 &lt;p&gt;本文下载工具在中国大陆可能会遇到网络问题，请自行解决&lt;/p&gt;
 &lt;h2&gt;模型管理工具&lt;/h2&gt;
 &lt;p&gt;所谓模型管理工具，就是可以本地运行管理大模型的工具，并且可以提供服务器功能，这样可以省去一些没有必要的麻烦。此外， 模型管理工具还可以提供本地聊天的功能，这样在网络不畅的情况下，也可以使用到最近最火的 DeepSeek。&lt;/p&gt;
 &lt;p&gt;比较流行的模型管理工具有   &lt;a href="https://ollama.com/" rel="noopener noreferrer" target="_blank"&gt;Ollama&lt;/a&gt; 和   &lt;a href="https://lmstudio.ai/" rel="noopener noreferrer" target="_blank"&gt;LM Studio&lt;/a&gt;. 这两个工具比起来， LM Studio 有一个 GUI 页面，下载模型也更方便，对新手比较友好。 所以本文将使用 LM Studio。&lt;/p&gt;
 &lt;h3&gt;安装模型&lt;/h3&gt;
 &lt;p&gt;如何找到合适的模型是一切开始的关键，当下比较流行的开源大模型有 deepseek-r1， Qwen， LLama 等，根据需要选择你喜欢的。作者需要使用到中文 - 英文翻译，所以选择了中文更友好的 deepseek 和 Qwen（千问）。&lt;/p&gt;
 &lt;p&gt;然后是根据自己 Mac 的配置选择合适的模型大小。 LM Studio 会把当前配置无法下载的模型禁止掉， 当然即使可以使用， 这和使用的舒服也有一定的区别。&lt;/p&gt;
 &lt;p&gt;作者的配置是 MacBook Pro M3 Max 36G 内存， 测试过程中，32B 大小的 DeepSeek R1 是可以正常使用的，但是运行速度会比较慢，简单对话没有问题， 但是翻译场景，尤其是比较大型的 PDF 就很让人着急，再加上 DeepSeek R1 还有大量推理过程，32B 模型的速度就更慢了。当然如果拥有更好配置的机器，尤其是大内存，肯定是越大的模型效果越好，这一点丰俭由人。&lt;/p&gt;
 &lt;p&gt;关于 DeepSeek R1 还有一点要说，DeepSeek R1 会展示出一个长的思维链， 这一点固然很棒，但是在翻译的场景下，思维链其实并不是必须的，甚至是累赘，拖慢翻译速度， 相比而言 Qwen 模型在这个场景下就是更好的那个选择，后文会给出一个解决思维链的方案。&lt;/p&gt;
 &lt;p&gt;总结一下，模型很多，各有利弊。根据自己的需求，配置选择合适的模型即可。我使用了 qwen2.5-7b-instruct-1m 用来翻译（14B 应该也没什么问题）。&lt;/p&gt;
 &lt;p&gt;可以参考下图进行下载安装。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmcYJDMNYGCw646bEyXrv1GZeLuYhR1BqgUi95imKnhmMV"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;启动服务&lt;/h3&gt;
 &lt;p&gt;接下来就是加载模型，启动服务，按照下图即可。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmT89csnB5Dx2VgxSQyVrPW3nEyc9yzMEWDbme6UhdeVMX"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmdzFfPL5pNUc4LeGBwbeU5NeXvEBAiwWWvHpq154GsH96"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;一旦加载成功，就可以使用这个大模型了。左边栏最上方一个是对话功能，在这里可以和你加载的大模型对话。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmP45hmbfEcFZBc4mxfmb8y2TDZJmZsBAihJfbqUtAJnaN"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;同时还可以复制代码到命令行检查模型是否正常运行以及服务是否正常启动。到这里，大模型相关的配置就结束了，恭喜你，已经获得了一个可以运行在本地，不受服务器影响， 快速响应的，专属于你的大模型了。&lt;/p&gt;
 &lt;p&gt;如果你把这个服务开放在局域网上，乃至公网你，你就可以在其他设备上访问到你的大模型了，这就是另一个故事了。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmVHjauMqLW4jZM91hyuZDKvHP6k67EGJGhhvLCGnvK37L"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmTLfCpF11Q6Vjdy7g7vxfHKYRTLHJJ6Sm3VWfxgjZzpjj"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;翻译 - Easydict&lt;/h2&gt;
 &lt;p&gt;本文使用了一个开源的本地翻译工具   &lt;a href="https://github.com/tisfeng/Easydict" rel="noopener noreferrer" target="_blank"&gt;Easydict&lt;/a&gt; , 当然如果有你发现了其他工具也可以使用。本文仅以此为例。&lt;/p&gt;
 &lt;h3&gt;安装&lt;/h3&gt;
 &lt;p&gt;你可以使用下面两种方式之一安装。&lt;/p&gt;
 &lt;p&gt;Easydict 最新版本支持系统 macOS 13.0+，如果系统版本为 macOS 11.0+，请使用   &lt;a href="https://github.com/tisfeng/Easydict/releases/tag/2.7.2" rel="noopener noreferrer" target="_blank"&gt;2.7.2&lt;/a&gt;。&lt;/p&gt;
 &lt;h3&gt;1. 手动下载安装&lt;/h3&gt;
 &lt;p&gt;  &lt;a href="https://github.com/tisfeng/Easydict/releases" rel="noopener noreferrer" target="_blank"&gt;下载&lt;/a&gt; 最新版本的 Easydict。&lt;/p&gt;
 &lt;h3&gt;2. Homebrew 安装&lt;/h3&gt;
 &lt;div&gt;Copy  &lt;pre&gt;   &lt;code&gt;brew install --cask easydict
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;h3&gt;配置&lt;/h3&gt;
 &lt;p&gt;安装成功后，点击按钮，选择配置， 进入配置页面。  &lt;br /&gt;
然后点击服务，配置我们自己的服务器地址， 这里其实选择 ollama 翻译 和 自定义 OpenAI 翻译理论上都可以。&lt;/p&gt;
 &lt;p&gt;输入自己的 服务器地址端口和模型名称即可，这些在 LM Studio 页面都能找到。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmeRwegm1jrV4a8jZWC9yqs1nbvKG9pkXnUWzUT2xRyMuU"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmVZkJqe8yTHjY1Mz14b5tEtWtqubTEemAaAMhH39ud9CD"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;使用&lt;/h3&gt;
 &lt;p&gt;配置会之后，就可以正常使用了。关于 Easydict 的具体使用方法，请参考对应的  &lt;a href="https://github.com/tisfeng/Easydict/tree/main" rel="noopener noreferrer" target="_blank"&gt;官方文档&lt;/a&gt; .&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmUHAcpF78XFz7ScQGRt4bsqxzyjzhfzKMMJ1h7B7rTsX9"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;另外要说明的是， EasyDict 也支持其他形式的 API ，也内置了翻译，不需要前面本地大模型那一套， 本身也是一个很好用的应用。&lt;/p&gt;
 &lt;h2&gt;翻译 - 沉浸式翻译&lt;/h2&gt;
 &lt;p&gt;沉浸式翻译几乎是 OpenAI 横空出世之后最火的一个浏览器翻译插件了。它也支持使用自定义 API 接口来进行翻译，且同时支持网页翻译和 PDF 翻译， 且翻译的展示效果非常优秀。&lt;/p&gt;
 &lt;p&gt;可以支持一个 pro 会员，这样就无需折腾，开箱即用。如果愿意继续折腾本地大模型，就往后看吧。&lt;/p&gt;
 &lt;p&gt;这是  &lt;a href="https://immersivetranslate.com/zh-Hans/" rel="noopener noreferrer" target="_blank"&gt;官网链接&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmP73FB2JzeU7JUtKpvS6og8MLvYpzEfqi2BMb7RiZ1Rdj"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;下载完插件之后，进入配置页面， 还是一样输入 API 地址和模型名称，就可以使用本地大模型进行沉浸式翻译了。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmP73FB2JzeU7JUtKpvS6og8MLvYpzEfqi2BMb7RiZ1Rdj"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="image" src="https://ipfs.crossbell.io/ipfs/QmbMRCm35ywTPiZpCW6hoSPLLHPgAdLfr4AFndwFbdrQdz"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;服务器转发&lt;/h2&gt;
 &lt;p&gt;走到这一步，几乎可是使用了，但是你会遇到一下问题&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;deepseek R1 的翻译结果会带上思维链，影响翻译体验&lt;/li&gt;
  &lt;li&gt;沉浸式翻译的 API 格式和 LM Studio 的 API 格式不能无缝对接，所以即使 API 通了，沉浸式翻译也无法显示翻译结果&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;所以，不得已，我用 Python web.py 写了一个最简单的本地服务器，用来转发请求，并在中间做一些微小的工作，解决上述两个小问题，使得翻译体验更好。这里附上代码，仅供参考。web.py 的默认端口是 8080， 也可以按需修改成你需要的&lt;/p&gt;
 &lt;p&gt;记得要安装 Python 并使用    &lt;code&gt;pip install webpy&lt;/code&gt;  &lt;br /&gt;
这里不再赘述&lt;/p&gt;
 &lt;div&gt;Copy  &lt;pre&gt;   &lt;code&gt;import web
import json
import requests
import re
import time

# 配置URLs路由
urls = (
    &amp;apos;/v1/chat/completions&amp;apos;, &amp;apos;ChatCompletions&amp;apos;,
    &amp;apos;/v1/models&amp;apos;, &amp;apos;Models&amp;apos;
)

def add_cors_headers():
    # 添加CORS相关的响应头
    web.header(&amp;apos;Access-Control-Allow-Origin&amp;apos;, &amp;apos;*&amp;apos;)
    web.header(&amp;apos;Access-Control-Allow-Credentials&amp;apos;, &amp;apos;true&amp;apos;)
    web.header(&amp;apos;Access-Control-Allow-Methods&amp;apos;, &amp;apos;GET, POST, OPTIONS&amp;apos;)
    web.header(&amp;apos;Access-Control-Allow-Headers&amp;apos;, &amp;apos;Content-Type, Authorization&amp;apos;)

def remove_think_tags(text):
    # 移除&amp;lt;think&amp;gt;标签及其内容
    return re.sub(r&amp;apos;&amp;lt;think&amp;gt;.*?&amp;lt;/think&amp;gt;&amp;apos;, &amp;apos;&amp;apos;, text, flags=re.DOTALL)

class ChatCompletions:
    def OPTIONS(self):
        # 处理预检请求
        add_cors_headers()
        return &amp;apos;&amp;apos;
        
    def POST(self):
        web.header(&amp;apos;Content-Type&amp;apos;, &amp;apos;application/json&amp;apos;)
        add_cors_headers()
        
        try:
            data = json.loads(web.data())
            lm_studio_url = &amp;quot;http://localhost:1234/v1/chat/completions&amp;quot;
            
            # 检查是否为流式请求
            is_stream = data.get(&amp;apos;stream&amp;apos;, False)
            
            # 转发请求到LM Studio
            response = requests.post(
                lm_studio_url,
                json=data,
                headers={&amp;apos;Content-Type&amp;apos;: &amp;apos;application/json&amp;apos;},
                stream=is_stream  # 设置stream参数
            )
            
            if is_stream:
                # 对于流式请求，先收集完整内容
                full_content = &amp;quot;&amp;quot;
                current_id = None
                
                def generate_stream():
                    nonlocal full_content, current_id
                    
                    for line in response.iter_lines():
                        if line:
                            line = line.decode(&amp;apos;utf-8&amp;apos;)
                            if line.startswith(&amp;apos;data: &amp;apos;):
                                line = line[6:]
                            if line == &amp;apos;[DONE]&amp;apos;:
                                # 处理完整内容并发送最后一个块
                                cleaned_content = remove_think_tags(full_content)
                                # 发送清理后的完整内容
                                final_chunk = {
                                    &amp;quot;id&amp;quot;: current_id,
                                    &amp;quot;object&amp;quot;: &amp;quot;chat.completion.chunk&amp;quot;,
                                    &amp;quot;created&amp;quot;: int(time.time()),
                                    &amp;quot;model&amp;quot;: &amp;quot;local-model&amp;quot;,
                                    &amp;quot;choices&amp;quot;: [{
                                        &amp;quot;index&amp;quot;: 0,
                                        &amp;quot;delta&amp;quot;: {
                                            &amp;quot;content&amp;quot;: cleaned_content
                                        },
                                        &amp;quot;finish_reason&amp;quot;: &amp;quot;stop&amp;quot;
                                    }]
                                }
                                yield f&amp;apos;data: {json.dumps(final_chunk)}\n\n&amp;apos;
                                yield &amp;apos;data: [DONE]\n\n&amp;apos;
                                continue
                                
                            try:
                                chunk_data = json.loads(line)
                                current_id = chunk_data.get(&amp;apos;id&amp;apos;, current_id)
                                
                                if &amp;apos;choices&amp;apos; in chunk_data:
                                    for choice in chunk_data[&amp;apos;choices&amp;apos;]:
                                        if &amp;apos;delta&amp;apos; in choice:
                                            if &amp;apos;content&amp;apos; in choice[&amp;apos;delta&amp;apos;]:
                                                # 累积内容而不是直接发送
                                                full_content += choice[&amp;apos;delta&amp;apos;][&amp;apos;content&amp;apos;]
                                
                                # 发送空的进度更新
                                progress_chunk = {
                                    &amp;quot;id&amp;quot;: current_id,
                                    &amp;quot;object&amp;quot;: &amp;quot;chat.completion.chunk&amp;quot;,
                                    &amp;quot;created&amp;quot;: int(time.time()),
                                    &amp;quot;model&amp;quot;: &amp;quot;local-model&amp;quot;,
                                    &amp;quot;choices&amp;quot;: [{
                                        &amp;quot;index&amp;quot;: 0,
                                        &amp;quot;delta&amp;quot;: {},
                                        &amp;quot;finish_reason&amp;quot;: None
                                    }]
                                }
                                yield f&amp;apos;data: {json.dumps(progress_chunk)}\n\n&amp;apos;
                                
                            except json.JSONDecodeError:
                                continue
                
                web.header(&amp;apos;Content-Type&amp;apos;, &amp;apos;text/event-stream&amp;apos;)
                web.header(&amp;apos;Cache-Control&amp;apos;, &amp;apos;no-cache&amp;apos;)
                web.header(&amp;apos;Connection&amp;apos;, &amp;apos;keep-alive&amp;apos;)
                return generate_stream()

            else:
                # 非流式请求的处理
                response_data = json.loads(response.text)
            
                if &amp;apos;choices&amp;apos; in response_data:
                    for choice in response_data[&amp;apos;choices&amp;apos;]:
                        if &amp;apos;message&amp;apos; in choice and &amp;apos;content&amp;apos; in choice[&amp;apos;message&amp;apos;]:
                            choice[&amp;apos;message&amp;apos;][&amp;apos;content&amp;apos;] = remove_think_tags(
                                choice[&amp;apos;message&amp;apos;][&amp;apos;content&amp;apos;]
                            )
                return json.dumps(response_data)
            
        except Exception as e:
            print(e)
            return json.dumps({
                &amp;quot;error&amp;quot;: {
                    &amp;quot;message&amp;quot;: str(e),
                    &amp;quot;type&amp;quot;: &amp;quot;proxy_error&amp;quot;
                }
            })

class Models:
    def OPTIONS(self):
        # 处理预检请求
        add_cors_headers()
        return &amp;apos;&amp;apos;
        
    def GET(self):
        web.header(&amp;apos;Content-Type&amp;apos;, &amp;apos;application/json&amp;apos;)
        add_cors_headers()
        # 返回一个模拟的模型列表
        return json.dumps({
            &amp;quot;data&amp;quot;: [
                {
                    &amp;quot;id&amp;quot;: &amp;quot;local-model&amp;quot;,
                    &amp;quot;object&amp;quot;: &amp;quot;model&amp;quot;,
                    &amp;quot;owned_by&amp;quot;: &amp;quot;local&amp;quot;
                }
            ]
        })

if __name__ == &amp;quot;__main__&amp;quot;:
    app = web.application(urls, globals())
    app.run() 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;p&gt;到这里，一个在 Mac 上运行本地部署的大模型，再加上网页、PDF 文档、文字翻译的工具链就完成了。这其中可以替代的选项还有很多。  &lt;br /&gt;
模型部署还有 ollama 等，大模型还可以用 Phi、llama 等， 转发服务器也可以用其他方案，或者可能还有不用转发的选项可以研究，翻译工具也可以用 Bob 来代替。总而言之，技术选择有很多，本文只是提供一个参考。时间还是要花在更重要的事上， 已经有了利器，赶快去善其事吧。&lt;/p&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>post 工具 AI Tech</category>
      <guid isPermaLink="true">https://itindex.net/detail/62973-mac-lm-studio</guid>
      <pubDate>Tue, 11 Feb 2025 23:18:29 CST</pubDate>
    </item>
    <item>
      <title>开源实时数据同步工具NiFi</title>
      <link>https://itindex.net/detail/62957-%E5%BC%80%E6%BA%90-%E5%AE%9E%E6%97%B6-%E6%95%B0%E6%8D%AE</link>
      <description>&lt;h2&gt;Apache NiFi简介&lt;/h2&gt;
 &lt;p&gt;Apache NiFi 是一个强大的数据流管理和自动化工具，旨在简化数据的采集、传输、处理和分发。它特别适合于构建和管理复杂的数据流管道，支持从各种数据源到不同目标系统的数据传输。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="245" src="https://www.biaodianfu.com/wp-content/uploads/2024/11/NiFi.png" width="525"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;Apache NiFi主要功能&lt;/h3&gt;
 &lt;p&gt;Apache NiFi 是一个用于自动化数据流的强大工具，具有广泛的功能集，旨在支持从各种数据源到不同目标的复杂数据流管道。以下是 Apache NiFi 的主要功能：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;数据采集与传输&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持从多种数据源采集数据，包括文件系统、数据库、HTTP 服务、消息队列（如 Kafka）、传感器设备等。&lt;/li&gt;
    &lt;li&gt;支持将数据传输到多种目标系统，如 HDFS、数据库、云存储服务、REST API 等。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;数据流可视化&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;提供直观的 Web 用户界面，用户可以通过拖拽和配置处理器来设计和管理数据流。&lt;/li&gt;
    &lt;li&gt;实时显示数据流的状态和性能指标，便于监控和调试。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;数据处理与转换&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;提供丰富的内置处理器，支持数据的解析、转换、清洗、聚合和格式化等操作。&lt;/li&gt;
    &lt;li&gt;支持复杂的数据处理逻辑，如条件路由、数据分片、合并和拆分。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;动态路由与优先级控制&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持根据数据内容或属性动态路由数据到不同的处理器或目标。&lt;/li&gt;
    &lt;li&gt;允许为数据流设置优先级，以控制数据处理的顺序和速度。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;实时流处理&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持实时数据流处理，能够在数据到达时立即执行处理操作。&lt;/li&gt;
    &lt;li&gt;事件驱动架构，处理器在接收到数据或触发条件时自动执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;分布式架构与扩展性&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持多节点集群部署，可以水平扩展以处理大规模数据流。&lt;/li&gt;
    &lt;li&gt;集群中的节点通过 Apache ZooKeeper 进行协调和管理。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;数据安全与合规&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持数据加密、访问控制和用户身份验证，确保数据的安全性。&lt;/li&gt;
    &lt;li&gt;提供数据审计功能，记录数据流的处理历史和用户操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;错误处理与重试机制&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;自动处理数据传输和处理过程中出现的错误，支持重试和故障转移。&lt;/li&gt;
    &lt;li&gt;提供数据回退和恢复功能，确保数据的可靠性和完整性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;可扩展性与集成性&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持自定义处理器和控制器服务的开发，用户可以根据需要扩展 NiFi 的功能。&lt;/li&gt;
    &lt;li&gt;与其他大数据工具和框架（如 Apache Kafka、Hadoop、Spark）紧密集成，支持复杂的数据处理和分析工作流。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;监控与管理&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;提供详细的日志记录和监控功能，帮助用户了解数据流的执行状态和性能指标。&lt;/li&gt;
    &lt;li&gt;支持告警和通知机制，用户可以根据特定条件设置告警，及时响应异常情况。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;Apache NiFi 的设计目标是提供一个灵活、高效且易于管理的数据流管理平台，适用于各种数据集成和处理场景。其丰富的功能集使其成为企业数据管道构建和管理的理想选择。&lt;/p&gt;
 &lt;h3&gt;Apache NiFi的优势&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;灵活性：通过丰富的处理器和自定义开发能力，NiFi 可以适应各种复杂的数据处理需求。&lt;/li&gt;
  &lt;li&gt;可扩展性：支持多节点集群部署，可以水平扩展以处理大规模数据流。&lt;/li&gt;
  &lt;li&gt;可视化管理：提供直观的 Web UI，用户可以轻松设计和管理数据流，无需编写复杂的代码。&lt;/li&gt;
  &lt;li&gt;高可用性：通过故障转移和数据重试机制，确保数据流的高可用性和可靠性。&lt;/li&gt;
  &lt;li&gt;安全性：支持数据加密、访问控制和审计，确保数据的安全性和隐私保护。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;Apache NiFi的架构&lt;/h2&gt;
 &lt;p&gt;Apache NiFi 的架构设计旨在提供一个灵活、高效且可扩展的数据流管理平台。它采用模块化设计，支持分布式部署，能够处理各种规模和复杂度的数据流任务。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="510" src="https://www.biaodianfu.com/wp-content/uploads/2024/11/nifi-2.png" width="970"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;核心组件&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;Web UI（用户界面）：NiFi 提供了一个直观的 Web 用户界面，用于设计、监控和管理数据流。用户可以通过拖拽和配置组件来构建数据流，并实时查看数据流的状态和性能指标。&lt;/li&gt;
  &lt;li&gt;FlowFile：FlowFile 是 NiFi 中的数据单元，包含数据内容和属性。每个 FlowFile 都有唯一标识符和元数据，支持数据的高效传输和处理。&lt;/li&gt;
  &lt;li&gt;处理器（Processor）：处理器是执行特定数据处理任务的基本单元。NiFi 提供了丰富的内置处理器，用于数据采集、转换、路由和传输。用户还可以开发自定义处理器以满足特定需求。&lt;/li&gt;
  &lt;li&gt;连接（Connection）：连接用于在处理器之间传递 FlowFile。连接可以配置为使用不同的队列策略，以控制数据的流动速度和优先级。&lt;/li&gt;
  &lt;li&gt;流程组（Process Group）：流程组用于组织和管理多个处理器和连接，形成逻辑上的子流程。这有助于复杂数据流的模块化设计和维护。&lt;/li&gt;
  &lt;li&gt;控制器服务（Controller Service）：控制器服务提供共享的配置和服务，例如数据库连接池、分布式缓存等。它们可以在多个处理器之间复用，提高资源利用率。&lt;/li&gt;
  &lt;li&gt;报告任务（Reporting Task）：报告任务用于生成和发送 NiFi 系统的运行状态和指标数据，通常用于监控和告警系统。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;工作流和数据流&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;数据采集与处理&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;数据流从输入处理器开始，输入处理器从外部数据源（如文件系统、HTTP、Kafka）获取数据并生成 FlowFile。&lt;/li&gt;
    &lt;li&gt;中间处理器对 FlowFile 进行处理，包括数据解析、转换、过滤和聚合等操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;数据路由与分发&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;根据业务规则和条件，NiFi 可以将 FlowFile 路由到不同的处理器或流程组。&lt;/li&gt;
    &lt;li&gt;输出处理器将处理后的 FlowFile 发送到目标系统（如 HDFS、数据库、云存储）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;实时监控与管理&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;Web UI 提供实时数据流监控功能，用户可以查看处理器的性能指标、队列长度、处理速率等。&lt;/li&gt;
    &lt;li&gt;用户可以动态调整数据流的配置和参数，以优化性能和处理逻辑。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;分布式架构&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;多节点集群&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;NiFi 支持多节点集群部署，可以通过增加节点来扩展处理能力。集群中的每个节点都可以执行数据流任务。&lt;/li&gt;
    &lt;li&gt;集群节点通过 Apache ZooKeeper 进行协调和管理，以确保任务的负载均衡和高可用性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;高可用性与故障转移&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;NiFi 采用主从架构，集群中一个节点为主节点（Primary Node），负责调度任务和管理集群配置。&lt;/li&gt;
    &lt;li&gt;在主节点故障时，集群会自动选举新的主节点，确保数据流的持续性和可靠性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;安全性&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;用户认证与授权&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持多种认证机制（如 LDAP、Kerberos），确保只有授权用户才能访问和管理 NiFi 系统。&lt;/li&gt;
    &lt;li&gt;提供细粒度的权限控制，用户可以对不同的数据流组件和操作进行授权。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;数据加密&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;支持数据传输加密和存储加密，确保数据在传输和存储过程中的安全性。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;审计与日志&lt;/strong&gt;：
   &lt;ul&gt;
    &lt;li&gt;提供详细的审计日志记录，记录用户操作和数据流处理历史，便于合规性检查和故障排查。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;Apache NiFi 的架构设计使其成为一个灵活、可扩展和安全的数据流管理平台，适用于各种规模和复杂度的数据集成和处理任务。其模块化设计和丰富的功能集使得用户能够高效地构建和管理复杂的数据流管道。&lt;/p&gt;
 &lt;h2&gt;Airflow、Kafka的对比&lt;/h2&gt;
 &lt;p&gt;Apache NiFi、Apache Airflow 和 Apache Kafka 都是现代数据处理和管理生态系统中的重要工具，但它们各自的设计目的和应用场景有所不同。以下是它们的详细对比：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td&gt;特性&lt;/td&gt;
   &lt;td&gt;Apache NiFi&lt;/td&gt;
   &lt;td&gt;Apache Airflow&lt;/td&gt;
   &lt;td&gt;Apache Kafka&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;主要用途&lt;/td&gt;
   &lt;td&gt;实时数据流管理和自动化&lt;/td&gt;
   &lt;td&gt;工作流调度和管理&lt;/td&gt;
   &lt;td&gt;消息队列和流处理&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;架构特点&lt;/td&gt;
   &lt;td&gt;可视化界面，事件驱动架构&lt;/td&gt;
   &lt;td&gt;编程接口定义工作流（DAGs），基于调度器和执行器&lt;/td&gt;
   &lt;td&gt;发布/订阅模型，分布式架构&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;数据处理&lt;/td&gt;
   &lt;td&gt;实时数据采集、转换和路由&lt;/td&gt;
   &lt;td&gt;批处理任务调度，不直接处理数据流&lt;/td&gt;
   &lt;td&gt;高吞吐量的消息传输，支持实时流处理&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;扩展性与部署&lt;/td&gt;
   &lt;td&gt;多节点集群，水平扩展&lt;/td&gt;
   &lt;td&gt;分布式调度和执行，支持多种执行器&lt;/td&gt;
   &lt;td&gt;水平扩展，通过分区和副本实现容错&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;安全性&lt;/td&gt;
   &lt;td&gt;细粒度权限控制和数据加密&lt;/td&gt;
   &lt;td&gt;用户认证和授权（RBAC）&lt;/td&gt;
   &lt;td&gt;SSL 加密、SASL 认证和 ACL 授权&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;应用场景&lt;/td&gt;
   &lt;td&gt;实时数据集成、物联网数据采集、日志管理和监控&lt;/td&gt;
   &lt;td&gt;定时数据处理任务、复杂的 ETL 管道、机器学习工作流&lt;/td&gt;
   &lt;td&gt;实时数据传输、日志收集和分析、事件驱动架构&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;p&gt;  &lt;strong&gt;对比总结&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;实时 vs 批处理：
   &lt;ul&gt;
    &lt;li&gt;NiFi：适合实时数据流处理和自动化。&lt;/li&gt;
    &lt;li&gt;Airflow：适合批处理任务调度和复杂的工作流管理。&lt;/li&gt;
    &lt;li&gt;Kafka：适合高吞吐量的消息传输和实时流处理。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;用户界面 vs 编程接口：
   &lt;ul&gt;
    &lt;li&gt;NiFi：提供可视化界面，适合需要快速构建和管理数据流的场景。&lt;/li&gt;
    &lt;li&gt;Airflow：提供编程接口，适合需要灵活定义复杂工作流的场景。&lt;/li&gt;
    &lt;li&gt;Kafka：主要通过编程接口和命令行工具进行管理和配置。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;数据流管理 vs 工作流调度 vs 消息队列：
   &lt;ul&gt;
    &lt;li&gt;NiFi：专注于数据流的管理和处理。&lt;/li&gt;
    &lt;li&gt;Airflow：专注于任务调度和工作流管理。&lt;/li&gt;
    &lt;li&gt;Kafka：专注于消息队列和流处理。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;根据具体的需求和场景，企业可以选择合适的工具或组合使用这些工具来构建复杂的数据处理和管理生态系统。例如，可以使用 NiFi 进行数据采集和预处理，使用 Kafka 进行高吞吐量的消息传输，使用 Airflow 进行批处理任务的调度和管理。&lt;/p&gt;
 &lt;div&gt;

  &lt;h3&gt;相关文章:&lt;/h3&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/kafka.html" rel="bookmark" title="&amp;#20998;&amp;#24067;&amp;#24335;&amp;#28040;&amp;#24687;&amp;#31995;&amp;#32479;Kafka"&gt;分布式消息系统Kafka&lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/hive-sql-guide.html" rel="bookmark" title="Hive SQL&amp;#31995;&amp;#32479;&amp;#21270;&amp;#23398;&amp;#20064;"&gt;Hive SQL系统化学习&lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/python-schedule.html" rel="bookmark" title="Python&amp;#33258;&amp;#21160;&amp;#21270;&amp;#20043;&amp;#23450;&amp;#26102;&amp;#20219;&amp;#21153;"&gt;Python自动化之定时任务&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>器→工具 开源项目 大数据</category>
      <guid isPermaLink="true">https://itindex.net/detail/62957-%E5%BC%80%E6%BA%90-%E5%AE%9E%E6%97%B6-%E6%95%B0%E6%8D%AE</guid>
      <pubDate>Sat, 09 Nov 2024 21:04:10 CST</pubDate>
    </item>
    <item>
      <title>数据湖存储系统Paimon</title>
      <link>https://itindex.net/detail/62956-%E6%95%B0%E6%8D%AE-%E7%B3%BB%E7%BB%9F-paimon</link>
      <description>&lt;h2&gt;Paimon简介&lt;/h2&gt;
 &lt;p&gt;Apache Paimon 是一个面向大数据生态系统的高性能数据湖存储系统。它最初是由 Flink 社区开发的，旨在为大数据处理提供高效的存储解决方案。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="520" src="https://www.biaodianfu.com/wp-content/uploads/2024/11/Paimon.png" width="1819"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Apache Paimon（以前称为 Flink Table Store）是一个专为流处理和批处理而设计的数据湖存储系统。它解决了现代数据处理中的一些关键问题，以下是一些主要的方面：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;统一的批处理和流处理：传统的数据处理系统通常将批处理和流处理分开，导致架构复杂性增加。Apache Paimon 提供了一种统一的存储格式，支持高效的批处理和流处理，简化了数据管道的构建和维护。&lt;/li&gt;
  &lt;li&gt;高效的数据更新和删除：许多数据湖解决方案在处理更新和删除操作时效率较低。Paimon 通过支持高效的增量更新和删除操作，提升了数据处理的灵活性，适合需要频繁更新的数据场景。&lt;/li&gt;
  &lt;li&gt;事务一致性：在数据湖中实现事务一致性是一个挑战。Paimon 提供了 ACID 事务支持，确保数据操作的原子性、一致性、隔离性和持久性，增强了数据的可靠性和一致性。&lt;/li&gt;
  &lt;li&gt;优化的存储格式：Paimon 使用了优化的存储格式，支持列式存储和高效的数据压缩，这不仅提高了查询性能，还降低了存储成本。&lt;/li&gt;
  &lt;li&gt;与 Apache Flink 的深度集成：Paimon 与 Apache Flink 深度集成，使得在 Flink 上构建实时数据应用变得更加容易。这种集成使得开发者可以利用 Flink 强大的流处理能力，直接在 Paimon 上执行复杂的实时分析任务。&lt;/li&gt;
  &lt;li&gt;元数据管理：Paimon 提供了强大的元数据管理功能，支持对大规模数据集的高效管理和操作，简化了数据治理和合规性管理。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;通过解决这些问题，Apache Paimon 为需要处理大规模数据的企业提供了一种高效、灵活且一致的数据存储和处理解决方案。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;设计目标&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;实时性：Apache Paimon 旨在支持实时数据处理和分析，使得用户可以对不断变化的数据进行快速查询和处理。&lt;/li&gt;
  &lt;li&gt;高吞吐和低延迟：系统设计考虑了高吞吐量和低延迟的需求，能够处理大规模数据的同时，保持较低的响应时间。&lt;/li&gt;
  &lt;li&gt;事务支持：支持 ACID 事务，以确保数据的一致性和可靠性，即使在高并发环境下也能保证数据的正确性。&lt;/li&gt;
  &lt;li&gt;易于集成：Paimon 可以与多种大数据处理框架无缝集成，如 Apache Flink、Apache Spark 等，提供灵活的数据处理能力。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;核心特性&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;数据湖架构：采用数据湖架构，允许用户在存储中保存大规模的结构化和非结构化数据，并对其进行管理和分析。&lt;/li&gt;
  &lt;li&gt;Schema 演化：支持动态的 Schema 演化，允许在不影响现有数据和查询的情况下进行 Schema 的更改。&lt;/li&gt;
  &lt;li&gt;高效的存储格式：使用高效的列式存储格式（如 Parquet 或 ORC），以减少存储空间和提高查询性能。&lt;/li&gt;
  &lt;li&gt;数据版本管理：提供数据版本管理功能，支持时间旅行查询（Time Travel Query），用户可以查询历史数据快照。&lt;/li&gt;
  &lt;li&gt;高可用性和扩展性：设计为分布式系统，能够在多节点环境中运行，提供高可用性和良好的扩展性。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" height="373" src="https://www.biaodianfu.com/wp-content/uploads/2024/11/apache-paimon.png" width="950"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;目前Apache Paimon提供以下核心能力：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;基于HDFS或者对象存储构建低成本的轻量级数据湖存储服务。&lt;/li&gt;
  &lt;li&gt;支持在流模式与批模式下读写大规模数据集。&lt;/li&gt;
  &lt;li&gt;支持分钟级到秒级数据新鲜度的批查询和OLAP查询。&lt;/li&gt;
  &lt;li&gt;支持消费与产生增量数据，可作为传统数仓与流式数仓的各级存储。&lt;/li&gt;
  &lt;li&gt;支持预聚合数据，降低存储成本与下游计算压力。&lt;/li&gt;
  &lt;li&gt;支持历史版本回溯。&lt;/li&gt;
  &lt;li&gt;支持高效的数据过滤。&lt;/li&gt;
  &lt;li&gt;支持表结构变更。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;应用场景&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;实时数据分析：适用于需要对流数据进行实时分析的场景，如金融交易分析、实时用户行为分析等。&lt;/li&gt;
  &lt;li&gt;大规模数据处理：适合需要处理和存储大规模数据的企业，提供高效的数据存储和查询能力。&lt;/li&gt;
  &lt;li&gt;数据湖和数据仓库集成：可以作为数据湖的一部分，与传统数据仓库系统集成，为数据分析提供灵活的解决方案。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;社区和发展&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;Apache Paimon 是 Apache 软件基金会下的一个开源项目，受益于活跃的开发者社区和用户群体。其持续的发展和更新，使其不断适应大数据领域的新需求和新挑战。&lt;/p&gt;
 &lt;h2&gt;paimon的生态系统&lt;/h2&gt;
 &lt;p&gt;Apache Paimon 的生态系统设计旨在与现有的大数据处理框架和工具无缝集成，从而提供灵活性和易用性。以下是关于 Paimon 在兼容性和集成方面的一些细节：&lt;/p&gt;
 &lt;h3&gt;兼容性&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;与 Hadoop 的兼容性：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;存储兼容性：Paimon 可以部署在 Hadoop 兼容的存储系统上，比如 HDFS。这使得用户可以利用现有的 Hadoop 基础设施来存储和管理数据。&lt;/li&gt;
  &lt;li&gt;生态系统工具支持：Paimon 可以与 Hadoop 生态系统中的其他工具（如 Hive）集成，支持在这些工具中查询和处理 Paimon 存储的数据。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;与 Spark 的兼容性：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;数据源和数据接收器：Paimon 提供了与 Apache Spark 的集成，允许 Spark 任务将数据写入 Paimon 或从 Paimon 读取数据。通过 Spark 的 DataFrame API，用户可以方便地对 Paimon 数据进行复杂的批处理分析。&lt;/li&gt;
  &lt;li&gt;流处理支持：Paimon 的流数据更新能力可以与 Spark Streaming 集成，实现实时数据处理。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;与 Flink 的兼容性：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;深度集成：Paimon 与 Apache Flink 的深度集成是其一大特色。Flink 用户可以使用 Paimon 作为流式和批处理作业的存储层，利用 Flink 强大的流处理能力直接对 Paimon 数据进行操作。&lt;/li&gt;
  &lt;li&gt;统一 API 支持：通过 Flink 的 Table API 和 SQL，用户可以在 Paimon 数据上执行统一的批处理和流处理任务。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;集成&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;与大数据处理框架的集成：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Paimon 提供了与多种大数据处理框架的连接器和 API，使得这些框架可以轻松地将数据读写到 Paimon。开发者可以通过标准的 API 和连接器将 Paimon 纳入现有的数据处理管道。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;与数据湖和数据仓库的集成：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Paimon 可以作为数据湖的一部分，与其他数据湖技术（如 Delta Lake 或 Apache Iceberg）共同使用，提供统一的存储和管理能力。&lt;/li&gt;
  &lt;li&gt;通过与数据仓库系统的集成，Paimon 可以支持更复杂的分析和查询需求。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;可扩展的插件体系：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Paimon 支持插件机制，允许用户和开发者根据具体需求扩展其功能。这种灵活性使得 Paimon 能够适应多种应用场景和技术栈。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;通过与这些大数据生态系统的兼容性和集成能力，Apache Paimon 提供了一种灵活而强大的解决方案，能够在不改变现有基础设施的情况下提升数据处理能力。&lt;/p&gt;
 &lt;h2&gt;Paimon的核心概念&lt;/h2&gt;
 &lt;p&gt;Apache Paimon 是一种专为流处理和批处理设计的数据湖存储系统，其数据存储设计旨在提供高效的数据读写、更新和删除操作。&lt;/p&gt;
 &lt;h3&gt;数据存储格式&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;列式存储：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Paimon 采用列式存储格式，类似于 Apache Parquet 或 ORC。这种格式有助于提高查询性能，特别是在需要扫描大量数据但只访问部分列的情况下。&lt;/li&gt;
  &lt;li&gt;列式存储也支持更高效的数据压缩，从而降低存储成本。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;分区和分桶：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;数据可以按特定的字段进行分区和分桶。这种方式有助于提高数据的访问速度，因为查询可以更快地定位到相关的数据分区或分桶。&lt;/li&gt;
  &lt;li&gt;分区和分桶策略可以根据数据访问模式进行配置，以优化性能。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;数据更新和删除&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;增量更新&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Paimon 支持高效的增量更新，这意味着可以在不重写整个数据集的情况下对数据进行更新。这对于需要频繁更新的数据集（如实时数据）非常重要。&lt;/li&gt;
  &lt;li&gt;通过维护数据的增量变化，Paimon 可以快速地应用更新而不影响整体性能。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;删除操作&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;支持基于条件的删除操作，允许用户删除符合特定条件的数据。&lt;/li&gt;
  &lt;li&gt;Paimon 通过维护有效数据的快照来管理删除操作，这样可以在不影响读取性能的情况下安全地删除数据。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;事务一致性&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;ACID 事务&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Paimon 提供了 ACID 事务支持，确保数据操作的原子性、一致性、隔离性和持久性。&lt;/li&gt;
  &lt;li&gt;事务支持使得用户可以安全地进行并发数据操作，而无需担心数据不一致的问题。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;快照机制&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Paimon 使用快照机制来管理数据版本和事务。这种机制允许用户查看和回滚到特定时间点的数据状态。&lt;/li&gt;
  &lt;li&gt;快照机制也有助于实现数据的时间旅行查询（Time Travel Query），用户可以查询历史数据状态。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;元数据管理&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;元数据存储&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Paimon 的元数据可以存储在多种后端，包括文件系统和数据库。元数据存储用于管理表结构、分区信息和快照等。&lt;/li&gt;
  &lt;li&gt;高效的元数据管理使得 Paimon 可以在大规模数据集上提供快速的查询和更新。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;Schema 演变&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Paimon 支持 Schema 演变，允许用户在不影响现有数据的情况下修改表结构。这种灵活性对于需要不断调整数据模型的应用非常有用。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;数据读写性能&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;高效的读取&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Paimon 的列式存储和分区策略使得读取操作非常高效，特别是在只需访问部分列或特定分区时。&lt;/li&gt;
  &lt;li&gt;支持向量化查询处理，进一步提高读取性能。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;写入优化&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;通过批量写入和增量更新机制，Paimon 优化了写入性能，减少了 I/O 开销。&lt;/li&gt;
  &lt;li&gt;支持流式数据写入，使其适合实时数据处理场景。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;索引&lt;/h3&gt;
 &lt;p&gt;索引是提高数据查询性能的有效工具。在 Paimon 中，虽然具体的索引机制可能依赖于底层的存储和计算引擎，但一般支持以下几种常见的索引类型：&lt;/p&gt;
 &lt;p&gt;主键索引：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;主键索引用于快速定位特定的记录。对于需要频繁进行更新和删除操作的表，主键索引是非常有用的。&lt;/li&gt;
  &lt;li&gt;使用场景：主键索引适用于需要快速检索单条记录的场景，如根据订单 ID 查询订单详情。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;二级索引：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;二级索引用于加速非主键列的查询。它允许在非主键列上进行高效的查找操作。&lt;/li&gt;
  &lt;li&gt;使用场景：在频繁按某个非主键字段进行过滤查询时，二级索引可以显著提高性能。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;分区索引：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;分区本身可以视作一种粗粒度的索引，通过将数据按某个字段分区，可以快速定位到相关的数据块。&lt;/li&gt;
  &lt;li&gt;使用场景：按时间或地理位置等字段进行查询时，分区索引可以减少扫描的数据量。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;缓存机制&lt;/h3&gt;
 &lt;p&gt;缓存机制通过在内存中存储数据的部分或全部，提高数据访问速度，减少对磁盘的 I/O 操作。&lt;/p&gt;
 &lt;p&gt;查询结果缓存：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;查询结果缓存是指将经常访问的查询结果存储在内存中，以便在重复查询时可以直接返回缓存结果，而无需重新计算。&lt;/li&gt;
  &lt;li&gt;使用场景：适用于经常重复执行相同查询的场景，如报表生成或仪表盘展示。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;数据块缓存：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;数据块缓存涉及将常用的数据块（如列块或行块）缓存到内存中，以加快读取速度。&lt;/li&gt;
  &lt;li&gt;使用场景：对于那些访问频率高的数据集，数据块缓存可以显著减少磁盘 I/O。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;元数据缓存：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;元数据缓存用于存储表结构、分区信息等元数据，以减少查询时的元数据加载时间。&lt;/li&gt;
  &lt;li&gt;使用场景：在大规模数据环境中，元数据缓存可以加快查询计划的生成。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;Paimon的使用&lt;/h2&gt;
 &lt;h3&gt;Paimon表的创建&lt;/h3&gt;
 &lt;p&gt;创建 Apache Paimon 表通常需要通过 SQL 语句来完成。Paimon 支持标准的 SQL 语法，可以使用各种计算框架（如 Apache Flink 或 Apache Spark）来执行这些 SQL 语句。&lt;/p&gt;
 &lt;h4&gt;使用 Apache Flink 创建 Paimon 表&lt;/h4&gt;
 &lt;p&gt;步骤：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;设置 Flink 环境：确保 Flink 已正确安装并配置好。确保 Flink 可以访问 Paimon 存储路径。&lt;/li&gt;
  &lt;li&gt;编写 Flink 作业：使用 Flink 的 Table API 或 SQL API 来创建 Paimon 表。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;示例代码：&lt;/p&gt;
 &lt;pre&gt;import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;

public class CreatePaimonTable {
    public static void main(String[] args) throws Exception {
        // 设置 Flink 执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);

        // 创建 Paimon 表
        tableEnv.executeSql(
            &amp;quot;CREATE TABLE paimon_table (&amp;quot; +
            &amp;quot;  id INT, &amp;quot; +
            &amp;quot;  name STRING, &amp;quot; +
            &amp;quot;  age INT, &amp;quot; +
            &amp;quot;  PRIMARY KEY (id) NOT ENFORCED&amp;quot; +
            &amp;quot;) WITH (&amp;quot; +
            &amp;quot;  &amp;apos;connector&amp;apos; = &amp;apos;paimon&amp;apos;,&amp;quot; +
            &amp;quot;  &amp;apos;path&amp;apos; = &amp;apos;path/to/paimon/table&amp;apos;&amp;quot; +
            &amp;quot;)&amp;quot;
        );

        // 打印表信息
        tableEnv.executeSql(&amp;quot;DESCRIBE paimon_table&amp;quot;).print();

        // 执行作业
        env.execute(&amp;quot;Create Paimon Table&amp;quot;);
    }
}
&lt;/pre&gt;
 &lt;h4&gt;使用 Apache Spark 创建 Paimon 表&lt;/h4&gt;
 &lt;p&gt;步骤：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;设置 Spark 环境：确保 Spark 已正确安装并配置好。确保 Spark 可以访问 Paimon 存储路径。&lt;/li&gt;
  &lt;li&gt;编写 PySpark 脚本：使用 PySpark 的 DataFrame API 或 SQL API 来创建 Paimon 表。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;示例代码：&lt;/p&gt;
 &lt;pre&gt;from pyspark.sql import SparkSession

# 创建 SparkSession
spark = SparkSession.builder \
    .appName(&amp;quot;Create Paimon Table&amp;quot;) \
    .config(&amp;quot;spark.jars.packages&amp;quot;, &amp;quot;&amp;lt;paimon-connector-package&amp;gt;&amp;quot;) \
    .getOrCreate()

# 创建 Paimon 表
spark.sql(&amp;quot;&amp;quot;&amp;quot;
    CREATE TABLE paimon_table (
        id INT,
        name STRING,
        age INT,
        PRIMARY KEY (id) NOT ENFORCED
    ) USING paimon
    OPTIONS (
        path &amp;apos;path/to/paimon/table&amp;apos;
    )
&amp;quot;&amp;quot;&amp;quot;)

# 打印表信息
spark.sql(&amp;quot;DESCRIBE paimon_table&amp;quot;).show()

# 停止 SparkSession
spark.stop()
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;注意事项&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;路径配置：path参数指定了 Paimon 表在文件系统中的存储路径。确保该路径是可写的，并且 Flink 或 Spark 有权限访问。&lt;/li&gt;
  &lt;li&gt;主键约束：PRIMARY KEY (id) NOT ENFORCED表示定义了一个主键，但不强制执行。Paimon 支持主键约束，但不强制执行可以提高写入性能。&lt;/li&gt;
  &lt;li&gt;连接器包：如果使用 Spark，确保在SparkSession 配置中指定了 Paimon 连接器包（&amp;lt;paimon-connector-package&amp;gt;）。这个包通常是通过 Maven 仓库提供的，需要查找并替换为实际的包名和版本。&lt;/li&gt;
  &lt;li&gt;Schema 设计：确保表的 Schema 设计合理，字段类型和名称符合业务需求。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;合理的分区&lt;/h3&gt;
 &lt;p&gt;在 Apache Paimon 中，对数据进行分区是一种有效的策略，可以提高查询性能和管理大规模数据集。分区允许将数据划分为更小的部分，使得查询可以更快地定位到相关的数据集，从而减少扫描的数据量。&lt;/p&gt;
 &lt;p&gt;分区是一种将数据集根据某些字段的值划分为多个逻辑部分的方式。每个分区包含特定字段值范围内的数据。常见的分区字段包括日期、地理位置或其他业务相关字段。&lt;/p&gt;
 &lt;h4&gt;如何设置分区&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;选择分区字段&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;选择合适的分区字段是分区策略的关键。通常选择那些在查询条件中经常使用的字段。&lt;/li&gt;
  &lt;li&gt;例如，对于时间序列数据，可以选择时间戳或日期字段进行分区。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;定义分区策略&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;在创建表时定义分区策略。Paimon 支持在表创建时指定分区字段。&lt;/li&gt;
  &lt;li&gt;例如，在 SQL 中创建一个分区表的语法如下：&lt;/li&gt;
&lt;/ul&gt;
 &lt;pre&gt;CREATE TABLE orders (
    order_id STRING,
    customer_id STRING,
    order_date DATE,
    amount DOUBLE
) PARTITIONED BY (order_date);
&lt;/pre&gt;
 &lt;p&gt;在这个例子中，order_date 字段被用作分区字段。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;数据写入和管理&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;当数据写入 Paimon 时，系统会根据定义的分区策略将数据分配到相应的分区中。&lt;/li&gt;
  &lt;li&gt;Paimon 自动管理分区的创建和维护，用户不需要手动管理分区文件或目录。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;分区的优点&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;提高查询性能：
   &lt;ul&gt;
    &lt;li&gt;通过限制查询扫描的分区数量，可以显著提高查询性能。例如，当查询条件包含分区字段时，系统只需扫描相关的分区。&lt;/li&gt;
    &lt;li&gt;这种优化特别适用于大规模数据集。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;简化数据管理：
   &lt;ul&gt;
    &lt;li&gt;分区使得数据管理更加简化。用户可以对单个分区执行操作（如删除或归档），而不影响其他分区的数据。&lt;/li&gt;
    &lt;li&gt;这在数据生命周期管理和存储优化方面非常有用。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;支持增量处理：
   &lt;ul&gt;
    &lt;li&gt;分区策略还可以帮助实现增量数据处理。例如，可以通过处理新的或特定的分区来实现增量更新或批量操作。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;分区的注意事项&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;分区数量：过多的分区可能导致元数据管理开销增加。因此，选择合适的分区粒度非常重要。&lt;/li&gt;
  &lt;li&gt;动态分区：对于某些场景，动态分区（即基于数据内容自动创建分区）可能是有用的，但也需要小心管理以避免过多的分区。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;数据导入Paimon&lt;/h3&gt;
 &lt;p&gt;将数据导入到 Apache Paimon 中，通常需要通过与大数据处理框架（如 Apache Flink 或 Apache Spark）的集成来实现。这是因为 Paimon 本身是一个数据湖存储系统，通常需要借助计算框架来进行数据的读写操作。以下是几种常见的方法：&lt;/p&gt;
 &lt;h4&gt;使用 Apache Flink 导入数据&lt;/h4&gt;
 &lt;p&gt;Apache Flink 是与 Paimon 集成最紧密的流处理框架。你可以通过 Flink 作业将数据导入到 Paimon。&lt;/p&gt;
 &lt;p&gt;步骤：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;设置 Flink 环境：确保 Flink 已正确安装并配置好。&lt;/li&gt;
  &lt;li&gt;配置 Paimon 表：在 Paimon 中创建一个目标表，定义表的 Schema（字段名称、类型等）。&lt;/li&gt;
  &lt;li&gt;编写 Flink 作业：编写一个 Flink 作业，使用 Paimon 提供的连接器来读取源数据（例如从 Kafka、文件系统、数据库等），并将其写入 Paimon 表。在 Flink 作业中，指定 Paimon 表的路径和配置。&lt;/li&gt;
  &lt;li&gt;运行 Flink 作业：提交并运行 Flink 作业，将数据流式写入 Paimon。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;使用 Apache Spark 导入数据&lt;/h4&gt;
 &lt;p&gt;Apache Spark 也是一个常用的数据处理框架，可以用于将批处理数据导入到 Paimon。&lt;/p&gt;
 &lt;p&gt;步骤：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;设置 Spark 环境：确保 Spark 已正确安装并配置好。&lt;/li&gt;
  &lt;li&gt;配置 Paimon 表：在 Paimon 中创建一个目标表。&lt;/li&gt;
  &lt;li&gt;编写 Spark 作业：使用 Spark 的 DataFrame API 读取源数据。使用 Paimon 的 Spark 连接器，将 DataFrame 写入到 Paimon 表。&lt;/li&gt;
  &lt;li&gt;运行 Spark 作业：提交并运行 Spark 作业，完成数据导入。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;使用命令行工具&lt;/h4&gt;
 &lt;p&gt;如果 Paimon 提供了命令行工具，你也可以直接使用这些工具将数据导入到 Paimon。&lt;/p&gt;
 &lt;p&gt;步骤：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;准备数据文件：准备好需要导入的数据文件，通常是 CSV、JSON 等格式。&lt;/li&gt;
  &lt;li&gt;使用命令行工具：使用 Paimon 提供的命令行工具，指定数据文件路径和目标表路径，将数据导入。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;使用 API&lt;/h4&gt;
 &lt;p&gt;如果需要更高的灵活性或集成到自定义应用程序中，你可以使用 Paimon 的 Java API 或其他语言支持的 API。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;步骤：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;编写代码：使用 Paimon 提供的 API，编写代码来读取源数据并写入 Paimon 表。&lt;/li&gt;
  &lt;li&gt;运行程序：编译并运行你的程序，将数据导入到 Paimon。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;注意事项&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Schema 设计：确保 Paimon 表的 Schema 与源数据的结构匹配。&lt;/li&gt;
  &lt;li&gt;数据格式：确认源数据格式与 Paimon 支持的格式兼容。&lt;/li&gt;
  &lt;li&gt;性能优化：根据数据量和集群配置，适当调整作业的并行度和资源分配，以提高导入性能。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;MySQL数据同步paimon示例&lt;/h4&gt;
 &lt;p&gt;要将 MySQL 的 binlog 数据导入到 Apache Paimon 中，你可以使用 Apache Flink 作为数据处理引擎，因为 Flink 提供了对 MySQL binlog 的良好支持。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;配置 MySQL binlog&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;启用 binlog：在 MySQL 配置文件中启用 binlog。&lt;/p&gt;
 &lt;pre&gt;[mysqld]
log-bin=mysql-bin
server-id=1
binlog-format=ROW
&lt;/pre&gt;
 &lt;p&gt;创建用户：为 Flink 创建一个用户，具有读取 binlog 的权限。&lt;/p&gt;
 &lt;pre&gt;CREATE USER &amp;apos;flink&amp;apos;@&amp;apos;%&amp;apos; IDENTIFIED BY &amp;apos;password&amp;apos;;
GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO &amp;apos;flink&amp;apos;@&amp;apos;%&amp;apos;;
FLUSH PRIVILEGES;
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;编写 Flink 作业&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;使用 Flink 的 MySQL CDC（Change Data Capture）连接器来读取 MySQL binlog 数据，并将其写入 Paimon。&lt;/p&gt;
 &lt;p&gt;Flink 作业示例：&lt;/p&gt;
 &lt;p&gt;依赖配置：确保在 Flink 项目中添加 MySQL CDC 连接器和 Paimon 连接器的依赖。&lt;/p&gt;
 &lt;p&gt;作业代码：&lt;/p&gt;
 &lt;pre&gt;import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.streaming.api.datastream.DataStream;
import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
import org.apache.flink.table.api.Table;
import org.apache.flink.connector.mysql.cdc.MySQLSource;
import org.apache.flink.connector.mysql.cdc.config.MySQLSourceConfigFactory;
import org.apache.flink.types.Row;

public class MySQLToPaimon {
    public static void main(String[] args) throws Exception {
        // 设置 Flink 执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);

        // 配置 MySQL Source
        MySQLSourceConfigFactory configFactory = MySQLSourceConfigFactory.newBuilder()
                .hostname(&amp;quot;your-mysql-host&amp;quot;)
                .port(3306)
                .databaseList(&amp;quot;your-database&amp;quot;)
                .tableList(&amp;quot;your-database.your-table&amp;quot;)
                .username(&amp;quot;flink&amp;quot;)
                .password(&amp;quot;password&amp;quot;)
                .build();

        MySQLSource&amp;lt;String&amp;gt; mySQLSource = MySQLSource.&amp;lt;String&amp;gt;builder()
                .hostname(&amp;quot;your-mysql-host&amp;quot;)
                .port(3306)
                .databaseList(&amp;quot;your-database&amp;quot;)
                .tableList(&amp;quot;your-database.your-table&amp;quot;)
                .username(&amp;quot;flink&amp;quot;)
                .password(&amp;quot;password&amp;quot;)
                .deserializer(new StringDebeziumDeserializationSchema())
                .build();

        // 读取 binlog 数据
        DataStream&amp;lt;String&amp;gt; mySQLStream = env.addSource(mySQLSource);

        // 将数据转换为表
        Table mySQLTable = tableEnv.fromDataStream(mySQLStream);

        // 写入 Paimon
        tableEnv.executeSql(
            &amp;quot;CREATE TABLE paimon_table (...) WITH (...)&amp;quot;
        );

        mySQLTable.executeInsert(&amp;quot;paimon_table&amp;quot;);

        // 执行作业
        env.execute(&amp;quot;MySQL Binlog to Paimon&amp;quot;);
    }
}
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;运行 Flink 作业&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;编译和打包：将 Flink 作业代码编译并打包成 JAR 文件。&lt;/li&gt;
  &lt;li&gt;提交作业：使用 Flink 提供的命令行工具或 Web UI 将作业提交到 Flink 集群。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;验证数据导入&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;检查 Paimon 表：在 Paimon 中检查数据是否正确导入。&lt;/li&gt;
  &lt;li&gt;监控作业：使用 Flink 的监控工具，确保作业正常运行，没有报错。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;注意事项&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;数据格式和 Schema：确保 MySQL 表的 Schema 与 Paimon 表的 Schema 一致。&lt;/li&gt;
  &lt;li&gt;错误处理：考虑添加错误处理机制，以便在读取 binlog 或写入 Paimon 过程中出现问题时能及时响应。&lt;/li&gt;
  &lt;li&gt;性能优化：根据数据量和集群配置，调整 Flink 作业的并行度和资源分配，以提高性能。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;Paimon数据查询&lt;/h3&gt;
 &lt;p&gt;查询 Apache Paimon 中的数据通常需要借助与之集成的计算框架，如 Apache Flink 或 Apache Spark。这些框架提供了灵活的查询能力，可以对存储在 Paimon 中的数据进行分析和处理。&lt;/p&gt;
 &lt;h4&gt;使用 Apache Flink 查询 Paimon 数据&lt;/h4&gt;
 &lt;p&gt;Flink 提供了流处理和批处理的能力，可以通过 SQL 或 Table API 来查询 Paimon 中的数据。&lt;/p&gt;
 &lt;p&gt;步骤：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;设置 Flink 环境：确保 Flink 已正确安装并配置好，并且可以访问 Paimon 存储。&lt;/li&gt;
  &lt;li&gt;编写 Flink SQL 查询：使用 Flink 的 Table API 或 SQL API 来查询 Paimon 中的数据。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;示例代码：&lt;/p&gt;
 &lt;pre&gt;import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
import org.apache.flink.table.api.Table;

public class PaimonQuery {
    public static void main(String[] args) throws Exception {
        // 设置 Flink 执行环境
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();
        StreamTableEnvironment tableEnv = StreamTableEnvironment.create(env);

        // 注册 Paimon 表
        tableEnv.executeSql(
            &amp;quot;CREATE TABLE paimon_table (&amp;quot; +
            &amp;quot;  id INT, &amp;quot; +
            &amp;quot;  name STRING, &amp;quot; +
            &amp;quot;  age INT&amp;quot; +
            &amp;quot;) WITH (&amp;quot; +
            &amp;quot;  &amp;apos;connector&amp;apos; = &amp;apos;paimon&amp;apos;,&amp;quot; +
            &amp;quot;  &amp;apos;path&amp;apos; = &amp;apos;path/to/paimon/table&amp;apos;&amp;quot; +
            &amp;quot;)&amp;quot;
        );

        // 执行查询
        Table result = tableEnv.sqlQuery(&amp;quot;SELECT * FROM paimon_table WHERE age &amp;gt; 30&amp;quot;);

        // 输出查询结果
        tableEnv.toChangelogStream(result).print();

        // 执行作业
        env.execute(&amp;quot;Paimon Query&amp;quot;);
    }
}
&lt;/pre&gt;
 &lt;h4&gt;使用 Apache Spark 查询 Paimon 数据&lt;/h4&gt;
 &lt;p&gt;Spark 也可以通过 DataFrame API 或 SQL 来查询 Paimon 中的数据。&lt;/p&gt;
 &lt;p&gt;步骤：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;设置 Spark 环境：确保 Spark 已正确安装并配置好，并且可以访问 Paimon 存储。&lt;/li&gt;
  &lt;li&gt;编写 Spark SQL 查询：使用 Spark 的 DataFrame API 或 SQL API 来查询 Paimon 中的数据。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;示例代码：&lt;/p&gt;
 &lt;pre&gt;import org.apache.spark.sql.SparkSession

object PaimonQuery {
  def main(args: Array[String]): Unit = {
    val spark = SparkSession.builder()
      .appName(&amp;quot;Paimon Query&amp;quot;)
      .getOrCreate()

    // 读取 Paimon 表
    val paimonDF = spark.read
      .format(&amp;quot;paimon&amp;quot;)
      .load(&amp;quot;path/to/paimon/table&amp;quot;)

    // 执行查询
    val result = paimonDF.filter(&amp;quot;age &amp;gt; 30&amp;quot;)

    // 显示查询结果
    result.show()

    spark.stop()
  }
}
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;注意事项&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Schema 一致性：确保查询中使用的 Schema 与 Paimon 表的 Schema 一致。&lt;/li&gt;
  &lt;li&gt;性能优化：根据查询的复杂度和数据量，调整 Flink 或 Spark 的资源配置以优化查询性能。&lt;/li&gt;
  &lt;li&gt;集成配置：在使用 Flink 或 Spark 进行查询时，确保正确配置了与 Paimon 的连接器。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;PySpark查询Paimon表示例&lt;/h4&gt;
 &lt;p&gt;要使用 PySpark 查询 Apache Paimon 中的数据，你需要确保 Paimon 和 Spark 环境已正确配置，并且可以通过 Spark SQL 或 DataFrame API 来访问和查询 Paimon 中的数据。以下是一个详细的指南，帮助你在 PySpark 中查询 Paimon 数据：&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;环境准备&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Spark 安装：确保已经安装并配置好 Apache Spark，并且可以使用 PySpark。&lt;/li&gt;
  &lt;li&gt;Paimon 连接器：确保 Spark 能够访问 Paimon 的数据存储路径，并配置好必要的连接器（如果需要）。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;启动 PySpark Shell 或 编写 PySpark 脚本&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;你可以选择在 PySpark Shell 中直接运行命令，或者编写一个独立的 PySpark 脚本。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;使用 PySpark Shell&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;pyspark --packages &amp;lt;paimon-connector-package&amp;gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;编写 PySpark 脚本&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;from pyspark.sql import SparkSession

# 创建 SparkSession
spark = SparkSession.builder \
    .appName(&amp;quot;Paimon Query&amp;quot;) \
    .config(&amp;quot;spark.jars.packages&amp;quot;, &amp;quot;&amp;lt;paimon-connector-package&amp;gt;&amp;quot;) \
    .getOrCreate()

# 读取 Paimon 表
paimon_df = spark.read \
    .format(&amp;quot;paimon&amp;quot;) \
    .load(&amp;quot;path/to/paimon/table&amp;quot;)

# 执行查询
result_df = paimon_df.filter(paimon_df.age &amp;gt; 30)

# 显示查询结果
result_df.show()

# 停止 SparkSession
spark.stop()
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;运行脚本或命令&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;如果使用的是 PySpark Shell，直接在 Shell 中输入相应的命令。&lt;/li&gt;
  &lt;li&gt;如果是独立的 PySpark 脚本，使用 Spark 提供的命令行工具运行脚本：spark-submit –packages &amp;lt;paimon-connector-package&amp;gt; your_script.py&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;注意事项&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Paimon 连接器：在启动 PySpark Shell 或运行 PySpark 脚本时，需要指定 Paimon 的连接器包。这个包可能是通过 Maven 仓库提供的，你需要查找并替换 &amp;lt;paimon-connector-package&amp;gt; 为实际的包名和版本。&lt;/li&gt;
  &lt;li&gt;数据路径：确保 load(“path/to/paimon/table”) 中的路径正确指向 Paimon 中存储数据的实际路径。&lt;/li&gt;
  &lt;li&gt;Schema 一致性：在编写查询时，确保使用的字段名和类型与 Paimon 表的 Schema 保持一致。&lt;/li&gt;
  &lt;li&gt;性能优化：根据数据量和查询复杂度，调整 Spark 的资源配置（如执行器数量和内存）以提高查询性能。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;Paimon数据版本管理&lt;/h3&gt;
 &lt;p&gt;Apache Paimon 提供了强大的数据版本管理和时间旅行功能，这些功能对于数据分析和管理非常有用，特别是在需要审计、调试或回溯历史数据时。以下是对 Paimon 数据版本控制和时间旅行功能的详细介绍：&lt;/p&gt;
 &lt;h4&gt;数据版本控制&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;版本化数据存储&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Paimon 采用版本化的数据存储机制，每次对数据的更改（如插入、更新或删除）都会生成一个新的版本。&lt;/li&gt;
  &lt;li&gt;这些版本通过快照（Snapshot）进行管理，每个快照代表数据在某一时间点的状态。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;快照管理&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;快照是 Paimon 版本控制的核心。每个快照都有一个唯一的标识符和时间戳，记录了自上一个快照以来的数据变化。&lt;/li&gt;
  &lt;li&gt;快照可以用于回滚数据到某个历史状态，或用于审计和分析。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;增量更新&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;通过维护数据的增量变化，Paimon 可以高效地管理版本。只需存储和处理自上一个版本以来的变化，而不必复制整个数据集。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;时间旅行功能&lt;/h4&gt;
 &lt;p&gt;  &lt;strong&gt;时间旅行查询&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;时间旅行功能允许用户查询数据在过去某一时间点的状态。通过指定快照 ID 或时间戳，用户可以检索历史数据。&lt;/li&gt;
  &lt;li&gt;这对于需要调试数据问题、执行回溯分析或验证数据变化的场景非常有用。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;SQL 支持&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Paimon 支持通过 SQL 语句执行时间旅行查询。用户可以使用特定的语法指定要查询的快照或时间。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;示例查询语法：&lt;/p&gt;
 &lt;p&gt;SELECT * FROM table_name FOR SYSTEM_TIME AS OF ‘2023-01-01 10:00:00’;&lt;/p&gt;
 &lt;p&gt;在这个例子中，查询将返回数据在指定时间点的状态。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;快照导航&lt;/strong&gt;：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;用户可以列出所有可用的快照，并选择特定的快照进行查询。这种导航能力使得用户可以方便地找到需要的历史版本。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;实践中的应用&lt;/h4&gt;
 &lt;ul&gt;
  &lt;li&gt;数据审计：可以使用时间旅行功能查看数据在某个历史时间点的状态，以满足审计和合规性要求。&lt;/li&gt;
  &lt;li&gt;错误回溯：在发现数据错误时，通过时间旅行功能回溯到错误发生之前的版本，从而帮助定位和修复问题。&lt;/li&gt;
  &lt;li&gt;变化分析：分析数据在不同时间点的变化趋势，以支持业务决策。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;管理和优化&lt;/h4&gt;
 &lt;ul&gt;
  &lt;li&gt;存储优化：虽然版本控制和时间旅行功能提供了极大的便利，但也会增加存储需求。可以通过配置保留策略，定期清理不再需要的历史版本来优化存储。&lt;/li&gt;
  &lt;li&gt;性能考虑：在执行时间旅行查询时，考虑到数据规模和查询复杂度，以确保查询性能符合要求。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;参考链接：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://paimon.apache.org/"&gt;Apache Paimon™&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://www.cnblogs.com/johnnyzen/p/18189005"&gt;[湖仓架构] Apache Paimon核心原理 – 千千寰宇 – 博客园 (cnblogs.com)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://www.cnblogs.com/itxiaoshen/p/17604141.html"&gt;新一代开源流数据湖平台Apache Paimon入门实操-上 – itxiaoshen – 博客园 (cnblogs.com)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://juejin.cn/post/7260389317176361016"&gt;当流计算邂逅数据湖：Paimon 的前生今世希望通过笔者以下的经历，回顾流计算一步一步扩大场景的过程，并引出 Apach – 掘金 (juejin.cn)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://www.alibabacloud.com/help/zh/flink/use-cases/build-a-streaming-data-warehouse-based-on-flink-and-apache-paimon"&gt;基于Flink+Paimon搭建流式湖仓 – 实时计算Flink版 – 阿里云 (alibabacloud.com)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;div&gt;

  &lt;h3&gt;相关文章:&lt;/h3&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/hive-sql-guide.html" rel="bookmark" title="Hive SQL&amp;#31995;&amp;#32479;&amp;#21270;&amp;#23398;&amp;#20064;"&gt;Hive SQL系统化学习&lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/spark-sql.html" rel="bookmark" title="Spark SQL &amp;#31995;&amp;#32479;&amp;#21270;&amp;#23398;&amp;#20064;"&gt;Spark SQL 系统化学习&lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/python-connect-database.html" rel="bookmark" title="Python&amp;#22914;&amp;#20309;&amp;#36830;&amp;#25509;&amp;#25968;&amp;#25454;&amp;#24211;"&gt;Python如何连接数据库&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>器→工具 开源项目 大数据 数据仓库 数据湖</category>
      <guid isPermaLink="true">https://itindex.net/detail/62956-%E6%95%B0%E6%8D%AE-%E7%B3%BB%E7%BB%9F-paimon</guid>
      <pubDate>Wed, 06 Nov 2024 21:53:23 CST</pubDate>
    </item>
    <item>
      <title>Python地理数据分析工具MovingPandas</title>
      <link>https://itindex.net/detail/62944-python-%E5%9C%B0%E7%90%86-%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90</link>
      <description>&lt;p&gt;MovingPandas 是一个用于分析轨迹数据的 Python 库。它在处理和分析移动对象的时空数据方面非常强大，适用于地理信息系统（GIS）、时空数据分析和可视化等领域。它是在热门的地理数据处理库 GeoPandas 的基础上构建的，GeoPandas 本身是建立在Pandas数据处理库之上的。MovingPandas 旨在提供高效、易于使用的工具，以便分析和处理包含位置信息的时间序列数据。MovingPandas使得研究移动模式、路径分析、时空聚类等任务变得更加高效和直观。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="719" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/MovingPandas.png" width="977"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;核心功能：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;轨迹数据表示。MovingPandas 使用 GeoPandas GeoDataFrames 来表示轨迹数据。每条轨迹由一系列带有时间戳的点组成，形成一条时空路径。&lt;/li&gt;
  &lt;li&gt;轨迹分割。可以根据时间间隔、距离阈值等条件将轨迹分割成多个子轨迹。这对于处理长轨迹或者在某些关键事件发生前后进行分析非常有用。&lt;/li&gt;
  &lt;li&gt;轨迹特征提取。提供了多种方法来计算轨迹的特征，比如速度、加速度、方向变化等。这些特征在进行模式识别和行为分析时非常有用。&lt;/li&gt;
  &lt;li&gt;轨迹聚类。支持基于轨迹的聚类分析，可以识别出类似移动模式的轨迹群体。常用的聚类方法包括基于密度的聚类（DBSCAN）、分层聚类等。&lt;/li&gt;
  &lt;li&gt;轨迹可视化。通过与 Matplotlib 和 Folium 等库的集成，MovingPandas 能够提供强大的轨迹数据可视化功能，包括静态和交互式地图。&lt;/li&gt;
  &lt;li&gt;时空聚合。支持时空聚合分析，比如计算某个区域在特定时间段内的平均速度、轨迹数量等。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;MovingPandas的使用&lt;/h2&gt;
 &lt;h3&gt;MovingPandas的安装&lt;/h3&gt;
 &lt;p&gt;MovingPandas作者推荐在Python 3.7及以上环境下安装。请确保你的Python版本符合这一要求。如果你已经安装了Anaconda，可以使用conda命令来安装MovingPandas及其依赖包。&lt;/p&gt;
 &lt;pre&gt;conda install -c conda-forge movingpandas&lt;/pre&gt;
 &lt;p&gt;MovingPandas同样可以使用pip进行安装，但是不推荐，主要原因是其依赖环境较为复杂，使用pip安装可能会出现依赖项缺失或版本冲突的问题。因此，推荐使用conda进行安装。&lt;/p&gt;
 &lt;h3&gt;MovingPandas接口详解&lt;/h3&gt;
 &lt;h4&gt;MovingPandas.Trajectory对象&lt;/h4&gt;
 &lt;p&gt;在 MovingPandas 中，Trajectory 类是核心组件之一，主要用于表示和处理单个轨迹。Trajectory 对象是一个时间序列的集合，其中每个数据点代表轨迹上的一个位置，包含了位置信息（经纬度或其他地理空间参考）、时间戳和其他可能的属性（如速度、方向等）。因此，一个 Trajectory 对象是连续移动的点组成的线，这些点按照时间顺序排列。&lt;/p&gt;
 &lt;p&gt;Trajectory 对象的主要特性：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;时间索引：Trajectory 对象的索引通常是时间戳，这使得基于时间的查询和操作变得直观和高效。&lt;/li&gt;
  &lt;li&gt;空间位置：每个时间点对应一个空间位置，这通常是通过经纬度坐标表示的。&lt;/li&gt;
  &lt;li&gt;其他属性：除了时间和位置，还可以包含其他相关的数据列，如速度、加速度、方向等，这些信息对于分析移动行为至关重要。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;创建 Trajectory 对象通常涉及几个步骤，首先你可能需要有一个包含时空数据的pandas DataFrame。这个DataFrame应该至少包含三列：表示时间戳的列（通常会被设置为索引）、表示X坐标的列（如经度）、表示Y坐标的列（如纬度）。然后，你可以使用 MovingPandas 提供的函数或方法（如TrajectoryCollection.from_geodataframe()）来创建一个或多个 Trajectory 对象。&lt;/p&gt;
 &lt;p&gt;class movingpandas.Trajectory(df, traj_id, traj_id_col=None, obj_id=None, t=None, x=None, y=None, crs=’epsg:4326′, parent=None)&lt;/p&gt;
 &lt;p&gt;参数说明：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;df：具有GeoPandas的geometry坐标列和时间戳索引的GeoDataFrame，或Pandas的DataFrame。必填参数。&lt;/li&gt;
  &lt;li&gt;traj_id：任意类型，表示轨迹的唯一标识符。必填参数。&lt;/li&gt;
  &lt;li&gt;obj_id：任意类型，表示移动物体的唯一标识符。默认为 None。&lt;/li&gt;
  &lt;li&gt;t：表示包含时间戳的列名，默认为 None。&lt;/li&gt;
  &lt;li&gt;x：表示包含x坐标的列名，使用Pandas的DataFrame需指定。默认为 None。&lt;/li&gt;
  &lt;li&gt;y：表示包含y坐标的列名，使用Pandas的DataFrame需指定。默认为 None。&lt;/li&gt;
  &lt;li&gt;crs：表示 x/y 坐标的坐标参考系统。默认为 “epsg:4326″，即 WGS84。&lt;/li&gt;
  &lt;li&gt;parent：一个Trajectory 对象，表示父轨迹。默认为 None。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;基本信息与操作&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;copy(): 返回轨迹对象的一个副本。&lt;/li&gt;
  &lt;li&gt;drop(**kwargs) 方法用于从数据集中删除满足特定条件的行或列。&lt;/li&gt;
  &lt;li&gt;plot(self, *args, **kwargs): 绘制轨迹。&lt;/li&gt;
  &lt;li&gt;explore(*args, **kwargs) 方法用于以交互方式可视化和分析数据，支持多种参数和选项以定制显示。&lt;/li&gt;
  &lt;li&gt;is_latlon() 方法用于判断轨迹数据是否采用经纬度坐标系。&lt;/li&gt;
  &lt;li&gt;is_valid() 方法用于检查轨迹数据是否有效，例如是否包含必要的字段和合理的坐标。&lt;/li&gt;
  &lt;li&gt;size() 方法用于返回轨迹中包含的定位点数量。&lt;/li&gt;
  &lt;li&gt;get_crs() 方法用于获取当前地理数据集的坐标参考系统（CRS），返回一个描述该坐标系的对象或信息。&lt;/li&gt;
  &lt;li&gt;to_crs(self, crs): 转换轨迹的坐标参考系统。&lt;/li&gt;
  &lt;li&gt;get_column_names() 方法用于获取数据集中的所有列名，返回一个包含列名的列表。这个方法通常用于快速查看数据集的结构或在进行数据处理时动态获取列名。&lt;/li&gt;
  &lt;li&gt;get_direction_col() 方法用于获取表示方向数据的列，这些数据通常以角度或方位形式存储。&lt;/li&gt;
  &lt;li&gt;get_distance_col() 方法用于获取表示距离数据的列，这些数据通常用于计算或分析两点之间的距离。&lt;/li&gt;
  &lt;li&gt;get_speed_col() 方法用于获取表示对象速度的列名。&lt;/li&gt;
  &lt;li&gt;get_timedelta_col() 方法用于获取表示时间增量的列名。&lt;/li&gt;
  &lt;li&gt;get_traj_id_col() 方法用于获取表示轨迹标识符的列名。&lt;/li&gt;
  &lt;li&gt;get_geom_col() 方法用于获取表示几何数据的列，该列通常包含地理空间信息，如点、线或多边形。&lt;/li&gt;
  &lt;li&gt;get_angular_difference_col() 方法用于获取包含角度差异的列，这些差异通常用于分析方向或角度变化。&lt;/li&gt;
  &lt;li&gt;to_point_gdf(self): 返回包含轨迹点的GeoDataFrame。&lt;/li&gt;
  &lt;li&gt;to_line_gdf(columns=None) 方法用于将轨迹数据转换为 GeoDataFrame 格式的线条几何数据，可以选择包含特定的列。&lt;/li&gt;
  &lt;li&gt;to_linestring() 方法用于将轨迹数据转换为 LineString 对象，表示轨迹的线条几何形状。&lt;/li&gt;
  &lt;li&gt;to_linestringm_wkt() 方法用于将轨迹数据转换为包含 ZM（高程和度量）信息的 WKT（Well-Known Text）格式的 LineStringM 字符串。&lt;/li&gt;
  &lt;li&gt;to_mf_json(datetime_to_str=True, temporal_columns=None) 方法用于将轨迹数据转换为 Moving Features JSON 格式，可以选择将日期时间转换为字符串，并指定时间相关的列。&lt;/li&gt;
  &lt;li&gt;to_point_gdf(return_orig_tz=False) 方法将轨迹数据转换为 GeoDataFrame 格式的点几何数据，可以选择返回原始时区的时间。&lt;/li&gt;
  &lt;li&gt;to_traj_gdf(wkt=False, agg=False) 方法将轨迹数据转换为 GeoDataFrame 格式，可以选择生成 WKT 格式的几何数据或进行聚合处理。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;轨迹分析与聚合统计&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;get_bbox(self): 返回轨迹的范围 (bounding box)。&lt;/li&gt;
  &lt;li&gt;get_start_location(self): 返回轨迹的起始位置。&lt;/li&gt;
  &lt;li&gt;get_end_location(self): 返回轨迹的结束位置。&lt;/li&gt;
  &lt;li&gt;get_start_time() 方法用于获取时间序列数据或对象轨迹的起始时间。&lt;/li&gt;
  &lt;li&gt;get_end_time() 方法用于获取某个事件或过程的结束时间，通常返回一个时间戳或日期时间对象。&lt;/li&gt;
  &lt;li&gt;get_max(column) 方法用于获取指定列 column 中的最大值。&lt;/li&gt;
  &lt;li&gt;get_min(column) 方法用于获取指定列 column 中的最小值。&lt;/li&gt;
  &lt;li&gt;get_position_at(t, method=’interpolated’) 方法用于获取在时间点 t 处的对象位置，默认使用插值方法来计算位置。&lt;/li&gt;
  &lt;li&gt;get_row_at(t, method=’nearest’) 方法用于获取在时间点 t 附近的对象所在的行，默认使用最近邻方法来选择行。&lt;/li&gt;
  &lt;li&gt;get_length(units=(None, None, None, None)) 方法用于计算并获取几何对象的长度，可以接受多个单位参数来指定长度的测量单位。&lt;/li&gt;
  &lt;li&gt;get_mcp() 方法用于获取某个对象的最小凸包 (Minimum Convex Polygon, MCP)，通常用于地理空间分析中确定一组点的最小包围区域。&lt;/li&gt;
  &lt;li&gt;add_direction(self, overwrite=False): 计算并添加方向信息到轨迹数据中。&lt;/li&gt;
  &lt;li&gt;get_direction() 方法用于计算和获取两个地理点之间的方向或方位角，通常以度数表示。&lt;/li&gt;
  &lt;li&gt;get_duration(self): 返回轨迹的总时长。&lt;/li&gt;
  &lt;li&gt;add_distance(overwrite=False, name=’distance’, units=None)：计算并添加轨迹数据中相邻点之间的距离信息。&lt;/li&gt;
  &lt;li&gt;add_acceleration(self, overwrite=False, name=’acceleration’): 计算并添加加速度信息到轨迹数据中。&lt;/li&gt;
  &lt;li&gt;add_speed(self, overwrite=False): 计算并添加速度信息到轨迹数据中。&lt;/li&gt;
  &lt;li&gt;add_angular_difference(overwrite=False, name=’angular_difference’)：计算并添加轨迹中相邻点之间的角度差异信息到轨迹数据中。&lt;/li&gt;
  &lt;li&gt;add_timedelta(overwrite=False, name=’timedelta’) ：计算并添加轨迹数据中相邻点之间的时间差信息。&lt;/li&gt;
  &lt;li&gt;add_traj_id(overwrite=False) 方法用于为轨迹数据添加或覆盖轨迹ID列，以标识相同轨迹中的所有点。&lt;/li&gt;
  &lt;li&gt;get_segment_between(t1, t2) 方法用于获取在时间点 t1 和 t2 之间的对象轨迹或数据段。&lt;/li&gt;
  &lt;li&gt;get_linestring_between(t1, t2, method=’interpolated’) 方法用于生成并获取在时间点 t1 和 t2 之间的一条线串，默认使用插值方法。&lt;/li&gt;
  &lt;li&gt;get_sampling_interval() 方法用于获取时间序列数据中的采样时间间隔。&lt;/li&gt;
  &lt;li&gt;hausdorff_distance(other, units=(None, None, None, None)) 方法用于计算当前轨迹与另一个轨迹之间的Hausdorff距离，并允许指定单位。&lt;/li&gt;
  &lt;li&gt;hvplot(*args, **kwargs) 方法用于使用hvPlot库创建高度可定制的图形和可视化。&lt;/li&gt;
  &lt;li&gt;hvplot_pts(*args, **kwargs) 方法用于使用hvPlot库对地理点数据进行可视化并创建交互式图形。&lt;/li&gt;
  &lt;li&gt;interpolate_position_at(t) 方法用于在给定时间 t 处插值并返回轨迹的位置。&lt;/li&gt;
  &lt;li&gt;intersection(feature, point_based=False) 方法用于计算轨迹与给定地理特征的交集，并可以选择基于点的方式进行计算。&lt;/li&gt;
  &lt;li&gt;intersects(polygon) 方法用于判断轨迹是否与指定的多边形区域相交。&lt;/li&gt;
  &lt;li&gt;clip(self, polygon): 按多边形裁剪轨迹。&lt;/li&gt;
  &lt;li&gt;apply_offset_minutes(column, offset) 方法用于将指定列的时间值按给定的分钟数进行偏移调整。&lt;/li&gt;
  &lt;li&gt;apply_offset_seconds(column, offset) 方法用于将指定列的时间值按给定的秒数进行偏移调整。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;MovingPandas.TrajectoryCollection对象&lt;/h4&gt;
 &lt;p&gt;TrajectoryCollection 类是 MovingPandas 中用于表示多条轨迹的集合。它允许用户以集合的形式操作多条轨迹，支持对这些轨迹的批量处理和分析。&lt;/p&gt;
 &lt;p&gt;可以通过传递一系列 Trajectory 对象来创建一个 TrajectoryCollection。每个 Trajectory 对象代表一条轨迹，包含了时间和位置的信息。&lt;/p&gt;
 &lt;p&gt;class movingpandas.TrajectoryCollection(data, traj_id_col=None, obj_id_col=None, t=None, x=None, y=None, crs=’epsg:4326′, min_length=0, min_duration=None)&lt;/p&gt;
 &lt;p&gt;参数说明：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;data (list[Trajectory] 或 GeoDataFrame 或 DataFrame) – 包含 Trajectory 对象的列表，或一个包含轨迹 ID、点几何列和时间戳索引的 GeoDataFrame。&lt;/li&gt;
  &lt;li&gt;traj_id_col (string) – 包含轨迹 ID 的 GeoDataFrame 列名。&lt;/li&gt;
  &lt;li&gt;obj_id_col (string) – 包含移动对象 ID 的 GeoDataFrame 列名。&lt;/li&gt;
  &lt;li&gt;t (string) – 包含时间戳的 DataFrame 列名。&lt;/li&gt;
  &lt;li&gt;x (string) – 包含 x 坐标的 DataFrame 列名。&lt;/li&gt;
  &lt;li&gt;y (string) – 包含 y 坐标的 DataFrame 列名。&lt;/li&gt;
  &lt;li&gt;crs (string) – x/y 坐标的坐标参考系 (CRS)。&lt;/li&gt;
  &lt;li&gt;min_length (numeric) – 期望的轨迹最小长度。长度使用 CRS 单位计算，若 CRS 是地理坐标系（例如 EPSG:4326 WGS84），则长度以米为单位计算。（较短的轨迹将被丢弃。）&lt;/li&gt;
  &lt;li&gt;min_duration (timedelta) – 期望的轨迹最短持续时间。（较短的轨迹将被丢弃。）&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;相比MovingPandas.Trajectory多了一些方法：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;filter(predicate): 根据给定条件过滤轨迹集合。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;MovingPandas.TrajectoryCollectionAggregator对象&lt;/h4&gt;
 &lt;p&gt;MovingPandas.TrajectoryCollectionAggregator 是 MovingPandas 库中的一个类，主要用于对轨迹集合进行聚合操作。通过对轨迹数据进行空间和时间上的聚合，可以帮助用户有效地分析和总结移动模式。&lt;/p&gt;
 &lt;p&gt;class movingpandas.TrajectoryCollectionAggregator(traj_collection, max_distance, min_distance, min_stop_duration, min_angle=45)&lt;/p&gt;
 &lt;p&gt;参数说明&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;traj_collection (TrajectoryCollection) – 要进行聚合的 TrajectoryCollection 对象。&lt;/li&gt;
  &lt;li&gt;max_distance (float) – 重要点之间的最大距离（距离使用 CRS 单位计算，若 CRS 是地理坐标系，例如 EPSG:4326 WGS84，则距离以米为单位计算）。&lt;/li&gt;
  &lt;li&gt;min_distance (float) – 重要点之间的最小距离。&lt;/li&gt;
  &lt;li&gt;min_stop_duration (datetime.timedelta) – 停止检测所需的最短持续时间。&lt;/li&gt;
  &lt;li&gt;min_angle (float) – 提取重要点的最小角度。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;相关方法：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;get_clusters_gdf() 方法返回一个 GeoDataFrame，其中包含聚合后的轨迹数据的簇（clusters）。&lt;/li&gt;
  &lt;li&gt;get_flows_gdf() 方法返回一个 GeoDataFrame，其中包含聚合后的轨迹数据的流动（flows）信息。&lt;/li&gt;
  &lt;li&gt;get_significant_points_gdf() 方法返回一个 GeoDataFrame，其中包含从轨迹数据中提取的显著点（significant points）。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;MovingPandas.TrajectoryCleaner对象&lt;/h4&gt;
 &lt;p&gt;MovingPandas.TrajectoryCleaner 是 MovingPandas 库中的一个类，专门用于清理轨迹数据。清理操作可以帮助去除数据中的噪声、填补缺失值以及进行其他预处理步骤，确保轨迹数据的质量和一致性。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;IqrCleaner(traj) 是一个类，用于基于四分位数范围 (IQR) 方法来清理轨迹数据中的异常值。&lt;/li&gt;
  &lt;li&gt;OutlierCleaner(traj) 是一个类，用于通过多种方法识别和清理轨迹数据中的离群点（异常值）。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;MovingPandas.TrajectoryGeneralizer对象&lt;/h4&gt;
 &lt;p&gt;MovingPandas.TrajectoryGeneralizer 是 MovingPandas 库中的一个类，用于对轨迹数据进行简化和概括。通过轨迹数据的概括，可以减少数据量，提高处理效率，并且在某些应用场景下有助于更清晰地展示轨迹特征。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;TrajectoryGeneralizer(traj) 是一个类，用于通过多种算法对轨迹数据进行简化和概括，以减少数据量并提高处理效率。&lt;/li&gt;
  &lt;li&gt;DouglasPeuckerGeneralizer(traj) 是一个类，专门使用 Douglas-Peucker 算法对轨迹数据进行简化，保留主要特征点以减少数据量。&lt;/li&gt;
  &lt;li&gt;MinDistanceGeneralizer(traj) 是一个类，用于根据最小距离间隔对轨迹数据进行简化，移除距离变化小于指定阈值的点。&lt;/li&gt;
  &lt;li&gt;MinTimeDeltaGeneralizer(traj) 是一个类，用于根据最小时间间隔对轨迹数据进行简化，移除时间间隔小于指定阈值的点。&lt;/li&gt;
  &lt;li&gt;TopDownTimeRatioGeneralizer(traj) 是一个类，用于通过时间比率算法对轨迹数据进行简化，保留关键时间点以减少数据量。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;MovingPandas.TrajectorySmoother对象&lt;/h4&gt;
 &lt;p&gt;MovingPandas.TrajectorySmoother 是一个类，用于对轨迹数据进行平滑处理。轨迹平滑通常是为了减少由于数据采集误差和噪声导致的轨迹抖动和异常点，从而得到更加平滑和准确的轨迹线条。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;KalmanSmootherCV(traj) 是一个类，用于使用常速模型（Constant Velocity Model）的卡尔曼滤波算法对轨迹数据进行平滑处理，以减少噪声和抖动。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;MovingPandas.TrajectorySplitter对象&lt;/h4&gt;
 &lt;p&gt;MovingPandas.TrajectorySplitter 是一个类，用于将轨迹数据根据特定条件进行分割。这在处理长时间、多段的轨迹数据时特别有用，比如在分析车辆行驶路径、运动员运动轨迹或动物迁徙路径时，可以根据特定的规则将连续的轨迹分割成多个部分，以便进行更细致的分析。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;TrajectorySplitter(traj) 是一个类，用于根据指定的条件（如距离、时间或速度）对轨迹数据进行分割，生成多个段以便更细致的分析。&lt;/li&gt;
  &lt;li&gt;TemporalSplitter(traj) 是一个类，用于根据时间间隔对轨迹数据进行分割，将轨迹分成多个时间段以便更细致的时间序列分析。&lt;/li&gt;
  &lt;li&gt;ObservationGapSplitter(traj) 是一个类，用于根据观测数据中的时间间隙对轨迹进行分割，当连续观测点之间的时间间隔超过指定阈值时，将轨迹分割成多个部分。&lt;/li&gt;
  &lt;li&gt;SpeedSplitter(traj) 是一个类，用于根据速度阈值对轨迹数据进行分割，当轨迹点的速度超过指定阈值时，将轨迹分割成多个部分。&lt;/li&gt;
  &lt;li&gt;StopSplitter(traj) 是一个类，用于根据停留点（长时间停留的点）对轨迹数据进行分割，将轨迹分成移动段和停留段以便更细致的分析。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;MovingPandas.TrajectoryStopDetector对象&lt;/h4&gt;
 &lt;p&gt;TrajectoryStopDetector 通过分析轨迹点的时空属性来识别停留点。它会检查一个轨迹对象中的每个点，并根据设定的阈值参数（如最小速度、最小停留时间和最小停留距离等）来鉴定轨迹中是否存在停留段。&lt;/p&gt;
 &lt;p&gt;class movingpandas.TrajectoryStopDetector(traj, n_threads=1)&lt;/p&gt;
 &lt;p&gt;方法介绍：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;get_stop_points(max_diameter, min_duration) 是 TrajectoryStopDetector 类中的方法，用于根据最大停留直径和最小持续时间来识别和提取轨迹数据中的停留点，并返回包含这些停留点的 GeoDataFrame。&lt;/li&gt;
  &lt;li&gt;get_stop_segments(max_diameter, min_duration) 是 TrajectoryStopDetector 类中的方法，用于根据最大停留直径和最小持续时间来识别和提取轨迹中的停留段，并返回包含这些停留段的列表。&lt;/li&gt;
  &lt;li&gt;get_stop_time_ranges(max_diameter, min_duration) 是 TrajectoryStopDetector 类中的方法，用于根据最大停留直径和最小持续时间来识别停留时间范围，并返回停留时间段的列表。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;MovingPandas使用实例&lt;/h2&gt;
 &lt;h3&gt;准备工作&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;加载需要的库&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;import pandas as pd
import geopandas as gpd
import movingpandas as mpd
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
import folium
import bokeh.io
bokeh.io.output_notebook()
from holoviews import opts
opts.defaults(opts.Overlay(active_tools=[&amp;quot;wheel_zoom&amp;quot;], frame_width=500, frame_height=400))
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;加载数据&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;df = pd.read_excel(&amp;quot;driver_log.xlsx&amp;quot;)

# 将DataFrame 转换为 GeoDataFrame
gdf = gpd.GeoDataFrame(df, geometry=gpd.points_from_xy(df.lon, df.lat), crs=&amp;apos;EPSG:4326&amp;apos;)

# 将GeoDataFrame转化为TrajectoryCollection对象
tc = mpd.TrajectoryCollection(gdf, traj_id_col=&amp;apos;session_id&amp;apos;, obj_id_col = &amp;apos;driver_no&amp;apos;, t=&amp;apos;log_time&amp;apos;)
# 过滤某个司机的轨迹
df[&amp;apos;driver_no&amp;apos;].value_counts()
df[&amp;apos;driver_no&amp;apos;].value_counts().plot(kind=&amp;apos;bar&amp;apos;, figsize=(15,3))
driver_tc = tc.filter(&amp;apos;driver_no&amp;apos;, &amp;apos;DR202407021504081000000&amp;apos;)

# 展示司机轨迹
driver_tc.plot()
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="413" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/driver_traj.png" width="450"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;# 获取单个轨迹
my_traj = driver_tc.trajectories[0]

# 展示单个轨迹
traj_plot = my_traj.hvplot(title=&amp;quot;Trajectory {}&amp;quot;.format(my_traj.id),line_width=7.0, tiles=&amp;quot;CartoLight&amp;quot;, color=&amp;quot;slategray&amp;quot;)
traj_plot
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="636" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/my_traj.png" width="815"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;停留点检测&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;针对单轨迹停留点检测&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;detector = mpd.TrajectoryStopDetector(my_traj)
## 检测停留的时间（这里检测5分钟位移100米以内）
stop_time_ranges = detector.get_stop_time_ranges(min_duration=timedelta(seconds=300), max_diameter=100)
## 检测停留的时间
for stop_time in stop_time_ranges:
    print(stop_time)
## 检测停留点
stop_points = detector.get_stop_points(min_duration=timedelta(seconds=300), max_diameter=100)
stop_points
## 展示停留点
stop_point_plot = traj_plot * stop_points.hvplot(geo=True, size=&amp;quot;duration_s&amp;quot;, color=&amp;quot;deeppink&amp;quot;)
stop_point_plot
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="631" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/stop-point.png" width="817"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 停留点信息
stop_points_gdf = gpd.GeoDataFrame(stop_points, geometry=&amp;quot;geometry&amp;quot;, crs=&amp;quot;EPSG:4326&amp;quot;)
stop_points_gdf
## 使用folium展示停留点
# m = my_traj.explore(color=&amp;quot;blue&amp;quot;,style_kwds={&amp;quot;weight&amp;quot;: 4},name=&amp;quot;Trajectory&amp;quot;)
# stop_points_gdf.explore(m=m,color=&amp;quot;red&amp;quot;,style_kwds={&amp;quot;style_function&amp;quot;: lambda x: {&amp;quot;radius&amp;quot;: x[&amp;quot;properties&amp;quot;][&amp;quot;duration_s&amp;quot;] / 10 }},name=&amp;quot;Stop points&amp;quot;)
# folium.TileLayer(&amp;quot;OpenStreetMap&amp;quot;).add_to(m)
# folium.LayerControl().add_to(m)
# m
## 停留轨迹
stop_segments = detector.get_stop_segments(min_duration=timedelta(seconds=60), max_diameter=100)
stop_segments.to_traj_gdf()
## 停留轨迹
stop_segment_plot = stop_point_plot * stop_segments.hvplot(line_width=7.0, tiles=None, color=&amp;quot;orange&amp;quot;)
stop_segment_plot
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="629" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/stop_segment.png" width="811"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 使用folium绘图
# m = my_traj.explore(
#     color=&amp;quot;blue&amp;quot;,
#     popup=True,
#     style_kwds={&amp;quot;weight&amp;quot;: 4},
#     name=&amp;quot;Trajectory&amp;quot;,
# )

# stop_segments.explore(
#     m=m,
#     color=&amp;quot;orange&amp;quot;,
#     popup=True,
#     style_kwds={&amp;quot;weight&amp;quot;: 4},
#     name=&amp;quot;Stop segments&amp;quot;,
# )

# stop_points_gdf.explore(
#     m=m,
#     color=&amp;quot;red&amp;quot;,
#     tooltip=&amp;quot;stop_id&amp;quot;,
#     popup=True,
#     marker_kwds={&amp;quot;radius&amp;quot;: 3},
#     name=&amp;quot;Stop points&amp;quot;,
# )

# folium.TileLayer(&amp;quot;CartoDB positron&amp;quot;).add_to(m)
# folium.LayerControl().add_to(m)

# m

## 行驶线路
split = mpd.StopSplitter(my_traj).split(min_duration=timedelta(seconds=300), max_diameter=100)
split.to_traj_gdf()
## 可视化行驶线路
split.explore(column=&amp;quot;session_id&amp;quot;, tiles=&amp;quot;CartoDB positron&amp;quot;, style_kwds={&amp;quot;weight&amp;quot;: 4})

## 整体可视化
stop_segment_plot + split.hvplot(title=&amp;quot;Trajectory {} split at stops&amp;quot;.format(my_traj.id),line_width=7.0,tiles=&amp;quot;CartoLight&amp;quot;)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="457" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/stop_segment_plot.png" width="1058"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;轨迹合集的经停点检测&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;## 停留点检测
detector = mpd.TrajectoryStopDetector(driver_tc)
stop_points = detector.get_stop_points(min_duration=timedelta(seconds=300), max_diameter=100)
stop_points
## 停留点可视化
ax = driver_tc.plot(figsize=(7, 7))
stop_points.plot(ax=ax, color=&amp;quot;red&amp;quot;)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="486" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/TrajectoryStopDetector.png" width="603"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 使用folium可视化
## 使用方folium可视化
# m = driver_tc.explore(
#     column=&amp;quot;session_id&amp;quot;,
#     popup=True,
#     style_kwds={&amp;quot;weight&amp;quot;: 4},
#     name=&amp;quot;Trajectories&amp;quot;,
# )

# stop_points.explore(
#     m=m,
#     color=&amp;quot;red&amp;quot;,
#     tooltip=&amp;quot;stop_id&amp;quot;,
#     popup=True,
#     marker_kwds={&amp;quot;radius&amp;quot;: 5},
#     name=&amp;quot;Stop points&amp;quot;,
# )

# folium.TileLayer(&amp;quot;CartoDB positron&amp;quot;).add_to(m)
# folium.LayerControl().add_to(m)

# m
&lt;/pre&gt;
 &lt;h3&gt;速度计算&lt;/h3&gt;
 &lt;pre&gt;## 单轨迹增加速度
my_traj.add_speed(overwrite=True,units=(&amp;quot;km&amp;quot;, &amp;quot;h&amp;quot;))
my_traj.df.head()

## 展示速度
my_traj.plot(column=&amp;quot;speed&amp;quot;, linewidth=5, capstyle=&amp;apos;round&amp;apos;, legend=True)
# my_traj.hvplot(c=&amp;apos;speed&amp;apos;, clim=(0,20), line_width=7.0, tiles=&amp;apos;CartoLight&amp;apos;, cmap=&amp;apos;Viridis&amp;apos;, colorbar=True)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="404" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/speed.png" width="546"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 添加方向
my_traj.add_direction(overwrite=True)
my_traj.df.head()

## 添加时差
my_traj.add_timedelta(overwrite=True)
my_traj.df.head()

## 添加距离
my_traj.add_distance(overwrite=True, name=&amp;quot;distance (km)&amp;quot;, units=&amp;quot;m&amp;quot;)
my_traj.df.head()

## 添加加速度
my_traj.add_acceleration(overwrite=True, name=&amp;quot;acceleration (mph/s)&amp;quot;, units=(&amp;quot;mi&amp;quot;, &amp;quot;h&amp;quot;, &amp;quot;s&amp;quot;))
my_traj.df.head()

## 轨迹集增加速度
driver_tc.add_speed(overwrite=True,units=(&amp;quot;km&amp;quot;, &amp;quot;h&amp;quot;))
driver_tc.plot(column=&amp;apos;speed&amp;apos;, linewidth=5, capstyle=&amp;apos;round&amp;apos;, legend=True, vmax=20)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="418" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/speed2.png" width="534"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;提取位置&lt;/h3&gt;
 &lt;pre&gt;## 获取起点与终点
ax = my_traj.plot()
gpd.GeoSeries(my_traj.get_start_location()).plot(ax=ax, color=&amp;apos;blue&amp;apos;)
gpd.GeoSeries(my_traj.get_end_location()).plot(ax=ax, color=&amp;apos;red&amp;apos;)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="417" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/location.png" width="574"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 获取特定时间点的位置
t = datetime(2024,7,3,9,30,0)
print(my_traj.get_position_at(t, method=&amp;quot;nearest&amp;quot;))
print(my_traj.get_position_at(t, method=&amp;quot;interpolated&amp;quot;))
print(my_traj.get_position_at(t, method=&amp;quot;ffill&amp;quot;)) # from the previous row
print(my_traj.get_position_at(t, method=&amp;quot;bfill&amp;quot;)) # from the following row

point = my_traj.get_position_at(t, method=&amp;quot;interpolated&amp;quot;)
ax = my_traj.plot()
gpd.GeoSeries(point).plot(ax=ax, color=&amp;apos;red&amp;apos;, markersize=100)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="417" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/time-point.png" width="574"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 获取特定时间区间的位置
segment = my_traj.get_segment_between(datetime(2024,7,3,9,10,0), datetime(2024,7,3,9,30,0))
print(segment)
ax = my_traj.plot()
segment.plot(ax=ax, color=&amp;apos;red&amp;apos;, linewidth=5)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="446" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/segment.png" width="542"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 获取特定区域内的轨迹
from shapely.geometry import Polygon

xmin, xmax, ymin, ymax = 104.135, 104.137, 30.642, 30.643
polygon = Polygon([(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin), (xmin, ymin)])
intersections = my_traj.clip(polygon)
ax = my_traj.plot()
gpd.GeoSeries(polygon).plot(ax=ax, color=&amp;apos;lightgray&amp;apos;)
intersections.plot(ax=ax, color=&amp;apos;red&amp;apos;, linewidth=5, capstyle=&amp;apos;round&amp;apos;)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="446" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/intersections.png" width="542"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;导出轨迹&lt;/h3&gt;
 &lt;pre&gt;## 返回 GeoDataFrame
driver_tc.to_point_gdf()
driver_tc.to_line_gdf()
driver_tc.to_traj_gdf(wkt=True) # 生成wkt格式的聚合

# 聚合数据
driver_tc.add_speed(overwrite=True,units=(&amp;quot;km&amp;quot;, &amp;quot;h&amp;quot;))
driver_tc.to_traj_gdf(agg={&amp;apos;speed&amp;apos;:[&amp;apos;min&amp;apos;, &amp;apos;max&amp;apos;,&amp;apos;mode&amp;apos;]})

# 导出数据
export_gdf = driver_tc.to_traj_gdf(agg={&amp;apos;speed&amp;apos;:[&amp;apos;min&amp;apos;, &amp;apos;max&amp;apos;,&amp;apos;mode&amp;apos;]})
export_gdf.to_file(&amp;quot;temp.gpkg&amp;quot;, layer=&amp;apos;trajectories&amp;apos;, driver=&amp;quot;GPKG&amp;quot;)
gpd.read_file(&amp;apos;temp.gpkg&amp;apos;).plot()
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="413" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/export_gdf.png" width="450"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;轨迹分割&lt;/h3&gt;
 &lt;pre&gt;## 数据准备
my_traj.add_speed(overwrite=True,units=(&amp;quot;km&amp;quot;, &amp;quot;h&amp;quot;))
my_traj.plot(column=&amp;apos;speed&amp;apos;, vmax=20, linewidth=5, capstyle=&amp;apos;round&amp;apos;, figsize=(9,3), legend=True )
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="308" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/my_traj.plot_.png" width="455"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 根据观测数据中的时间间隙对轨迹进行分割
split = mpd.ObservationGapSplitter(my_traj).split(gap=timedelta(minutes=1))
split.to_traj_gdf()
fig, axes = plt.subplots(nrows=1, ncols=len(split), figsize=(19,4))
for i, traj in enumerate(split):
    traj.plot(ax=axes[i], linewidth=5.0, capstyle=&amp;apos;round&amp;apos;, column=&amp;apos;speed&amp;apos;, vmax=20)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="288" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/split.png" width="1047"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 根据停留点（长时间停留的点）对轨迹数据进行分割
split = mpd.StopSplitter(my_traj).split(max_diameter=10, min_duration=timedelta(minutes=1), min_length=20)
split.to_traj_gdf()
fig, axes = plt.subplots(nrows=1, ncols=len(split), figsize=(19,4))
for i, traj in enumerate(split):
    traj.plot(ax=axes[i], linewidth=5.0, capstyle=&amp;apos;round&amp;apos;, column=&amp;apos;speed&amp;apos;, vmax=20)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="269" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/split2.png" width="1055"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 根据速度阈值对轨迹数据进行分割
split = mpd.SpeedSplitter(my_traj).split(speed=0, duration=timedelta(minutes=1))
split.to_traj_gdf()
fig, axes = plt.subplots(nrows=1, ncols=len(split), figsize=(19,4))
for i, traj in enumerate(split):
    traj.plot(ax=axes[i], linewidth=5.0, capstyle=&amp;apos;round&amp;apos;, column=&amp;apos;speed&amp;apos;, vmax=20)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="284" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/split3.png" width="1031"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;轨迹抽稀&lt;/h3&gt;
 &lt;pre&gt;## 展示原始轨迹
plot_defaults = {&amp;apos;linewidth&amp;apos;:5, &amp;apos;capstyle&amp;apos;:&amp;apos;round&amp;apos;, &amp;apos;figsize&amp;apos;:(9,3), &amp;apos;legend&amp;apos;:True}
my_traj.add_speed(overwrite=True,units=(&amp;quot;km&amp;quot;, &amp;quot;h&amp;quot;))
my_traj.plot(column=&amp;apos;speed&amp;apos;, vmax=20, **plot_defaults)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="308" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/plot_defaults.png" width="455"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 使用 Douglas-Peucker 算法对轨迹数据进行简化
dp_generalized  = mpd.DouglasPeuckerGeneralizer(my_traj).generalize(tolerance=0.0001)
dp_generalized.plot(column=&amp;apos;speed&amp;apos;, vmax=20, **plot_defaults)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="308" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/dp_generalized.png" width="448"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;print(&amp;apos;Original length: %s&amp;apos;%(my_traj.get_length()))
print(&amp;apos;Generalized length: %s&amp;apos;%(dp_generalized.get_length()))

## 根据最小时间间隔对轨迹数据进行简化
time_generalized = mpd.MinTimeDeltaGeneralizer(my_traj).generalize(tolerance=timedelta(minutes=3))
time_generalized.plot(column=&amp;apos;speed&amp;apos;, vmax=20, **plot_defaults)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="308" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/time_generalized.png" width="448"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 通过时间比率算法对轨迹数据进行简化
tdtr_generalized = mpd.TopDownTimeRatioGeneralizer(my_traj).generalize(tolerance=0.001)
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(19,4))
tdtr_generalized.plot(ax=axes[0], column=&amp;apos;speed&amp;apos;, vmax=20, **plot_defaults)
dp_generalized.plot(ax=axes[1], column=&amp;apos;speed&amp;apos;, vmax=20, **plot_defaults)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="344" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/tdtr_generalized.png" width="1198"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(19,4))
tdtr_generalized.plot(ax=axes[0], column=&amp;apos;speed&amp;apos;, vmax=20, **plot_defaults)
time_generalized.plot(ax=axes[1], column=&amp;apos;speed&amp;apos;, vmax=20, **plot_defaults)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="344" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/tdtr_generalized.plot_.png" width="1199"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;平滑轨迹&lt;/h3&gt;
 &lt;pre&gt;split = mpd.ObservationGapSplitter(my_traj).split(gap=timedelta(minutes=1))
smooth = mpd.KalmanSmootherCV(split).smooth(process_noise_std=0.1, measurement_noise_std=10)
hvplot_defaults = {&amp;apos;tiles&amp;apos;:&amp;apos;CartoLight&amp;apos;, &amp;apos;frame_height&amp;apos;:320, &amp;apos;frame_width&amp;apos;:320, &amp;apos;cmap&amp;apos;:&amp;apos;Viridis&amp;apos;, &amp;apos;colorbar&amp;apos;:True}
kwargs = {**hvplot_defaults, &amp;apos;line_width&amp;apos;:4}
(split.hvplot(title=&amp;apos;Original Trajectories&amp;apos;, **kwargs) +  smooth.hvplot(title=&amp;apos;Smooth Trajectories&amp;apos;, **kwargs))
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="378" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/smooth.png" width="733"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;kwargs = {**hvplot_defaults, &amp;apos;c&amp;apos;:&amp;apos;speed&amp;apos;, &amp;apos;line_width&amp;apos;:7, &amp;apos;clim&amp;apos;:(0,20)}
(split.trajectories[1].hvplot(title=&amp;apos;Original Trajectory&amp;apos;, **kwargs) + smooth.trajectories[1].hvplot(title=&amp;apos;Smooth Trajectory&amp;apos;, **kwargs))
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="380" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/split.trajectories.png" width="846"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;traj = split.trajectories[1]

cleaned = traj.copy()
cleaned = mpd.OutlierCleaner(cleaned).clean(alpha=2)

smoothed = mpd.KalmanSmootherCV(cleaned).smooth(process_noise_std=0.1, measurement_noise_std=10)
    
(traj.hvplot(title=&amp;apos;Original Trajectory&amp;apos;, **kwargs) + 
 cleaned.hvplot(title=&amp;apos;Cleaned Trajectory&amp;apos;, **kwargs) + 
 smoothed.hvplot(title=&amp;apos;Cleaned &amp;amp; Smoothed Trajectory&amp;apos;, **kwargs))
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="386" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/smoothed.png" width="1301"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;轨迹聚类和分类&lt;/h3&gt;
 &lt;pre&gt;## 查看数据
driver_tc.explore(column=&amp;quot;session_id&amp;quot;, cmap=&amp;quot;plasma&amp;quot;, style_kwds={&amp;quot;weight&amp;quot;: 4})
## 根据最小距离间隔对轨迹数据进行简化
generalized = mpd.MinDistanceGeneralizer(driver_tc).generalize(tolerance=100)
generalized.to_traj_gdf()

## 对轨迹进行聚合操作
aggregator = mpd.TrajectoryCollectionAggregator(
    generalized,
    max_distance=1000,
    min_distance=100,
    min_stop_duration=timedelta(minutes=10),
)

## 提取显著点
pts = aggregator.get_significant_points_gdf()
pts.hvplot(geo=True, tiles=&amp;quot;OSM&amp;quot;)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="634" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/pts.png" width="819"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 获取聚合轨迹的簇
clusters = aggregator.get_clusters_gdf()
(pts.hvplot(geo=True, tiles=&amp;quot;OSM&amp;quot;) * clusters.hvplot(geo=True, color=&amp;quot;red&amp;quot;))
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="638" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/clusters.png" width="815"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 使用folium绘制
# m = pts.explore(marker_kwds={&amp;quot;radius&amp;quot;: 3}, name=&amp;quot;Significant points&amp;quot;)
# clusters.explore(m=m, color=&amp;quot;red&amp;quot;, marker_kwds={&amp;quot;radius&amp;quot;: 3}, name=&amp;quot;Cluster centroids&amp;quot;)
# folium.TileLayer(&amp;quot;CartoDB positron&amp;quot;).add_to(m)
# folium.LayerControl().add_to(m)
# m

## 获取聚合后的轨迹数据的流动
flows = aggregator.get_flows_gdf()
(flows.hvplot(geo=True, hover_cols=[&amp;quot;weight&amp;quot;], line_width=dim(&amp;quot;weight&amp;quot;) * 7, color=&amp;quot;#1f77b3&amp;quot;,tiles=&amp;quot;CartoLight&amp;quot;) * clusters.hvplot(geo=True, color=&amp;quot;red&amp;quot;, size=dim(&amp;quot;n&amp;quot;)))
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="626" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/flows.png" width="829"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 使用Folium绘制
# m = flows.explore(style_kwds={&amp;quot;weight&amp;quot;: 5},name=&amp;quot;Flows&amp;quot;)
# clusters.explore( m=m,color=&amp;quot;red&amp;quot;,style_kwds={&amp;quot;style_function&amp;quot;: lambda x: {&amp;quot;radius&amp;quot;: x[&amp;quot;properties&amp;quot;][&amp;quot;n&amp;quot;]}}, name=&amp;quot;Clusters&amp;quot;)
# folium.TileLayer(&amp;quot;OpenStreetMap&amp;quot;).add_to(m)
# folium.LayerControl().add_to(m)
# m
&lt;/pre&gt;
 &lt;h3&gt;距离计算&lt;/h3&gt;
 &lt;pre&gt;## 选择2个轨迹
my_traj = driver_tc.trajectories[3]
toy_traj = driver_tc.trajectories[1]
## 呈现数据
ax = my_traj.plot()
toy_traj.plot(ax=ax, color=&amp;apos;red&amp;apos;)
&lt;/pre&gt;
 &lt;p&gt;  &lt;img alt="" height="343" src="https://www.biaodianfu.com/wp-content/uploads/2024/10/toy_traj.plot_.png" width="565"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;pre&gt;## 计算记录
print(f&amp;apos;Distance: {toy_traj.distance(my_traj)} meters&amp;apos;) # 返回最短距离
print(f&amp;apos;Hausdorff distance: {toy_traj.hausdorff_distance(my_traj):.2f} meters&amp;apos;) # 返回Hausdorff距离
&lt;/pre&gt;
 &lt;p&gt;Hausdorff距离可以理解为：对于集合A 中的每个点，计算它到集合B的最近距离，然后在这些距离中找到最大值；反过来对于集合 B 中的每个点，计算它到集合A 的最近距离，然后在这些距离中找到最大值。Hausdorff距离是这两个最大值中的较大者。&lt;/p&gt;
 &lt;p&gt;参考链接：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://github.com/movingpandas/movingpandas"&gt;movingpandas/movingpandas: Movement trajectory classes and functions built on top of GeoPandas (github.com)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://movingpandas.org/"&gt;MovingPandas | A Python library for movement data exploration and analysis&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://movingpandas.org/examples.html"&gt;Tutorials | MovingPandas&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://movingpandas.readthedocs.io/en/main/"&gt;MovingPandas Documentation — MovingPandas main documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;div&gt;

  &lt;h3&gt;相关文章:&lt;/h3&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/seaborn.html" rel="bookmark" title="Python&amp;#25968;&amp;#25454;&amp;#21487;&amp;#35270;&amp;#21270;&amp;#20043;Seaborn"&gt;Python数据可视化之Seaborn&lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/learn-css.html" rel="bookmark" title="CSS&amp;#20307;&amp;#31995;&amp;#21270;&amp;#23398;&amp;#20064;"&gt;CSS体系化学习&lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/arima.html" rel="bookmark" title="&amp;#26102;&amp;#38388;&amp;#24207;&amp;#21015;&amp;#39044;&amp;#27979;&amp;#20043;ARIMA"&gt;时间序列预测之ARIMA&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>器→工具 工具软件 开源项目 GIS</category>
      <guid isPermaLink="true">https://itindex.net/detail/62944-python-%E5%9C%B0%E7%90%86-%E6%95%B0%E6%8D%AE%E5%88%86%E6%9E%90</guid>
      <pubDate>Wed, 09 Oct 2024 19:54:20 CST</pubDate>
    </item>
    <item>
      <title>开源可视化报表工具：Superset</title>
      <link>https://itindex.net/detail/62903-%E5%BC%80%E6%BA%90-%E5%8F%AF%E8%A7%86%E5%8C%96-%E5%B7%A5%E5%85%B7</link>
      <description>&lt;h2&gt;Superset简介&lt;/h2&gt;
 &lt;p&gt;Superset 是一个开源的数据可视化和数据探索平台，最初由 Airbnb 开发，后来成为了 Apache 软件基金会的顶级项目。它支持各种类型的数据源，如数据库和 SQL 引擎，并提供了一个易于使用的界面来创建和共享仪表板和图表。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="528" src="https://www.biaodianfu.com/wp-content/uploads/2024/01/superset-1.jpg" width="780"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;主要特点包括：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;数据可视化: Superset 提供了丰富的图表库，支持从简单的折线图和条形图到更复杂的地理信息系统 (GIS) 可视化等。&lt;/li&gt;
  &lt;li&gt;数据探索: 用户可以通过 Superset 的 SQL 编辑器执行查询，探索数据，并将结果可视化。&lt;/li&gt;
  &lt;li&gt;仪表板: 可以将多个图表组合成仪表板，为数据分析提供全面视图。&lt;/li&gt;
  &lt;li&gt;安全性和权限管理: Superset 支持细粒度的访问控制，允许管理员定义用户和角色，控制对数据和功能的访问。&lt;/li&gt;
  &lt;li&gt;易于集成: 作为一个开源工具，Superset 可以与多种数据源和其他数据工具集成。&lt;/li&gt;
  &lt;li&gt;自定义和扩展: 用户可以根据需要自定义图表和界面，并且可以开发新的可视化插件。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;Superset 适用于数据分析师和开发人员，帮助他们快速有效地探索和可视化数据，从而做出更好的数据驱动决策。&lt;/p&gt;
 &lt;p&gt;看板示例：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="447" src="https://www.biaodianfu.com/wp-content/uploads/2024/01/superset-2.jpg" width="780"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;拖拽式看板编辑器：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="414" src="https://www.biaodianfu.com/wp-content/uploads/2024/01/superset-3.jpg" width="780"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;SQL编辑器：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="546" src="https://www.biaodianfu.com/wp-content/uploads/2024/01/sql-lab.jpg" width="780"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;Superset架构&lt;/h2&gt;
 &lt;p&gt;Apache Superset 是一款开源的数据可视化和数据探索平台，它的架构设计允许用户轻松地进行数据分析并创建交互式的仪表板。Superset的架构主要由以下几个核心组件构成：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="464" src="https://www.biaodianfu.com/wp-content/uploads/2024/01/superset-4-1.png" width="780"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Web服务器&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Flask：Superset使用Flask作为其Web框架，处理HTTP请求和渲染界面。&lt;/li&gt;
  &lt;li&gt;Gunicorn：在生产环境中，通常使用Gunicorn作为WSGI HTTP服务器来运行Flask应用。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;SQL查询引擎&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;SQLAlchemy：Superset通过SQLAlchemy与数据源进行交互，它支持多种数据库。&lt;/li&gt;
  &lt;li&gt;Pandas：在某些情况下，Superset会使用Pandas库来处理数据。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;数据库&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;元数据数据库：存储Superset的配置信息、仪表板定义、数据源定义等。&lt;/li&gt;
  &lt;li&gt;缓存数据库：用于缓存数据，提高查询性能。Redis和Memcached是常用的选项。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;前端&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;React &amp;amp; JavaScript：Superset的前端主要使用React框架结合JavaScript开发，用于实现用户界面的交互和动态展示。&lt;/li&gt;
  &lt;li&gt;js：图表的渲染利用了D3.js库，提供丰富的可视化选项。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;安全性&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;认证与授权：Superset提供灵活的认证选项（如LDAP、OAuth、数据库等）和基于角色的访问控制（RBAC）。&lt;/li&gt;
  &lt;li&gt;数据安全：支持数据级别的安全控制，确保用户只能访问授权的数据。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;扩展性&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;插件系统：Superset支持自定义插件，允许用户扩展新的可视化类型或其他功能。&lt;/li&gt;
  &lt;li&gt;API：提供REST API，支持与其他系统的集成。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;任务调度器&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Celery：用于执行后台任务，如异步查询和发送报告。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;Superset功能扩展&lt;/h2&gt;
 &lt;p&gt;拓展Apache Superset主要涉及添加新的可视化类型、增强现有功能、集成更多数据源等方面。&lt;/p&gt;
 &lt;p&gt;开发自定义可视化插件&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Superset支持通过插件机制添加新的图表和可视化类型。&lt;/li&gt;
  &lt;li&gt;可以使用React和JavaScript开发新的可视化组件。&lt;/li&gt;
  &lt;li&gt;开发完成后，将插件包含在Superset的配置中，使其成为可用的可视化类型。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;集成更多数据源&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Superset通过SQLAlchemy与数据源进行交互，可以添加对新数据库的支持。&lt;/li&gt;
  &lt;li&gt;通过添加相应的数据库驱动和SQLAlchemy方言，可以实现新的数据库支持。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;增强现有功能&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;对Superset的源代码进行修改，可以增强或改变现有功能。&lt;/li&gt;
  &lt;li&gt;包括改进用户界面、增加新的数据处理功能、优化性能等。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;使用API进行集成&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Superset提供了REST API，可以用来与其他系统集成。&lt;/li&gt;
  &lt;li&gt;例如，可以通过API自动化仪表板的创建、更新数据源等。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;安全性和认证的定制&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;可以定制Superset的安全性和认证机制，如集成企业的单点登录（SSO）系统。&lt;/li&gt;
  &lt;li&gt;修改认证流程以支持LDAP、OAuth等。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;使用和配置Celery任务调度器&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;使用Celery来优化和管理后台任务，如数据刷新、报告发送等。&lt;/li&gt;
  &lt;li&gt;可以定制Celery的配置以满足特定的性能和规模需求。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;参考链接：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://superset.apache.org/"&gt;Welcome | Superset (apache.org)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://github.com/apache/superset"&gt;GitHub – apache/superset: Apache Superset is a Data Visualization and Data Exploration Platform&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://github.com/amancevice/docker-superset"&gt;GitHub – amancevice/docker-superset: Docker image for Airbnb’s Superset&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;div&gt;

  &lt;h3&gt;相关文章:&lt;/h3&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/docker-jupyterhub.html" rel="bookmark" title="Docker&amp;#23433;&amp;#35013;&amp;#22810;&amp;#29992;&amp;#25143;&amp;#29256;JupyterHub"&gt;Docker安装多用户版JupyterHub&lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/hive-udf.html" rel="bookmark" title="Hive UDF&amp;#30340;&amp;#24320;&amp;#21457;&amp;#31616;&amp;#20171;"&gt;Hive UDF的开发简介&lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/microsoft-rest-api-guidelines.html" rel="bookmark" title="Microsoft REST API Guidelines&amp;#20013;&amp;#25991;&amp;#32763;&amp;#35793;"&gt;Microsoft REST API Guidelines中文翻译&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>器→工具 工具软件 BI</category>
      <guid isPermaLink="true">https://itindex.net/detail/62903-%E5%BC%80%E6%BA%90-%E5%8F%AF%E8%A7%86%E5%8C%96-%E5%B7%A5%E5%85%B7</guid>
      <pubDate>Thu, 04 Jan 2024 12:54:55 CST</pubDate>
    </item>
    <item>
      <title>文字语义纠错技术探索与实践-张健</title>
      <link>https://itindex.net/detail/62552-%E6%96%87%E5%AD%97-%E8%AF%AD%E4%B9%89-%E6%8A%80%E6%9C%AF</link>
      <description>&lt;h1&gt;  &lt;strong&gt;  背景    &lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;文本语义纠错的使用场景非常广泛，基本上只要涉及到写作就有文本纠错的需求。书籍面市前就有独立的校对的环节来保障出版之后不出现明显的问题。在新闻中我们也时不时看到因为文字审核没到位造成大乌龙的情况，包括上市公司在公开文书上把“临时大会”写成为“临死大会”，政府文件把“报效国家”写成了“报销国家”。有关文本纠错的辅助工具能给文字工作人员带来较大的便利，对审核方面的风险也大幅降低。&lt;/p&gt;



 &lt;p&gt;除了不同的写作场景，文本纠错还会用在其他一些智能处理系统中，具体的情况包括：音频通话记录经过自动语音识别（ASR）转写成文本之后，存在一些转译错误；光学字符识别（OCR）系统识别图片中的文字并进行提取，会存在字符识别错误；在搜索引擎或自动问答系统里面，用户在查询过程中的输入错误，往往会导致系统无法理解用户的真实意图，需要进行查询纠正改写。这些情况都需要通过文本纠错技术来进行修正，使产品整体的用户体验更加友好。&lt;/p&gt;



 &lt;p&gt;文本语义纠错在学术领域有三个子任务，分别是拼写检查（Spelling Check）、语法检错（Grammatical Error Detection）和语法纠错（Grammatical Error Correction）。其中语法检错是对文本中的语法错误进行检测，拼写检查是对文本中的错别字进行修正，语法纠错是纠正文本中的语法错误。拼写检查在英文场景表现为单词拼写错误，在中文场景表现为音近形近错别字。而语法纠错除此之外，还包括字词缺失、字词冗余、字词使用不当、语序不当等错误类型。语法纠错区别于拼写检查的一个显著特点是，语法纠错纠正后的文本和原始文本的长度不一定相等，而拼写检查纠正前后的文本长度都是保持一致的，这也决定了两者的算法支持存在差异。一般来说，拼写检查可以看作为语法纠错的一个任务子集。&lt;/p&gt;



 &lt;p&gt;我们对语法纠错的问题作一下形式化定义，输入的原始文本定义为X={x1,x2,...,xn};原始文本正确的纠正结果文本序列定义为Y={y1,y2,...,ym}，算法预测输出的文本，定义为P={p1,p2,...,pk}。&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;评估指标 &lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;在开始我们的文本语义纠错算法探索之旅之前，我们先思考一个问题，究竟怎么样的模型表现才是公认更有效的，这个好坏应该从何种方式、如何量化地评估出来。这也是我们在解决其他所有类型的NLP任务都需要先考虑的问题，这个问题就是如何定义我们的评测指标。下面罗列了纠错算法常用的一些评测指标：&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;01&lt;/strong&gt;  &lt;strong&gt;M2（MaxMatch）&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;M2指标主要是通过计算输出文本和原始文本之间的编辑集合G，然后与人工标注的编辑集合E结合，计算准确率、召回率、F0.5值（采用F0.5表示对准确率更加关注）。这里的编辑理解为一个转换动作，经过一组转换动作，可以完成原始文本到纠正文本的转换，M2指标定义形如：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/2f96b650-a98a-4bf3-bf58-fdc5258275b8.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;F0.5=1.25*RP/(R+0.25P)&lt;/p&gt;



 &lt;p&gt;下表罗列了一组示例和计算过程：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/81f46c04-6037-4075-b7aa-0d62628ab074.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;表 1 纠错文本示例&lt;/p&gt;



 &lt;p&gt;其中编辑集合G={孜→自，书→书写}，人工标注编辑集合E={孜→自,俱→具,读书→读}&lt;/p&gt;



 &lt;p&gt;可以计算出来:&lt;/p&gt;



 &lt;p&gt;P=1/2=0.5&lt;/p&gt;



 &lt;p&gt;R=1/3=0.33&lt;/p&gt;



 &lt;p&gt;F0.5=1.25*0.33*0.5/(0.33+0.25*0.5)=0.45&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;02 &lt;/strong&gt;  &lt;strong&gt;ERRANT&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;ERRANT[1]是升级版的M2。M2的局限性也比较明显，依靠前置的人工标注，有比较大的工作量，而且人工标注编辑集合产生的方式可能不太一致，导致匹配不准。ERRANT在生成标准答案的编辑集合和生成预测的编辑集合都采用了自动判别的方式，同时支持了25种的错误类型，输出了更丰富维度的错误报告信息。缺点是该工具面向英文，中文需要做较大改造。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;03&lt;/strong&gt;  &lt;strong&gt;面向标注形态的其他指标&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;上述两者在处理纠错任务评测时存在一些缺点，包括M2不支持检错性能评估，编辑不能正确反映合理纠错动作等等。  &lt;br /&gt;  &lt;br /&gt;我们会在一些学术评测上看到，根据对待纠文本进行的错误标注类型来制定的评测指标。下面举了NLPCC2022语法纠错评测指标为例，它对应的错误类型总共有赘余(Redundant Words，R)、遗漏(Missing Words，M)、误用(Word Selection，S)、错序(Word Ordering Errors，W)四类，评估的维度包含以下方面：&lt;/p&gt;



 &lt;ul&gt;
  &lt;li&gt;假阳性（False Positive）：正确句子被判包含错误的比例。&lt;/li&gt;



  &lt;li&gt;侦测层（Detective-level）：对句子是否包含错误做二分判断。从句子是否有错，判断p/r/f1&lt;/li&gt;



  &lt;li&gt;识别层（Identification-level）：给出错误点的错误类型。按一个句子的错误种类计算p/r/f1&lt;/li&gt;



  &lt;li&gt;定位层（Position-level）：对错误点的位置和覆盖范围进行判断，以字符偏移量计。错误位置是否对计算p/r/f1&lt;/li&gt;



  &lt;li&gt;修正层（Correction-level）：提交针对字符串误用（S）和缺失（M）两种错误类型的修正词语。修正词语可以是一个词，也可以是一个词组。M/S的修正词语角度&lt;/li&gt;
&lt;/ul&gt;



 &lt;p&gt;由于纠错任务本身的特殊性（同一个错误的文本可以有多种正确的纠正答案，或者同一个位置可以采用不同的错误类型进行标注），目前现存的评测指标大都有其局限性，如何定义主客观、统一、合理的语法纠错评测指标仍然在不断探讨。&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt; 公开数据集&lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;在确定了评估指标之后，我们已经确定了评判算法好坏的一个标准。锅已经端好，就等米下锅了,数据对于算法研发人员来说是必需品，一方面它是验证效果的信息来源,另一方面它是进行模型构建的训练语料。比较好的方式是从公开的渠道获取比较优质的标注数据。目前公开的中文语义纠错数据集包括NLPCC2018[2]、NLPTEA2020[3]、SIGHAN2015[4]等,较多是非母语学生学习汉语收集得来的语料集，训练和验证的数据标注形式如图所示:&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/ccee8a06-344d-443d-baaf-12959d28383b.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;图1 公开数据集（NLPTEA2020、NLPCC2020和SIGHAN2015）&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;无监督方法&lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;文本语义纠错的算法整体可以分成无监督和有监督的两种方式，我们先从无监督的方法开始看。无监督方法的核心是如何构建一个好用的语言模型，并且用在纠错的任务上。对于NLPer来说，我们经历了太多的预训练语言模型，像BERT、XLNet、GPT3等等，其本质还是语言模型或者说经典语言模型的一些变种。语言模型实际上是对文本序列的概率分布进行建模，通俗地来表达，语言模型是判断一句话是不是符合常理，或者说话应该怎么说才合理（符合概率分布）。这个正好就对应上了纠错任务的本质需求，我们从最经典的N元语言模型开始来介绍一下语法纠错的处理逻辑。   &lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;01&lt;/strong&gt;  &lt;strong&gt;n元语言模型&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;一个语言模型构建字符串的概率分布p(W)，假设p(W)是字符串作为句子的概率，则概率由下边的公式计算：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/68f2b5ca-b777-4865-8668-29f0032daf32.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;但是这样去计算句子概率会导致庞大的计算量，导致根据马尔科夫假设，一个词只和他前面n-1个词相关性最高，这就是n元语法模型，简化后的计算公式为：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/1ea20eca-647d-435c-a787-719f694e93a4.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;在得到这个结论之后，我们尝试使用N元语言模型来解决拼写检查的问题。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;假设我们采用的是5元语言模型，训练阶段使用大量的语料来进行统计所有的p(w5|w1w2w3w4)并存储起来。在预测阶段，设定待纠正的文本序列为W={w1,w2,...,wn}，针对每个位置的wk，我们通过预先构建好的混淆集获得w的音近形近字wk&amp;apos;。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;然后通过上述公式分别计算原始文本和修改文本的句子概率P(w1...wk...wn)、P(w1...wk&amp;apos;...wn)。如果P(w1...wk’...wn)&amp;gt;P(w1...wk...wn)，则说明修改后文本的通顺度提升（概率升高），可以接受该纠正修改（wk替换为wk&amp;apos;）。&lt;/p&gt;



 &lt;p&gt;从而我们的纠错执行过程则包含如下：&lt;/p&gt;



 &lt;ul&gt;
  &lt;li&gt;计算输入句子的归一化对数概率，并且为句子的每个字构建一个混淆集合；&lt;/li&gt;



  &lt;li&gt;对句子每个字针对其不同混淆字重新打分，应用单个最佳进行校正，将概率提高到当前最高值以上；&lt;/li&gt;



  &lt;li&gt;重复上面过程直至概率没变化。&lt;/li&gt;
&lt;/ul&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/2a13b0c9-02e2-4b46-bbca-739442d93356.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;图2 N元语言模型纠错执行计算过程&lt;/p&gt;



 &lt;p&gt;上述过程比较好理解，同时可以明显看出来一些硬伤，包括会OOV（未登录词）问题导致语言模型计算出来的概率为0；模型会过分优待高频短串，或者忽视低频短串。这时候需要通过平滑技术来改善概率算法，典型平滑方法包含Add-one、Interpolation和Modified Kneser-ney等。此外，仍有些难以通过技术手段解决的问题，包括上下文范围局限较大（n 的增加会导致计算和资源消耗成倍增加）和缺少泛化（缺乏实际予以的理解），此时需要引入基于神经网络的语言模型。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;02&lt;/strong&gt;  &lt;strong&gt;基于神经网络的语言模型&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;比较经典的基于神经网络的语言模型，数学表达式可以写为：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/832df924-e20f-4732-b0e7-854f6b37fbe5.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;以k元文法为例，把前k-1个词作为特征，用softmax预测最后一个词。&lt;/p&gt;



 &lt;p&gt;一般基于神经网络的语言模型设计得更加复杂，会把上下文的信息形成特征，来预测当中的每一个词。定义基于上下文context下wi的预测概率为P(wi|context_i),句子的概率可以表示为：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/87ec0acb-8593-4e18-88d4-28ebb4b57258.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;方法[5]就是采用了BERT和GPT作为基础的语言模型来计算句子的概率。&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;有监督方法&lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;无监督的纠错算法在处理文本时存在以下弱点：容易受局部高频或低频的序列影响，效果不够稳定；在需要对准确率和召回率进行平衡调整时，不太好通过阈值的方式进行控制；可以较好应用在拼写检查的任务上，但是对于句子长度有变化的语法纠错任务支持就比较弱。此时需要使用有监督算法来作为实现手段。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;01&lt;/strong&gt;  &lt;strong&gt;NMT/Seq2Seq&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;解决字词冗余/缺失这类纠错前后句子长度有变化的任务，我们第一感觉就想起可以通过文本生成的方式来训练对应的模型实现该功能。而且语法纠错任务和文本生成任务的形态基本上是一致的，也导致了文本生成模型很自然地被研究者注意，引入到语法纠错的任务领域。&lt;/p&gt;



 &lt;p&gt;NMT-based GEC[6]是第一篇通过使用神经网络机器翻译来实现语法纠错的文章。2014年seq2seq模型一提出即引发了较大反响，后续seq2seq成为了文本生成的主流结构。seq2seq将一个作为输入的序列映射为一个作为输出的序列，这一过程由编码（Encoder）输入与解码（Decoder）输出两个环节组成, 前者负责把序列编码成一个固定长度的向量，这个向量作为输入传给后者，输出可变长度的向量。下图展现了一个基础的seq2seq结构。&lt;/p&gt;


 &lt;div&gt;
  &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/326e96bc-a4f8-4c11-84c6-6e52e6de0f6a.jpg"&gt;&lt;/img&gt;&lt;/div&gt;


 &lt;p&gt;图3  seq2seq结构&lt;/p&gt;



 &lt;p&gt;方法[7]使用了经典的Encoder-Decoder模型结构来解决中文语法纠错问题，嵌入层使用了特殊的嵌入表示，同时在编码层使用了卷积神经网络强化了纠错的局部性，具体的模型结构如下：&lt;/p&gt;


 &lt;div&gt;
  &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/93b9891a-35da-4951-8c17-72af28cd587a.jpg"&gt;&lt;/img&gt;&lt;/div&gt;


 &lt;p&gt;图4 Encoder-Decoder结构纠错模型&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;02 &lt;/strong&gt;  &lt;strong&gt;LaserTagger&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;由于书写错误的出现概率普遍不高，纠错任务本身的输入输出存在大量重叠（基本不用改），所以大多数文本可以保持不变。但是我们在通过seq2seq的方式进行实现时，对于正常的字符也要全部进行预测，造成效率非常低下。因此谷歌在EMNLP 2019提出了LaserTagger，在使用Encoder-Decoder的模型结构条件下，把预测的内容从文字变成了编辑操作类型。lasertagger其模型结构（采用BERT作为编码层、自回归Transformer作为解码层）如下所示：  &lt;br /&gt;  &lt;br /&gt;  &lt;img src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/86e82b87-6d4c-4671-b7a3-c5e9c3a67112.jpg"&gt;&lt;/img&gt;&lt;/p&gt;



 &lt;p&gt;图5 LaserTagger纠错模型&lt;/p&gt;



 &lt;p&gt;编辑操作类型包含Keep（将单词复制到输出中），Delete（删除单词）和Add（在之前添加短语X），其中被添加的短语来自一个受限的词汇表。  &lt;br /&gt;通过结构的改造，lasertagger体现了推理速度快和样本训练效率高的有点。因为预测的类型只有三种，相对于seq2seq而言，解码的空间大幅降低，推理性能提升明显，相对于BERT+seq2seq的模型结构，larserTagger的性能提升接近100倍。同时因为预测的内容求解空间也大幅降低，所以对样本的需求量也大幅减少，在1000份的样本下也能取得不错的效果。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;03 &lt;/strong&gt;  &lt;strong&gt;PIE&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;与LaserTagger同年提出来的PIE（Parallel Iterative Edit Models）[8]同样是针对seq2seq 生成文本的可控性较差，推理速度也比较慢的问题进行来改进。与LarserTagger类似，PIE构造模型来对编辑操作进行预测，不过编辑操作的类型稍有区别，多了一个替换（replace)和词性变换（面向英文）。在处理替换和添加操作时，PIE将BERT编码层进行了扩展来支持替换和添加的信息输入，采用了一个双层的双向transformer，结构如下所示：  &lt;br /&gt;  &lt;br /&gt;  &lt;img src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/ce4efddc-017c-431b-8cca-212469d098b9.jpg"&gt;&lt;/img&gt;&lt;/p&gt;



 &lt;p&gt;图6 PIE纠错模型&lt;/p&gt;



 &lt;p&gt;上图表示了一个长度为3的文本输入（x1,x2,x3)。在最底层的输入层，M表示mask标识符的嵌入向量，p表示位置嵌入，x表示词嵌入。在中间层和输出层，r表示对应位置的替换信息，h表示对应位置的的原始信息，a表示对应位置的插入信息。之后利用三类信息来分别计算不同操作的概率，并归一化，CARDT 分别代表复制、插入、替换、删除、词形变换，计算公式如下：&lt;/p&gt;



 &lt;img alt="" src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/06b728d3-f079-45ff-97bf-9c5defebc4b9.jpg"&gt;&lt;/img&gt;



 &lt;p&gt;纠错过程中，PIE模型输出概率最高的编辑操作，完成修改后再迭代地进行预测，直至句子不发生改变后停止。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;PIE定义的结构可以实现在并行解码的同时保持较高的准确率，它在这篇文章第一次提出了seq2edit的概念。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;04 &lt;/strong&gt;  &lt;strong&gt;GECToR&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;GECToR[9]提出了一种序列标注模型，编码层由预训练的 BERT 型 transformer 组成，上面堆叠两个线性层，顶部有 softmax 层。模型输出的标签包含了基本变换和g-变换两种类型。其中基本变换包含保留（KEEP）、删除（DELETE）、添加（APPEND）和替换（REPLACE)。g-变换主要面向英文，针对了英语的语法变化总结出了5大类（大小写、单词合并、单词拆分、单复数和时态）29个小类的状态变换。  &lt;br /&gt;  &lt;br /&gt;GECToR另外两个亮点是引入了不同的预训练Transformer解码器（包括XLNet、RoBERTa、ALBERT、BERT和GPT-2）并进行了比较，以及采用了三阶段的训练方式。第一阶段使用了大量（九百万）实验合成的包含语法错误+语法正确的句子对进行预训练，第二阶段使用了少量的公开纠错数据集的句子对进行Fine-tuning，第三阶段使用了语法错误+正确和语法正确+正确的句子对来进行Fine-tuning，实验证明第三阶段的Fine-tuning有效果提升。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;在预测阶段，GECToR也是采用了多轮预测的方案。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;05 &lt;/strong&gt;  &lt;strong&gt;PLOME&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;PLOME[10]在2021ACL发表，是针对中文文本纠错任务构建的预训练语言模型，结构和BERT比较类似（12层Transformer）。PLOME的创新点主要在于采用了基于混淆集的掩码策略、把拼音和字形信息作为模型输入以及把字符和拼音的预测任务作为了模型的训练和微调目标。  &lt;br /&gt;  &lt;br /&gt;PLOME的掩码策略主要是基于以下4种：字音混淆词替换(Phonic Masking)、字形混淆词替换(Shape Masking)、随机替换（Random Masking）、原词不变（Unchanging）。PLOME的掩码策略同样仅遮盖15%的token，且4种MASK策略占比分别为: 60% 、15%、10%、15%。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;词嵌入模块方面，PLOME采用了字符嵌入(character embedding)、位置嵌入( position embedding)、语音嵌入(phonic embedding)和形状嵌入(shape embedding)。其中，字符嵌入和位置嵌入与BERT的输入一致。其中构建语音嵌入时，使用Unihan数据库得到字符-拼音的映射表(不考虑音调)， 然后将每一个字的多个拼音字母序列输入到GRU网络中，得到该字的拼音嵌入向量。同样，构建字形嵌入时，使用Chaizi数据库得到字形的笔画顺序，然后将字形的笔画顺序序列输入到GRU网络中，得到该字的字形嵌入向量。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;在训练任务方面，PLOME训练了2个任务，字符预测和BERT一样，增加了拼音的预测，预测被替换词的正确发音，更够更好解决同音和音近错误。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;PLOME预训练语言模型的下游任务主要是文本纠错任务。该任务的输入是字符序列 ,输出是预测的字符序列。该论文仅在拼写检查任务上做了验证。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;06&lt;/strong&gt;  &lt;strong&gt; 其他策略&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;（1）COPY机制&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;COPY机制同样是利用目标文本和源文本有大量重复这个特点。研究[11]提出了Copy-Augment的GEC模型，其主要思想是：在生成序列过程中，考虑两个生成分布：分别是复制输入序列中的词语（0/1表示是否复制)的概率分布和从候选词典中的词语生成的概率分布。将两者的概率分布加权求和作为最终生成的概率分布，进而预测每一个时刻生成的词语。基本架构如下：  &lt;br /&gt;  &lt;img src="https://yixiaoer-img.oss-cn-shanghai.aliyuncs.com/20221221/9169ccac-2eb0-47b6-b540-3754a8ce576d.jpg"&gt;&lt;/img&gt;&lt;/p&gt;



 &lt;p&gt;图7 COPY机制&lt;/p&gt;



 &lt;p&gt;该模型将简单的词语复制任务交给了Copy机制，将模型结构中的Attention等结构更多地用来学习比较难的新词生成，对训练更加可控。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;（2）数据增强&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;数据增强可以通过基于规则和基于生成的方式实现。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;基于规则这个比较简单，我们可以按照错误的类型（字词冗余、缺失、词序错误等）针对性地制定策略构造包含语法错误的样本，然后扔到模型来进行训练。不过基于规则的方式有点过于粗暴，很可能规则生成的错误与实际产生的错误差距比较大，或者比较不符合常规认知。&lt;/p&gt;



 &lt;p&gt;  &lt;br /&gt;第二种方式就是基于生成。最早基于生成的数据增强方式应该是回译法，就是将文本从一个语种翻译到另外一个语种，然后再翻译回来，从而构造了句子对，这种数据增强形式针对正常的编辑写作可能不太有效，更加符合跨语言学习的用户的错误特点。另外有研究[12]结合了分类器和自编码器来联合训练，达到生成固定类型错误样本的目的。而研究[13]通过对GEC模型进行对抗攻击，可以生成有价值的带有语法错误的句子，可以利用生成的句子训练GEC模型，提升性能的同时提升鲁棒性。&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;处理难点与技术挑战 &lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;  &lt;strong&gt;01&lt;/strong&gt;  &lt;strong&gt;语料收集&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;目前公开的中文语义纠错数据集主要是不同母语的人学习汉语作为第二语言收集得来的语料集，目前大部分关于语法纠错的算法模型都是基于这些数据集来做效果验证的，不过我们实际中要处理的数据通常并不是同样的形式诞生，更多是掌握汉语作为母语的人由于失误导致的语法错误，这种情况和公开预料的情况差别比较大，错误的分布差距也比较大，从而通过公开语料集训练得来的模型在上线到正常的业务流程里面，效果通常都会比较一般。    &lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;02&lt;/strong&gt;  &lt;strong&gt;长依赖&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;长距离包括跨语句依赖在论文等文本中很常见，一旦出现错误，很难察觉并纠正。当前语法研究大多集中在单个语句的语法检查和纠错，很少涉及长距离语法问题，相关数据集和模型方法缺失，是语法纠错的难题之一。  &lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;03&lt;/strong&gt;  &lt;strong&gt;模型的泛化能力与鲁棒性&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;一般来说，不同行业、不同领域的文本在措辞运用、表达习惯和专有名词等方面都存在较大的差异。譬如说政务机关红头文件非常严谨的语言表达和自媒体新闻相对较自由的文风就有明显的差别，又譬如金融行研报告和医学论文在基本内容和专业术语上也截然不同。在一个领域性能出色的纠错模型在切换到另外一个领域，往往效果下降明显。如何提升模型的泛化能力和鲁棒性，面临着巨大的技术挑战。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;04&lt;/strong&gt;  &lt;strong&gt;效果指标与体验的平衡&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;SOTA的指标可以刷到，但是这些模型一旦介入实际场景的数据，效果会差得一塌糊涂，这个一方面是由于模型和场景紧密相关，另外一方面是，通常公开数据集的错误分布是呈高密度，但是实际场景是低密度，会容易导致非常高的误判。譬如说SOTA里面准确率的指标是80%，对于在低密度错误的样本中，很可能准确率会下降到20~30%左右。纠错系统的体验会比较差。&lt;/p&gt;



 &lt;p&gt;  &lt;strong&gt;05&lt;/strong&gt;  &lt;strong&gt;效果指标与纠错性能的平衡&lt;/strong&gt;&lt;/p&gt;



 &lt;p&gt;工业界往往会采用pipeline的方式，先对文本进行检错，如果检测出来有错误，再对文本进行纠错处理。但是这个检错阶段的错误会传递到纠错阶段，导致效果下降。如果直接走seq2seq或seq2edit的纠错模型，或者需要融合多种模型策略来生成最终纠错结果，纠错的性能会下降非常快，部分实验3000字的纠错可能需要长达40~60秒，这个无法处理大量并发的文本纠错需求。我们需要再效果和性能上取得平衡，或者有更好的方法在保障效果指标的前提下提升纠错性能。&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;达观在语义纠错方面的产品实践 &lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;达观数据在语义纠错方面有比较深入的产品实践，开发出的投行质控系统和公文智能处理系统均处理了相关场景。&lt;/p&gt;



 &lt;p&gt;达观投行质控系统基于深度学习、NLP（自然语言处理）算法，帮助用户解决几大文书审核场景，包括:文书格式纠错，文字纠错和完整性审核；文档目录智能识别，一键定位：文档条款内容智能提取，方便业务人员对条款内容进行预审；支持文档多版本的内容比对等。&lt;/p&gt;



 &lt;p&gt;达观智能公文处理系统，严格遵循《党政机关公文处理工作条例》和《党政机关公文格式》规定，通过公文智能分析、公文知识库引用、公文审校、公文排版、公文格式纠错、公文内容语义纠错、公文在线比对修改等一体化的功能，实现基础字词校对准确率超90%、法律引用校验和公务文书完整性校对准确率超95%，有力提升政府机关整体公文质量，避免公文“带病”发布情况，确保政府机关公信度。&lt;/p&gt;



 &lt;p&gt;参考文献：&lt;/p&gt;



 &lt;p&gt;[1] Korre K, Pavlopoulos J. Errant: Assessing and improving grammatical error type classification[C]//Proceedings of the The 4th Joint SIGHUM Workshop on Computational Linguistics for Cultural Heritage, Social Sciences, Humanities and Literature. 2020: 85-89.&lt;/p&gt;



 &lt;p&gt;[2] Zhao Y, Jiang N, Sun W, et al. Overview of the nlpcc 2018 shared task: Grammatical error correction[C]//CCF International Conference on Natural Language Processing and Chinese Computing. Springer, Cham, 2018: 439-445.&lt;/p&gt;



 &lt;p&gt;[3] Rao G, Yang E, Zhang B. Overview of NLPTEA-2020 shared task for Chinese grammatical error diagnosis[C]//Proceedings of the 6th Workshop on Natural Language Processing Techniques for Educational Applications. 2020: 25-35.&lt;/p&gt;



 &lt;p&gt;[4] Tseng Y H, Lee L H, Chang L P, et al. Introduction to SIGHAN 2015 bake-off for Chinese spelling check[C]//Proceedings of the Eighth SIGHAN Workshop on Chinese Language Processing. 2015: 32-37.&lt;/p&gt;



 &lt;p&gt;[5] Alikaniotis D, Raheja V. The unreasonable effectiveness of transformer language models in grammatical error correction[J]. arXiv preprint arXiv:1906.01733, 2019.&lt;/p&gt;



 &lt;p&gt;[6] Yuan Z, Briscoe T. Grammatical error correction using neural machine translation[C]//Proceedings of the 2016 Conference of the North American Chapter of the Association for Computational Linguistics: Human Language Technologies. 2016: 380-386.&lt;/p&gt;



 &lt;p&gt;[7] Ren H, Yang L, Xun E. A sequence to sequence learning for Chinese grammatical error correction[C]//CCF International Conference on Natural Language Processing and Chinese Computing. Springer, Cham, 2018: 401-410.&lt;/p&gt;



 &lt;p&gt;[8] Awasthi A, Sarawagi S, Goyal R, et al. Parallel iterative edit models for local sequence transduction[J]. arXiv preprint arXiv:1910.02893, 2019.&lt;/p&gt;



 &lt;p&gt;[9] Omelianchuk K, Atrasevych V, Chernodub A, et al. GECToR--grammatical error correction: tag, not rewrite[J]. arXiv preprint arXiv:2005.12592, 2020.&lt;/p&gt;



 &lt;p&gt;[10] Liu S, Yang T, Yue T, et al. PLOME: Pre-training with misspelled knowledge for Chinese spelling correction[C]//Proceedings of the 59th Annual Meeting of the Association for Computational Linguistics and the 11th International Joint Conference on Natural Language Processing (Volume 1: Long Papers). 2021: 2991-3000.&lt;/p&gt;



 &lt;p&gt;[11] Zhao W, Wang L, Shen K, et al. Improving grammatical error correction via pre-training a copy-augmented architecture with unlabeled data[J]. arXiv preprint arXiv:1903.00138, 2019.&lt;/p&gt;



 &lt;p&gt;[12] Wan Z, Wan X, Wang W. Improving grammatical error correction with data augmentation by editing latent representation[C]//Proceedings of the 28th International Conference on Computational Linguistics. 2020: 2202-2212.&lt;/p&gt;



 &lt;p&gt;[13] Wang L, Zheng X. Improving grammatical error correction models with purpose-built adversarial examples[C]//Proceedings of the 2020 Conference on Empirical Methods in Natural Language Processing (EMNLP). 2020: 2858-2869.&lt;/p&gt;



 &lt;h1&gt;  &lt;strong&gt;作者简介&lt;/strong&gt;&lt;/h1&gt;



 &lt;p&gt;张健，达观数据联合创始人，复旦大学计算机软件与理论硕士，曾就职于盛大集团和腾讯文学，担任人工智能和大数据技术专家职位。目前担任达观数据文本应用部总负责人，对于机器学习算法和自然语言处理领域的研发有丰富的实践经验和技术积累，负责客户意见洞察系统、智能客服工单分析系统、文本语义纠错系统、事件分析平台、文本智能审核系统等多个文本应用产品的开发和落地。荣获上海市浦东新区科学技奖、“2021上海科技青年35人引领计划”、上海市青年科技启明星等多个奖项。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>MIT自然语言处理 NLP开源工具 中文信息处理 人工智能 机器学习</category>
      <guid isPermaLink="true">https://itindex.net/detail/62552-%E6%96%87%E5%AD%97-%E8%AF%AD%E4%B9%89-%E6%8A%80%E6%9C%AF</guid>
      <pubDate>Wed, 21 Dec 2022 15:10:41 CST</pubDate>
    </item>
    <item>
      <title>使用Excel搭建推荐系统</title>
      <link>https://itindex.net/detail/62488-excel-%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F</link>
      <description>&lt;p&gt;在上一篇  &lt;a href="https://www.biaodianfu.com/spreadsheets-and-excel.html"&gt;重新认识Excel&lt;/a&gt;的文章中，提到了Excel无所不能，然后就想到了曾经看到的这篇关于如何使用Excel搭建推荐引擎的文章。于是找了出来做了下简单的翻译（只翻译了重点部分）。&lt;/p&gt;
 &lt;p&gt;在互联网上有无限的货架空间，找到你想看的东西可能会让人筋疲力尽。幸运的是，与决策疲劳作斗争是 Netflix 的工作……而且他们很擅长。太擅长了。他们神奇地向您推荐完美的电影，这样您的眼睛就会一直盯着管子，他们会把您的拖延变成周末沙发上的狂欢。该死的，Netflix。你的秘诀是什么？你怎么这么了解我们？“魔法”非常简单，本教程使用分步电子表格揭示了其中的秘密。&lt;/p&gt;
 &lt;p&gt;尽管自Netflix Prize 竞赛以来有大量关于推荐系统的论文或视频，但大多数要么 (A) 技术太高，初学者无法使用，要么 (B) 水平太高，不实用。&lt;/p&gt;
 &lt;p&gt;在这篇文章中，我们将从头开始构建一个电影推荐系统，其中包含简单的英语解释和可以在 Excel 中遵循的分步公式。所有梯度下降推导都是手工计算的，您可以使用 Excel 下拉过滤器来微调模型的超参数并增强您的理解。&lt;/p&gt;
 &lt;h2&gt;学习内容&lt;/h2&gt;
 &lt;ul&gt;
  &lt;li&gt;构建帮助赢得 100 万美元 Netflix 奖金的算法版本 SVD++ 背后的确切步骤。&lt;/li&gt;
  &lt;li&gt;机器如何实际学习（梯度下降）。即使您从未告诉过 Netflix，也可以观看 Netflix 了解您的电影品味。&lt;/li&gt;
  &lt;li&gt;超参数调优。了解如何调整模型输入 （学习率、L2 正则化、# of epochs、权重初始化）以获得更好的预测。&lt;/li&gt;
  &lt;li&gt;模型评估和可视化。了解训练数据和测试数据之间的区别，如何防止过度拟合，并了解如何可视化模型的特征。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;在简要介绍了推荐系统之后，我将通过以下 4 个部分来构建一个模型来预测少数好莱坞明星的电影评分。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;第一部分：模型概览&lt;/li&gt;
  &lt;li&gt;第二部分：观看魔法秀（权重初始化和训练）&lt;/li&gt;
  &lt;li&gt;第三部分：魔法揭秘（梯度下降、导数）。我将逐步讲解机器学习魔法背后的数学，我将使用实数作为例子代入批量梯度下降的公式（不会使用“宏”或者Excel求解器之类的东西隐藏细节）。&lt;/li&gt;
  &lt;li&gt;第四部分：模型评估和可视化&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;推荐系统简介&lt;/h2&gt;
 &lt;p&gt;电影推荐系统可以简化为两大类：协同过滤（询问密友）和基于内容的过滤（标签匹配）&lt;/p&gt;
 &lt;h3&gt;协同过滤&lt;/h3&gt;
 &lt;p&gt;协同过滤基于类似行为进行推荐。&lt;/p&gt;
 &lt;p&gt;如果Ross和Rachel过去喜欢类似的东西，那么我们将Rachel喜欢而Ross没看过的电影向Ross推荐。你可以将他们看成是“协同”过滤网络货架上的噪音的“品味分身”。如果两个用户的评分有强相关性，那么我们就认定这两个用户“相似”。评分可以是隐式的，也可以是显式的：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;隐式（Binging）—— 整个周末，Ross和Rachel都沉溺于老剧《老友记》。尽管他们没人点赞，但我们相当确定他们喜欢《老友记》（以及他们可能有点自恋）。&lt;/li&gt;
  &lt;li&gt;显式（喜欢）—— Ross和Rachel都点了赞。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;协同过滤有两种：近邻方法和潜因子模型（矩阵分解的一种形式）。本文将聚焦一种称为  &lt;a href="https://www.biaodianfu.com/matrix-factorization.html#SVD++"&gt;SVD++&lt;/a&gt;的潜因子模型。&lt;/p&gt;
 &lt;h3&gt;基于内容的过滤&lt;/h3&gt;
 &lt;p&gt;基于你过去喜欢的内容的明确标签（类型、演员，等等），Netflix向你推荐具有类似标签的新内容。&lt;/p&gt;
 &lt;h3&gt;100 万美元的赢家&lt;/h3&gt;
 &lt;p&gt;当数据集足够大时，协同过滤（CF）是电影推荐器中的不二之选。&lt;/p&gt;
 &lt;p&gt;虽然这两个大类之间有无数的混合和变化，但出人意料的是，当CF模型足够好时，加上元数据并没有帮助。这是为什么呢？人会说谎，行动不会。让数据自己说话。人们所说的他们喜欢的东西（用户偏好、调查等）与他们的行为之间存在很大差距。最好让人们的观看行为自己说话。窍门：想要改善Netflix推荐？访问  &lt;a href="http://www.netflix.com/WiViewingActivity"&gt;/WiViewingActivity&lt;/a&gt;清理你的观看记录，移除你不喜欢的项。）&lt;/p&gt;
 &lt;p&gt;2009年，Netflix奖励了一队研究人员一百万美元，这个团队开发了一个算法，将Netflix的预测精确度提升了10%. 尽管获胜算法实际上是超过100种算法的集成，SVD++（一种协同过滤算法）是其中最关键的算法之一，贡献了大多数收益，目前仍在生产环境中使用。&lt;/p&gt;
 &lt;p&gt;我们将创建的SVD++模型（奇异值分解逼近）和Simon Funk的博客文章中提到差不多。这篇不出名的文章是2006年Simon在Netflix竞赛开始时写的，首次提出了SVD++模型。在SVD++模型成功之后，几乎所有的Netflix竞赛参加者都用它。&lt;/p&gt;
 &lt;h3&gt;SVD++ 关键思想&lt;/h3&gt;
 &lt;ul&gt;
  &lt;li&gt;“奇异值”（电影评分）可以“分解”或由一组隐藏的潜在因素（用户偏好和电影特征）决定，这些潜在因素直观地代表了类型、演员等。&lt;/li&gt;
  &lt;li&gt;潜在因素可以使用梯度下降和已知的电影评分迭代学习。&lt;/li&gt;
  &lt;li&gt;用户/电影偏见会影响某人的评分，并且也会被学习。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;简单，但功能强大。让我们深入挖掘。&lt;/p&gt;
 &lt;h2&gt;第一部分：模型概览&lt;/h2&gt;
 &lt;h3&gt;数据&lt;/h3&gt;
 &lt;p&gt;博客文章模型使用 30 个虚构评分（5 个用户 x 6 部电影）来简化教程。要在我们进行过程中跟随并试验模型，您可以在  &lt;a href="https://drive.google.com/open?id=1y4X8H56TS6M7AXAU7yIm0EyxhqNUy1sz"&gt;此处&lt;/a&gt;下载电子表格（Excel 或 Google 表格） 。&lt;/p&gt;
 &lt;h3&gt;拆分数据——训练集和测试集&lt;/h3&gt;
 &lt;p&gt;我们将使用25项评价来训练模型，剩下5项评价测试模型的精确度。&lt;/p&gt;
 &lt;p&gt;我们的目标是创建一个在25项已知评价（训练数据）上表现良好的系统，并希望它在5项隐藏（但已知）评价（测试数据）上做出良好的预测。&lt;/p&gt;
 &lt;p&gt;如果我们有更多数据，我们本可以将数据分为3组——训练集（约70%）、验证集（约20%）、测试集（约10%）。&lt;/p&gt;
 &lt;h3&gt;评价预测公式&lt;/h3&gt;
 &lt;p&gt;评价预测是用户/电影特征的矩阵乘法（“点积”）加上用户偏置，再加上电影偏置。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="422" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel1.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;公式为：&lt;/p&gt;
 &lt;p&gt;$$\hat{r}_{i,j}=((u_1 m_1)+(u_2 m_2)+(u_3 m_3)+u_{bias}+m_{bias})$$&lt;/p&gt;
 &lt;p&gt;其中：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;$\hat{r}_{i,j}$表示用户i对电影j的预测评价&lt;/li&gt;
  &lt;li&gt;$u_1$、$u_2$、$u_3$为用户潜因子&lt;/li&gt;
  &lt;li&gt;$m_1$、$m_2$、$m_3$为电影潜因子&lt;/li&gt;
  &lt;li&gt;$u_{bias}$为用户偏置&lt;/li&gt;
  &lt;li&gt;$m_{bias}$为电影偏置&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;用户/电影特征&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;直觉上说，这些特征表示类型、演员、片长、导演、年代等因素。尽管我们并不清楚每项特征代表什么，但是当我们将其可视化后（见第四部分）我们可以凭直觉猜测它们可能代表什么。&lt;/li&gt;
  &lt;li&gt;出于简单性，我使用了3项特征，但实际的模型可能有50、100乃至更多特征。特征过多时，模型将“过拟合/记忆”你的训练数据，难以很好地推广到测试数据的预测上。&lt;/li&gt;
  &lt;li&gt;如果用户的第1项特征（让我们假定它表示“喜剧”）值较高，同时电影的“喜剧”特征的值也很高，那么电影的评价会比较高。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;用户/电影偏置&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;用户偏置取决于评价标准的宽严程度。如果Netflix上所有的平均评分是3.5，而你的所有评分的均值是4.0，那么你的偏置是0.5. 电影偏置同理。如果《泰坦尼克号》的所有用户的评分均值为4.25，那么它的偏置是0.75（= 4.25 – 3.50）。&lt;/p&gt;
 &lt;h3&gt;RMSE —— 评估预测精确度&lt;/h3&gt;
 &lt;p&gt;RMSE = Root Mean Squared Error （均方根误差）&lt;/p&gt;
 &lt;p&gt;RMSE是一个数字，尝试回答以下问题“平均而言，预测评价和实际平均差了几颗星（1-5）？”&lt;/p&gt;
 &lt;p&gt;RMSE越低，意味着预测越准……&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="532" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-2.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;观察：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;我们只在意绝对值差异。相比实际评分高估了1分的预测，和相比实际评分低估了1分的预测，误差相等，均为1。&lt;/li&gt;
  &lt;li&gt;RMSE是误差同数量级的平均，而不是误差绝对值的平均。在我们上面的例子中，误差绝对值的平均是75（1 + 1 + 0.25 = 2.25，2.25 / 3 = 0.75），但RMSE是0.8292. RMSE给较大的误差更高的权重，这很有用，因为我们更不希望有较大的误差。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;超参数调整&lt;/h3&gt;
 &lt;p&gt;通过电子表格的下拉过滤器，可以调整模型的3个超参数。你应该测试下每种超参数，看看它们对误差的影响。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;训练epoch数—— 1个epoch意味着整个训练集都过了一遍&lt;/li&gt;
  &lt;li&gt;学习率—— 控制调整权重/偏置的速度&lt;/li&gt;
  &lt;li&gt;L2（lambda）惩罚因子—— 帮助模型预防过拟合训练数据，以更好地概括未见测试数据。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" height="109" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-3.png" width="324"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;现在，让我们看一场魔法秀，看看模型是如何从随机权重开始，学习最优权重的。&lt;/p&gt;
 &lt;h2&gt;第二部分：观看魔法秀（权重初始化和训练）&lt;/h2&gt;
 &lt;p&gt;观看梯度下降的实际操作感觉就像您在观看 David Blaine 的魔术。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;他到底是怎么知道我会在52张牌中选这张的呢？&lt;/li&gt;
  &lt;li&gt;等等，他刚刚是不是浮空了？&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;最后你深感敬畏，想要知道魔术是如何变的。我会分两步演示，接着揭露魔法背后的数学。&lt;/p&gt;
 &lt;h3&gt;抽一张卡，随便抽一张（权重初始化）&lt;/h3&gt;
 &lt;p&gt;在训练开始，用户/电影特征的权重是随机分配的，接着算法在训练中学习最佳的权重。&lt;/p&gt;
 &lt;p&gt;为了揭示这看起来有多么“疯狂”，我们可以随机猜测数字，然后让计算机学习最佳数字。下面是两种权重初始化方案的比较：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;简单—— 用户特征我随机选择了1、0.2、0.3，剩下的特征都分配0.1.&lt;/li&gt;
  &lt;li&gt;Kaiming He—— 更正式、更好的初始化方法，从高斯分布（“钟形曲线”）中随机抽样作为权重，高斯分布的均值为零，标准差由特征个数决定（细节见后）。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;观赏魔术（查看训练误差）&lt;/h3&gt;
 &lt;p&gt;看看使用以上两种方案学习权重最佳值的效果，从开始（epoch 0）到结束（epoch 50），RMSE训练误差是如何变化的：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="1200" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-5.gif" width="1068"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;如你所见，两种权重初始化方法在训练结束时都会收敛到类似的“误差”（0.12 与 0.17），但“   &lt;a href="http://www.jefkine.com/deep/2016/08/08/initialization-of-deep-networks-case-of-rectifiers/"&gt;Kaiming He”&lt;/a&gt;方法更快地收敛到较低的误差。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;关键要点：无论我们从哪个权重开始，机器都会随着时间的推移学习到好的值！&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="365" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-6.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;注意：如果你想要试验其他初始化权重，可以在电子表格的“hyperparameters_and_initial_wts”表的G3-J7、N3-Q8单元格中输入你自己的值。权重取值范围为-1到1.&lt;/p&gt;
 &lt;p&gt;想要了解更多关于Kaiming He初始化的内容，请接着读下去；否则，可以直接跳到第3部分学习算法的数学。&lt;/p&gt;
 &lt;h3&gt;Kaiming He权重初始化&lt;/h3&gt;
 &lt;p&gt;权重 = 正态分布随机抽样，分布均值为0，标准差为 (=SquareRoot(2/# of features))&lt;/p&gt;
 &lt;p&gt;电子表格中的值由以下公式得到：=NORMINV(RAND(),0,SQRT(2/3))&lt;/p&gt;
 &lt;p&gt;$$W_l \sim \mathcal{N}(0, \sqrt{\frac{2}{n_l}}) \text{and} \mathbf{b}=0$$&lt;/p&gt;
 &lt;h2&gt;第三部分：魔法揭秘&lt;/h2&gt;
 &lt;p&gt;现在，是时候书呆一点，一步一步地了解梯度下降的数学了。如果你不是真想知道魔法是如何起效的，那么可以跳过这一部分，直接看第4部分。&lt;/p&gt;
 &lt;p&gt;梯度下降是在训练时使用的迭代算法，通过梯度下降更新电影特征、用户偏好的权重和偏置，以做出更好的预测。&lt;/p&gt;
 &lt;p&gt;梯度下降的一般周期为：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;第 1 步 – 定义成本/损失函数以最小化和初始化权重&lt;/li&gt;
  &lt;li&gt;第 2 步 — 计算预测&lt;/li&gt;
  &lt;li&gt;第 3 步 – 计算梯度（相对于每个权重的成本变化）&lt;/li&gt;
  &lt;li&gt;第 4 步——在最小化成本的方向上更新每个权重“一点点地”（学习率）&lt;/li&gt;
  &lt;li&gt;第 5 步 — 重复第 2-4 步&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;你可以访问电子表格的“training”（训练）表，其中第11-16行是更新Tina Fey的第一项用户特征的过程。&lt;/p&gt;
 &lt;p&gt;由于数据集很小，我们将使用批量梯度下降。这意味着我们在训练时将使用整个数据集（在我们的例子中，一个用户的所有电影），而不是像随机梯度下降之类的算法一样每次迭代一个样本（在我们的例子中，一个用户的一部电影），当数据集较大时，随机梯度下降更快。&lt;/p&gt;
 &lt;h3&gt;定义最小化的代价函数&lt;/h3&gt;
 &lt;p&gt;我们将使用下面的公式，我们的目标是找到合适的潜因子（矩阵U、M）的值，以最小化SSE（平方误差之和）加上一个帮助模型提升概括性的L2权重惩罚项。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="529" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-7.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;下面是Excel中的代价函数计算。计算过程忽略了1/2系数，因为它们仅用于梯度下降以简化计算。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="396" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-8.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;L2正则化和过拟合&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;我们加入了权重惩罚（L2正则化或“岭回归”）以防止潜因子值过高。这确保模型没有“过拟合”（也就是记忆）训练数据，否则模型在未见的测试电影上表现不会好。&lt;/p&gt;
 &lt;p&gt;之前，我们没有使用L2正则化惩罚（系数为0）的情况下训练模型，50个epoch后，RMSE训练误差为0.12.&lt;/p&gt;
 &lt;p&gt;但是模型在测试数据上的表现如何呢？&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="402" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-9.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;如果我们将 L2 惩罚因子从 0.000 更改为 0.300，我们的模型应该可以更好地概括未见过的测试数据：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="426" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-10.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;计算预测&lt;/h3&gt;
 &lt;p&gt;我们将计算Tina的电影预测。我们将忽略《泰坦尼克号》，因为它在测试数据集中，不在训练数据集中。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="313" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-11.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;计算梯度&lt;/h3&gt;
 &lt;p&gt;目标是找到误差对应于将更新的权重的梯度（“坡度”）。&lt;/p&gt;
 &lt;p&gt;得出梯度之后，稍微将权重“移动一点点”，沿着梯度的反方向“下降”，在对每个权重进行这一操作后，下一epoch的代价应该会低一些。&lt;/p&gt;
 &lt;p&gt;“移动一点点”具体移动多少，取决于学习率。在得到梯度（3.3）之后，会用到学习率。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="238" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-12.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;梯度下降法则：将权重往梯度的反方向移动，以减少误差&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;第1步：计算Tina Fey的第一个潜因子的代价梯度（$u_1$）&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;1.1整理代价目标函数，取代价在Tina Fey的第一个潜因子（$u_1$）上的偏导数。&lt;/p&gt;
 &lt;p&gt;$$\frac{\partial J(cost)}{\partial u_1}=\frac{1}{2} \sum_{(i, j): r(i,)=1}(\hat{r}_{i, j}-r_{i, j})^2+\frac{1}{2} \lambda(\sum_i\left\|u_i\right\|^2+\sum_j\left\|m_j\right\|^2)$$&lt;/p&gt;
 &lt;p&gt;1.2整理预测评价函数，改写为用户潜因子的平方和加上电影潜因子的平方和&lt;/p&gt;
 &lt;p&gt;$$\frac{\partial J}{\partial u_1}=\frac{1}{2} \sum(((u_1 m_1)+(u_2 m_2)+(u_3 m_3)+u_{bias }+m_{bias})-r_{i, j})^2+ \frac{1}{2} \lambda \sum(u_1^2+u_2^2+u_3^2)+\frac{1}{2} \lambda \sum (m_1^2+m_2^2+m_3^2)$$&lt;/p&gt;
 &lt;p&gt;1.3将公式每部分中的$u_1$视为常数，取$u_1$在公式每部分的代价上的偏导数。&lt;/p&gt;
 &lt;p&gt;1.3.1（Part 1 of 3）应用“链式法则”以得到偏导数。链式法则意味着我们将((外层函数的导数)*内层函数)* (内部函数的导数)&lt;/p&gt;
 &lt;p&gt;外层函数的导数：&lt;/p&gt;
 &lt;p&gt;$$\begin{gathered}=\frac{1}{2} \sum(((u_1 m_1)+(u_2 m_2)+(u_3 m_3)+u_{bias}+m_{bias})-r_{i, j})^2 \leftarrow \text { power rule} \\=2 \times \frac{1}{2}(((u_1 m_1)+(u_2 m_2)+(u_3 m_3)+u_{bias}+m_{bias})-r_{i, j})^1 \\=\text { (predicted rating }-\text { actual rating }) \\=(\text { error })\end{gathered}$$&lt;/p&gt;
 &lt;p&gt;内层函数的导数：&lt;/p&gt;
 &lt;p&gt;$$\begin{gathered}=\frac{1}{2} \sum((u_1 m_1)+(u_2 m_2)+(u_3 m_3)+u_{bias}+m_{bias})-r_{i, j} \leftarrow \text {constant rule} \\ ((u_1 m_1)+(0)+(0)+0+0)-0 \\ =m_1 \\ 1.3.1=(\text{error} x m_1)\end{gathered}$$&lt;/p&gt;
 &lt;p&gt;1.3.1（Part 2 of 3）应用“幂法则”以得到偏导数。根据幂法则，指数为2，所以将指数降1，并乘上系数1/2. $u_2$和$u_3$视作常数，变为0.&lt;/p&gt;
 &lt;p&gt;$$\begin{gathered}=\frac{1}{2} \lambda \sum(u_1^2+u_2^2+u_3^2) \\=2 \times \frac{1}{2} \times \lambda(u_1^1+0+0) \\=\lambda \times u_1 \\1.3 .2= (\lambda \times u_1)\end{gathered}$$&lt;/p&gt;
 &lt;p&gt;1.3.3 (Part 3 of 3 ) 应用“常数法则”以得到偏导数。&lt;/p&gt;
 &lt;p&gt;$$\begin{gathered}=\frac{1}{2} \lambda \sum(m_1^2+m_2^2+m_3^2) \\=0\end{gathered}$$&lt;/p&gt;
 &lt;p&gt;由于$u_1$对这些项毫无影响，结果是0。&lt;/p&gt;
 &lt;p&gt;$$1.3.3 =0$$&lt;/p&gt;
 &lt;p&gt;1.4 结合1.3.1、1.3.2、1.3.3得到代价在$u_1$上的偏导数。&lt;/p&gt;
 &lt;p&gt;$$\begin{aligned} \quad \text { Part } 1+\text { Part } 2+\text { Part } 3 \\ \frac{\partial J}{\partial u_1} &amp;amp;=(\text { error } x m_1)+(\lambda \times u_1)+0 \\ &amp;amp;=(\text { error } x m_1)+(\lambda \times u_1)\end{aligned}$$&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;第2步：对训练集中Tina看过的每部电影，利用前面的公式计算梯度，接着计算Tina看过的所有电影的平均梯度。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="509" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-13.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;更新权重&lt;/h3&gt;
 &lt;p&gt;学习Tina的旧$u_1$，学习率($\alpha$)，以及上面计算的平均梯度，更新$u_1$。我们将使用的学习率为0.3。&lt;/p&gt;
 &lt;p&gt;Gradient descent formula:&lt;/p&gt;
 &lt;p&gt;$$New \ u_1= old \ u_1-\alpha (average \ gradient)$$&lt;/p&gt;
 &lt;p&gt;$$New \ u_1=(0.66)-0.3(1.92)$$&lt;/p&gt;
 &lt;p&gt;$$New \ u_1=(0.66)+0.58$$&lt;/p&gt;
 &lt;p&gt;$$New \ u_1=(0.08)$$&lt;/p&gt;
 &lt;p&gt;“training”（训练）表的X11-X16单元格对应上面的计算过程。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="251" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-14.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;你可以看到，电影特征和用户/电影偏置以类似的方式更新。&lt;/p&gt;
 &lt;p&gt;每一个训练epoch更新所有的电影/用户特征及偏置。&lt;/p&gt;
 &lt;h2&gt;第四部分：模型评估和可视化&lt;/h2&gt;
 &lt;p&gt;现在我们已经训练好了模型，让我们可视化电影的2个潜因子。&lt;/p&gt;
 &lt;p&gt;如果我们的模型更复杂，包括10、20、50+潜因子，我们可以使用一种称为“  &lt;a href="https://www.biaodianfu.com/pca.html"&gt;主成分分析（PCA）&lt;/a&gt;”的技术提取出最重要的特征，接着将其可视化。&lt;/p&gt;
 &lt;p&gt;相反，我们的模型仅仅包括3项特征，所以我们将可视化其中的2项特征，基于学习到的特征将每部电影绘制在图像上。绘制图像之后，我们可以解释每项特征“可能代表什么”。&lt;/p&gt;
 &lt;p&gt;从直觉出发，电影特征1可能解释为悲剧与喜剧，而电影特征3可能解释为男性向与女性向。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="735" src="https://www.biaodianfu.com/wp-content/uploads/2022/11/excel-15.png" width="700"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;这不是完美的解释，但还算一种合理的解释。《勇士》（warrior）一般归为剧情片，而不是喜剧片。不过其他电影基本符合以上解释。&lt;/p&gt;
 &lt;h2&gt;总结&lt;/h2&gt;
 &lt;p&gt;电影评价由一个电影向量和一个用户向量组成。在你评价了一些电影之后（显式或隐式），推荐系统将利用群体的智慧和你的评价预测你可能喜欢的其他电影。向量（或“潜因子”）的维度取决于数据集的大小，可以通过试错法确定。&lt;/p&gt;
 &lt;p&gt;我鼓励你实际操作下电子表格，看看改变模型的超参数会带来什么改变。&lt;/p&gt;
 &lt;p&gt;参考链接：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://towardsdatascience.com/netflix-and-chill-building-a-recommendation-system-in-excel-c69b33c914f4"&gt;Netflix and Chill: Building a Recommendation System in Excel&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;div&gt;
  &lt;h3&gt;相关文章:&lt;/h3&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/lda-latent-dirichlet-allocation.html" rel="bookmark" title="&amp;#20027;&amp;#39064;&amp;#27169;&amp;#22411;LDA(Latent Dirichlet Allocation)&amp;#21021;&amp;#25506;"&gt;主题模型LDA(Latent Dirichlet Allocation)初探 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/learning-to-ranking.html" rel="bookmark" title="&amp;#25490;&amp;#24207;&amp;#20248;&amp;#21270;&amp;#31639;&amp;#27861;Learning to Ranking"&gt;排序优化算法Learning to Ranking &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/matrix-factorization.html" rel="bookmark" title="&amp;#25512;&amp;#33616;&amp;#31639;&amp;#27861;&amp;#20043;&amp;#30697;&amp;#38453;&amp;#20998;&amp;#35299;"&gt;推荐算法之矩阵分解 &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>器→工具 工具软件 数据 术→技巧 机器学习</category>
      <guid isPermaLink="true">https://itindex.net/detail/62488-excel-%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F</guid>
      <pubDate>Sun, 13 Nov 2022 23:06:06 CST</pubDate>
    </item>
    <item>
      <title>Linux文件系统inode详解</title>
      <link>https://itindex.net/detail/62362-linux-%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F-inode</link>
      <description>&lt;h2&gt;文件系统&lt;/h2&gt;
 &lt;p&gt;文件系统是操作系统中负责管理持久数据的子系统，说简单点，就是负责把用户的文件存到磁盘硬件中，因为即使计算机断电了，磁盘里的数据并不会丢失，所以可以持久化的保存文件。&lt;/p&gt;
 &lt;p&gt;文件系统的基本数据单位是文件，它的目的是对磁盘上的文件进行组织管理，那组织的方式不同，就会形成不同的文件系统。&lt;/p&gt;
 &lt;p&gt;Linux 最经典的一句话是：「一切皆文件」，不仅普通的文件和目录，就连块设备、管道、socket 等，也都是统一交给文件系统管理的。&lt;/p&gt;
 &lt;p&gt;Linux 文件系统会为每个文件分配两个数据结构：索引节点（index node）和目录项（directory entry），它们主要用来记录文件的元信息和目录层次结构。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="357" src="https://www.biaodianfu.com/wp-content/uploads/2022/08/inode.png" width="480"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;索引节点，也就是inode，用来记录文件的元信息，比如 inode 编号、文件大小、访问权限、创建时间、修改时间、数据在磁盘的位置等等。索引节点是文件的唯一标识，它们之间一一对应，也同样都会被存储在硬盘中，所以索引节点同样占用磁盘空间。&lt;/li&gt;
  &lt;li&gt;目录项，也就是dentry，用来记录文件的名字、索引节点指针以及与其他目录项的层级关联关系。多个目录项关联起来，就会形成目录结构，但它与索引节点不同的是，目录项是由内核维护的一个数据结构，不存放于磁盘，而是缓存在内存。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;由于索引节点唯一标识一个文件，而目录项记录着文件的名，所以目录项和索引节点的关系是多对一，也就是说，一个文件可以有多个目录。比如，硬链接的实现就是多个目录项中的索引节点指向同一个文件。&lt;/p&gt;
 &lt;p&gt;注意，目录也是文件，也是用索引节点唯一标识，和普通文件不同的是，普通文件在磁盘里面保存的是文件数据，而目录文件在磁盘里面保存子目录或文件。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;目录项和目录是一个东西吗？&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;虽然名字很相近，但是它们不是一个东西，目录是个文件，持久化存储在磁盘，而目录项是内核一个数据结构，缓存在内存。&lt;/p&gt;
 &lt;p&gt;如果查询目录频繁从磁盘读，效率会很低，所以内核会把已经读过的目录用目录项这个数据结构缓存在内存，下次再次读到相同的目录时，只需从内存读就可以，大大提高了文件系统的效率。&lt;/p&gt;
 &lt;p&gt;注意，目录项这个数据结构不只是表示目录，也是可以表示文件的。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;文件数据是如何存储在磁盘的呢？&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;磁盘读写的最小单位是扇区，扇区的大小只有 512字节，那么如果数据大于512字节时候，磁盘需要不停地移动磁头来查找数据，我们知道一般的文件很容易超过512字节那么如果把多个扇区合并为一个块，那么磁盘就可以提高效率了。那么磁头一次读取多个扇区就为一个块“block”（Linux上称为块，Windows上称为簇）。所以，文件系统把多个扇区组成了一个逻辑块，每次读写的最小单位就是逻辑块（数据块），Linux 中的逻辑块大小为 4KB，也就是一次性读写 8 个扇区，这将大大提高了磁盘的读写的效率。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="333" src="https://www.biaodianfu.com/wp-content/uploads/2022/08/filesystem.png" width="480"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;文件系统记录的数据，除了其自身外，还有数据的权限信息，所有者等属性，这些信息都保存在inode中，那么谁来记录inode信息和文件系统本身的信息呢，比如说文件系统的格式，inode与data的数量呢？那么就有一个超级区块（supper block）来记录这些信息了。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="601" src="https://www.biaodianfu.com/wp-content/uploads/2022/08/supper-block.png" width="720"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;superblock：记录此 filesystem 的整体信息，包括inode/block的总量、使用量、剩余量， 以及文件系统的格式与相关信息等&lt;/li&gt;
  &lt;li&gt;inode：记录文件的属性信息，可以使用stat命令查看inode信息。&lt;/li&gt;
  &lt;li&gt;block：实际文件的内容，如果一个文件大于一个块时候，那么将占用多个block，但是一个块只能存放一个文件。（因为数据是由inode指向的，如果有两个文件的数据存放在同一个块中，就会乱套了）&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" height="254" src="https://www.biaodianfu.com/wp-content/uploads/2022/08/data.png" width="480"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;node用来指向数据block，那么只要找到inode，再由inode找到block编号，那么实际数据就能找出来了。&lt;/p&gt;
 &lt;p&gt;索引节点是存储在硬盘上的数据，为了加速文件的访问，通常会把索引节点加载到内存中。我们不可能把超级块和索引节点区全部加载到内存，这样内存肯定撑不住，所以只有当需要使用的时候，才将其加载进内存，它们加载进内存的时机是不同的：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;超级块：当文件系统挂载时进入内存；&lt;/li&gt;
  &lt;li&gt;索引节点区：当文件被访问时进入内存；&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;虚拟文件系统&lt;/h2&gt;
 &lt;p&gt;文件系统的种类众多，而操作系统希望对用户提供一个统一的接口，于是在用户层与文件系统层引入了中间层，这个中间层就称为虚拟文件系统（Virtual File System，VFS）。VFS 定义了一组所有文件系统都支持的数据结构和标准接口，这样程序员不需要了解  &lt;a href="https://www.biaodianfu.com/linux-windows-mac-os-file_systems.html"&gt;文件系统&lt;/a&gt;的工作原理，只需要了解 VFS 提供的统一接口即可。在 Linux 文件系统中，用户空间、系统调用、虚拟机文件系统、缓存、文件系统以及存储之间的关系如下图：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="630" src="https://www.biaodianfu.com/wp-content/uploads/2022/08/vfs.png" width="480"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Linux 支持的文件系统也不少，根据存储位置的不同，可以把文件系统分为三类：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;磁盘的文件系统，它是直接把数据存储在磁盘中，比如 Ext 2/3/4、XFS 等都是这类文件系统。&lt;/li&gt;
  &lt;li&gt;内存的文件系统，这类文件系统的数据不是存储在硬盘的，而是占用内存空间，我们经常用到的/proc 和 /sys 文件系统都属于这一类，读写这类文件，实际上是读写内核中相关的数据。&lt;/li&gt;
  &lt;li&gt;网络的文件系统，用来访问其他计算机主机数据的文件系统，比如 NFS、SMB 等等。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;文件系统首先要先挂载到某个目录才可以正常使用，比如 Linux 系统在启动时，会把文件系统挂载到根目录。&lt;/p&gt;
 &lt;p&gt;Linux 采用为分层的体系结构，将用户接口层、文件系统实现和存储设备的驱动程序分隔开，进而兼容不同的文件系统。虚拟文件系统（Virtual File System, VFS）是 Linux 内核中的软件层，它在内核中提供了一组标准的、抽象的文件操作，允许不同的文件系统实现共存，并向用户空间程序提供统一的文件系统接口。下面这张图展示了 Linux 虚拟文件系统的整体结构：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="391" src="https://www.biaodianfu.com/wp-content/uploads/2022/08/vfs-architecture.png" width="640"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt; &lt;/p&gt;
 &lt;p&gt;从上图可以看出，用户空间的应用程序直接、或是通过编程语言提供的库函数间接调用内核提供的 System Call 接口（如open()、write()等）执行文件操作。System Call 接口再将应用程序的参数传递给虚拟文件系统进行处理。&lt;/p&gt;
 &lt;p&gt;每个文件系统都为 VFS 实现了一组通用接口，具体的文件系统根据自己对磁盘上数据的组织方式操作相应的数据。当应用程序操作某个文件时，VFS 会根据文件路径找到相应的挂载点，得到具体的文件系统信息，然后调用该文件系统的对应操作函数。&lt;/p&gt;
 &lt;p&gt;VFS 提供了两个针对文件系统对象的缓存 INode Cache 和 DEntry Cache，它们缓存最近使用过的文件系统对象，用来加快对 INode 和 DEntry 的访问。Linux 内核还提供了 Buffer Cache 缓冲区，用来缓存文件系统和相关块设备之间的请求，减少访问物理设备的次数，加快访问速度。Buffer Cache 以 LRU 列表的形式管理缓冲区。&lt;/p&gt;
 &lt;p&gt;VFS 的好处是实现了应用程序的文件操作与具体的文件系统的解耦，使得编程更加容易：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;应用层程序只要使用 VFS 对外提供的read()、write()等接口就可以执行文件操作，不需要关心底层文件系统的实现细节；&lt;/li&gt;
  &lt;li&gt;文件系统只需要实现 VFS 接口就可以兼容 Linux，方便移植与维护；&lt;/li&gt;
  &lt;li&gt;无需关注具体的实现细节，就实现跨文件系统的文件操作。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;了解 Linux 文件系统的整体结构后，下面主要分析 Linux VFS 的技术原理。由于文件系统与设备驱动的实现非常复杂，笔者也未接触过这方面的内容，因此文中不会涉及具体文件系统的实现。&lt;/p&gt;
 &lt;h3&gt;VFS 结构&lt;/h3&gt;
 &lt;p&gt;Linux 以一组通用对象的角度看待所有文件系统，每一级对象之间的关系如下图所示：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="427" src="https://www.biaodianfu.com/wp-content/uploads/2022/08/vfs-object.png" width="640"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;fd 与 file&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;每个进程都持有一个fd[]数组，数组里面存放的是指向file结构体的指针，同一进程的不同fd可以指向同一个file对象；&lt;/p&gt;
 &lt;p&gt;file是内核中的数据结构，表示一个被进程打开的文件，和进程相关联。当应用程序调用open()函数的时候，VFS 就会创建相应的file对象。它会保存打开文件的状态，例如文件权限、路径、偏移量等等。&lt;/p&gt;
 &lt;pre&gt;// https://elixir.bootlin.com/linux/v5.4.93/source/include/linux/fs.h#L936 结构体已删减
struct file {
    struct path                   f_path;
    struct inode                  *f_inode;
    const struct file_operations  *f_op;
    unsigned int                  f_flags;
    fmode_t                       f_mode;
    loff_t                        f_pos;
    struct fown_struct            f_owner;
}

// https://elixir.bootlin.com/linux/v5.4.93/source/include/linux/path.h#L8
struct path {
    struct vfsmount  *mnt;
    struct dentry    *dentry;
}
&lt;/pre&gt;
 &lt;p&gt;从上面的代码可以看出，文件的路径实际上是一个指向 DEntry 结构体的指针，VFS 通过 DEntry 索引到文件的位置。&lt;/p&gt;
 &lt;p&gt;除了文件偏移量f_pos是进程私有的数据外，其他的数据都来自于 INode 和 DEntry，和所有进程共享。不同进程的file对象可以指向同一个 DEntry 和 Inode，从而实现文件的共享。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;DEntry 与 INode&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;Linux文件系统会为每个文件都分配两个数据结构，目录项（DEntry, Directory Entry）和索引节点（INode, Index Node）。&lt;/p&gt;
 &lt;p&gt;DEntry 用来保存文件路径和 INode 之间的映射，从而支持在文件系统中移动。DEntry 由 VFS 维护，所有文件系统共享，不和具体的进程关联。dentry对象从根目录“/”开始，每个dentry对象都会持有自己的子目录和文件，这样就形成了文件树。举例来说，如果要访问”/home/beihai/a.txt”文件并对他操作，系统会解析文件路径，首先从“/”根目录的dentry对象开始访问，然后找到”home/“目录，其次是“beihai/”，最后找到“a.txt”的dentry结构体，该结构体里面d_inode字段就对应着该文件。&lt;/p&gt;
 &lt;pre&gt;// https://elixir.bootlin.com/linux/v5.4.93/source/include/linux/dcache.h#L89 结构体已删减
struct dentry {
    struct dentry *d_parent;     // 父目录
    struct qstr d_name;          // 文件名称
    struct inode *d_inode;       // 关联的 inode
    struct list_head d_child;    // 父目录中的子目录和文件
    struct list_head d_subdirs;  // 当前目录中的子目录和文件
}
&lt;/pre&gt;
 &lt;p&gt;每一个dentry对象都持有一个对应的inode对象，表示 Linux 中一个具体的目录项或文件。INode 包含管理文件系统中的对象所需的所有元数据，以及可以在该文件对象上执行的操作。&lt;/p&gt;
 &lt;pre&gt;// https://elixir.bootlin.com/linux/v5.4.93/source/include/linux/fs.h#L628 结构体已删减
struct inode {
    umode_t                 i_mode;          // 文件权限及类型
    kuid_t                  i_uid;           // user id
    kgid_t                  i_gid;           // group id

    const struct inode_operations    *i_op;  // inode 操作函数，如 create，mkdir，lookup，rename 等
    struct super_block      *i_sb;           // 所属的 SuperBlock

    loff_t                  i_size;          // 文件大小
    struct timespec         i_atime;         // 文件最后访问时间
    struct timespec         i_mtime;         // 文件最后修改时间
    struct timespec         i_ctime;         // 文件元数据最后修改时间（包括文件名称）
    const struct file_operations    *i_fop;  // 文件操作函数，open、write 等
    void                    *i_private;      // 文件系统的私有数据
}
&lt;/pre&gt;
 &lt;p&gt;虚拟文件系统维护了一个 DEntry Cache 缓存，用来保存最近使用的 DEntry，加速查询操作。当调用open()函数打开一个文件时，内核会第一时间根据文件路径到 DEntry Cache 里面寻找相应的 DEntry，找到了就直接构造一个file对象并返回。如果该文件不在缓存中，那么 VFS 会根据找到的最近目录一级一级地向下加载，直到找到相应的文件。期间 VFS 会缓存所有被加载生成的dentry。&lt;/p&gt;
 &lt;p&gt;INode 存储的数据存放在磁盘上，由具体的文件系统进行组织，当需要访问一个 INode 时，会由文件系统从磁盘上加载相应的数据并构造 INode。一个 INode 可能被多个 DEntry 所关联，即相当于为某一文件创建了多个文件路径（通常是为文件建立硬链接）。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;SuperBlock&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;SuperBlock 表示特定加载的文件系统，用于描述和维护文件系统的状态，由 VFS 定义，但里面的数据根据具体的文件系统填充。每个 SuperBlock 代表了一个具体的磁盘分区，里面包含了当前磁盘分区的信息，如文件系统类型、剩余空间等。SuperBlock 的一个重要成员是链表s_list，包含所有修改过的 INode，使用该链表很容易区分出来哪个文件被修改过，并配合内核线程将数据写回磁盘。SuperBlock 的另一个重要成员是s_op，定义了针对其 INode 的所有操作方法，例如标记、释放索引节点等一系列操作。&lt;/p&gt;
 &lt;pre&gt;// https://elixir.bootlin.com/linux/v5.4.93/source/include/linux/fs.h#L1425 结构体已删减
struct super_block {
    struct list_head    s_list;               // 指向链表的指针
    dev_t               s_dev;                // 设备标识符
    unsigned long       s_blocksize;          // 以字节为单位的块大小
    loff_t              s_maxbytes;           // 文件大小上限
    struct file_system_type    *s_type;       // 文件系统类型
    const struct super_operations    *s_op;   // SuperBlock 操作函数，write_inode、put_inode 等
    const struct dquot_operations    *dq_op;  // 磁盘限额函数
    struct dentry        *s_root;             // 根目录
}
&lt;/pre&gt;
 &lt;p&gt;SuperBlock 是一个非常复杂的结构，通过 SuperBlock 我们可以将一个实体文件系统挂载到 Linux 上，或者对 INode 进行增删改查操作。所以一般文件系统都会在磁盘上存储多份 SuperBlock，防止数据意外损坏导致整个分区无法读取。&lt;/p&gt;
 &lt;h3&gt;inode 内容&lt;/h3&gt;
 &lt;p&gt;inode包含很多的文件元信息，但不包含文件名，例如：字节数、属主UserID、属组GroupID、读写执行权限、时间戳等。而文件名存放在目录当中，但Linux系统内部不使用文件名，而是使用inode号码识别文件。对于系统来说文件名只是inode号码便于识别的别称。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;stat&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;查看inode信息&lt;/p&gt;
 &lt;pre&gt;[root@localhost ~]# mkdir test
[root@localhost ~]# echo &amp;quot;this is test file&amp;quot; &amp;gt; test.txt
[root@localhost ~]# stat test.txt
  File: ‘test.txt’
  Size: 18              Blocks: 8          IO Block: 4096   regular file
Device: fd00h/64768d    Inode: 33574994    Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Context: unconfined_u:object_r:admin_home_t:s0
Access: 2019-08-28 19:55:05.920240744 +0800
Modify: 2019-08-28 19:55:05.920240744 +0800
Change: 2019-08-28 19:55:05.920240744 +0800
 Birth: -
&lt;/pre&gt;
 &lt;p&gt;三个主要的时间属性：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;ctime：change time是最后一次改变文件或目录（属性）的时间，例如执行chmod，chown等命令。&lt;/li&gt;
  &lt;li&gt;atime：access time是最后一次访问文件或目录的时间。&lt;/li&gt;
  &lt;li&gt;mtime：modify time是最后一次修改文件或目录（内容）的时间。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;file&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;查看文件类型&lt;/p&gt;
 &lt;pre&gt;[root@localhost ~]# file test
test: directory
[root@localhost ~]# file test.txt
test.txt: ASCII text
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;inode 号码&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;表面上，用户通过文件名打开文件，实际上，系统内部将这个过程分为三步：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;系统找到这个文件名对应的inode号码；&lt;/li&gt;
  &lt;li&gt;通过inode号码，获取inode信息；&lt;/li&gt;
  &lt;li&gt;根据inode信息，找到文件数据所在的block，并读出数据。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;其实系统还要根据inode信息，看用户是否具有访问的权限，有就指向对应的数据block，没有就返回权限拒绝。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;ls -i&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;直接查看文件i节点号，也可以通过stat查看文件inode信息查看i节点号。&lt;/p&gt;
 &lt;pre&gt;[root@localhost ~]# ls -i
33574991 anaconda-ks.cfg      2086 test  33574994 test.txt
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;inode 大小&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;inode也会消耗硬盘空间，所以格式化的时候，操作系统自动将硬盘分成两个区域。一个是数据区，存放文件数据；另一个是inode区，存放inode所包含的信息。每个inode的大小，一般是128字节或256字节。通常情况下不需要关注单个inode的大小，而是需要重点关注inode总数。inode总数在格式化的时候就确定了。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;df -i&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;查看硬盘分区的inode总数和已使用情况&lt;/p&gt;
 &lt;pre&gt;[root@localhost ~]# df -i
Filesystem               Inodes IUsed   IFree IUse% Mounted on
/dev/mapper/centos-root 8910848 26029 8884819    1% /
devtmpfs                 230602   384  230218    1% /dev
tmpfs                    233378     1  233377    1% /dev/shm
tmpfs                    233378   487  232891    1% /run
tmpfs                    233378    16  233362    1% /sys/fs/cgroup
/dev/sda1                524288   328  523960    1% /boot
tmpfs                    233378     1  233377    1% /run/user/0
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;文件的读写&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;文件系统在打开一个文件时，要做的有：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;系统找到这个文件名对应的inode：在目录表中查找该文件名对应的项，由此得到该文件相对应的 inode 号&lt;/li&gt;
  &lt;li&gt;通过inode号，获取到磁盘中的inode信息，其中最重要的内容是磁盘地址表&lt;/li&gt;
  &lt;li&gt;通过inode信息中的磁盘地址表，文件系统把分散存放的文件物理块连接成文件的逻辑结构。在磁盘地址表中有 13 个块号，文件将以块号在磁盘地址表中出现的顺序依次读取相应的块。找到文件数据所在的block，读出数据。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;根据以上流程，我们可以发现，inode应该是有一个专门的存储区域的，以方便系统快速查找。事实上，一块磁盘创建的时候，操作系统自动将硬盘分成两个区域：存放文件数据的数据区，与存放inode信息的inode区（inode table）。&lt;/p&gt;
 &lt;p&gt;每个inode的大小一般是128B或者256B。inode节点的总数，在格式化时就给定，一般是每1KB或每2KB就设置一个inode。假定在一块1GB的硬盘中，每个inode节点的大小为128字节，每1KB就设置一个inode，那么inode table的大小就会达到128MB，占整块硬盘的12.8%。&lt;/p&gt;
 &lt;p&gt;也就是说，每个分区的inode总数从格式化之后就固定了，因此有可能会出现存储空间没有占满，但因为小文件太多而耗尽了inode的情况。这个时候就只能清除inode占用高的文件或者目录或修改inode数量了，当然，inode的调整需要重新格式化磁盘，需要确保数据已经得到有效备份后，再进行此操作。&lt;/p&gt;
 &lt;p&gt;这时候又产生了新的问题：文件创建时要为文件分配哪一个inode号呢？即如何保证分配的inode号没有被占用？  &lt;br /&gt;
既然是”是否被占用”的问题，使用位图是最佳方案，像bmap记录block的占用情况一样。标识inode号是否被分配的位图称为inodemap简称为imap。这时要为一个文件分配inode号只需扫描imap即可知道哪一个inode号是空闲的。&lt;/p&gt;
 &lt;p&gt;(位图法就是bitmap的缩写。所谓bitmap，就是用每一位来存放某种状态，适用于大规模数据，但数据状态又不是很多的情况。)  &lt;br /&gt;
类似bmap块位图一样，inode号是预先规划好的。inode号分配后，文件删除也会释放inode号。分配和释放的inode号，像是在一个地图上挖掉一块，用完再补回来一样。  &lt;br /&gt;
imap存在着和bmap和inode table一样需要解决的问题：如果文件系统比较大，imap本身就会很大，每次存储文件都要进行扫描，会导致效率不够高。同样，优化的方式是将文件系统占用的block划分成块组，每个块组有自己的imap范围，以减少检索时间。&lt;/p&gt;
 &lt;p&gt;利用df -i命令可以查看inode数量方面的信息&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;文件的操作&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;系统对文件的操作会可能影响inode：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;复制：创建一个包含全部数据与新inode号的新文件&lt;/li&gt;
  &lt;li&gt;移动：在同一磁盘下移动时，所在目录改变，node号与实际数据存储的块的位置都不会变化。跨磁盘移动当然会删除本磁盘的数据并创建一条新的数据在另一块磁盘中。&lt;/li&gt;
  &lt;li&gt;硬链接： 同一个inode号代表的文件有多个文件名，即可以用不同的文件名访问同一份数据，但是它们指向的inode编号是相同的，并且文件元数据中链接数会增加。不可以对目录创建硬链接。&lt;/li&gt;
  &lt;li&gt;软链接： 软链接的本质是一个链接文件，其中存储的了对另一个文件的指针。所以对一个文件创建软链接，inode号不相同，创建软链接文件的链接数不会增加。可以对目录创建软链接。&lt;/li&gt;
  &lt;li&gt;删除：当删除文件时，会先检查inode中的链接数。如果链接数大于1，就只会删掉一个硬链接，不影响数据。如果链接数等于1，那么这个inode就会被释放掉，对应的inode指向的块也会被标记为空闲的（数据不会被置零，所以硬盘数据被误删除后，若没有新数据写入可恢复）。如果是软链接，原文件被删除后链接文件就变成了悬挂链接（dangling link），无法正常访问了。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;利用inode还可以删除一些文件名中有转义字符或控制字符的文件，最典型的就是开头为减号-的文件。这种无法直接用rm命令来搞，就可以先查出它们的inode编号再删除：   &lt;code&gt;find ./ -inum 10086 -exec rm {} \&lt;/code&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;特有现象&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;由于inode号码与文件名分离，导致一些Unix/Linux系统具备以下几种特有的现象。&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;文件名包含特殊字符，可能无法正常删除。这时直接删除inode，能够起到删除文件的作用；   &lt;code&gt;find ./* -inum 节点号 -delete&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;移动文件或重命名文件，只是改变文件名，不影响inode号码；&lt;/li&gt;
  &lt;li&gt;打开一个文件以后，系统就以inode号码来识别这个文件，不再考虑文件名。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;这种情况使得软件更新变得简单，可以在不关闭软件的情况下进行更新，不需要重启。因为系统通过inode号码，识别运行中的文件，不通过文件名。更新的时候，新版文件以同样的文件名，生成一个新的inode，不会影响到运行中的文件。等到下一次运行这个软件的时候，文件名就自动指向新版文件，旧版文件的inode则被回收。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;inode 耗尽故障&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;由于硬盘分区的inode总数在格式化后就已经固定，而每个文件必须有一个inode，因此就有可能发生inode节点用光，但硬盘空间还剩不少，却无法创建新文件。同时这也是一种攻击的方式，所以一些公用的文件系统就要做磁盘限额，以防止影响到系统的正常运行。至于修复，很简单，只要找出哪些大量占用i节点的文件删除就可以了。&lt;/p&gt;
 &lt;h2&gt;硬链接与软连接&lt;/h2&gt;
 &lt;p&gt;Linux系统中有一种比较特殊的文件称之为链接（link）。通俗地说，链接就是从一个文件指向另外一个文件的路径。linux中链接分为俩种，硬链接和软链接。简单来说，硬链接相当于源文件和链接文件在磁盘和内存中共享一个inode，因此，链接文件和源文件有不同的dentry，因此，这个特性决定了硬链接无法跨越文件系统，而且我们无法为目录创建硬链接。软链接和硬链接不同，首先软链接可以跨越文件系统，其次，链接文件和源文件有着不同的inode和dentry，因此，两个文件的属性和内容也截然不同，软链接文件的文件内容是源文件的文件名。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="442" src="https://www.biaodianfu.com/wp-content/uploads/2022/08/hardlink.png" width="640"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;硬链接是多个目录项中的「索引节点」指向一个文件，也就是指向同一个 inode，但是 inode 是不可能跨越文件系统的，每个文件系统都有各自的 inode 数据结构和列表，所以硬链接是不可用于跨文件系统的。由于多个目录项都是指向一个 inode，那么只有删除文件的所有硬链接以及源文件时，系统才会彻底删除该文件。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="389" src="https://www.biaodianfu.com/wp-content/uploads/2022/08/hard-link.png" width="720"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;软链接相当于重新创建一个文件，这个文件有独立的 inode，但是这个文件的内容是另外一个文件的路径，所以访问软链接的时候，实际上相当于访问到了另外一个文件，所以软链接是可以跨文件系统的，甚至目标文件被删除了，链接文件还是在的，只不过指向的文件找不到了而已。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="375" src="https://www.biaodianfu.com/wp-content/uploads/2022/08/soft-link.png" width="720"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;软硬链接实现的原理不同
   &lt;ul&gt;
    &lt;li&gt;硬链接是建立一个目录项，包含文件名和文件的inode，但inode是原来文件的inode号，并不建立其所对应得数据。所以硬链接并不占用inode。&lt;/li&gt;
    &lt;li&gt;软连接也创建一个目录项，也包含文件名和文件的inode，但它的inode指向的并不是原来文件名所指向的数据的inode，而是新建一个inode，并建立数据，数据指向的是原来文件名，所以原来文件名的字符数，即为软连接所占字节数&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;软硬链接所能创建的目标有区别
   &lt;ul&gt;
    &lt;li&gt;因为每个分区各有一套不同的inode表，所以硬链接不能跨分区创建而软连接可以,因为软连接指向的书文件名。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;硬链接不能指向目录
   &lt;ul&gt;
    &lt;li&gt;如果说目录有硬链接那么可能引入死循环，但是你可能会疑问软连接也会陷入循环啊，答案当然不是，因为软连接是存在自己的数据的，可以查看自己的文件属性，既然可以判断出来软连接，那么自然不会陷入循环，并且系统在连续遇到8个符号连接后就停止遍历。但是硬链接可就不行了，因为他的inode号一致，所以就判断不出是硬链接，所以就会陷入死循环了。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;参考链接：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://wingsxdu.com/posts/linux/vfs/"&gt;Linux 虚拟文件系统&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://devconnected.com/understanding-hard-and-soft-links-on-linux/"&gt;Understanding Hard and Soft Links on Linux&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;div&gt;
  &lt;h3&gt;相关文章:&lt;/h3&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/hello-world.html" rel="bookmark" title="C&amp;#35821;&amp;#35328;&amp;#20043;Hello World&amp;#31243;&amp;#24207;&amp;#32534;&amp;#35793;"&gt;C语言之Hello World程序编译 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/fastfm.html" rel="bookmark" title="Python&amp;#22240;&amp;#23376;&amp;#20998;&amp;#35299;&amp;#24211;&amp;#65306;fastFM"&gt;Python因子分解库：fastFM &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/linux-windows-mac-os-file_systems.html" rel="bookmark" title="Linux/Windows/Mac OS&amp;#25991;&amp;#20214;&amp;#31995;&amp;#32479;"&gt;Linux/Windows/Mac OS文件系统 &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>器→工具 工具软件 Linux</category>
      <guid isPermaLink="true">https://itindex.net/detail/62362-linux-%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F-inode</guid>
      <pubDate>Sat, 13 Aug 2022 09:50:00 CST</pubDate>
    </item>
    <item>
      <title>排查 K8S Pod  被 OOM 的思路及建议</title>
      <link>https://itindex.net/detail/62049-k8s-pod-oom</link>
      <description>&lt;p&gt;K8S + 容器的云原生生态，改变了服务的交付方式，自愈能力和自动扩缩等功能简直不要太好用。&lt;/p&gt;
 &lt;p&gt;有好的地方咱要夸，不好的地方咱也要说，真正的业务是部署于容器内部，而容器之外，又有一逻辑层 Pod 。&lt;/p&gt;
 &lt;p&gt;对于容器和 K8S 不怎么熟悉的人，一旦程序发生了问题，排查问题就是个头疼的问题。&lt;/p&gt;
 &lt;p&gt;这两天一直在排查一个 Pod OOM 的问题，花了不少的时间，感觉有必要写下来，帮助自己梳理的同时，也能给其他人一些思路。&lt;/p&gt;
 &lt;h2&gt;1. 问题描述&lt;/h2&gt;
 &lt;p&gt;事情的主角是 kubevirt 的一个开源项目叫 cdi，它的用途是在虚拟机启动之前将虚拟机的镜像导入到系统盘中。&lt;/p&gt;
 &lt;p&gt;在使用过程中，我们发现 cdi 在导入数据时会占用大量的内存空间。&lt;/p&gt;
 &lt;p&gt;而 cdi-controller 在创建  cdi-importer 的 pod 时，默认限定其最高只能使用 600M 的内存，到最后呢，pod 就发生了 OOMKilled。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;[root@master01 ~]# kubectl get po
NAME                               READY   STATUS      RESTARTS   AGE
importer-wbm-vda          0/1     OOMKilled   1          76s
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;经过测试，cdi-importer 的 limits.memory 要设置 6 个 G 才比较保险。&lt;/p&gt;
 &lt;p&gt;我们一致都对 cdi-importer 要占用 6G 内存表示费解，想找下原因，看看有没有优化的空间。&lt;/p&gt;
 &lt;h2&gt;2. 思路一：内存泄露&lt;/h2&gt;
 &lt;p&gt;我第一时间想到的是，有没有可能是代码问题导致发生了内存泄露？&lt;/p&gt;
 &lt;p&gt;当即使用   &lt;code&gt;ps aux&lt;/code&gt; 和   &lt;code&gt;top -p [pid]&lt;/code&gt; 工具去查看进程的 rss，发现程序本身的内存占用并不高，最多才 50M。&lt;/p&gt;
 &lt;p&gt;和 limits.memory=600M 相比，差得有点大，按道理是不可能出现 OOM 的，怎么回事呢？难道 top 和 ps 的数据不准？&lt;/p&gt;
 &lt;p&gt;正常检查 Go 程序的内在泄露，会使用 pprof 工具，不如我再用 pprof 去分析一下内存吧，做个双向验证吧&lt;/p&gt;
 &lt;p&gt;在程序入口处加一如下代码后&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;import _  &amp;quot;net/http/pprof&amp;quot;

func main() {
    go func() {
        log.Println(http.ListenAndServe(&amp;quot;localhost:35526&amp;quot;, nil))
    }()
    // more code...
}
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;再次使用 bazel 进行编译，制作镜像，创建 pod&lt;/p&gt;
 &lt;p&gt;然后通过   &lt;code&gt;kubectl top pod xxx&lt;/code&gt; 观察内存的变化，在将到达最大值的时候，调用如下命令开启一个 pprof 的交互式界面&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;go tool pprof  http://ip:port/debug/pprof/heap
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;输入 top 就可以看到占用内存前 10 的函数调用，可以看到程序占用的总内存也才8M 而已，占用最高的函数也才 4M&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://image.iswbm.com/20220121183811.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;到这边，已经基本可以确定程序本身没有发生所猜想的内存泄漏。&lt;/p&gt;
 &lt;h2&gt;3. 思路二：查看 OOM 日志&lt;/h2&gt;
 &lt;p&gt;发生了 oom ，不如看看 oom 的日志，看看能不能发现点什么？&lt;/p&gt;
 &lt;p&gt;通过 dmesg 打印出 oom 的相关日志&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;[580237.375615] memory: usage 585936kB, limit 585936kB, failcnt 75129
[580237.375616] memory+swap: usage 585936kB, limit 9007199254740988kB, failcnt 0
[580237.375618] kmem: usage 24148kB, limit 9007199254740988kB, failcnt 0
[580237.375618] Memory cgroup stats for /kubepods/burstable/pod6b212546-f5dd-4fdf-bcc7-72a686638102:

[580237.375639] [ pid ]   uid  tgid total_vm      rss pgtables_bytes swapents oom_score_adj name
[580237.375949] [17998]     0 17998      242        1    28672        0          -998 pause
[580237.375955] [18524]     0 18524   675338     5758   499712        0           999 cdi-importer
[580237.375963] [ 3422]     0  3422   191966     4380   327680        0           999 qemu-img
[580237.375966] oom-kill:constraint=CONSTRAINT_MEMCG,nodemask=(null),cpuset=ea887b1c9c5c8e734ac798fedd2bf5d39c0b7ce5ad961027dfc1ca138a23a2e8,mems_allowed=0-1,oom_memcg=/kubepods/burstable/pod6b212546-f5dd-4fdf-bcc7-72a686638102,task_memcg=/kubepods/burstable/pod6b212546-f5dd-4fdf-bcc7-72a686638102/ea887b1c9c5c8e734ac798fedd2bf5d39c0b7ce5ad961027dfc1ca138a23a2e8,task=cdi-importer,pid=18524,uid=0
[580237.376066] Memory cgroup out of memory: Killed process 18524 (cdi-importer) total-vm:2701352kB, anon-rss:23032kB, file-rss:0kB, shmem-rss:0kB, UID:0
[580237.466313] oom_reaper: reaped process 18524 (cdi-importer), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;日志的最开始处，打印了内存的限制为 585936 kb，而当前已使用  585936kb，这么看确实是不够了。&lt;/p&gt;
 &lt;p&gt;但到底哪里不够用了呢？&lt;/p&gt;
 &lt;p&gt;从后面的日志  cdi-importer 的 rss 才 23032kB，23 M 而已，应该还剩 500 多 M 啊，怎么就说我不够了？&lt;/p&gt;
 &lt;p&gt;这下真的麻了，一个问号还没有解决，脑子里又蹦出来新的问号。&lt;/p&gt;
 &lt;h2&gt;4. 思路三：缓存做崇&lt;/h2&gt;
 &lt;p&gt;通过不断的 Google 搜索，我查到了 kubectl top 得到的内存使用数据原来是这么计算的&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;memory.usage_in_bytes-total_inactive_file
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;从这个公式可以看出， kubectl top 得到的内存使用数据原来是包含 cache 的。&lt;/p&gt;
 &lt;p&gt;到这里，我相信很多人会认为 k8s 这样的计算是不准确的，rss 才是进程真正使用的内存吧。&lt;/p&gt;
 &lt;p&gt;起初，我也是这么觉得的，直到我翻看了 k8s 关于这块的 issue 已经存在很多年了，一直到至今还没有解决，出于对 k8s 开发团队的信任，我选择相信这种计算方式是“正确的”，全球顶尖的开发团队会放任一个 bug 存在如此之久？&lt;/p&gt;
 &lt;p&gt;可就算是正确的又怎样呢？问题仍然摆在这里，并没有一丝一毫的进展。&lt;/p&gt;
 &lt;p&gt;就在我一筹莫展的时候，前面的 cache 让我有一点灵感。&lt;/p&gt;
 &lt;p&gt;在 OOM 后，我特地去查看了该容器的 cgroup 文件，发现在 memory.meminfo 里的 free 已经小于 1M 了，而相反的 cached 的值却几乎等于容器的最高内存限制。&lt;/p&gt;
 &lt;p&gt;突然之间，我感觉到曙光就在眼前，有可能还真的是 cache 占用了内存才导致的 OOM&lt;/p&gt;
 &lt;p&gt;回想一下，正常的 cache 可以提高磁盘数据的读写数据，在读的时候，会拷贝一份文件数据放到内存中，这部分是可回收的，一旦程序内存不足了，会回收部分 cache 的空间，保证程序的正常运行。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://image.iswbm.com/20220121225633.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;可见读文件的缓存，不会影响内存的申请，更别说 OOM，但在写的时候，情况就不一样了&lt;/p&gt;
 &lt;p&gt;在写的时候，由于进程处理数据的速度，可能会远大于数据落盘的速度，所以为提高格式转化和数据导入的速度，一般会先将转化好的数据存入缓存中，存入缓存后，进程可以立马 return 回去继续下一堆数据的处理，不用傻傻地等待数据全写入磁盘。&lt;/p&gt;
 &lt;p&gt;而存在于缓存之中的数据，则由操作系统同步写入磁盘，这样一来，数据落盘就变成了一个异步的过程，大大提高了写入的速度。&lt;/p&gt;
 &lt;p&gt;大腿一拍，这不就有可能会出问题吗？&lt;/p&gt;
 &lt;p&gt;如果 qemu-img 处理数据的速度远大于 cache 存入磁盘的速度，就会出现内存不足啦。&lt;/p&gt;
 &lt;p&gt;问题好像发现了，可该如何验证呢？&lt;/p&gt;
 &lt;p&gt;去查看了一下 qemu-img 的参数，发现有一个 -t 的参数可以指定 cache mode，有如下 5 种选择：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;writeback/unsafe：app —-qemu write—-&amp;gt; host page cache  — os flush —&amp;gt; disk cache — hw flush —&amp;gt; disk&lt;/li&gt;
  &lt;li&gt;none:          app  — qemu write—-&amp;gt; disk write cache  —- hw flush —&amp;gt; disk&lt;/li&gt;
  &lt;li&gt;writethrough:     app — qemu write—-&amp;gt; host page cache, disk&lt;/li&gt;
  &lt;li&gt;directsync:       app — qemu write —&amp;gt; disk&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;通过阅读 cdi-importer 代码，可以看到它使用的是 writeback，即先将转化好的数据写入缓存中，提高速度。&lt;/p&gt;
 &lt;p&gt;然后我也发现了 directsync 这个选项，就是不使用缓存，直接将数据写入磁盘，这正是我想要的啊。&lt;/p&gt;
 &lt;p&gt;重新改了下 cdi 的代码，编译，制作镜像，创建 pod，还真的是再也没有出现 OOM ，到现在问题全部解决了，真的爽啊~&lt;/p&gt;
 &lt;h2&gt;5. 总结一下&lt;/h2&gt;
 &lt;p&gt;由于是第一次处理 OOM，因此这个排查的过程，花了不少的时间，不过归根结底还是我对基础的不牢固导致的。&lt;/p&gt;
 &lt;p&gt;在此之前，我潜意识里以为只有进程实际占用的内存才是 oom 的依据，没有想到缓存分为两种：读缓存和写缓存，读缓存是可随时回收的内存空间，不会引起内存问题，但写缓存，是不能随时回收的内存空间，只有将数据存入磁盘后，内在才能回收，这部分是有可能会引起内存问题的。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Kubernetes k8s oom 工具 测试</category>
      <guid isPermaLink="true">https://itindex.net/detail/62049-k8s-pod-oom</guid>
      <pubDate>Fri, 21 Jan 2022 22:15:48 CST</pubDate>
    </item>
    <item>
      <title>对标 VS Code，JetBrains 的下一代 IDE ：Fleet</title>
      <link>https://itindex.net/detail/61922-vs-code-jetbrains</link>
      <description>&lt;p&gt;昨天 （11月29日）， JetBrains 网站上出现了一个全新的 IDE —  Fleet&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://image.iswbm.com/20211129215746.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;它是谁呢？这软件的风格，怎么看都不像 JB 的亲儿子。。&lt;/p&gt;
 &lt;p&gt;不过，我很负责任地告诉，这就是 JetBrains 的下一代 IDE ，妥妥的亲儿子。&lt;/p&gt;
 &lt;p&gt;目前 Fleet 还处于开发阶段，还没有开放下载使用，如果你想尝鲜，可以通过这个链接（https://www.jetbrains.com/fleet/preview/）填写一下表格申请。&lt;/p&gt;
 &lt;p&gt;看到这个消息，我就赶紧去申请了，但何时会通过，官方表示也不清楚。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://image.iswbm.com/20211129213059.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;虽然还无法使用，不过可以从官网上已经能 get 到一些关于 Fleet 的信息。&lt;/p&gt;
 &lt;p&gt;Fleet 声称是从头开始构建的，拥有全新的架构和用户界面。但我不得不说，你的界面真的很像 VS Code（这口锅你是背定了），虽然比 VS Code 会好看一点。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://image.iswbm.com/20211129220213.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;虽然 Fleet 定位轻量级编辑器，但该有的东西，它一样也没落下：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;智能补全&lt;/li&gt;
  &lt;li&gt;重构&lt;/li&gt;
  &lt;li&gt;导航&lt;/li&gt;
  &lt;li&gt;调试&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;以及 IDE 中一直存在的所有功能它都有，更重要的是，一键即可使用所有这些功能。&lt;/p&gt;
 &lt;p&gt;过去使用 JetBrains 的产品，通常是会多少种语言，就安装多少个 JetBrains IDE，如今你再也不用为你的内存和硬盘担心了，Fleet 和 VS Code 一样，适用于多种流行的编程语言。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://image.iswbm.com/20211129221105.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;除了一些常规的功能和需求之外 ，Fleet 也给我们带来了一些惊喜。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;第一个惊喜&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;Fleet 是分布式的，它支持如下场景&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;协作开发：多个客户在同一个开发环境中工作并相互交互。&lt;/li&gt;
  &lt;li&gt;远程/云 IDE： 托管在其他地方的开发环境，例如远程机器、集群或云。&lt;/li&gt;
  &lt;li&gt;多目标文件系统：开发和运行一个涉及多台机器或容器的项目，例如，一个基于微服务的应用程序。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" src="http://image.iswbm.com/20211129222034.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;第二个惊喜&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;Space 提供了编排支持，可从源仓库轻松启动远程服务器实例，支持使用 Dockerfile 进行自定义。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://image.iswbm.com/20211129222930.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;第三个惊喜&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;Fleet 全方位支持团队协作，同团队的人可以同时开发同一个项目，编辑同一个文件或者不同文件，运行测试、访问终端以及执行协作 IDE 所期望的其他功能。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://image.iswbm.com/20211129223458.gif"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;JetBrains 是一家伟大的公司，做为一位开发者，可以没用过它的产品，但不可能没听过它的大名。&lt;/p&gt;
 &lt;p&gt;它们家的产品（比如 PyCharm，Goland ），经常拿来和 VS Code 做比较。&lt;/p&gt;
 &lt;p&gt;他们两者的关系有点像 Django 和 Flask，JetBrains 开箱即用，不用太多的配置就可以直接上手，这就导致了 PyCharm 非常重，一些配置不太给力的电脑，可能会内存不够用，而相反， VS Code 则非常轻量，需要你自行安装一些插件才能用得顺手。&lt;/p&gt;
 &lt;p&gt;JetBrains 做为 IDE 专业户，对开发工具的理解非常到位，做出来的产品做了比较重之外，很少有其他槽点，因此我可以完全相信 JetBrains 的产品能力和开发能力。&lt;/p&gt;
 &lt;p&gt;希望，我可以同时卸载 PyCharm Goland Clion 的这一天，能早一点到来…&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>工具库 Flask 工具 测试 网站</category>
      <guid isPermaLink="true">https://itindex.net/detail/61922-vs-code-jetbrains</guid>
      <pubDate>Mon, 29 Nov 2021 23:00:08 CST</pubDate>
    </item>
    <item>
      <title>Draw.io 15.8.4 - 免费开源的绘图软件</title>
      <link>https://itindex.net/detail/61912-draw-io-%E5%85%8D%E8%B4%B9</link>
      <description>&lt;h1&gt;  &lt;a href="https://www.nite07.com/#&amp;#31616;&amp;#20171;" title="&amp;#31616;&amp;#20171;"&gt;&lt;/a&gt;简介&lt;/h1&gt; &lt;p&gt;draw.io desktop是一款非常好用的在线流程图绘制工具，允许用户能够快速、自由的创建简单的图标、流程图、网页模版构架图、框架图等，并可通过浏览器Chrome插件就可以快速创建想要的效果图，适用于商务、工程、电气、网络设计、软件设计等诸多领域的专业绘图。&lt;/p&gt; &lt;h1&gt;  &lt;a href="https://www.nite07.com/#&amp;#25130;&amp;#22270;" title="&amp;#25130;&amp;#22270;"&gt;&lt;/a&gt;截图&lt;/h1&gt; &lt;img src="https://cdn.jsdelivr.net/gh/nitezs/ImageHost/draw-io/2.jpg"&gt;&lt;/img&gt;  &lt;h1&gt;  &lt;a href="https://www.nite07.com/#&amp;#19979;&amp;#36733;" title="&amp;#19979;&amp;#36733;"&gt;&lt;/a&gt;下载&lt;/h1&gt; &lt;a href="https://file.nite07.com/d_0/draw.io-15.8.4-windows-no-installer.exe?v" title="&amp;#21338;&amp;#23458;&amp;#19979;&amp;#36733;"&gt;博客下载&lt;/a&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>实用资源 Windows 好软推荐 绘图工具 中文</category>
      <guid isPermaLink="true">https://itindex.net/detail/61912-draw-io-%E5%85%8D%E8%B4%B9</guid>
      <pubDate>Wed, 24 Nov 2021 22:15:39 CST</pubDate>
    </item>
    <item>
      <title>使用zimg搭建图片服务器</title>
      <link>https://itindex.net/detail/61906-zimg-%E5%9B%BE%E7%89%87-%E6%9C%8D%E5%8A%A1%E5%99%A8</link>
      <description>&lt;p&gt;一般的大型网站都会将图片存放在专门的服务器，这样可以很好的提升网站的性能。比较简单的方式是采用云厂商提供的服务，比如七牛云、又拍云等。今天要介绍的是一款开源的实现方案zing。&lt;/p&gt;
 &lt;h2&gt;zimg简介&lt;/h2&gt;
 &lt;p&gt;  &lt;a href="http://zimg.buaa.us/"&gt;zimg&lt;/a&gt;是一套国人针对图片处理服务器而设计开发的开源程序，目的是解决图片服务中如下三个问题：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;大流量：对于一些中小型网站来说，流量问题就是成本问题，图片相对于文本来说流量增加了一个数量级，省下的每一个字节都是白花花的银子。所以凡是涉及到图片的互联网应用，都应该统筹规划，降低流量节约开支。&lt;/li&gt;
  &lt;li&gt;高并发：高并发的问题在用户量较低时几乎不会出现，但是一旦用户攀升，或者遇到热点事件，比如网站被人上传了一张爆炸性的新闻图片，短时间内将会涌入大量的浏览请求，如果架构设计得不好，又没有紧急应对方案，很可能导致大量的等待、更多的页面刷新和更多请求的死循环。总的来说，就是要把图片服务的性能做得足够好。&lt;/li&gt;
  &lt;li&gt;海量存储：Facebook用户上传图片上亿张，总容量超过了nPB，这样的数量级是一般企业无法承受的。虽然很难做出一个可以跟Facebook比肩的应用，但是从架构设计的角度来说，良好的拓展方案还是要有的。需要提前设计出最合适的海量图片数据存储方案和操作方便的拓容方案，以应对将来不断增长的业务需求。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;以上三个问题，其实也是相互制约和钳制的，比如要想降低流量，就需要大量的计算，导致请求处理时间延长，系统单位时间内的处理能力下降；再比如为了存储更多的图片，必然要在查找上消耗资源，同样也会降低处理能力。所以，图片服务虽然看起来业务简单，实际做起来也不是一件小事。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="330" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/zimg.png" width="660"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;zimg的定位：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;zimg是图像存储和处理服务器。您可以使用URL参数从zimg获得压缩和缩放的图像。&lt;/li&gt;
  &lt;li&gt;zimg的并发I/O，分布式存储和及时处理能力非常出色。您不再需要在图像服务器中使用nginx。在基准测试中，zimg可以在高并发级别上每秒处理3000个以上的图像下载任务和每秒90000个以上的HTTP回显请求。性能高于PHP或其他图像处理服务器。&lt;/li&gt;
  &lt;li&gt;用于中小型的图床服务&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;以下是zimg支持的功能：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;所有图片默认返回质量为75%，JPEG格式的压缩图片，这样肉眼无法识辨，但是体积减小&lt;/li&gt;
  &lt;li&gt;获取宽度为x，被等比例缩放的图片&lt;/li&gt;
  &lt;li&gt;获取旋转后的图片&lt;/li&gt;
  &lt;li&gt;获取指定区域固定大小的图片&lt;/li&gt;
  &lt;li&gt;获取特定尺寸的图片，由于与原图比例不同，尽可能展示最多的图片内容，缩放之后多余的部分需要裁掉&lt;/li&gt;
  &lt;li&gt;获取特定尺寸的图片，要展示图片所有内容，因此图片会被拉伸到新的比例而变形&lt;/li&gt;
  &lt;li&gt;获取特定尺寸的图片，但是不需要缩放，只用展示图片核心内容即可&lt;/li&gt;
  &lt;li&gt;获取按指定百分比缩放的图片&lt;/li&gt;
  &lt;li&gt;获取指定压缩比的图片&lt;/li&gt;
  &lt;li&gt;获取去除颜色的图片&lt;/li&gt;
  &lt;li&gt;获取指定格式的图片&lt;/li&gt;
  &lt;li&gt;获取图片信息&lt;/li&gt;
  &lt;li&gt;删除指定图片&lt;/li&gt;
  &lt;li&gt;以上这些功能的提供，仅需要一个url+特定的参数，通过get方式就可以完成。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;zimg的设计思路&lt;/h2&gt;
 &lt;p&gt;想要在展现图片这件事情上有最好的表现，首先需要从整体业务中将图片服务部分分离出来。使用单独的域名和建立独立的图片服务器有很多好处，比如：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;CDN分流。如果你有注意的话，热门网站的图片地址都有特殊的域名，比如微博的是sinaimg.cn，人人的是fmn.xnpic.com等等，域名不同可以在CDN解析的层面就做到非常明显的优化效果。&lt;/li&gt;
  &lt;li&gt;浏览器并发连接数限制。一般来说，浏览器加载HTML资源时会建立很多的连接，并行地下载资源。不同的浏览器对同一主机的并发连接数限制是不同的。如果把图片服务器独立出来，就不会占用掉对主站连接数的名额，一定程度上提升了网站的性能。&lt;/li&gt;
  &lt;li&gt;浏览器缓存。现在的浏览器都具有缓存功能，但是由于cookie的存在，大部分浏览器不会缓存带有cookie的请求，导致的结果是大量的图片请求无法命中，只能重新下载。独立域名的图片服务器，可以很大程度上缓解此问题。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;图片服务器被独立出来之后，会面临两个选择，主流的方案是前端采用Nginx，中间是PHP或者自己开发的模块，后端是物理存储；比较特别一些的，比如Facebook，他们把图片的请求处理和存储合并成一体，叫做haystack，这样做的好处是，haystack只会处理与图片相关的请求，剥离了普通http服务器繁杂的功能，更加轻量高效，同时也使部署和运维难度降低。zimg采用的是与Facebook相似的策略，将图片处理的大权收归自己所有，绝大部分事情都由自己处理，除非特别必要，最小程度地引入第三方模块。&lt;/p&gt;
 &lt;h2&gt;zimg的架构设计&lt;/h2&gt;
 &lt;p&gt;为了极致的性能表现，zimg全部采用C语言开发，总体上分为三个层次，前端http处理层，中间图片处理层和后端的存储层。下图为zimg架构设计图：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="787" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/zimg-architecture.png" width="660"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;http处理层引入基于libevent的libevhtp库，专门处理基本http请求 。&lt;/li&gt;
  &lt;li&gt;图片处理层采用imagemagick库。&lt;/li&gt;
  &lt;li&gt;存储层采用memcached缓存加直接读写硬盘的方案，后期可能会引入TFS4等。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;为了避免数据库带来的性能瓶颈，zimg不引入结构化数据库，图片的查找全部采用哈希来解决。事实上图片服务器的设计，是一个在I/O与CPU运算之间的博弈过程，最好的策略当然是继续拆：CPU敏感的http和图片处理层部署于运算能力更强的机器上，内存敏感的cache层部署于内存更大的机器上，I/O敏感的物理存储层则放在配备SSD的机器上，但并不是所有人都能负担得起这么奢侈的配置。zimg折中成本和业务需求，目前只需要部署在一台服务器上。由于不同服务器硬件不同，I/O和CPU运算速度差异很大，很难一棒子定死。zimg所选择的思路是，尽量减少I/O，将压力放在CPU上，事实证明这样的思路基本没错，在硬盘性能很差的机器上效果更加明显；即使以后SSD全面普及，CPU的运算能力也会相应提升，总体来说zimg的方案也不会太失衡。&lt;/p&gt;
 &lt;h2&gt;zimg的代码实现&lt;/h2&gt;
 &lt;p&gt;虽然zimg在二进制实体上没有分模块，上面已经提到了原因，现阶段面向中小型的服务，单机部署即可，但是代码上是分离的。&lt;/p&gt;
 &lt;h3&gt;main.c&lt;/h3&gt;
 &lt;p&gt;main.c是程序的入口，主要功能是处理启动参数，部分参数功能如下：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;-p [port] 监听端口号，默认4869&lt;/li&gt;
  &lt;li&gt;-t [thread_num] 线程数，默认4，请调整为具体服务器的CPU核心数&lt;/li&gt;
  &lt;li&gt;-k [max_keepalive_num] 最高保持连接数，默认1，不启用长连接，0为启用&lt;/li&gt;
  &lt;li&gt;-l 启用log，会带来很大的性能损失，自行斟酌是否开启&lt;/li&gt;
  &lt;li&gt;-M [memcached_ip] 启用缓存的连接IP&lt;/li&gt;
  &lt;li&gt;-m [memcached_port] 启用缓存的连接端口&lt;/li&gt;
  &lt;li&gt;-b [backlog_num] 每个线程的最大连接数，默认1024，酌情设置&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;zhttpd.c&lt;/h3&gt;
 &lt;p&gt;zhttpd.c是解析http请求的部分，分为GET和POST两大部分，GET请求会根据请求的URL参数去寻找图片并转给图片处理层处理，最后将结果返回给用户；POST接收上传请求然后将图片存入计算好的路径中。为了实现zimg的总体设计愿景，zhttpd承担了很大部分的工作，也有一些关键点，下面捡重点的说一下：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;在zimg中图片的唯一Key值就是该图片的MD5，这样既可以隐藏路径，又能减少前端（指zimg前面的部分，可能是你的应用服务器）和zimg本身的存储压力，是避免引入结构化存储部分的关键，所以所有GET请求都是基于MD5拼接而成的。假如你的网站某个地方需要展示一张图片，这个图片原图的大小是1000*1000，但是你想要展示的地方只有300*300，你会怎么做呢？一般还是依靠CSS来进行控制，但是这样的话就会造成很多流量的浪费。为此，zimg提供了图片裁剪功能，你所需要做的就是在图片URL后面加上w=300&amp;amp;h=300（width和height）即可。&lt;/li&gt;
  &lt;li&gt;在图片上传部分，如果我们的图片服务器前端采用Nginx，上传功能用PHP实现，需要写的代码很少，但是性能很差。首先PHP接收到Nginx传过来的请求后，会根据http协议（RFC1867）分离出其中的二进制文件，存储在一个临时目录里，等我们在PHP代码里使用$_FILES[“upfile”][tmp_name]获取到文件后计算MD5再存储到指定目录，在这个过程中有一次读文件一次写文件是多余的，其实最好的情况是我们拿到http请求中的二进制文件（最好在内存里），直接计算MD5然后存储。于是自己去阅读了PHP的源代码，自己实现了POST文件的解析，让http层直接和存储层连在了一起，提高了上传图片的性能。除了POST请求这个例子，zimg代码中有多处都体现了这种“减少磁盘I/O，尽量在内存中读写”和“避免内存复制”的思想，一点点的积累，最终将会带来优秀的表现。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;zimg.c&lt;/h3&gt;
 &lt;p&gt;zimg.c是调用imagemagick处理图片的部分，现阶段zimg服务于存储量在TB级别的单机图片服务器，所以存储路径采用2级子目录的方案。由于Linux同目录下的子目录数最好不要超过2000个，再加上MD5的值本身就是32位十六进制数，zimg就采取了一种非常取巧的方式：根据MD5的前六位进行哈希，1-3位转换为十六进制数后除以4，范围正好落在1024以内，以这个数作为第一级子目录；4-6位同样处理，作为第二级子目录；二级子目录下是以MD5命名的文件夹，每个MD5文件夹内存储图片的原图和其他根据需要存储的版本，假设一个图片平均占用空间200KB，一台zimg服务器支持的总容量就可以计算出来了：1024 * 1024 * 1024 * 200KB = 200TB&lt;/p&gt;
 &lt;p&gt;除了路径规划，zimg另一大功能就是压缩图片。从用户角度来说，zimg返回来的图片只要看起来跟原图差不多就行了，如果确实需要原图，也可以通过将所有参数置空的方式来获得。基于这样的条件，zimg.c对于所有转换的图片都进行了压缩，压缩之后肉眼几乎无法分辨，但是体积将减少67.05%。具体的处理方式为：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;图片裁剪时使用LanczosFilter滤镜；&lt;/li&gt;
  &lt;li&gt;以75%的压缩率进行压缩；&lt;/li&gt;
  &lt;li&gt;去除图片的Exif信息；&lt;/li&gt;
  &lt;li&gt;转换为JPEG格式。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;经过这样的处理之后可以很大程度的减少流量，实现设计目标。&lt;/p&gt;
 &lt;h3&gt;zcache.c&lt;/h3&gt;
 &lt;p&gt;zcache.c是引入memcached缓存的部分，引入缓存是很重要的，尤其是图片量级上升之后。在zimg中缓存被作为一个很重要的功能，几乎所有zimg.c中的查找部分都会先去检查缓存是否存在。比如：我想要a（代表某MD5）图片裁剪为100*100之后再灰白化的版本，那么过程是先去找a&amp;amp;w=100&amp;amp;h=100&amp;amp;g=1的缓存是否存在，不存在的话去找这个文件是否存在（这个请求所对应的文件名为 a/100*100pg），还不存在就去找这个分辨率的彩色图缓存是否存在，若依然不存在就去找彩色图文件是否存在（对应的文件名为 a/100*100p），若还是没有，那就去查询原图的缓，原图缓存依然未命中的话，只能打开原图文件了，然后开始裁剪，灰白化，然后返回给用户并存入缓存中。&lt;/p&gt;
 &lt;p&gt;可以看出，上面过程中如果某个环节命中缓存，就会相应地减少I/O或图片处理的运算次数。众所周知内存和硬盘的读写速度差距是巨大的，那么这样的设计对于热点图片抗压将会十分重要。&lt;/p&gt;
 &lt;p&gt;除了上述核心代码以外就是一些支持性的代码了，比如log部分，md5计算部分，util部分等。&lt;/p&gt;
 &lt;h2&gt;zimg的部署安装（centos 7）&lt;/h2&gt;
 &lt;p&gt;安装依赖库:&lt;/p&gt;
 &lt;pre&gt;sudo yum install -y  wget openssl-devel cmake libevent-devel libjpeg-devel giflib-devel libpng-devel libwebp-devel ImageMagick-devel libmemcached-devel 
sudo yum install -y glibc-headers gcc-c++
sudo yum install -y build-essential nasm
&lt;/pre&gt;
 &lt;p&gt;安装依赖：&lt;/p&gt;
 &lt;pre&gt;# openssl
mkdir /usr/local/zimg/openssl
cd /usr/local/zimg/openssl
wget http://www.openssl.org/source/openssl-1.0.1i.tar.gz
tar zxvf openssl-1.0.1i.tar.gz
cd openssl-1.0.1i
./config shared --prefix=/usr/local --openssldir=/usr/ssl
make &amp;amp;&amp;amp; make install

# cmake
mkdir /usr/local/zimg/cmake
cd /usr/local/zimg/cmake
wget http://www.cmake.org/files/v3.0/cmake-3.0.1.tar.gz
tar xzvf cmake-3.0.1.tar.gz 
cd cmake-3.0.1
./bootstrap --prefix=/usr/local 
make &amp;amp;&amp;amp; make install

# libevent
mkdir /usr/local/zimg/libevent
cd /usr/local/zimg/libevent
wget http://cloud.github.com/downloads/libevent/libevent/libevent-2.0.21-stable.tar.gz
tar zxvf libevent-2.0.21-stable.tar.gz
cd libevent-2.0.21-stable
./configure --prefix=/usr/local 
make &amp;amp;&amp;amp; make install

# libjpeg-turbo
mkdir /usr/local/zimg/libjpeg-turbo
cd /usr/local/zimg/libjpeg-turbo
wget https://downloads.sourceforge.net/project/libjpeg-turbo/1.3.1/libjpeg-turbo-1.3.1.tar.gz
tar zxvf libjpeg-turbo-1.3.1.tar.gz
cd libjpeg-turbo-1.3.1
./configure --prefix=/usr/local --with-jpeg8
make &amp;amp;&amp;amp; make install

# webp
mkdir /usr/local/zimg/webp
cd /usr/local/zimg/
wget http://downloads.webmproject.org/releases/webp/libwebp-0.4.1.tar.gz
tar zxvf libwebp-0.4.1.tar.gz
cd libwebp-0.4.1
./configure
make
sudo make install

# jpegsrc
mkdir /usr/local/zimg/jpegsrc
cd /usr/local/zimg/
wget http://www.ijg.org/files/jpegsrc.v8b.tar.gz
tar -xf  jpegsrc.v8b.tar.gz
cd jpeg-8b
./configure --prefix=/usr/local --enable-shared --enable-static
make &amp;amp;&amp;amp; make install

# imageMagic
mkdir /usr/local/zimg/imageMagick
cd /usr/local/zimg/
wget http://www.imagemagick.org/download/ImageMagick.tar.gz
tar zxvf ImageMagick.tar.gz
cd ImageMagick-6.9.1-10
./configure  --prefix=/usr/local 
make &amp;amp;&amp;amp; make install

# libmemcached
wget https://launchpad.net/libmemcached/1.0/1.0.18/+download/libmemcached-1.0.18.tar.gz
tar zxvf libmemcached-1.0.18.tar.gz
cd libmemcached-1.0.18
./configure -prefix=/usr/local 
make &amp;amp;&amp;amp;　make install
&lt;/pre&gt;
 &lt;p&gt;可选的插件：&lt;/p&gt;
 &lt;pre&gt;# memcached
wget http://www.memcached.org/files/memcached-1.4.19.tar.gz
tar zxvf memcached-1.4.19.tar.gz
cd memcached-1.4.19
./configure --prefix=/usr/local
make
make install

# beansdb
git clone https://github.com/douban/beansdb
cd beansdb
./configure --prefix=/usr/local
make

# benseye
git clone git@github.com:douban/beanseye.git
cd beanseye
make

# SSDB
wget --no-check-certificate https://github.com/ideawu/ssdb/archive/master.zip
unzip master
cd ssdb-master
make

# twemproxy
git clone git@github.com:twitter/twemproxy.git
cd twemproxy
autoreconf -fvi
./configure --enable-debug=log
make
src/nutcracker -h

&lt;/pre&gt;
 &lt;p&gt;构建zimg&lt;/p&gt;
 &lt;pre&gt;cd /usr/local
#git clone https://github.com/buaazp/zimg -b master --depth=1
cd zimg   
make
&lt;/pre&gt;
 &lt;p&gt;安装成功后：&lt;/p&gt;
 &lt;pre&gt;cd /usr/local/zimg/bin
./zimg conf/zimg.lua

&lt;/pre&gt;
 &lt;p&gt;打开http://localhost:4869看是否安装成功。&lt;/p&gt;
 &lt;p&gt;如果嫌手动安装太麻烦,就直接使用docker镜像&lt;/p&gt;
 &lt;pre&gt;# 拉取zimg镜像
$ docker pull iknow0612/zimg
# 启动zimg容器
$ docker run -it -d -p 4869:4869 -v /data/zimg/:/zimg/bin/img --name my_zimg iknow0612/zimg sh app.sh
&lt;/pre&gt;
 &lt;p&gt;可以自己基于zimg再封装图片服务。&lt;/p&gt;
 &lt;p&gt;参考链接：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://github.com/buaazp/zimg"&gt;https://github.com/buaazp/zimg&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://github.com/fengkuangdejava/java-springboot-zimg"&gt;https://github.com/fengkuangdejava/java-springboot-zimg&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://github.com/liyouzhi/zimg-python"&gt;https://github.com/liyouzhi/zimg-python&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;div&gt;
  &lt;h3&gt;相关文章:&lt;/h3&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/skyline-centos-7.html" rel="bookmark" title="Skyline&amp;#23454;&amp;#25112;&amp;#65306;CentOS 7&amp;#37096;&amp;#32626;"&gt;Skyline实战：CentOS 7部署 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/hello-world.html" rel="bookmark" title="C&amp;#35821;&amp;#35328;&amp;#20043;Hello World&amp;#31243;&amp;#24207;&amp;#32534;&amp;#35793;"&gt;C语言之Hello World程序编译 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/uber-h3.html" rel="bookmark" title="&amp;#31354;&amp;#38388;&amp;#32034;&amp;#24341;&amp;#20043;Uber H3"&gt;空间索引之Uber H3 &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>器→工具 开源项目 术→技巧 研发 运维</category>
      <guid isPermaLink="true">https://itindex.net/detail/61906-zimg-%E5%9B%BE%E7%89%87-%E6%9C%8D%E5%8A%A1%E5%99%A8</guid>
      <pubDate>Sat, 20 Nov 2021 08:30:23 CST</pubDate>
    </item>
    <item>
      <title>如何用 Python 发送告警通知到微信？</title>
      <link>https://itindex.net/detail/61880-python-%E5%BE%AE%E4%BF%A1</link>
      <description>&lt;p&gt;大家好，我是明哥。&lt;/p&gt;
 &lt;p&gt;最近当上了阿里云的推广大使，带了 200 名读者一起免费领取了阿里云的服务器，每个人都说 『真香』。&lt;/p&gt;
 &lt;p&gt;组织第一期活动，其实还是有不少的问题，主要流程上的问题。&lt;/p&gt;
 &lt;p&gt;为了让整个流程更加自动化，操作更加流畅，我利用公众号的开发能力，将整个过程集成到公众号消息的自助查询。&lt;/p&gt;
 &lt;p&gt;其中有一步就是确认用户的购买资格，只要在我的公众号后台回复对应的阿里云ID 我这边就会去查询阿里云后台的关联数据，但阿里云的 cookie 数个小时就会失效，这样一来就有点尴尬，后台的爬虫失效了参与的读者还在一直查，一直查，查到的都是未关联的数据。&lt;/p&gt;
 &lt;p&gt;这时候实时告警就显得非常重要，常见的告警方式有：邮件，电话，短信，微信。&lt;/p&gt;
 &lt;p&gt;短信和电话，通常是收费的（有不收费的，可以分享一下），而邮件又不是那么及时，因此最后我选择微信通知。&lt;/p&gt;
 &lt;p&gt;这里说的微信，是企业微信，而我之前用注册过个体户的执照，因此可以很轻松就可以注册自己的企业微信。&lt;/p&gt;
 &lt;h2&gt;1. 新建应用&lt;/h2&gt;
 &lt;p&gt;登陆网页版企业微信 (https://work.weixin.qq.com/)，点击   &lt;strong&gt;应用管理&lt;/strong&gt; -&amp;gt;   &lt;strong&gt;应用&lt;/strong&gt; -&amp;gt;   &lt;strong&gt;创建应用&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://image.iswbm.com/20211105220741.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;上传应用的 logo，输入应用名称，再选择可见范围，成功创建一个告警应用&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://image.iswbm.com/20211105221312.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;2. 获取Secret&lt;/h2&gt;
 &lt;p&gt;使用 Python 发送告警请求，其实就只使用到两个接口&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;获取 Token&lt;/strong&gt; ：    &lt;code&gt;https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={corpid}&amp;amp;corpsecret={secret}&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;发送请求&lt;/strong&gt;：   &lt;code&gt;https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token={token}&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;可以看到，最重要的是 corpid 和 secret:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;corpid：唯一标识你的企业&lt;/li&gt;
  &lt;li&gt;secret：应用级的密钥，有了程序才知道你要发送该企业的哪个应用&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;corpid 可以通过   &lt;strong&gt;我的企业&lt;/strong&gt; -&amp;gt;   &lt;strong&gt;企业信息&lt;/strong&gt; 获取&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://image.iswbm.com/20211105225506.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;而 secret 获取相对麻烦一点，点击前面创建应用，点击 查看 secret&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://image.iswbm.com/20211106134909.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;然后再点击发送就会发送到你的企业微信上&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://image.iswbm.com/20211106134943.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;最后将 corpid 和 secret 填入下面的常量中。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;import json
import datetime
import requests

CORP_ID = &amp;quot;&amp;quot;
SECRET = &amp;quot;&amp;quot;

class WeChatPub:
    s = requests.session()

    def __init__(self):
        self.token = self.get_token()

    def get_token(self):
        url = f&amp;quot;https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={CORP_ID}&amp;amp;corpsecret={SECRET}&amp;quot;
        rep = self.s.get(url)
        if rep.status_code != 200:
            print(&amp;quot;request failed.&amp;quot;)
            return
        return json.loads(rep.content)[&amp;apos;access_token&amp;apos;]


    def send_msg(self, content):
        url = &amp;quot;https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=&amp;quot; + self.token
        header = {
            &amp;quot;Content-Type&amp;quot;: &amp;quot;application/json&amp;quot;
        }
        form_data = {
            &amp;quot;touser&amp;quot;: &amp;quot;@all&amp;quot;,
            &amp;quot;toparty&amp;quot;: &amp;quot; PartyID1 | PartyID2 &amp;quot;,
            &amp;quot;totag&amp;quot;: &amp;quot; TagID1 | TagID2 &amp;quot;,
            &amp;quot;msgtype&amp;quot;: &amp;quot;textcard&amp;quot;,
            &amp;quot;agentid&amp;quot;: 1000002,
            &amp;quot;textcard&amp;quot;: {
                &amp;quot;title&amp;quot;: &amp;quot;服务异常告警&amp;quot;,
                &amp;quot;description&amp;quot;: content,
                &amp;quot;url&amp;quot;: &amp;quot;URL&amp;quot;,
                &amp;quot;btntxt&amp;quot;: &amp;quot;更多&amp;quot;
            },
            &amp;quot;safe&amp;quot;: 0
        }
        rep = self.s.post(url, data=json.dumps(form_data).encode(&amp;apos;utf-8&amp;apos;), headers=header)
        if rep.status_code != 200:
            print(&amp;quot;request failed.&amp;quot;)
            return
        return json.loads(rep.content)
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;然后就可以通过 send_msg 函数发送消息了。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;wechat = WeChatPub()
now = datetime.datetime.now()
timenow = now.strftime(&amp;apos;%Y年%m月%d日 %H:%M:%S&amp;apos;)
wechat.send_msg(f&amp;quot;&amp;lt;div class=\&amp;quot;gray\&amp;quot;&amp;gt;{timenow}&amp;lt;/div&amp;gt; &amp;lt;div class=\&amp;quot;normal\&amp;quot;&amp;gt;阿里云 cookie 已失效&amp;lt;/div&amp;gt;&amp;lt;div class=\&amp;quot;highlight\&amp;quot;&amp;gt;请尽快更换新的 cookie&amp;lt;/div&amp;gt;&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;只要你的企业微信没有关闭通知的权限，那你的手机立马就会弹出这个告警信息。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="http://image.iswbm.com/20211105225409.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;简单几步就对接了企业微信，实现了手机的实时告警功能，推荐有企业微信的同学使用。&lt;/p&gt;
 &lt;p&gt;当然一定有更多，更好用的实现方法，我只是我选择了其中一种，大家有不错的思路也可以分享在评论区。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>Python 工具使用 Go Python 密钥 微信</category>
      <guid isPermaLink="true">https://itindex.net/detail/61880-python-%E5%BE%AE%E4%BF%A1</guid>
      <pubDate>Sat, 06 Nov 2021 22:54:11 CST</pubDate>
    </item>
    <item>
      <title>HTTP抓包工具之Charles</title>
      <link>https://itindex.net/detail/61869-http-%E5%B7%A5%E5%85%B7-charles</link>
      <description>&lt;h2&gt;Charles简介&lt;/h2&gt;
 &lt;p&gt;Charles是一个HTTP代理服务器，当浏览器连接Charles的代理访问互联网时，Charles可以监控浏览器发送和接收的所有数据。它允许一个开发者查看所有连接互联网的HTTP通信，这些包括request, response和HTTP headers （包含cookies与caching信息）。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="197" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/charles.jpg" width="560"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Charles主要功能：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;支持SSL代理。可以截取分析SSL的请求。&lt;/li&gt;
  &lt;li&gt;支持流量控制。可以模拟慢速网络以及等待时间（latency）较长的请求。&lt;/li&gt;
  &lt;li&gt;支持AJAX调试。可以自动将json或xml数据格式化，方便查看。&lt;/li&gt;
  &lt;li&gt;支持AMF调试。可以将Flash Remoting 或 Flex Remoting信息格式化，方便查看。&lt;/li&gt;
  &lt;li&gt;支持重发网络请求，方便后端调试。&lt;/li&gt;
  &lt;li&gt;支持修改网络请求参数。&lt;/li&gt;
  &lt;li&gt;支持网络请求的截获并动态修改。&lt;/li&gt;
  &lt;li&gt;检查HTML，CSS和RSS内容是否符合W3C标准。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;以上介绍了Charles的主要功能，个人在使用过程中主要用的是抓取HTTP和HTTPS请求。特别是HTTPS的请求，抓取起来还是有一些麻烦，特此记录。&lt;/p&gt;
 &lt;h2&gt;Charles 抓包原理&lt;/h2&gt;
 &lt;p&gt;市面上绝大多数的抓包软件，背后的原理都是中间人攻击（Man-in-the-middle attack，缩写：MITM）。&lt;/p&gt;
 &lt;p&gt;维基百科是这样定义 MITM 的：中间人攻击在密码学和计算机安全领域中是指攻击者与通讯的两端分别建立独立的联系，并交换其所收到的数据，使通讯的两端认为他们正在通过一个私密的连接与对方直接对话，但事实上整个会话都被攻击者完全控制。&lt;/p&gt;
 &lt;p&gt;上面的定义写的很清晰，下图中结合箭头方向就能看懂 HTTP Packets 的流向：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="323" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/MITM.png" width="674"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;Charles的使用&lt;/h2&gt;
 &lt;p&gt;Charles的安装过程是比较简单，只需到  &lt;a href="https://www.charlesproxy.com/"&gt;官网&lt;/a&gt;下载安装即可。比较困难的是HTTPS请求的配置。&lt;/p&gt;
 &lt;h3&gt;Windows下HTTPS请求抓包&lt;/h3&gt;
 &lt;p&gt;1、配置SSL支持。点击【Proxy】–&amp;gt;【SSL Proxying Settings…】，在弹出选项卡中，勾选【Enable SSL Proxying】点击【add】，在Host输入【*】表示接收任何主机，在Prot输入【*】表示任何端口，最后点击【ok】保存。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="425" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/ssl.png" width="525"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;2、安装证书。点击【Help】–&amp;gt;【SSL Proxying】–&amp;gt;【Install Charles Root Certificate】，按照引导流程安装证书。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="606" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/ca.png" width="431"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;完成后打开IE进行测试：出现证书错误！&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="258" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/ie.png" width="500"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;打开Chrome测试：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="391" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/chrome.png" width="500"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;打开Edge测试：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="317" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/edge.png" width="500"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;都被安全拦截了，装了证书都不起作用。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;解决方案：安装Firefox！&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;安装完毕后在开启Charles时，使用Firefox打开，http://chls.pro/ssl，弹出如下页面：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="313" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/firefox.png" width="434"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;选择保存文件后按确定。文件默认保存到下载文件夹。&lt;/p&gt;
 &lt;p&gt;打开Firefox【设置】–&amp;gt;【隐私与安全】–&amp;gt;【证书】–&amp;gt;【查看证书】&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="91" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/pem-1.png" width="500"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;使用【证书管理器】–&amp;gt;【证书办法机构】–&amp;gt;【导入】进行导入操作。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="365" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/pem-2.png" width="500"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;iOS下HTTPS请求抓包&lt;/h3&gt;
 &lt;p&gt;在PC上开启共享网络。将手机连接到PC共享的WIFI上。&lt;/p&gt;
 &lt;p&gt;在手机上设置代理地址，代理IP为PC的IP，端口为Charles的端口。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="156" src="https://www.biaodianfu.com/wp-content/uploads/2021/11/ios.png" width="500"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;在手机自带浏览器Safari中输入chls.pro，完成后需要进入【设置】安装描述文件。安装完毕后，如果是iOS 10 以后需要进入【设置】–&amp;gt;【通用】–&amp;gt;【关于本机】–&amp;gt;【证书信任设置】，开启证书。&lt;/p&gt;
 &lt;h3&gt;Android下HTTPS请求抓包&lt;/h3&gt;
 &lt;p&gt;在PC上开启共享网络。将手机连接到PC共享的WIFI上。&lt;/p&gt;
 &lt;p&gt;在手机上设置代理地址，代理IP为PC的IP，端口为Charles的端口。&lt;/p&gt;
 &lt;p&gt;在手机默认浏览器中输入chls.pro，下载downloadfile.crt文件，然后在【我的下载】中进行打开，按引导进行安装。&lt;/p&gt;
 &lt;div&gt;
  &lt;h3&gt;相关文章:&lt;/h3&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/python-build-web-server.html" rel="bookmark" title="Python &amp;#20174;0&amp;#21040;1&amp;#25645;&amp;#24314;Web &amp;#26381;&amp;#21153;&amp;#22120;"&gt;Python 从0到1搭建Web 服务器 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/skyline-centos-7.html" rel="bookmark" title="Skyline&amp;#23454;&amp;#25112;&amp;#65306;CentOS 7&amp;#37096;&amp;#32626;"&gt;Skyline实战：CentOS 7部署 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/install-wordpress-on-ubuntu-20-04-with-lemp-stack.html" rel="bookmark" title="Ubuntu Server 20.04 WordPress&amp;#29615;&amp;#22659;&amp;#23433;&amp;#35013;&amp;#19982;&amp;#37197;&amp;#32622;"&gt;Ubuntu Server 20.04 WordPress环境安装与配置 &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>器→工具 工具软件 抓包 爬虫</category>
      <guid isPermaLink="true">https://itindex.net/detail/61869-http-%E5%B7%A5%E5%85%B7-charles</guid>
      <pubDate>Tue, 02 Nov 2021 09:20:20 CST</pubDate>
    </item>
    <item>
      <title>[译]strace的10个命令</title>
      <link>https://itindex.net/detail/61383-strace-%E5%91%BD%E4%BB%A4</link>
      <description>&lt;p&gt;strace是一个在类Unix操作系统如Linux上做debugging和trouble shooting的超级好用的工具。它可以捕获和记录进程的所有系统调用，以及这个进程接收的所有信号。&lt;/p&gt;
 &lt;p&gt;原文:   &lt;a href="https://www.tecmint.com/strace-commands-for-troubleshooting-and-debugging-linux/#:~:text=strace%20is%20a%20powerful%20command,signals%20received%20by%20the%20process." rel="external" target="_blank"&gt;10 Strace Commands for Troubleshooting and Debugging Linux Processes&lt;/a&gt;&lt;/p&gt;
 &lt;a&gt;&lt;/a&gt;
 &lt;p&gt;如果你的操作系统还没有安装strace，你可以运行下面的命令进行安装：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;$ sudo apt install strace	#Debian/Ubuntu &lt;/div&gt;     &lt;div&gt;# yum install strace		#RHEL/CentOS&lt;/div&gt;     &lt;div&gt;# dnf install strace		#Fedora 22+&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;p&gt;如果一个程序崩溃或以一种出乎意料的方式运行，您可以通过它的系统调用来获得在执行过程中到底发生了什么的线索。我们将在后面看到，系统调用可以分为不同的事件：与进程管理相关的事件、以文件为参数的事件、涉及网络、内存映射、信号、IPC以及与文件描述符相关的系统调用。&lt;/p&gt;
 &lt;p&gt;可以使用strace运行程序/命令，也可以使用  &lt;code&gt;-p&lt;/code&gt;选项将PID传递给它，如下面示例所示。&lt;/p&gt;
 &lt;h2&gt;追踪Linux系统调用&lt;/h2&gt;
 &lt;p&gt;你可以通过运行下面的命令，追踪  &lt;code&gt;df&lt;/code&gt;命令的系统调用。&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;     &lt;div&gt;4&lt;/div&gt;     &lt;div&gt;5&lt;/div&gt;     &lt;div&gt;6&lt;/div&gt;     &lt;div&gt;7&lt;/div&gt;     &lt;div&gt;8&lt;/div&gt;     &lt;div&gt;9&lt;/div&gt;     &lt;div&gt;10&lt;/div&gt;     &lt;div&gt;11&lt;/div&gt;     &lt;div&gt;12&lt;/div&gt;     &lt;div&gt;13&lt;/div&gt;     &lt;div&gt;14&lt;/div&gt;     &lt;div&gt;15&lt;/div&gt;     &lt;div&gt;16&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;$ strace df -h&lt;/div&gt;     &lt;div&gt;execve(&amp;quot;/bin/df&amp;quot;, [&amp;quot;df&amp;quot;, &amp;quot;-h&amp;quot;], [/* 50 vars */]) = 0&lt;/div&gt;     &lt;div&gt;brk(NULL)                               = 0x136e000&lt;/div&gt;     &lt;div&gt;access(&amp;quot;/etc/ld.so.nohwcap&amp;quot;, F_OK)      = -1 ENOENT (No such file or directory)&lt;/div&gt;     &lt;div&gt;mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f82f78fd000&lt;/div&gt;     &lt;div&gt;access(&amp;quot;/etc/ld.so.preload&amp;quot;, R_OK)      = -1 ENOENT (No such file or directory)&lt;/div&gt;     &lt;div&gt;open(&amp;quot;/etc/ld.so.cache&amp;quot;, O_RDONLY|O_CLOEXEC) = 3&lt;/div&gt;     &lt;div&gt;fstat(3, {st_mode=S_IFREG|0644, st_size=147662, ...}) = 0&lt;/div&gt;     &lt;div&gt;mmap(NULL, 147662, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f82f78d8000&lt;/div&gt;     &lt;div&gt;close(3)                                = 0&lt;/div&gt;     &lt;div&gt;access(&amp;quot;/etc/ld.so.nohwcap&amp;quot;, F_OK)      = -1 ENOENT (No such file or directory)&lt;/div&gt;     &lt;div&gt;open(&amp;quot;/lib/x86_64-linux-gnu/libc.so.6&amp;quot;, O_RDONLY|O_CLOEXEC) = 3&lt;/div&gt;     &lt;div&gt;read(3, &amp;quot;\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0&amp;gt;\0\1\0\0\0P\t\2\0\0\0\0\0&amp;quot;..., 832) = 832&lt;/div&gt;     &lt;div&gt;fstat(3, {st_mode=S_IFREG|0755, st_size=1868984, ...}) = 0&lt;/div&gt;     &lt;div&gt;mmap(NULL, 3971488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f82f7310000&lt;/div&gt;     &lt;div&gt;...&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;p&gt;从上面的输出结果，我们可以看到几种类型的系统调用，比如&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;open(&amp;quot;/etc/ld.so.cache&amp;quot;, O_RDONLY|O_CLOEXEC) = 3&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;p&gt;其中&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;open: 系统调用的类型&lt;/li&gt;
  &lt;li&gt;(“/etc/ld.so.cache”, O_RDONLY|O_CLOEXEC) : 系统调用的参数&lt;/li&gt;
  &lt;li&gt;3: 系统调用的返回结果&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;下面是df命令执行的时候write系统调用信息:&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;     &lt;div&gt;4&lt;/div&gt;     &lt;div&gt;5&lt;/div&gt;     &lt;div&gt;6&lt;/div&gt;     &lt;div&gt;7&lt;/div&gt;     &lt;div&gt;8&lt;/div&gt;     &lt;div&gt;9&lt;/div&gt;     &lt;div&gt;10&lt;/div&gt;     &lt;div&gt;11&lt;/div&gt;     &lt;div&gt;12&lt;/div&gt;     &lt;div&gt;13&lt;/div&gt;     &lt;div&gt;14&lt;/div&gt;     &lt;div&gt;15&lt;/div&gt;     &lt;div&gt;16&lt;/div&gt;     &lt;div&gt;17&lt;/div&gt;     &lt;div&gt;18&lt;/div&gt;     &lt;div&gt;19&lt;/div&gt;     &lt;div&gt;20&lt;/div&gt;     &lt;div&gt;21&lt;/div&gt;     &lt;div&gt;22&lt;/div&gt;     &lt;div&gt;23&lt;/div&gt;     &lt;div&gt;24&lt;/div&gt;     &lt;div&gt;25&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;mmap(NULL, 26258, PROT_READ, MAP_SHARED, 3, 0) = 0x7f82f78f5000&lt;/div&gt;     &lt;div&gt;close(3)                                = 0&lt;/div&gt;     &lt;div&gt;fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0&lt;/div&gt;     &lt;div&gt;write(1, &amp;quot;Filesystem      Size  Used Avail&amp;quot;..., 49Filesystem      Size  Used Avail Use% Mounted on&lt;/div&gt;     &lt;div&gt;) = 49&lt;/div&gt;     &lt;div&gt;write(1, &amp;quot;udev            3.9G     0  3.9G&amp;quot;..., 43udev            3.9G     0  3.9G   0% /dev&lt;/div&gt;     &lt;div&gt;) = 43&lt;/div&gt;     &lt;div&gt;write(1, &amp;quot;tmpfs           788M  9.6M  779M&amp;quot;..., 43tmpfs           788M  9.6M  779M   2% /run&lt;/div&gt;     &lt;div&gt;) = 43&lt;/div&gt;     &lt;div&gt;write(1, &amp;quot;/dev/sda10      324G  252G   56G&amp;quot;..., 40/dev/sda10      324G  252G   56G  82% /&lt;/div&gt;     &lt;div&gt;) = 40&lt;/div&gt;     &lt;div&gt;write(1, &amp;quot;tmpfs           3.9G  104M  3.8G&amp;quot;..., 47tmpfs           3.9G  104M  3.8G   3% /dev/shm&lt;/div&gt;     &lt;div&gt;) = 47&lt;/div&gt;     &lt;div&gt;write(1, &amp;quot;tmpfs           5.0M  4.0K  5.0M&amp;quot;..., 48tmpfs           5.0M  4.0K  5.0M   1% /run/lock&lt;/div&gt;     &lt;div&gt;) = 48&lt;/div&gt;     &lt;div&gt;write(1, &amp;quot;tmpfs           3.9G     0  3.9G&amp;quot;..., 53tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup&lt;/div&gt;     &lt;div&gt;) = 53&lt;/div&gt;     &lt;div&gt;write(1, &amp;quot;cgmfs           100K     0  100K&amp;quot;..., 56cgmfs           100K     0  100K   0% /run/cgmanager/fs&lt;/div&gt;     &lt;div&gt;) = 56&lt;/div&gt;     &lt;div&gt;write(1, &amp;quot;tmpfs           788M   36K  788M&amp;quot;..., 53tmpfs           788M   36K  788M   1% /run/user/1000&lt;/div&gt;     &lt;div&gt;) = 53&lt;/div&gt;     &lt;div&gt;close(1)                                = 0&lt;/div&gt;     &lt;div&gt;close(2)                                = 0&lt;/div&gt;     &lt;div&gt;exit_group(0)                           = ?&lt;/div&gt;     &lt;div&gt;+++ exited with 0 +++&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;h2&gt;根据进程PID进行追踪&lt;/h2&gt;
 &lt;p&gt;如果一个进程已经在运行，你可以通过它的pid进行追踪，它会显示追踪后这个进程的系统调用，使用  &lt;code&gt;CTRL+C&lt;/code&gt;退出。&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;     &lt;div&gt;4&lt;/div&gt;     &lt;div&gt;5&lt;/div&gt;     &lt;div&gt;6&lt;/div&gt;     &lt;div&gt;7&lt;/div&gt;     &lt;div&gt;8&lt;/div&gt;     &lt;div&gt;9&lt;/div&gt;     &lt;div&gt;10&lt;/div&gt;     &lt;div&gt;11&lt;/div&gt;     &lt;div&gt;12&lt;/div&gt;     &lt;div&gt;13&lt;/div&gt;     &lt;div&gt;14&lt;/div&gt;     &lt;div&gt;15&lt;/div&gt;     &lt;div&gt;16&lt;/div&gt;     &lt;div&gt;17&lt;/div&gt;     &lt;div&gt;18&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;$ sudo strace -p 3569&lt;/div&gt;     &lt;div&gt;strace: Process 3569 attached&lt;/div&gt;     &lt;div&gt;restart_syscall(&amp;lt;... resuming interrupted poll ...&amp;gt;) = 1&lt;/div&gt;     &lt;div&gt;recvmsg(4, {msg_name(0)=NULL, msg_iov(1)=[{&amp;quot;U\2\24\300!\247\330\0\3\24\4\0\20\0\0\0\0\0\0\24\24\24\24\24\0\0\3\37%\2\0\0&amp;quot;, 4096}], msg_controllen=0, msg_flags=0}, 0) = 32&lt;/div&gt;     &lt;div&gt;recvmsg(4, 0x7ffee4dbf870, 0)           = -1 EAGAIN (Resource temporarily unavailable)&lt;/div&gt;     &lt;div&gt;recvmsg(4, 0x7ffee4dbf850, 0)           = -1 EAGAIN (Resource temporarily unavailable)&lt;/div&gt;     &lt;div&gt;poll([{fd=3, events=POLLIN}, {fd=4, events=POLLIN}, {fd=5, events=POLLIN}, {fd=10, events=POLLIN}, {fd=30, events=POLLIN}, {fd=31, events=POLLIN}], 6, -1) = 1 ([{fd=31, revents=POLLIN}])&lt;/div&gt;     &lt;div&gt;read(31, &amp;quot;\372&amp;quot;, 1)                     = 1&lt;/div&gt;     &lt;div&gt;recvmsg(4, 0x7ffee4dbf850, 0)           = -1 EAGAIN (Resource temporarily unavailable)&lt;/div&gt;     &lt;div&gt;poll([{fd=3, events=POLLIN}, {fd=4, events=POLLIN}, {fd=5, events=POLLIN}, {fd=10, events=POLLIN}, {fd=30, events=POLLIN}, {fd=31, events=POLLIN}], 6, 0) = 1 ([{fd=31, revents=POLLIN}])&lt;/div&gt;     &lt;div&gt;read(31, &amp;quot;\372&amp;quot;, 1)                     = 1&lt;/div&gt;     &lt;div&gt;recvmsg(4, 0x7ffee4dbf850, 0)           = -1 EAGAIN (Resource temporarily unavailable)&lt;/div&gt;     &lt;div&gt;poll([{fd=3, events=POLLIN}, {fd=4, events=POLLIN}, {fd=5, events=POLLIN}, {fd=10, events=POLLIN}, {fd=30, events=POLLIN}, {fd=31, events=POLLIN}], 6, 0) = 0 (Timeout)&lt;/div&gt;     &lt;div&gt;mprotect(0x207faa20000, 8192, PROT_READ|PROT_WRITE) = 0&lt;/div&gt;     &lt;div&gt;mprotect(0x207faa20000, 8192, PROT_READ|PROT_EXEC) = 0&lt;/div&gt;     &lt;div&gt;mprotect(0x207faa21000, 4096, PROT_READ|PROT_WRITE) = 0&lt;/div&gt;     &lt;div&gt;mprotect(0x207faa21000, 4096, PROT_READ|PROT_EXEC) = 0&lt;/div&gt;     &lt;div&gt;...&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;h2&gt;得到进程的汇总信息&lt;/h2&gt;
 &lt;p&gt;使用  &lt;code&gt;-c&lt;/code&gt;参数，可以得到追踪的每一种系统调用的耗时、次数和失败数，如下所示：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;     &lt;div&gt;4&lt;/div&gt;     &lt;div&gt;5&lt;/div&gt;     &lt;div&gt;6&lt;/div&gt;     &lt;div&gt;7&lt;/div&gt;     &lt;div&gt;8&lt;/div&gt;     &lt;div&gt;9&lt;/div&gt;     &lt;div&gt;10&lt;/div&gt;     &lt;div&gt;11&lt;/div&gt;     &lt;div&gt;12&lt;/div&gt;     &lt;div&gt;13&lt;/div&gt;     &lt;div&gt;14&lt;/div&gt;     &lt;div&gt;15&lt;/div&gt;     &lt;div&gt;16&lt;/div&gt;     &lt;div&gt;17&lt;/div&gt;     &lt;div&gt;18&lt;/div&gt;     &lt;div&gt;19&lt;/div&gt;     &lt;div&gt;20&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;$ sudo strace -c -p 3569&lt;/div&gt;     &lt;div&gt;&lt;/div&gt;     &lt;div&gt;strace: Process 3569 attached&lt;/div&gt;     &lt;div&gt;&lt;/div&gt;     &lt;div&gt;^Cstrace: Process 3569 detached&lt;/div&gt;     &lt;div&gt;% time     seconds  usecs/call     calls    errors syscall&lt;/div&gt;     &lt;div&gt;------ ----------- ----------- --------- --------- ----------------&lt;/div&gt;     &lt;div&gt; 99.73    0.016000           8      1971           poll&lt;/div&gt;     &lt;div&gt;  0.16    0.000025           0       509        75 futex&lt;/div&gt;     &lt;div&gt;  0.06    0.000010           0      1985      1966 recvmsg&lt;/div&gt;     &lt;div&gt;  0.06    0.000009           0      2336           mprotect&lt;/div&gt;     &lt;div&gt;  0.00    0.000000           0       478           read&lt;/div&gt;     &lt;div&gt;  0.00    0.000000           0        13           write&lt;/div&gt;     &lt;div&gt;  0.00    0.000000           0        29           mmap&lt;/div&gt;     &lt;div&gt;  0.00    0.000000           0         9           munmap&lt;/div&gt;     &lt;div&gt;  0.00    0.000000           0        18           writev&lt;/div&gt;     &lt;div&gt;  0.00    0.000000           0       351           madvise&lt;/div&gt;     &lt;div&gt;  0.00    0.000000           0         1           restart_syscall&lt;/div&gt;     &lt;div&gt;------ ----------- ----------- --------- --------- ----------------&lt;/div&gt;     &lt;div&gt;100.00    0.016044                  7700      2041 total&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;h2&gt;打印指令指针&lt;/h2&gt;
 &lt;p&gt;  &lt;code&gt;-i&lt;/code&gt;可以显示每一次系统调用的时候的指令指针。&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;     &lt;div&gt;4&lt;/div&gt;     &lt;div&gt;5&lt;/div&gt;     &lt;div&gt;6&lt;/div&gt;     &lt;div&gt;7&lt;/div&gt;     &lt;div&gt;8&lt;/div&gt;     &lt;div&gt;9&lt;/div&gt;     &lt;div&gt;10&lt;/div&gt;     &lt;div&gt;11&lt;/div&gt;     &lt;div&gt;12&lt;/div&gt;     &lt;div&gt;13&lt;/div&gt;     &lt;div&gt;14&lt;/div&gt;     &lt;div&gt;15&lt;/div&gt;     &lt;div&gt;16&lt;/div&gt;     &lt;div&gt;17&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;$ sudo strace -i df -h&lt;/div&gt;     &lt;div&gt;[00007f0d7534c777] execve(&amp;quot;/bin/df&amp;quot;, [&amp;quot;df&amp;quot;, &amp;quot;-h&amp;quot;], [/* 17 vars */]) = 0&lt;/div&gt;     &lt;div&gt;[00007faf9cafa4b9] brk(NULL)            = 0x12f0000&lt;/div&gt;     &lt;div&gt;[00007faf9cafb387] access(&amp;quot;/etc/ld.so.nohwcap&amp;quot;, F_OK) = -1 ENOENT (No such file or directory)&lt;/div&gt;     &lt;div&gt;[00007faf9cafb47a] mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7faf9cd03000&lt;/div&gt;     &lt;div&gt;[00007faf9cafb387] access(&amp;quot;/etc/ld.so.preload&amp;quot;, R_OK) = -1 ENOENT (No such file or directory)&lt;/div&gt;     &lt;div&gt;[00007faf9cafb327] open(&amp;quot;/etc/ld.so.cache&amp;quot;, O_RDONLY|O_CLOEXEC) = 3&lt;/div&gt;     &lt;div&gt;[00007faf9cafb2b4] fstat(3, {st_mode=S_IFREG|0644, st_size=147662, ...}) = 0&lt;/div&gt;     &lt;div&gt;[00007faf9cafb47a] mmap(NULL, 147662, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7faf9ccde000&lt;/div&gt;     &lt;div&gt;[00007faf9cafb427] close(3)             = 0&lt;/div&gt;     &lt;div&gt;[00007faf9cafb387] access(&amp;quot;/etc/ld.so.nohwcap&amp;quot;, F_OK) = -1 ENOENT (No such file or directory)&lt;/div&gt;     &lt;div&gt;[00007faf9cafb327] open(&amp;quot;/lib/x86_64-linux-gnu/libc.so.6&amp;quot;, O_RDONLY|O_CLOEXEC) = 3&lt;/div&gt;     &lt;div&gt;[00007faf9cafb347] read(3, &amp;quot;\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0&amp;gt;\0\1\0\0\0P\t\2\0\0\0\0\0&amp;quot;..., 832) = 832&lt;/div&gt;     &lt;div&gt;[00007faf9cafb2b4] fstat(3, {st_mode=S_IFREG|0755, st_size=1868984, ...}) = 0&lt;/div&gt;     &lt;div&gt;[00007faf9cafb47a] mmap(NULL, 3971488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7faf9c716000&lt;/div&gt;     &lt;div&gt;[00007faf9cafb517] mprotect(0x7faf9c8d6000, 2097152, PROT_NONE) = 0&lt;/div&gt;     &lt;div&gt;...&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;h2&gt;显示每一次调用的时间&lt;/h2&gt;
 &lt;p&gt;  &lt;code&gt;-t&lt;/code&gt;参数可以显示时间戳。&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;     &lt;div&gt;4&lt;/div&gt;     &lt;div&gt;5&lt;/div&gt;     &lt;div&gt;6&lt;/div&gt;     &lt;div&gt;7&lt;/div&gt;     &lt;div&gt;8&lt;/div&gt;     &lt;div&gt;9&lt;/div&gt;     &lt;div&gt;10&lt;/div&gt;     &lt;div&gt;11&lt;/div&gt;     &lt;div&gt;12&lt;/div&gt;     &lt;div&gt;13&lt;/div&gt;     &lt;div&gt;14&lt;/div&gt;     &lt;div&gt;15&lt;/div&gt;     &lt;div&gt;16&lt;/div&gt;     &lt;div&gt;17&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;$ sudo strace -t df -h&lt;/div&gt;     &lt;div&gt;15:19:25 execve(&amp;quot;/bin/df&amp;quot;, [&amp;quot;df&amp;quot;, &amp;quot;-h&amp;quot;], [/* 17 vars */]) = 0&lt;/div&gt;     &lt;div&gt;15:19:25 brk(NULL)                      = 0x234c000&lt;/div&gt;     &lt;div&gt;15:19:25 access(&amp;quot;/etc/ld.so.nohwcap&amp;quot;, F_OK) = -1 ENOENT (No such file or directory)&lt;/div&gt;     &lt;div&gt;15:19:25 mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f8c7f1d9000&lt;/div&gt;     &lt;div&gt;15:19:25 access(&amp;quot;/etc/ld.so.preload&amp;quot;, R_OK) = -1 ENOENT (No such file or directory)&lt;/div&gt;     &lt;div&gt;15:19:25 open(&amp;quot;/etc/ld.so.cache&amp;quot;, O_RDONLY|O_CLOEXEC) = 3&lt;/div&gt;     &lt;div&gt;15:19:25 fstat(3, {st_mode=S_IFREG|0644, st_size=147662, ...}) = 0&lt;/div&gt;     &lt;div&gt;15:19:25 mmap(NULL, 147662, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f8c7f1b4000&lt;/div&gt;     &lt;div&gt;15:19:25 close(3)                       = 0&lt;/div&gt;     &lt;div&gt;15:19:25 access(&amp;quot;/etc/ld.so.nohwcap&amp;quot;, F_OK) = -1 ENOENT (No such file or directory)&lt;/div&gt;     &lt;div&gt;15:19:25 open(&amp;quot;/lib/x86_64-linux-gnu/libc.so.6&amp;quot;, O_RDONLY|O_CLOEXEC) = 3&lt;/div&gt;     &lt;div&gt;15:19:25 read(3, &amp;quot;\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0&amp;gt;\0\1\0\0\0P\t\2\0\0\0\0\0&amp;quot;..., 832) = 832&lt;/div&gt;     &lt;div&gt;15:19:25 fstat(3, {st_mode=S_IFREG|0755, st_size=1868984, ...}) = 0&lt;/div&gt;     &lt;div&gt;15:19:25 mmap(NULL, 3971488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f8c7ebec000&lt;/div&gt;     &lt;div&gt;15:19:25 mprotect(0x7f8c7edac000, 2097152, PROT_NONE) = 0&lt;/div&gt;     &lt;div&gt;...&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;h2&gt;显示系统调用的耗时&lt;/h2&gt;
 &lt;p&gt;  &lt;code&gt;-T&lt;/code&gt;参数可以显示系统调用的耗时时间。&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;     &lt;div&gt;4&lt;/div&gt;     &lt;div&gt;5&lt;/div&gt;     &lt;div&gt;6&lt;/div&gt;     &lt;div&gt;7&lt;/div&gt;     &lt;div&gt;8&lt;/div&gt;     &lt;div&gt;9&lt;/div&gt;     &lt;div&gt;10&lt;/div&gt;     &lt;div&gt;11&lt;/div&gt;     &lt;div&gt;12&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;$ sudo strace -T df -h&lt;/div&gt;     &lt;div&gt;&lt;/div&gt;     &lt;div&gt;execve(&amp;quot;/bin/df&amp;quot;, [&amp;quot;df&amp;quot;, &amp;quot;-h&amp;quot;], [/* 17 vars */]) = 0 &amp;lt;0.000287&amp;gt;&lt;/div&gt;     &lt;div&gt;brk(NULL)                               = 0xeca000 &amp;lt;0.000035&amp;gt;&lt;/div&gt;     &lt;div&gt;access(&amp;quot;/etc/ld.so.nohwcap&amp;quot;, F_OK)      = -1 ENOENT (No such file or directory) &amp;lt;0.000028&amp;gt;&lt;/div&gt;     &lt;div&gt;mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f9aff2b1000 &amp;lt;0.000020&amp;gt;&lt;/div&gt;     &lt;div&gt;access(&amp;quot;/etc/ld.so.preload&amp;quot;, R_OK)      = -1 ENOENT (No such file or directory) &amp;lt;0.000019&amp;gt;&lt;/div&gt;     &lt;div&gt;open(&amp;quot;/etc/ld.so.cache&amp;quot;, O_RDONLY|O_CLOEXEC) = 3 &amp;lt;0.000022&amp;gt;&lt;/div&gt;     &lt;div&gt;fstat(3, {st_mode=S_IFREG|0644, st_size=147662, ...}) = 0 &amp;lt;0.000015&amp;gt;&lt;/div&gt;     &lt;div&gt;mmap(NULL, 147662, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f9aff28c000 &amp;lt;0.000019&amp;gt;&lt;/div&gt;     &lt;div&gt;close(3)                                = 0 &amp;lt;0.000014&amp;gt;&lt;/div&gt;     &lt;div&gt;...&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;h2&gt;只追踪特定的系统调用&lt;/h2&gt;
 &lt;p&gt;下面的命令中,  &lt;code&gt;trace=write&lt;/code&gt;是使用一个特定的表达式做筛选，只追踪特定的系统调用。表达式可以是signal, abbrev, verbose, raw, read 和 write等几种类型。&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;     &lt;div&gt;4&lt;/div&gt;     &lt;div&gt;5&lt;/div&gt;     &lt;div&gt;6&lt;/div&gt;     &lt;div&gt;7&lt;/div&gt;     &lt;div&gt;8&lt;/div&gt;     &lt;div&gt;9&lt;/div&gt;     &lt;div&gt;10&lt;/div&gt;     &lt;div&gt;11&lt;/div&gt;     &lt;div&gt;12&lt;/div&gt;     &lt;div&gt;13&lt;/div&gt;     &lt;div&gt;14&lt;/div&gt;     &lt;div&gt;15&lt;/div&gt;     &lt;div&gt;16&lt;/div&gt;     &lt;div&gt;17&lt;/div&gt;     &lt;div&gt;18&lt;/div&gt;     &lt;div&gt;19&lt;/div&gt;     &lt;div&gt;20&lt;/div&gt;     &lt;div&gt;21&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;$ sudo strace -e trace=write df -h&lt;/div&gt;     &lt;div&gt;&lt;/div&gt;     &lt;div&gt;write(1, &amp;quot;Filesystem      Size  Used Avail&amp;quot;..., 49Filesystem      Size  Used Avail Use% Mounted on&lt;/div&gt;     &lt;div&gt;) = 49&lt;/div&gt;     &lt;div&gt;write(1, &amp;quot;udev            3.9G     0  3.9G&amp;quot;..., 43udev            3.9G     0  3.9G   0% /dev&lt;/div&gt;     &lt;div&gt;) = 43&lt;/div&gt;     &lt;div&gt;write(1, &amp;quot;tmpfs           788M  9.6M  779M&amp;quot;..., 43tmpfs           788M  9.6M  779M   2% /run&lt;/div&gt;     &lt;div&gt;) = 43&lt;/div&gt;     &lt;div&gt;write(1, &amp;quot;/dev/sda10      324G  252G   56G&amp;quot;..., 40/dev/sda10      324G  252G   56G  82% /&lt;/div&gt;     &lt;div&gt;) = 40&lt;/div&gt;     &lt;div&gt;write(1, &amp;quot;tmpfs           3.9G  104M  3.8G&amp;quot;..., 47tmpfs           3.9G  104M  3.8G   3% /dev/shm&lt;/div&gt;     &lt;div&gt;) = 47&lt;/div&gt;     &lt;div&gt;write(1, &amp;quot;tmpfs           5.0M  4.0K  5.0M&amp;quot;..., 48tmpfs           5.0M  4.0K  5.0M   1% /run/lock&lt;/div&gt;     &lt;div&gt;) = 48&lt;/div&gt;     &lt;div&gt;write(1, &amp;quot;tmpfs           3.9G     0  3.9G&amp;quot;..., 53tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup&lt;/div&gt;     &lt;div&gt;) = 53&lt;/div&gt;     &lt;div&gt;write(1, &amp;quot;cgmfs           100K     0  100K&amp;quot;..., 56cgmfs           100K     0  100K   0% /run/cgmanager/fs&lt;/div&gt;     &lt;div&gt;) = 56&lt;/div&gt;     &lt;div&gt;write(1, &amp;quot;tmpfs           788M   28K  788M&amp;quot;..., 53tmpfs           788M   28K  788M   1% /run/user/1000&lt;/div&gt;     &lt;div&gt;) = 53&lt;/div&gt;     &lt;div&gt;+++ exited with 0 +++&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;p&gt;又比如&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;$ sudo strace -e trace=open,close df -h&lt;/div&gt;     &lt;div&gt;$ sudo strace -e trace=open,close,read,write df -h&lt;/div&gt;     &lt;div&gt;$ sudo strace -e trace=all df -h&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;h2&gt;基于特定条件的系统调用&lt;/h2&gt;
 &lt;p&gt;可以针对特定类型进行追踪(process、file、memory、network、signal)等等。&lt;/p&gt;
 &lt;p&gt;针对进行管理的追踪:&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;     &lt;div&gt;4&lt;/div&gt;     &lt;div&gt;5&lt;/div&gt;     &lt;div&gt;6&lt;/div&gt;     &lt;div&gt;7&lt;/div&gt;     &lt;div&gt;8&lt;/div&gt;     &lt;div&gt;9&lt;/div&gt;     &lt;div&gt;10&lt;/div&gt;     &lt;div&gt;11&lt;/div&gt;     &lt;div&gt;12&lt;/div&gt;     &lt;div&gt;13&lt;/div&gt;     &lt;div&gt;14&lt;/div&gt;     &lt;div&gt;15&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;$ sudo strace -q -e trace=process df -h	&lt;/div&gt;     &lt;div&gt;&lt;/div&gt;     &lt;div&gt;execve(&amp;quot;/bin/df&amp;quot;, [&amp;quot;df&amp;quot;, &amp;quot;-h&amp;quot;], [/* 17 vars */]) = 0&lt;/div&gt;     &lt;div&gt;arch_prctl(ARCH_SET_FS, 0x7fe2222ff700) = 0&lt;/div&gt;     &lt;div&gt;Filesystem      Size  Used Avail Use% Mounted on&lt;/div&gt;     &lt;div&gt;udev            3.9G     0  3.9G   0% /dev&lt;/div&gt;     &lt;div&gt;tmpfs           788M  9.6M  779M   2% /run&lt;/div&gt;     &lt;div&gt;/dev/sda10      324G  252G   56G  82% /&lt;/div&gt;     &lt;div&gt;tmpfs           3.9G  104M  3.8G   3% /dev/shm&lt;/div&gt;     &lt;div&gt;tmpfs           5.0M  4.0K  5.0M   1% /run/lock&lt;/div&gt;     &lt;div&gt;tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup&lt;/div&gt;     &lt;div&gt;cgmfs           100K     0  100K   0% /run/cgmanager/fs&lt;/div&gt;     &lt;div&gt;tmpfs           788M   28K  788M   1% /run/user/1000&lt;/div&gt;     &lt;div&gt;exit_group(0)                           = ?&lt;/div&gt;     &lt;div&gt;+++ exited with 0 +++&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;p&gt;针对文件系统调用的追踪：&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;     &lt;div&gt;4&lt;/div&gt;     &lt;div&gt;5&lt;/div&gt;     &lt;div&gt;6&lt;/div&gt;     &lt;div&gt;7&lt;/div&gt;     &lt;div&gt;8&lt;/div&gt;     &lt;div&gt;9&lt;/div&gt;     &lt;div&gt;10&lt;/div&gt;     &lt;div&gt;11&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;$ sudo strace -q  -e trace=file df -h&lt;/div&gt;     &lt;div&gt;&lt;/div&gt;     &lt;div&gt;execve(&amp;quot;/bin/df&amp;quot;, [&amp;quot;df&amp;quot;, &amp;quot;-h&amp;quot;], [/* 17 vars */]) = 0&lt;/div&gt;     &lt;div&gt;access(&amp;quot;/etc/ld.so.nohwcap&amp;quot;, F_OK)      = -1 ENOENT (No such file or directory)&lt;/div&gt;     &lt;div&gt;access(&amp;quot;/etc/ld.so.preload&amp;quot;, R_OK)      = -1 ENOENT (No such file or directory)&lt;/div&gt;     &lt;div&gt;open(&amp;quot;/etc/ld.so.cache&amp;quot;, O_RDONLY|O_CLOEXEC) = 3&lt;/div&gt;     &lt;div&gt;access(&amp;quot;/etc/ld.so.nohwcap&amp;quot;, F_OK)      = -1 ENOENT (No such file or directory)&lt;/div&gt;     &lt;div&gt;open(&amp;quot;/lib/x86_64-linux-gnu/libc.so.6&amp;quot;, O_RDONLY|O_CLOEXEC) = 3&lt;/div&gt;     &lt;div&gt;open(&amp;quot;/usr/lib/locale/locale-archive&amp;quot;, O_RDONLY|O_CLOEXEC) = 3&lt;/div&gt;     &lt;div&gt;open(&amp;quot;/usr/share/locale/locale.alias&amp;quot;, O_RDONLY|O_CLOEXEC) = 3&lt;/div&gt;     &lt;div&gt;...&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;p&gt;针对内存的追踪:&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;     &lt;div&gt;4&lt;/div&gt;     &lt;div&gt;5&lt;/div&gt;     &lt;div&gt;6&lt;/div&gt;     &lt;div&gt;7&lt;/div&gt;     &lt;div&gt;8&lt;/div&gt;     &lt;div&gt;9&lt;/div&gt;     &lt;div&gt;10&lt;/div&gt;     &lt;div&gt;11&lt;/div&gt;     &lt;div&gt;12&lt;/div&gt;     &lt;div&gt;13&lt;/div&gt;     &lt;div&gt;14&lt;/div&gt;     &lt;div&gt;15&lt;/div&gt;     &lt;div&gt;16&lt;/div&gt;     &lt;div&gt;17&lt;/div&gt;     &lt;div&gt;18&lt;/div&gt;     &lt;div&gt;19&lt;/div&gt;     &lt;div&gt;20&lt;/div&gt;     &lt;div&gt;21&lt;/div&gt;     &lt;div&gt;22&lt;/div&gt;     &lt;div&gt;23&lt;/div&gt;     &lt;div&gt;24&lt;/div&gt;     &lt;div&gt;25&lt;/div&gt;     &lt;div&gt;26&lt;/div&gt;     &lt;div&gt;27&lt;/div&gt;     &lt;div&gt;28&lt;/div&gt;     &lt;div&gt;29&lt;/div&gt;     &lt;div&gt;30&lt;/div&gt;     &lt;div&gt;31&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;$ sudo strace -q -e trace=memory df -h	&lt;/div&gt;     &lt;div&gt;&lt;/div&gt;     &lt;div&gt;brk(NULL)                               = 0x77a000&lt;/div&gt;     &lt;div&gt;mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe8f4658000&lt;/div&gt;     &lt;div&gt;mmap(NULL, 147662, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe8f4633000&lt;/div&gt;     &lt;div&gt;mmap(NULL, 3971488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fe8f406b000&lt;/div&gt;     &lt;div&gt;mprotect(0x7fe8f422b000, 2097152, PROT_NONE) = 0&lt;/div&gt;     &lt;div&gt;mmap(0x7fe8f442b000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c0000) = 0x7fe8f442b000&lt;/div&gt;     &lt;div&gt;mmap(0x7fe8f4431000, 14752, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fe8f4431000&lt;/div&gt;     &lt;div&gt;mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe8f4632000&lt;/div&gt;     &lt;div&gt;mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe8f4631000&lt;/div&gt;     &lt;div&gt;mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fe8f4630000&lt;/div&gt;     &lt;div&gt;mprotect(0x7fe8f442b000, 16384, PROT_READ) = 0&lt;/div&gt;     &lt;div&gt;mprotect(0x616000, 4096, PROT_READ)     = 0&lt;/div&gt;     &lt;div&gt;mprotect(0x7fe8f465a000, 4096, PROT_READ) = 0&lt;/div&gt;     &lt;div&gt;munmap(0x7fe8f4633000, 147662)          = 0&lt;/div&gt;     &lt;div&gt;mmap(NULL, 2981280, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe8f3d93000&lt;/div&gt;     &lt;div&gt;brk(NULL)                               = 0x77a000&lt;/div&gt;     &lt;div&gt;brk(0x79b000)                           = 0x79b000&lt;/div&gt;     &lt;div&gt;mmap(NULL, 619, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fe8f4657000&lt;/div&gt;     &lt;div&gt;mmap(NULL, 26258, PROT_READ, MAP_SHARED, 3, 0) = 0x7fe8f4650000&lt;/div&gt;     &lt;div&gt;Filesystem      Size  Used Avail Use% Mounted on&lt;/div&gt;     &lt;div&gt;udev            3.9G     0  3.9G   0% /dev&lt;/div&gt;     &lt;div&gt;tmpfs           788M  9.6M  779M   2% /run&lt;/div&gt;     &lt;div&gt;/dev/sda10      324G  252G   56G  82% /&lt;/div&gt;     &lt;div&gt;tmpfs           3.9G  104M  3.8G   3% /dev/shm&lt;/div&gt;     &lt;div&gt;tmpfs           5.0M  4.0K  5.0M   1% /run/lock&lt;/div&gt;     &lt;div&gt;tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup&lt;/div&gt;     &lt;div&gt;cgmfs           100K     0  100K   0% /run/cgmanager/fs&lt;/div&gt;     &lt;div&gt;tmpfs           788M   28K  788M   1% /run/user/1000&lt;/div&gt;     &lt;div&gt;+++ exited with 0 +++&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;p&gt;以及针对网络和信号的追踪:&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;$ sudo strace -e trace=network df -h&lt;/div&gt;     &lt;div&gt;$ sudo strace -e trace=signal df -h&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;h2&gt;将追踪结果写入到文件&lt;/h2&gt;
 &lt;p&gt;  &lt;code&gt;-o&lt;/code&gt;参数将标准输出写入到文件:&lt;/p&gt;
 &lt;table&gt;  &lt;tr&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;1&lt;/div&gt;     &lt;div&gt;2&lt;/div&gt;     &lt;div&gt;3&lt;/div&gt;     &lt;div&gt;4&lt;/div&gt;     &lt;div&gt;5&lt;/div&gt;     &lt;div&gt;6&lt;/div&gt;     &lt;div&gt;7&lt;/div&gt;     &lt;div&gt;8&lt;/div&gt;     &lt;div&gt;9&lt;/div&gt;     &lt;div&gt;10&lt;/div&gt;     &lt;div&gt;11&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;   &lt;td&gt;    &lt;pre&gt;     &lt;div&gt;$ sudo strace -o df_debug.txt df -h&lt;/div&gt;     &lt;div&gt;&lt;/div&gt;     &lt;div&gt;Filesystem      Size  Used Avail Use% Mounted on&lt;/div&gt;     &lt;div&gt;udev            3.9G     0  3.9G   0% /dev&lt;/div&gt;     &lt;div&gt;tmpfs           788M  9.6M  779M   2% /run&lt;/div&gt;     &lt;div&gt;/dev/sda10      324G  252G   56G  82% /&lt;/div&gt;     &lt;div&gt;tmpfs           3.9G  104M  3.8G   3% /dev/shm&lt;/div&gt;     &lt;div&gt;tmpfs           5.0M  4.0K  5.0M   1% /run/lock&lt;/div&gt;     &lt;div&gt;tmpfs           3.9G     0  3.9G   0% /sys/fs/cgroup&lt;/div&gt;     &lt;div&gt;cgmfs           100K     0  100K   0% /run/cgmanager/fs&lt;/div&gt;     &lt;div&gt;tmpfs           788M   28K  788M   1% /run/user/1000&lt;/div&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

 &lt;h2&gt;显示strace的debug信息&lt;/h2&gt;
 &lt;p&gt;  &lt;code&gt;-d&lt;/code&gt;可以显示strace的debug信息。&lt;/p&gt;
 &lt;p&gt;更多的信息参考  &lt;code&gt;man strace&lt;/code&gt;。&lt;/p&gt;
 &lt;p&gt;其它一些参考资料:&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://www.tecmint.com/command-line-tools-to-monitor-linux-performance/" rel="external" target="_blank"&gt;20 Command Line Tools to Monitor Linux Performance&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://www.tecmint.com/command-line-tools-to-monitor-linux-performance/" rel="external" target="_blank"&gt;Sysdig – A Powerful System Monitoring and Troubleshooting Tool for Linux&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://www.tecmint.com/trace-shell-script-execution-in-linux/" rel="external" target="_blank"&gt;How to Trace Execution of Commands in Shell Script with Shell Tracing&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://www.tecmint.com/bcc-best-linux-performance-monitoring-tools/" rel="external" target="_blank"&gt;BCC – Dynamic Tracing Tools for Linux Performance Monitoring, Networking and More&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>工具</category>
      <guid isPermaLink="true">https://itindex.net/detail/61383-strace-%E5%91%BD%E4%BB%A4</guid>
      <pubDate>Fri, 30 Apr 2021 18:34:01 CST</pubDate>
    </item>
    <item>
      <title>URL Disabler 1.0 免安裝版 - 禁止瀏覽指定網站</title>
      <link>https://itindex.net/detail/61343-url-disabler</link>
      <description>禁止瀏覽指定網站 - URL Disabler，可以輸入指定的網址讓瀏覽器無法瀏覽該網站，支援Chrome、Firefox、Edge瀏覽器，匯出匯入網址清單、設定軟體開啟密碼、讓瀏覽器無法下載檔案、指定只套用於某個瀏覽器，它不使用修改hosts檔的老方法，所以比較不容易被破解。（阿榮）（下載）


官方網站：Sordum.org
軟體性質：免費軟體
介面語言：英文
系統需求：Windows 10/8.x/7/Vista/XP（32及64位元）
關鍵字：URLDisabler, URLDisablerPortable

URL Disabler is a Portable (it requires no installation) and freeware to simplify the URL blocking process for Google Chrome , Firefox &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>1 系統工具 1.3 管理操作 [免安裝] [新進軟體] Sordum</category>
      <guid isPermaLink="true">https://itindex.net/detail/61343-url-disabler</guid>
      <pubDate>Fri, 16 Apr 2021 00:54:00 CST</pubDate>
    </item>
    <item>
      <title>风险控制：信用评分卡模型</title>
      <link>https://itindex.net/detail/61198-%E9%A3%8E%E9%99%A9%E6%8E%A7%E5%88%B6-%E4%BF%A1%E7%94%A8%E8%AF%84%E5%88%86-%E6%A8%A1%E5%9E%8B</link>
      <description>&lt;h2&gt;什么是信用评分卡模型？&lt;/h2&gt;
 &lt;p&gt;评分卡模型又叫做信用评分卡模型，最早由美国信用评分巨头FICO公司于20世纪60年代推出，在信用风险评估以及金融风险控制领域中广泛使用。银行利用评分卡模型对客户的信用历史数据的多个特征进行打分，得到不同等级的信用评分，从而判断客户的优质程度，据此决定是否准予授信以及授信的额度和利率。相较资深从业人员依靠自身的经验设置的专家规则，评分卡模型的使用具有很明显的优点：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;strong&gt;判断快速&lt;/strong&gt;：系统只需要按照评分卡逐项打分，最后通过相应的公式计算出总分，即可准确判断出是否为客户授信以及额度和利率。&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;客观透明&lt;/strong&gt;：评分卡模型的标准是统一的，无论是客户还是风险审核人员，都可以通过评分卡一眼看出评分结果和评判依据。&lt;/li&gt;
  &lt;li&gt;   &lt;strong&gt;应用范围广&lt;/strong&gt;：由于评分卡的评分项是客观计算，其得出的分数具有广泛的参考性和适用性。例如，生活中常见的支付宝芝麻信用分，就是依据评分卡模型计算得出。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" height="263" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/credit-score.png" width="750"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;评分卡模型在银行不同的业务阶段体现的方式和功能也不一样。按照借贷用户的借贷时间，评分卡模型可以划分为以下三种：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;贷前：申请评分卡（Application score card），又称为A卡
   &lt;ul&gt;
    &lt;li&gt;更准确地评估申请人的未来表现(违约率)，降低坏帐率&lt;/li&gt;
    &lt;li&gt;加快(自动化)审批流程, 降低营运成本&lt;/li&gt;
    &lt;li&gt;增加审批决策的客观性和一致性，提高客户满意度&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;贷中：行为评分卡（Behavior score card），又称为B卡
   &lt;ul&gt;
    &lt;li&gt;更好的客户管理策略, 提高赢利&lt;/li&gt;
    &lt;li&gt;减少好客户的流失&lt;/li&gt;
    &lt;li&gt;对可能拖欠的客户，提早预警&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;贷后：催收评分卡（Collection score card），又称为C卡
   &lt;ul&gt;
    &lt;li&gt;优化催收策略，提高欠帐的回收率&lt;/li&gt;
    &lt;li&gt;减少不必要的催收行为，降低营运成本&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;评分卡模型示例：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="432" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/credit-score-example.png" width="429"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;一个用户总的评分等于基准分加上对客户各个属性的评分。举个例子某客户年龄为27岁，性别为男，婚姻状况为已婚，学历为本科，月收入为10000，那么他的评分为：223+8+4+8+8+13=264&lt;/p&gt;
 &lt;h2&gt;如何搭信用评分卡模型？&lt;/h2&gt;
 &lt;p&gt;有了上面的评分卡示例，接下来需要考虑的是如何生成类似上面的表格：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;变量特征是如何选取的？&lt;/li&gt;
  &lt;li&gt;特征的变量范围是如何进行划分的？&lt;/li&gt;
  &lt;li&gt;每个字段的分值是如何设定的？&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;变量选择&lt;/h3&gt;
 &lt;p&gt;变量筛选的主要目的：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;剔除跟目标变量不太相关的特征&lt;/li&gt;
  &lt;li&gt;消除由于线性相关的变量，避免特征冗余&lt;/li&gt;
  &lt;li&gt;减轻后期验证、部署、监控的负担&lt;/li&gt;
  &lt;li&gt;保证变量的可解释性&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;单变量筛选&lt;/h4&gt;
 &lt;p&gt;单变量的筛选基于变量预测能力，常用方法：&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;基于IV值的变量筛选&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;WOE的取值范围是[-∞,+∞]，当分箱中好坏客户比例等于整体好坏客户比例时，WOE为0。&lt;/li&gt;
  &lt;li&gt;对于变量的一个分箱，这个分组的好坏客户比例与整体好坏客户比例相差越大，IV值越大，否则，IV值越小。&lt;/li&gt;
  &lt;li&gt;IV值的取值范围是[0,+∞)，当分箱中只包含好客户或坏客户时，IV = +∞，当分箱中好坏客户比例等于整体好坏客户比例时，IV为0。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;在评分卡建模流程中，WOE（Weight of Evidence）常用于特征变换，IV（Information Value）则用来衡量特征的预测能力。&lt;/p&gt;
 &lt;p&gt;WOE（Weight of Evidence）叫做证据权重，WOE在业务中常有哪些应用呢？&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;处理缺失值：当数据源没有100%覆盖时，那就会存在缺失值，此时可以把null单独作为一个分箱。这点在分数据源建模时非常有用，可以有效将覆盖率哪怕只有20%的数据源利用起来。&lt;/li&gt;
  &lt;li&gt;处理异常值：当数据中存在离群点时，可以把其通过分箱离散化处理，从而提高变量的鲁棒性（抗干扰能力）。例如，age若出现200这种异常值，可分入“age &amp;gt; 60”这个分箱里，排除影响。&lt;/li&gt;
  &lt;li&gt;业务解释性：我们习惯于线性判断变量的作用，当x越来越大，y就越来越大。但实际x与y之间经常存在着非线性关系，此时可经过WOE变换。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;$$WOE_i = ln(\frac{Bad_i}{Bad_T}/\frac{Good_i}{Good_T}) = ln(\frac{Bad_i}{Bad_T})-ln(\frac{Good_i}{Good_T})$$&lt;/p&gt;
 &lt;p&gt;IV（Information Value）是与WOE密切相关的一个指标，常用来评估变量的预测能力。因而可用来快速筛选变量。在应用实践中，其评价标准如下：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="137" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/IV.png" width="252"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;而IV的计算公式定义如下，其可认为是WOE的加权和：&lt;/p&gt;
 &lt;p&gt;$$IV_i = (\frac{Bad_i}{Bad_T}-\frac{Good_i}{Good_T}) * WOE_i$$&lt;/p&gt;
 &lt;p&gt;$$IV = \sum_{i=1}^{n}IV_i$$&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;基于stepwise的变量筛选&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;基于stepwise的变量筛选方法也是评分卡中变量筛选最常用的方法之一。具体包括三种筛选变量的方式：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;前向选择forward：逐步将变量一个一个放入模型，并计算相应的指标，如果指标值符合条件，则保留，然后再放入下一个变量，直到没有符合条件的变量纳入或者所有的变量都可纳入模型。&lt;/li&gt;
  &lt;li&gt;后向选择backward：一开始将所有变量纳入模型，然后挨个移除不符合条件的变量，持续此过程，直到留下所有最优的变量为止。&lt;/li&gt;
  &lt;li&gt;逐步选择stepwise：该算法是向前选择和向后选择的结合，逐步放入最优的变量、移除最差的变量。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;基于特征重要度的变量筛选&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;基于特征重要度的变量筛选方法是目前机器学习最热门的方法之一，其原理主要是通过随机森林和GBDT等集成模型选取特征的重要度。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;基于LASSO正则化的变量筛选&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;L1正则化通常称为Lasso正则化，它是在代价函数上增加了一个L1范数。&lt;/p&gt;
 &lt;p&gt;随着机器学习的发展，变量选择的方法也在增加。信用风险模型中典型的变量选择方法：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="701" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/features.png" width="695"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;更多方法请参考：  &lt;a href="https://www.biaodianfu.com/feature-selection.html"&gt;机器学习之特征选择方法&lt;/a&gt;&lt;/p&gt;
 &lt;h4&gt;变量相关性分析&lt;/h4&gt;
 &lt;p&gt;常用分析方法：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;变量两两相关性分析&lt;/li&gt;
  &lt;li&gt;变量的多重共线性分析&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;为什么要进行相关性分析？&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;设想建立一个具有两变量$X_1$和$X_2$的线性模型，真实模型是$Y=X_1+X_2$。如果$X_1$和$X_2$线性相关（比如说$X_1\approx 2X_2$），那么拟合模型$Y=3X_2$, $Y=2X_1-X_2$或$Y=51X_1-99X_2$的效果都一样好，理想状态下，系数权重会有无数种取法，使系数权重变得无法解释，导致变量的每个分段的得分也有无数种取法（后面我们会发现变量中不同分段的评分会用到变量的系数）。&lt;/p&gt;
 &lt;p&gt;当两个变量具有高相关性时，保留IV值大。&lt;/p&gt;
 &lt;h3&gt;变量分箱&lt;/h3&gt;
 &lt;p&gt;评分卡模型通过对变量进行分箱来实现变量的分段。那么什么是分箱呢？以下为分箱的定义：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;对连续变量进行分段离散化&lt;/li&gt;
  &lt;li&gt;将多状态的离散变量进行合并，减少离散变量的状态数&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;常见的分箱类型有以下几种：&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;无监督分箱&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;等频分箱：把自变量按从小到大的顺序排列，根据自变量的个数等分为k部分，每部分作为一个分箱。&lt;/li&gt;
  &lt;li&gt;等距分箱：把自变量按从小到大的顺序排列，将自变量的取值范围分为k个等距的区间，每个区间作为一个分箱。&lt;/li&gt;
  &lt;li&gt;聚类分箱：用k-means聚类法将自变量聚为k类，但在聚类过程中需要保证分箱的有序性。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;由于无监督分箱仅仅考虑了各个变量自身的数据结构，并没有考虑自变量与目标变量之间的关系，因此无监督分箱不一定会带来模型性能的提升。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;有监督分箱&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;包括 Split 分箱和 Merge 分箱&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Split 分箱是一种自上而下(即基于分裂)的数据分段方法。Split 分箱和决策树比较相似，切分点的选择指标主要有 Entropy，Gini 指数和 IV 值等。&lt;/li&gt;
  &lt;li&gt;Merge 分箱，是一种自底向上(即基于合并)的数据离散化方法。Merge 分箱常见的类型为Chimerge分箱。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;ChiMerge 分箱&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;ChiMerge 分箱是目前最流行的分箱方式之一，其基本思想是如果两个相邻的区间具有类似的类分布，则这两个区间合并；否则，它们应保持分开。Chimerge通常采用卡方值来衡量两相邻区间的类分布情况。&lt;/p&gt;
 &lt;p&gt;ChiMerge的具体算法如下：&lt;/p&gt;
 &lt;ol&gt;
  &lt;li&gt;输入：分箱的最大区间数n&lt;/li&gt;
  &lt;li&gt;初始化&lt;/li&gt;
&lt;/ol&gt;
 &lt;ul&gt;
  &lt;li&gt;连续值按升序排列，离散值先转化为坏客户的比率，然后再按升序排列&lt;/li&gt;
  &lt;li&gt;为了减少计算量，对于状态数大于某一阈值 (建议为100) 的变量，利用等频分箱进行粗分箱&lt;/li&gt;
  &lt;li&gt;若有缺失值，则缺失值单独作为一个分箱&lt;/li&gt;
&lt;/ul&gt;
 &lt;ol start="3"&gt;
  &lt;li&gt;合并区间&lt;/li&gt;
&lt;/ol&gt;
 &lt;ul&gt;
  &lt;li&gt;计算每一对相邻区间的卡方值&lt;/li&gt;
  &lt;li&gt;将卡方值最小的一对区间合并&lt;/li&gt;
  &lt;li&gt;重复以上两个步骤，直到分箱数量不大于n&lt;/li&gt;
&lt;/ul&gt;
 &lt;ol start="4"&gt;
  &lt;li&gt;分箱后处理&lt;/li&gt;
&lt;/ol&gt;
 &lt;ul&gt;
  &lt;li&gt;对于坏客户比例为 0 或 1 的分箱进行合并 (一个分箱内不能全为好客户或者全为坏客户)。&lt;/li&gt;
  &lt;li&gt;对于分箱后某一箱样本占比超过 95% 的箱子进行删除。&lt;/li&gt;
  &lt;li&gt;检查缺失分箱的坏客户比例是否和非缺失分箱相等，如果相等，进行合并。&lt;/li&gt;
&lt;/ul&gt;
 &lt;ol start="5"&gt;
  &lt;li&gt;输出：分箱后的数据和分箱区间。&lt;/li&gt;
&lt;/ol&gt;
 &lt;p&gt;总结一下特征分箱的优势：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;特征分箱可以有效处理特征中的缺失值和异常值。&lt;/li&gt;
  &lt;li&gt;特征分箱后，数据和模型会更稳定。&lt;/li&gt;
  &lt;li&gt;特征分箱可以简化逻辑回归模型，降低模型过拟合的风险，提高模型的泛化能力。&lt;/li&gt;
  &lt;li&gt;将所有特征统一变换为类别型变量。&lt;/li&gt;
  &lt;li&gt;分箱后变量才可以使用标准的评分卡格式，即对不同的分段进行评分。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;为了创建一个对过度拟合具有弹性的健壮模型，每个箱应该包含来自总账户的足够数量的观察结果（大多数从业者建议的最小值为5%），如果最大箱占据了总样本量的90%以上，那么弃用该变量。&lt;/p&gt;
 &lt;h3&gt;WOE编码&lt;/h3&gt;
 &lt;p&gt;在风控用到的数据里，我们会用到两种变量：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Numerical Variable，数值变量。例如逾期金额，天数&lt;/li&gt;
  &lt;li&gt;Categorical Variable，类别变量。例如客户职业&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;在制作评分卡过程中，我们还需要把数值变量变成类别变量，例如客户年龄段，我们可以划分为[20及以下],[21-30],[31-40],[41-50],[51-60],[61-70],[70以上]七个类别，这时候我们就把数值变成了类别。这种把数值变成类别的技巧叫做分箱（binning）。&lt;/p&gt;
 &lt;p&gt;但是当把所有变量都变成类别后，这时候你也许有这个疑惑：怎么去训练一个模型呢？例如逻辑回归，只能用数值作为特征输入。怎么把类别变成数值呢？&lt;/p&gt;
 &lt;p&gt;你这时候想到的可能是one-hot encoding，但还是有问题，对于逻辑回归来说，one-hot encoding输出的矩阵太稀疏了，很难让逻辑回归有很好的效果。这时候，我们可以试试把类别或者分箱转化成响应的数值。这个分数必须和必须有这个特性：分数越大，代表这个变量给bad label的贡献度越大，这个贡献度，视运算符号不同，可以是正向，也可以是负向，但我们期望它们之间有个线性关系。这时候我们需要引入WOE编码。&lt;/p&gt;
 &lt;p&gt;在变量筛选中对WOE已经有了简单的介绍。这里以实例进行介绍：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="154" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/woe.png" width="951"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;我们观察Bad Rate 和WOE的关系，可以看到WOE越大，Bad Rate越高，也就是说，通过WOE变换，特征值不仅仅代表一个分类，还代表了这个分类的权重。&lt;/p&gt;
 &lt;p&gt;对于类别变量进行WOE编码很好理解，为什么数值变量需要在分箱以后再进行WOE编码？分箱+WOE编码主要要解决的问题是把非线性的特征转化为线性。&lt;/p&gt;
 &lt;p&gt;例如在风控场景里，我们可能用到客户的年龄做特征。我们知道肯定不是年龄越大风险越高，或者年龄越大风险越低，一定是有个年龄段的风险是比其他年龄段高些。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="643" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/woe-1.png" width="406"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;总结下WOE编码的优势：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;可提升模型的预测效果&lt;/li&gt;
  &lt;li&gt;将自变量规范到同一尺度上&lt;/li&gt;
  &lt;li&gt;WOE能反映自变量取值的贡献情况&lt;/li&gt;
  &lt;li&gt;有利于对变量的每个分箱进行评分&lt;/li&gt;
  &lt;li&gt;转化为连续变量之后，便于分析变量与变量之间的相关性&lt;/li&gt;
  &lt;li&gt;与独热向量编码相比，可以保证变量的完整性，同时避免稀疏矩阵和维度灾难&lt;/li&gt;
&lt;/ul&gt;
 &lt;h3&gt;模型训练&lt;/h3&gt;
 &lt;p&gt;Logistic回归是信用评分中用于解决二元分类问题的常用技术。逻辑回归通过sigmoid函数$ y = \frac{1}{1+e^{-z}}$ 将线性回归模型$z=\boldsymbol{w}^T\boldsymbol{x}+b$产生的预测值转换为一个接近0或1的拟合值：&lt;/p&gt;
 &lt;p&gt;$$h(x)=\frac{1}{1+e^{-z}}=\frac{1}{1+e^{-(\boldsymbol{w}^T\boldsymbol{x}+b)}}$$&lt;/p&gt;
 &lt;p&gt;上式的$h(x)$可视为事件发生的概率$p(y=1|\boldsymbol{x})$，变换后得到：$\ln\frac{p}{1-p}=z=\boldsymbol{w}^T\boldsymbol{x}+b$&lt;/p&gt;
 &lt;p&gt;其中，$p/(1-p)$为比率(odds)，即违约概率与正常概率的比值。$\ln{p/(1-p)}$为logit函数，即比率的自然对数。因此，逻辑回归实际上是用比率的自然对数作为因变量的线性回归模型。&lt;/p&gt;
 &lt;p&gt;在模型拟合之前，变量选择的再一次迭代对于检查新的WOE变换变量是否仍然是良好的模型候选变量是有价值的。优选的候选变量是具有较高信息值（通常在0.1和0.5之间）的变量，与因变量具有线性关系，在所有类别中具有良好的覆盖率，具有正态分布，包含显著的总体贡献，并且与业务相关。&lt;/p&gt;
 &lt;p&gt;由逻辑回归的基本原理，我们将客户违约的概率表示为p，则正常的概率为1-p。因此，可以得到：&lt;/p&gt;
 &lt;p&gt;$$Odds = \frac{p}{1-p}$$&lt;/p&gt;
 &lt;p&gt;此时，客户违约的概率p可表示为：&lt;/p&gt;
 &lt;p&gt;$$p = \frac{Odds}{1+Odds}$$&lt;/p&gt;
 &lt;p&gt;评分卡设定的分值刻度可以通过将分值表示为比率对数的线性表达式来定义，即可表示为下式：&lt;/p&gt;
 &lt;p&gt;$$Score = A – B\log(Odds)$$&lt;/p&gt;
 &lt;p&gt;其中，A和B是常数。式中的负号可以使得违约概率越低，得分越高。通常情况下，这是分值的理想变动方向，即高分值代表低风险，低分值代表高风险。 逻辑回归模型计算比率如下所示：&lt;/p&gt;
 &lt;p&gt;$$ log(Odds)=\beta _0 + \beta _1x_1+…+\beta _nx_n$$&lt;/p&gt;
 &lt;p&gt;其中，用建模参数拟合模型可以得到模型参数$\beta _0,\beta _1,…,\beta _n$。 式中的常数A、B的值可以通过将两个已知或假设的分值带入计算得到。通常情况下，需要设定两个假设：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;给某个特定的比率设定特定的预期分值&lt;/li&gt;
  &lt;li&gt;确定比率翻番的分数（PDO） 根据以上的分析，我们首先假设比率为x的特定点的分值为P。则比率为2x的点的分值应该为P+PDO。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;代入式中，可以得到如下两个等式：&lt;/p&gt;
 &lt;p&gt;$$P = A – B\log(x)$$&lt;/p&gt;
 &lt;p&gt;$$P – PDO = A – B\log(2x)$$&lt;/p&gt;
 &lt;p&gt;假设设定评分卡刻度使得比率为1:20（违约正常比）时的分值为50分，PDO为10分，代入式中求得：B=14.43，A=6.78 则分值的计算公式可表示为：&lt;/p&gt;
 &lt;p&gt;$$Score = 6.78 -14.43\log(Odds)$$&lt;/p&gt;
 &lt;p&gt;评分卡刻度参数A和B确定以后，就可以计算比率和违约概率，以及对应的分值了。通常将常数A称为补偿，常数B称为刻度。 则评分卡的分值可表达为：&lt;/p&gt;
 &lt;p&gt;$$Score = A – B\{\beta _0+\beta _1x_1+…+\beta _nx_n\}$$&lt;/p&gt;
 &lt;p&gt;式中：变量$x_1,…,x_n$是出现在最终模型中的自变量，即为入模指标。由于此时所有变量都用WOE转换进行了转换，可以将这些自变量中的每一个都写$(\beta _i\omega _{ij})\delta _{ij}$的形式：&lt;/p&gt;
 &lt;p&gt;$$Score = A-B\{\beta _0+(\beta _1\omega _{11})\delta _{11}+(\beta _1\omega _{12})\delta _{12}+…+(\beta _2\omega _{21})\delta _{21}+…\}$$&lt;/p&gt;
 &lt;p&gt;式中：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;$\omega _{ij}$为第i行第j个变量的WOE，为已知变量&lt;/li&gt;
  &lt;li&gt;$\beta _i$为逻辑回归方程中的系数，为已知变量&lt;/li&gt;
  &lt;li&gt;$\delta _{ij}$为二元变量，表示变量i是否取第j个值。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;上式可重新表示为：&lt;/p&gt;
 &lt;p&gt;$$Score = (A-B\beta _0)-(B\beta _1\omega _{11})\delta _{11}-(B\beta _1\omega _12)\delta _{12}-…-(B\beta _x\omega _{x1}-…$$&lt;/p&gt;
 &lt;p&gt;此式即为最终评分卡公式。如果$x_1…x_n$变量取不同行并计算其WOE值，式中表示的标准评分卡格式，如表3.20所示：$(A-B\beta _0)$；由于分值分配公式中的负号，模型参数$\beta _0,\beta _1,…,\beta _n$也应该是负值；变量$x_i$的第j行的分值取决于以下三个数值：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="331" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/Logistic.png" width="573"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h4&gt;拒绝推断&lt;/h4&gt;
 &lt;p&gt;申请评分卡的模型开发过程中使用的数据实际上并不是从申请总体样本中随机选择的，而仅仅是从过去已经被接受的客户样本中选择的。因此，开发申请评分卡时将对被拒绝客户的状态进行推断并纳入模型开发数据集中，即拒绝推断过程。拒绝推断的常用方法包括：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;简单赋值法：人为指定被拒绝账户的标签
   &lt;ul&gt;
    &lt;li&gt;忽略被拒绝申请&lt;/li&gt;
    &lt;li&gt;所有被拒申请赋值为违约标签&lt;/li&gt;
    &lt;li&gt;按比例赋值，使得其坏客户率是通过样本的2~5倍以上&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
  &lt;li&gt;强化法：通过外推法确定拒绝账户的标签
   &lt;ul&gt;
    &lt;li&gt;简单强化法：使用通过客户开发的模型对被拒绝客户评分，将其中低分段赋予违约标签。使得拒绝客户的坏客户率为通过的2~5倍以上&lt;/li&gt;
    &lt;li&gt;模糊强化法：通过模型计算得到正常和违约概率。&lt;/li&gt;
    &lt;li&gt;打包强化法：先用开发的评分卡对被拒客户评分，然后指定每个分值区间的违约客户数量。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;h4&gt;模型表现&lt;/h4&gt;
 &lt;p&gt;模型评估是模型构建过程的最后一步。它由三个不同的阶段组成：评估，验证和接受。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;评估准确性&lt;/strong&gt; – 我是否构建了正确的模型？ – 是第一个要求测试模型的问题。评估的关键指标是统计测量，包括模型准确性，复杂性，错误率，模型拟合统计，变量统计，显著性值和优势比。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;验证稳健性&lt;/strong&gt; – 我是否构建了正确的模型？ – 从分类准确性和统计评估转向排名能力和业务评估时，是下一个要问的问题。&lt;/p&gt;
 &lt;p&gt;验证度量的选择取决于模型分类器的类型。二元分类问题最常见的指标是增益图，提升图，ROC曲线和Kolmogorov-Smirnov图。ROC曲线是可视化模型性能的最常用工具。它是一个多用途工具，用于：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;冠军挑战者选择最佳表现模式的方法&lt;/li&gt;
  &lt;li&gt;在看不见的数据上测试模型性能并将其与训练数据进行比较&lt;/li&gt;
  &lt;li&gt;选择最佳阈值，最大化真阳性率，同时最小化假阳性率&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;通过绘制灵敏度与不同阈值的误报概率（误报率）来创建ROC曲线。评估不同阈值下的性能指标是ROC曲线的理想特征。根据业务策略，不同类型的业务问题将具有不同的阈值。&lt;/p&gt;
 &lt;p&gt;ROC曲线下面积（AUC）是指示分类器预测能力的有用度量。在信用风险中，0.75或更高的AUC是行业认可的标准和模型验收的先决条件。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;接受有用性&lt;/strong&gt; – 模型是否会被接受？ – 是最后一个问题，以便测试该模型是否对商业前景有价值。这是数据科学家必须将模型结果回放给业务并“捍卫”其模型的关键阶段。关键评估标准是模型的商业利益，因此，效益分析是呈现结果的核心部分。数据科学家应该尽一切努力以简洁的方式呈现结果，因此结果和发现很容易理解。如果不能实现这一点，可能会导致模型拒绝，从而导致项目失败。&lt;/p&gt;
 &lt;p&gt;模型一旦对齐，下一步就是将模型调整到业务所需的比例。这称为缩放。缩放作为一种测量工具，可以在不同的评分卡中提供分数的一致性和标准化。最低和最高分数值以及分数范围有助于风险解释，并应向业务部门报告。通常，业务要求是对多个评分卡使用相同的分数范围，因此它们都具有相同的风险解释。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;信用风控策略&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;模型开发之后需要基于建模样本确定风控策略。一个好的风控策略应具备：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;增加客户数量&lt;/li&gt;
  &lt;li&gt;减少风险损失&lt;/li&gt;
  &lt;li&gt;最大化利润&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;基于开发的评分卡，我们可以获得建模样本的审批决策表。结合审批决策表与损失或者利润目标，制定常用风控策略：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;评分临界值：实现通过率、坏客户率、或利润损失率等业务目标&lt;/li&gt;
  &lt;li&gt;通过交叉决策矩阵实现风险定价，实现差异化的利率、额度等：
   &lt;ul&gt;
    &lt;li&gt;风险评分与利润损失比&lt;/li&gt;
    &lt;li&gt;风险评分与债务收入比&lt;/li&gt;
    &lt;li&gt;风险评分与流失倾向评分&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;一个好的模型一般应具有以下特征：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;在进行数据描述时变量应该有意义。通常，某些变在特定客群的不同风险模型中重复出现。例如，信用卡行为评分卡模型中，授信使用率经常出现；申请评分卡模型中收入水平、职业和历史信贷产品拥有情况比人口统计变量重要。&lt;/li&gt;
  &lt;li&gt;变量的预测力或贡献度，应该在模型的变量之间分布。&lt;/li&gt;
  &lt;li&gt;模型中不应该包含太多变量。通常，包含的变量不超过9~20个(最优10~12个)。变量太多可能导致过拟合，变量太少往往区分度不够。&lt;/li&gt;
  &lt;li&gt;最终模型的变量应该能够确保包含稳健一致的数据，并在后续实施阶段能够准确获取。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;评分卡模型搭建实战&lt;/h2&gt;
 &lt;p&gt;数据来源：  &lt;a href="https://www.kaggle.com/c/GiveMeSomeCredit/"&gt;Give Me Some Credit比赛&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;数据字段说明：&lt;/p&gt;
 &lt;table&gt;

  &lt;tr&gt;
   &lt;td width="281"&gt;列名&lt;/td&gt;
   &lt;td width="441"&gt;字段说明&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;SeriousDlqin2yrs&lt;/td&gt;
   &lt;td width="441"&gt;两年内是否有严重违约（好坏用户判断）&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;RevolvingUtilizationOfUnsecuredLines&lt;/td&gt;
   &lt;td width="441"&gt;可用信贷额度比例，信用卡和个人信用额度（不动产和汽车贷款等分期付款债务除外）的总余额除以信用额度之和&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;age&lt;/td&gt;
   &lt;td width="441"&gt;借款人年龄&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;NumberOfTime30-59DaysPastDueNotWorse&lt;/td&gt;
   &lt;td width="441"&gt;两年内35-59天逾期次数&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;DebtRatio&lt;/td&gt;
   &lt;td width="441"&gt;借款人负债比率（每月债务支付、赡养费、生活费之和除以月收入）&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;MonthlyIncome&lt;/td&gt;
   &lt;td width="441"&gt;借款人月收入&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;NumberOfOpenCreditLinesAndLoans&lt;/td&gt;
   &lt;td width="441"&gt;开放式信贷和贷款数量&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;NumberOfTimes90DaysLate&lt;/td&gt;
   &lt;td width="441"&gt;两年内90天或高于90天逾期的次数&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;NumberRealEstateLoansOrLines&lt;/td&gt;
   &lt;td width="441"&gt;不动产贷款或额度数量&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;NumberOfTime60-89DaysPastDueNotWorse&lt;/td&gt;
   &lt;td width="441"&gt;两年内60-89天逾期次数&lt;/td&gt;
&lt;/tr&gt;
  &lt;tr&gt;
   &lt;td width="281"&gt;NumberOfDependents&lt;/td&gt;
   &lt;td width="441"&gt;借款人家属数量（不包括本人在内）&lt;/td&gt;
&lt;/tr&gt;

&lt;/table&gt;
 &lt;h3&gt;  &lt;strong&gt;探索数据&lt;/strong&gt;&lt;/h3&gt;
 &lt;pre&gt;import pandas as pd
from dataprep.eda import plot
import warnings
warnings.filterwarnings(&amp;apos;ignore&amp;apos;)

train_data = pd.read_csv(&amp;apos;cs-training.csv&amp;apos;, index_col=0)
train_data.columns = [&amp;apos;严重违约&amp;apos;, &amp;apos;可用额度比例&amp;apos;,&amp;apos;年龄&amp;apos;, &amp;apos;35-69天逾期次数&amp;apos;, &amp;apos;负债比例&amp;apos;,&amp;apos;月收入&amp;apos;,&amp;apos;普通贷款数量&amp;apos;,&amp;apos;高于90天逾期次数&amp;apos;,&amp;apos;不动产贷款数量&amp;apos;,&amp;apos;60-89天逾期次数&amp;apos;,&amp;apos;家属数量&amp;apos;]
train_data = train_data[[&amp;apos;年龄&amp;apos;,&amp;apos;家属数量&amp;apos;,&amp;apos;月收入&amp;apos;,&amp;apos;负债比例&amp;apos;,&amp;apos;可用额度比例&amp;apos;,&amp;apos;普通贷款数量&amp;apos;,&amp;apos;不动产贷款数量&amp;apos;,&amp;apos;35-69天逾期次数&amp;apos;,&amp;apos;60-89天逾期次数&amp;apos;,&amp;apos;高于90天逾期次数&amp;apos;,&amp;apos;严重违约&amp;apos;]]


# 手工探索数据
print(train_data.shape)
print(train_data.info())
print(train_data.isnull().sum())
print(train_data.describe().T)
print(train_data[&amp;apos;严重违约&amp;apos;].value_counts())
print(train_data[&amp;apos;严重违约&amp;apos;].sum()/train_data[&amp;apos;严重违约&amp;apos;].count())

# 使用EDA工具探索数据
plot(train_data)
&lt;/pre&gt;
 &lt;h3&gt;  &lt;strong&gt;数据预处理&lt;/strong&gt;&lt;/h3&gt;
 &lt;p&gt;  &lt;strong&gt;1）缺失值处理&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;常见方法：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;直接删除含有缺失值的样本&lt;/li&gt;
  &lt;li&gt;根据样本之间的相似性填补缺失值&lt;/li&gt;
  &lt;li&gt;根据变量之间的相关关系填补缺失值&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;存在缺失的特征：月收入、家属人数&lt;/p&gt;
 &lt;p&gt;这里假设一个人的月收入和家属人数和自身的 其他个人特征有关联，这里根据变量之间的相关关系采用随机森林法填补。&lt;/p&gt;
 &lt;pre&gt;from sklearn.ensemble import RandomForestRegressor

def fill_income_missing(data, to_fill):
    df = data.copy()
    columns = [*df.columns]
    columns.remove(to_fill)
    
    # 移除有缺失值的列
    columns.remove(&amp;apos;家属数量&amp;apos;)
    X = df.loc[:, columns]
    y = df.loc[:, to_fill]
    X_train = X.loc[df[to_fill].notnull()]
    y_train = y.loc[df[to_fill].notnull()]
    X_pred = X.loc[df[to_fill].isnull()]
    rfr = RandomForestRegressor(random_state=22, n_estimators=200, max_depth=3, n_jobs=-1)
    rfr.fit(X_train, y_train)
    y_pred = rfr.predict(X_pred).round()
    df.loc[df[to_fill].isnull(), to_fill] = y_pred
    return df

def fill_dependents_missing(data, to_fill):
    df = data.copy()
    columns = [*df.columns]
    columns.remove(to_fill)
    
    X = df.loc[:, columns]
    y = df.loc[:, to_fill]
    X_train = X.loc[df[to_fill].notnull()]
    y_train = y.loc[df[to_fill].notnull()]
    X_pred = X.loc[df[to_fill].isnull()]
    rfr = RandomForestRegressor(random_state=22, n_estimators=200, max_depth=3, n_jobs=-1)
    rfr.fit(X_train, y_train)
    y_pred = rfr.predict(X_pred).round()
    df.loc[df[to_fill].isnull(), to_fill] = y_pred
return df

train_data = fill_income_missing(train_data, &amp;apos;月收入&amp;apos;)
train_data = fill_dependents_missing(train_data, &amp;apos;家属数量&amp;apos;)
print(train_data.isnull().sum())
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;2) 异常值处理&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;a. 删除年龄为0的数据&lt;/p&gt;
 &lt;pre&gt;train_data = train_data.loc[train_data[&amp;apos;年龄&amp;apos;] &amp;gt; 0]&lt;/pre&gt;
 &lt;p&gt;b. 去除逾期次数中的异常数据&lt;/p&gt;
 &lt;pre&gt;import matplotlib.pyplot as plt

columns = [&amp;apos;35-69天逾期次数&amp;apos;,&amp;apos;60-89天逾期次数&amp;apos;,&amp;apos;高于90天逾期次数&amp;apos;]
train_data.loc[:, columns].plot.box(vert=False)

train_data = train_data[(train_data[&amp;apos;35-69天逾期次数&amp;apos;] &amp;lt; 90) &amp;amp; (train_data[&amp;apos;60-89天逾期次数&amp;apos;] &amp;lt; 90)  &amp;amp; (train_data[&amp;apos;高于90天逾期次数&amp;apos;] &amp;lt; 90)]
&lt;/pre&gt;
 &lt;h3&gt;  &lt;strong&gt;信用卡模型训练&lt;/strong&gt;&lt;/h3&gt;
 &lt;p&gt;这里直接使用  &lt;a href="https://github.com/Lantianzz/Scorecard-Bundle"&gt;Scorecard-Bundle&lt;/a&gt;这个Python包进行训练。Scorecard-Bundle是一个基于Python的高级评分卡建模API，实施方便且符合Scikit-Learn的调用习惯，包含的类均遵守Scikit-Learn的fit-transform-predict习惯。Scorecard-Bundle包括基于ChiMerge的特征离散化、WOE编码、基于信息值（IV）和共线性的特征评估、基于逻辑回归的评分卡模型、以及针对二元分类任务的模型评估。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;1） 特征离散化（ChiMerge）&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;from scorecardbundle.feature_discretization import ChiMerge as cm
from scorecardbundle.feature_discretization import FeatureIntervalAdjustment as fia
from scorecardbundle.feature_encoding import WOE as woe
from scorecardbundle.feature_selection import FeatureSelection as fs
from scorecardbundle.model_training import LogisticRegressionScoreCard as lrsc
from scorecardbundle.model_evaluation import ModelEvaluation as me

X = train_data.iloc[:, :-1]
y = train_data.iloc[:, -1]

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0, test_size=0.25)

trans_cm = cm.ChiMerge(max_intervals=10, min_intervals=5, output_dataframe=True)
result_cm = trans_cm.fit_transform(X_train, y_train) 
print(trans_cm.boundaries_) # 每个特征的区间切分
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;2） 特征编码（WOE）和评估（IV）&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;trans_woe = woe.WOE_Encoder(output_dataframe=True)
result_woe = trans_woe.fit_transform(result_cm, y_train)
print(trans_woe.iv_) # 每个特征的信息值 (iv)
print(trans_woe.result_dict_) # 每个特征的WOE字典和信息值 (iv)
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;3) 手动调整分箱&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;观察每一个特征的分布和响应率，确定分箱是否合理，如果不合理就需要人工设置边界。&lt;/p&gt;
 &lt;pre&gt;col = &amp;apos;年龄&amp;apos;
fia.plot_event_dist(result_cm[col],y_train,x_rotation=60)
new_x = cm.assign_interval_str(X_train[col].values,[22, 33, 43, 53, 62, 67, 74]) # apply new interval boundaries to the feature
woe.woe_vector(new_x, y_train.values) # check the information value of the resulted feature that applied the new intervals
fia.plot_event_dist(new_x,y_train, x_label=col,x_rotation=60)

feature_list = []
result_cm[col] = new_x # great explainability and predictability. Select.
feature_list.append(col)
print(feature_list)
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;4) WOE编码&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;完成全部特征的分组检查后，再次将分组特征进行WOE编码&lt;/p&gt;
 &lt;pre&gt;trans_woe = woe.WOE_Encoder(output_dataframe=True)
result_woe = trans_woe.fit_transform(result_cm[feature_list], y_train) 
print(result_woe.head())
print(trans_woe.iv_)
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;5) 特征选择&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;剔除预测力过低（通常用IV不足0.02筛选）、以及相关性过高引起共线性问题的特征。(相关性过高的阈值默认为皮尔森相关性系数大于0.6，可通过threshold_corr参数调整)&lt;/p&gt;
 &lt;pre&gt;fs.selection_with_iv_corr(trans_woe, result_woe) # corr_with 列示了与该特征相关性过高的特征和相关系数&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;6) 模型训练&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;model = lrsc.LogisticRegressionScoreCard(trans_woe, PDO=-20, basePoints=100, verbose=True)
model.fit(result_woe, y_train)
print(model.woe_df_) # 从woe_df_属性中可得评分卡规则
&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;7) 模型校验&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;sc_table = model.woe_df_.copy()
result = model.predict(X_train[feature_list], load_scorecard=sc_table) # Scorecard should be applied on the original feature values
result_test = model.predict(X_test[feature_list], load_scorecard=sc_table) # Scorecard should be applied on the original feature values
result.head() # if model object&amp;apos;s verbose parameter is set to False, predict will only return Total scores

# Train
evaluation = me.BinaryTargets(y_train, result[&amp;apos;TotalScore&amp;apos;])
evaluation.plot_all()
  
# Validation
evaluation = me.BinaryTargets(y_test, result_test[&amp;apos;TotalScore&amp;apos;])
evaluation.plot_all()
&lt;/pre&gt;
 &lt;p&gt;模型相关数据：&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" height="462" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/k-s-curve.png" width="648"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;KS指标: 用以评估模型对好、坏客户的判别区分能力，计算累计坏客户与累计好客户百分比的最大差距。KS值范围在0%-100%，判别标准如下：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;KS: &amp;lt;20% : 差&lt;/li&gt;
  &lt;li&gt;KS: 20%-40% : 一般&lt;/li&gt;
  &lt;li&gt;KS: 41%-50% : 好&lt;/li&gt;
  &lt;li&gt;KS: 51%-75% : 非常好&lt;/li&gt;
  &lt;li&gt;KS: &amp;gt;75% : 过高，需要谨慎的验证模型&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" height="462" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/roc.png" width="652"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;ROC曲线就越往左上方靠拢，它下面的面积(AUC)也就越大：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;如果AUC的值达到80，那说明分类器分类非常准确&lt;/li&gt;
  &lt;li&gt;如果AUC值在60～0.80之间，那分类器有优化空间，可以通过调节参数得到更好的性能&lt;/li&gt;
  &lt;li&gt;如果AUC值小于60，那说明分类器模型效果比较差&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;img alt="" height="462" src="https://www.biaodianfu.com/wp-content/uploads/2021/01/p-r.png" width="640"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;参考链接：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="http://shichen.name/slide/20171115scorecard/#1"&gt;信用评分卡模型开发与应用&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://www.yeeach.com/share/scorecard.png"&gt;R语言评分卡模型（公开版）&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://cloud.tencent.com/developer/article/1448182"&gt;手把手教你搭建评分卡模型&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;div&gt;
  &lt;h3&gt;相关文章:&lt;/h3&gt;  &lt;ol&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/imdb-sentiment-analysis-full-connection.html" rel="bookmark" title="&amp;#22810;&amp;#23618;&amp;#20840;&amp;#36830;&amp;#25509;&amp;#31070;&amp;#32463;&amp;#32593;&amp;#32476;&amp;#19982;&amp;#24773;&amp;#24863;&amp;#20998;&amp;#26512;"&gt;多层全连接神经网络与情感分析 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/cluster-score.html" rel="bookmark" title="&amp;#32858;&amp;#31867;&amp;#31639;&amp;#27861;&amp;#35780;&amp;#20272;&amp;#25351;&amp;#26631;"&gt;聚类算法评估指标 &lt;/a&gt;&lt;/li&gt;
   &lt;li&gt;    &lt;a href="https://www.biaodianfu.com/minimum-entropy-word-vector-dimension.html" rel="bookmark" title="&amp;#26368;&amp;#23567;&amp;#29109;&amp;#21407;&amp;#29702;&amp;#30830;&amp;#35748;&amp;#35789;&amp;#21521;&amp;#37327;&amp;#32500;&amp;#24230;"&gt;最小熵原理确认词向量维度 &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>器→工具 开源项目 数据 术→技巧 机器学习</category>
      <guid isPermaLink="true">https://itindex.net/detail/61198-%E9%A3%8E%E9%99%A9%E6%8E%A7%E5%88%B6-%E4%BF%A1%E7%94%A8%E8%AF%84%E5%88%86-%E6%A8%A1%E5%9E%8B</guid>
      <pubDate>Wed, 27 Jan 2021 20:13:13 CST</pubDate>
    </item>
    <item>
      <title>快速搭建准专业直播间（含直播设备介绍）</title>
      <link>https://itindex.net/detail/60804-%E4%B8%93%E4%B8%9A-%E7%9B%B4%E6%92%AD-%E7%9B%B4%E6%92%AD</link>
      <description>&lt;p align="center"&gt;  &lt;a href="https://www.shejidaren.com/dajian-zhuanye-zhibo-jian.html" target="_blank"&gt;   &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;"&gt;&lt;/img&gt;&lt;/a&gt;&lt;/p&gt;前言：相信大部分人做直播就是在自己的电脑旁边，弄个麦克风+摄像头，简单直接。但有没有想过做更专业一点的直播间呢？但又不会，而且怕太复杂成本高？本文是自ISUX团队在分享设计直播时，搭建直播间的一些心得，并没有想像中的复杂，成本也不会太高，做直播的你可以尝试一下哦。
 &lt;h3&gt;  &lt;strong&gt;一、直播间搭建&lt;/strong&gt;&lt;/h3&gt;
 &lt;strong&gt;目标&lt;/strong&gt;

直播间的搭建设计，以及设备的选型和配置方案需要满足I♡UX AIR的节目直播以及录制需求，包括但不限于：分享类、访谈类、圆桌类等形式。

 &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" height="820" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" width="1080"&gt;&lt;/img&gt;

 &lt;strong&gt;直播方式&lt;/strong&gt;

开始直播间搭建前，先要明确以何种方式直播。目前直播方式有两种：手机直播与推流直播。手机直播操作门槛低，实时互动性强；推流直播采用摄像机直播，画面清晰稳定，虽然对设备与技术有更高的要求，但功能多样，可以切换画面，以大特写突出重点，也可以在直播过程中贴图、贴文字。

I♡UX AIR属于授课型直播，嘉宾结合PPT对用户进行内容授课，重点在于PPT也就是内容的展示，同时嘉宾的出镜不可或缺，运营上，也有在直播中贴二维码推广社群以及用户调研等需求，综合以上因素，最后我们决定采用推流直播。

 &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" height="608" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878-18.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" width="1080"&gt;&lt;/img&gt;
 &lt;h3&gt;  &lt;strong&gt;搭建步骤&lt;/strong&gt;&lt;/h3&gt;
搭建一个直播间，需要三个方面的准备工作：摄像设备（直播系统），场地&amp;amp;背景布置，灯光布置。搭建周期上，建议提前两周搭建，在正式开播前至少完整测试两次流程，寻找问题，不断优化。

 &lt;strong&gt;01 &lt;/strong&gt; &lt;strong&gt;摄像设备（直播系统）&lt;/strong&gt;

硬软件互通的信号转换本质上也是一个小型网络，本次直播包含以下几部分：

 &lt;strong&gt;软件支持：&lt;/strong&gt;推流软件OBS、美颜软件YY开播、直播平台看点直播。

 &lt;strong&gt;摄像：&lt;/strong&gt;本次直播长达两小时，对画面的清晰度也有要求。我们采用双机位进行直播制作，其中主摄：索尼HXR-NX200 支持长时间录制，副摄：索尼A7R4 镜头清晰度高，作为副机位拍摄特写。

将摄像机的视频信号输出到电脑需要采集卡，我们使用了两种采集卡，分别是ACASIS hdmi高清视频采集卡，以及elgato HD60 S+，越贵的采集卡输出的视频质量以及色彩还原度越高。

 &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" height="861" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878-1.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" width="1080"&gt;&lt;/img&gt;

 &lt;strong&gt;导播：&lt;/strong&gt;考虑到视频源的切换需求，同时满足多路视频源的输入，我们配备了导播电脑与多路数字导播设备。

 &lt;strong&gt;收音：&lt;/strong&gt;收音使用RODE 罗德wireless go无线麦克风，方便携带，使用简单。

 &lt;strong&gt;信号系统设计思路&amp;amp;流程：&lt;/strong&gt;

 &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" height="799" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878-2.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" width="1080"&gt;&lt;/img&gt;

 &lt;strong&gt;02 场地&amp;amp;背景布置&lt;/strong&gt;

 &lt;strong&gt;场地：&lt;/strong&gt;我们使用的场地是南山影棚，面积约为15平方米。由于影棚日常需求排期紧张，在搭建上需要尽可能地方便快捷。在预案中我们将影棚区分为：摄制区与器材区

 &lt;strong&gt;摄制区：&lt;/strong&gt;嘉宾、电视、背景、装饰

 &lt;strong&gt;器材区：&lt;/strong&gt;摄像机、屏反、主播端电脑、导播电脑、灯光

 &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" height="1010" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878-3.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" width="1080"&gt;&lt;/img&gt;

 &lt;strong&gt;背景：&lt;/strong&gt;首次试播集我们主要采用静态的布景方式。由于成本较低且对场地的要求较少，操作也便捷，我们选择绒布作为布景材料。

与带货直播不同，我们的直播核心在主播和PPT的展示上，为了更好地呈现主播在镜头前的效果与PPT切换的顺畅衔接，背景设置我们采用的是深色系。此外人物的阴影会印在白色背景上，但不会印在吸光的黑色绒布上，这也是我们选择深色系的原因（tips：解决阴影的办法还有一种就是背景与主体的距离要足够远，灯光尽量选择较柔和的光线）。

直播过程若出现屏幕反光，对用户对主播都是不好的体验。解决屏幕反光有以下几种办法：将灯光的位置移出屏幕；选择磨砂材质的屏幕；给摄像机安装偏振镜。

 &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" height="1620" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878-4.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" width="1080"&gt;&lt;/img&gt;

 &lt;strong&gt;直播中：&lt;/strong&gt;为了嘉宾直播的流畅，我们会提前对好台本，控制时间。可以购买一个提词器装在镜头上，确保嘉宾大多数时间是看着镜头，又能看见台本的内容。

 &lt;strong&gt;推流流程：&lt;/strong&gt;首先在直播平台上获取推流地址以及密码，将其输入到推流软件中（obs），确认视频音频信号一切正常后在推流软件上点击开始推流，其次在直播平台上点击开始直播。（tips：网络建议100m以上）

 &lt;strong&gt;03 灯光系统&lt;/strong&gt;

 &lt;strong&gt;主光：&lt;/strong&gt;爱图仕300d2代

 &lt;strong&gt;副光：&lt;/strong&gt;爱图仕120d

 &lt;strong&gt;轮廓光：&lt;/strong&gt;爱图仕mini20

 &lt;strong&gt;原因：&lt;/strong&gt;该品牌的灯具颜色标准，没有色差，能使直播画面的观感达到最佳效果。

 &lt;strong&gt;布光方法：&lt;/strong&gt;三点式布光。

三点式布光，又称为区域照明，有三盏灯即可，分别为主体光、辅助光与轮廓光。一般用于较小范围的场景照明，因为直播间的面积有限，三点式布光是最简单且有效的布光方式。

其中爱图仕300d2代为主光，与主播呈45度角，爱图仕 120d为副光，将主播脸上的暗部打亮，主副光的光比为2：1。爱图仕mini20为轮廓光，光线打在主播的脑袋后方，为的是使黑色的头发与黑色背景分离开，从而使画面更有层次。

灯光尽量使用带柔光的设备，如柔光纸、柔光箱，目的是使主体上的阴影看起来不要太重，达到美观的效果。打光切忌主光方向与摄像机的拍摄方向相同，因为这样会使画面看起来太平而没有层次。

 &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" height="762" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878-5.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" width="1080"&gt;&lt;/img&gt;

 &lt;strong&gt;直播间&lt;/strong&gt;

 &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" height="810" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878-6.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" width="1080"&gt;&lt;/img&gt;

 &lt;strong&gt;直播画面&lt;/strong&gt;

 &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" height="607" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878-7.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" width="1080"&gt;&lt;/img&gt;  &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" height="608" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878-8.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" width="1080"&gt;&lt;/img&gt;
 &lt;h3&gt;  &lt;strong&gt;二、直播回顾&lt;/strong&gt;&lt;/h3&gt;
I♡UX AIR 是由腾讯ISUX用户体验设计部旗下原创馆举办的，面向广大设计师群体的在线直播论坛活动。I♡UX AIR采用线上直播的方式，邀请资深设计师畅谈有关设计及艺术的话题，用专业案例和系统实践，启迪设计师们的设计思维。本次我们邀请腾讯两位高级设计师为大家分享的是动画与交互设计的经验。

 &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" height="62" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878-9.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" width="900"&gt;&lt;/img&gt;

前期准备完毕，下午7点20我们的直播正式开始。

 &lt;strong&gt;7:20PM&lt;/strong&gt;

 &lt;strong&gt;-&lt;/strong&gt;

第一位分享嘉宾是腾讯ISUX高级多媒体设计师阿乐。

 &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" height="608" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878-10.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" width="1080"&gt;&lt;/img&gt;

平时我们看到的一则动画只有短短几分钟，背后却经过许多考量，耗费的时间与心血也不少。她先以PUPU蓝莓之夜和MOUSE Q宣传动画为例，详细讲解动画的制作流程。阿乐认为，一个结构清晰的分镜和故事版有助于设计师和设计师的团队迅速把握整个动画的风格/基调/节奏/时长和工作周期。

 &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" height="608" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878-11.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" width="1080"&gt;&lt;/img&gt;

在项目时间充足的情况下，许多人对排期并不是很重视，阿乐强调恰恰这一部分很重要，它能让团队明白你的策划流程。一般来说前期的文案脚本策划会用到至少一周的时间商定，这个部分商定好，进入到动画中期就不能修改了；动画中期要分阶段去总结已完成的内容，提交给项目方看；输出阶段则需要预留一些时间做好音效和各尺寸的适配。

 &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" height="608" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878-12.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" width="1080"&gt;&lt;/img&gt;

好的工具能事半功倍。针对动画制作流程、UI动效设计和团队协作，阿乐还为大家分享了许多高效实用的插件工具，既分析每款工具的优点，也讲述具体的操作方法。这一part的干货太多，屏幕外的观众笔记记到停不下来。

 &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" height="608" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878-13.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" width="1080"&gt;&lt;/img&gt;

 &lt;strong&gt;8:10PM&lt;/strong&gt;

-

第二位分享嘉宾是ISUX高级交互设计师歆婉。歆婉从轻聊APP出发， 用长达100多页的PPT，倾囊相授自己以及团队设计这款全新社交产品的思路与方法。

 &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" height="608" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878-14.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" width="1080"&gt;&lt;/img&gt;

一般来讲，线上交友会遇到四种连接，一种是连不上的人，一种是坏的连接，一种是虚假的“好连接”，还有一种是真正的连接，而轻聊的目的是让用户找到真正的连接。如何帮助用户找到真正的连接呢？歆婉先简要介绍陌生人引力社交公式，给大家对比男女交友或恋爱的心理差异后，再讲述如何围绕公式去做平衡需求以及解决核心关键点的设计。

 &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" height="608" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878-15.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" width="1080"&gt;&lt;/img&gt;

歆婉指出，人是由两种价值组成的，生物价值和社会价值。陌生人社交男性会更关注生物价值，女性则可能比较关注社会价值。所以在设计用户的个人主页时，在生物价值和社会价值的呈现上做了一个平衡，上层用视频和照片去展示生物价值，下层则展示社会价值，包括公司学校和职位等。

 &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" height="608" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878-16.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" width="1080"&gt;&lt;/img&gt;

用户是有惰性的，在如何激励用户完善资料方面，歆婉也为大家总结了一套方法。本次的分享主题是陌生人社交，设计方法之外，歆婉的讲解里还包含和夹杂了大量的脱单小技巧，不少单身粉丝听完笑称“脱单有望”了。

 &lt;img alt="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" height="608" src="http://images.shejidaren.com/wp-content/uploads/2020/08/40878-17.jpg" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;" width="1080"&gt;&lt;/img&gt;

本文分享到这里，感谢阅读！

 &lt;small&gt;文章来自公众号: 腾讯ISUX(https://isux.tencent.com/articles/i-love-ux-air）&lt;/small&gt; &lt;hr&gt;&lt;/hr&gt; &lt;br /&gt; &lt;br /&gt;(ノ◕‿◕)ノ*:･ﾟ✧  &lt;a href="http://hao.shejidaren.com" target="_blank" title="&amp;#35774;&amp;#35745;&amp;#23548;&amp;#33322;"&gt;查看最受欢迎 301 个设计网站&lt;/a&gt; *:･ﾟ✧ヽ(◕‿◕ヽ)  &lt;br /&gt; &lt;br /&gt; &lt;a href="http://hao.shejidaren.com/sheji-qq-qun.html" target="_blank" title="UI&amp;#35774;&amp;#35745;QQ&amp;#32676;"&gt;UI设计QQ群&lt;/a&gt;  ¦  &lt;a href="http://www.shejidaren.com/feed" target="_blank" title="RSS&amp;#35746;&amp;#38405;"&gt;RSS订阅&lt;/a&gt; ¦  &lt;a href="http://weibo.com/shejidaren888" target="_blank" title="&amp;#26032;&amp;#28010;&amp;#24494;&amp;#21338;"&gt;新浪微博&lt;/a&gt; ¦  &lt;a href="https://www.shejidaren.com/dajian-zhuanye-zhibo-jian.html" target="_blank" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;"&gt;本文链接&lt;/a&gt; ¦  &lt;a href="https://www.shejidaren.com/dajian-zhuanye-zhibo-jian.html#respond" target="_blank" title="&amp;#24555;&amp;#36895;&amp;#25645;&amp;#24314;&amp;#20934;&amp;#19987;&amp;#19994;&amp;#30452;&amp;#25773;&amp;#38388;&amp;#65288;&amp;#21547;&amp;#30452;&amp;#25773;&amp;#35774;&amp;#22791;&amp;#20171;&amp;#32461;&amp;#65289;&amp;#30340;&amp;#35780;&amp;#35770;"&gt;添加评论&lt;/a&gt;  &lt;br /&gt; &lt;img src="http://ww4.sinaimg.cn/large/6857cd42gw1f2n261vbdfj20cb04u0tb.jpg"&gt;&lt;/img&gt;&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>设计杂谈 工具 设计直播</category>
      <guid isPermaLink="true">https://itindex.net/detail/60804-%E4%B8%93%E4%B8%9A-%E7%9B%B4%E6%92%AD-%E7%9B%B4%E6%92%AD</guid>
      <pubDate>Tue, 04 Aug 2020 18:42:27 CST</pubDate>
    </item>
    <item>
      <title>Cloudflare 註冊最便宜網域不加價，整合免費 CDN 和 SSL 憑證</title>
      <link>https://itindex.net/detail/60742-cloudflare-cdn-ssl</link>
      <description>&lt;p&gt;  &lt;img alt="Cloudflare &amp;#35387;&amp;#20874;&amp;#26368;&amp;#20415;&amp;#23452;&amp;#32178;&amp;#22495;&amp;#19981;&amp;#21152;&amp;#20729;&amp;#65292;&amp;#25972;&amp;#21512;&amp;#20813;&amp;#36027; CDN &amp;#21644; SSL &amp;#24977;&amp;#35657;" height="444" src="https://free.com.tw/blog/wp-content/uploads/2020/07/Screenshot-2020-07-02-14.27.png" title="Cloudflare &amp;#35387;&amp;#20874;&amp;#26368;&amp;#20415;&amp;#23452;&amp;#32178;&amp;#22495;&amp;#19981;&amp;#21152;&amp;#20729;&amp;#65292;&amp;#25972;&amp;#21512;&amp;#20813;&amp;#36027; CDN &amp;#21644; SSL &amp;#24977;&amp;#35657;" width="640"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Cloudflare 在 2018 年推出   &lt;a href="https://free.com.tw/cloudflare-registrar/" rel="noopener noreferrer" target="_blank"&gt;Cloudflare Registrar&lt;/a&gt; 網域名稱註冊服務，直接加入域名註冊商，主打成本價也就是只收取向上層註冊中心、ICANN 支付的費用，沒有加價也不會有額外支出，相較於一般註冊商來說   &lt;strong&gt;Cloudflare 可能是市場上最便宜、最實惠的價格（畢竟它不是靠這個服務營利）。&lt;/strong&gt;另一方面，很多註冊商會在第一年使用超便宜價格吸引使用者註冊，後續加入附加服務、隱藏價格都成為註冊網址時容易誤入的陷阱。&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;在此之前    &lt;a href="https://www.cloudflare.com/zh-tw/products/registrar/" rel="noopener noreferrer" target="_blank"&gt;Cloudflare Registrar&lt;/a&gt; 僅允許使用者以網域移轉（Transfer）方式將網域名稱轉入，近期注意到 Cloudflare 已經開放網域註冊功能！&lt;/strong&gt;不過還沒有對所有帳戶開放，如果你看到如下圖網域測試版功能，點選後就能開啟註冊頁面（另一個值得一提的是 Cloudflare 終於推出  &lt;a href="https://www.cloudflare.com/zh-tw/" rel="noopener noreferrer" target="_blank"&gt;繁體中文&lt;/a&gt;介面，當然也還在測試版）。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="Cloudflare &amp;#35387;&amp;#20874;&amp;#32178;&amp;#22495;" height="400" src="https://free.com.tw/blog/wp-content/uploads/2020/07/Screenshot-2020-07-02-14.26.png" title="Screenshot 2020-07-02 14.26.png" width="580"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Cloudflare Registrar 註冊功能就和轉入網址一樣簡單，不同的是  &lt;strong&gt;第一年終於不用再去其他註冊商註冊、轉入，而是可以直接註冊網域並使用    &lt;a href="https://free.com.tw/cloudflare/" rel="noopener noreferrer" target="_blank"&gt;Cloudflare&lt;/a&gt; 提供的   &lt;a href="https://free.com.tw/cloudflare-dns/" rel="noopener noreferrer" target="_blank"&gt;網域名稱伺服器&lt;/a&gt;（DNS）&lt;/strong&gt;，享有網站加速及安全防護等功能。Cloudflare Registrar 支援非常多種  &lt;a href="https://www.cloudflare.com/zh-tw/tld-policies/" rel="noopener noreferrer" target="_blank"&gt;網域名稱後綴&lt;/a&gt;（TLD），不過很可惜的是國家和地區頂級域名（ccTLD）像是   &lt;code&gt;.com.tw&lt;/code&gt; 或   &lt;code&gt;.tw&lt;/code&gt; 暫時還無法註冊。&lt;/p&gt;
 &lt;p&gt;接下來我就簡單示範如何在 Cloudflare Registrar 註冊網域名稱，其他我推薦的註冊商還有：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;   &lt;a href="https://free.com.tw/gandiv5/" rel="noopener noreferrer" target="_blank"&gt;Gandi.net 全新改版！註冊網域名稱含免費 Whois 隱藏和信箱功能&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://free.com.tw/google-domains/" rel="noopener noreferrer" target="_blank"&gt;在 Google Domains 註冊購買網域名稱教學&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;   &lt;a href="https://free.com.tw/register-your-first-domain-name/" rel="noopener noreferrer" target="_blank"&gt;在網站搬家前，你應該先註冊一個自己的網址！四個推薦的網域名稱註冊商&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;Cloudflare Registrar&lt;/strong&gt;  &lt;br /&gt;
  &lt;a href="https://www.cloudflare.com/zh-tw/products/registrar/" rel="noopener noreferrer" target="_blank"&gt;https://www.cloudflare.com/zh-tw/products/registrar/&lt;/a&gt;&lt;/p&gt;
 &lt;h2&gt;使用教學&lt;/h2&gt;
 &lt;p&gt;STEP 1&lt;/p&gt;
 &lt;p&gt;登入 Cloudflare 控制台點選「  &lt;strong&gt;網域&lt;/strong&gt;」功能，在這裡可以管理你移轉到 Cloudflare Registrar 的所有網域名稱，像是開啟自動續費（Auto-renew）、查詢過期時間或管理   &lt;a href="https://zh.wikipedia.org/wiki/WHOIS" rel="noopener noreferrer" target="_blank"&gt;WHOIS&lt;/a&gt; 資訊等等。如果在側邊選單有出現「  &lt;strong&gt;Register&lt;/strong&gt;」就能使用 Cloudflare 註冊網址功能，我寫這篇文章時仍在測試階段。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="Cloudflare &amp;#35387;&amp;#20874;&amp;#32178;&amp;#22495;" height="391" src="https://free.com.tw/blog/wp-content/uploads/2020/07/Screenshot-2020-07-02-13.45.29-redacted.png" title="Screenshot 2020-07-02 13.45.29-redacted.png" width="640"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;STEP 2&lt;/p&gt;
 &lt;p&gt;下圖就是 Cloudflare Registrar 註冊網址功能，和其他註冊商一樣只要輸入關鍵字或是想要註冊的網址後就會在搜尋結果給出相關建議。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="Cloudflare &amp;#35387;&amp;#20874;&amp;#32178;&amp;#22495;" height="391" src="https://free.com.tw/blog/wp-content/uploads/2020/07/Screenshot-2020-07-02-13.45.png" title="Screenshot 2020-07-02 13.45.png" width="640"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Cloudflare Registrar 支援許多網域名稱後綴，像是近年比較新的 TLD 都能註冊，前面介紹有說到它目前缺點是  &lt;strong&gt;還不支援國家和地區頂級域名（ccTLD）&lt;/strong&gt;，如果你想註冊 .com.tw 之類的網址可能就要透過其他註冊商。&lt;/p&gt;
 &lt;p&gt;搜尋結果會列出可以註冊的網域名稱、價格，這個價格就是註冊時需要支付的最終價格，不會有其他額外費用。點選後方「  &lt;strong&gt;Purchase&lt;/strong&gt;」就能購買特定網址，如果無法註冊會跳出錯誤訊息，回到上一個步驟重新開始。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="Cloudflare &amp;#35387;&amp;#20874;&amp;#32178;&amp;#22495;" height="444" src="https://free.com.tw/blog/wp-content/uploads/2020/07/Screenshot-2020-07-02-13.46.png" title="Screenshot 2020-07-02 13.46.png" width="640"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;STEP 3&lt;/p&gt;
 &lt;p&gt;在註冊時遇到的第一個畫面會顯示註冊摘要，包括要註冊的網址、時間、自動續費功能和每年註冊價格，如果你已經有在使用 Cloudflare 付費功能，會以你設定在帳戶的信用卡進行付款，在註冊時也可選擇其他付款方式。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="Cloudflare &amp;#35387;&amp;#20874;&amp;#32178;&amp;#22495;" height="444" src="https://free.com.tw/blog/wp-content/uploads/2020/07/Screenshot-2020-07-02-14.00.png" title="Screenshot 2020-07-02 14.00.png" width="640"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;可一次註冊多個年份就不用持續付款，  &lt;strong&gt;不過在 Cloudflare 註冊較長時間不會獲得優惠&lt;/strong&gt;，建議有類似需求的朋友可以尋找看看其他註冊商的優惠券或優惠方案，可能換算下來會比 Cloudflare Registrar 更便宜划算。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="Cloudflare &amp;#35387;&amp;#20874;&amp;#32178;&amp;#22495;" height="475" src="https://free.com.tw/blog/wp-content/uploads/2020/07/Screenshot-2020-07-02-14.01.png" title="Screenshot 2020-07-02 14.01.png" width="640"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;STEP 4&lt;/p&gt;
 &lt;p&gt;點選右下角「  &lt;strong&gt;繼續&lt;/strong&gt;」到下一個頁面，需要填入註冊者相關資料，Cloudflare 提供免費的 WHOIS 隱私保護功能，預設情況下會開啟，避免其他人在 WHOIS 查到你的真實資料，在註冊時記得填寫正確的資訊，包括你的姓名、公司名稱、地址、電話和 Email，這是用來證明網域名稱擁有者的方式。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="Cloudflare &amp;#35387;&amp;#20874;&amp;#32178;&amp;#22495;" height="444" src="https://free.com.tw/blog/wp-content/uploads/2020/07/Screenshot-2020-07-02-14.01.58-redacted.png" title="Screenshot 2020-07-02 14.01.58-redacted.png" width="640"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;最終再次的確認註冊者資料，包括註冊網址的時間、自動續費、應付金額和付款方式，沒問題的話點選右下角「  &lt;strong&gt;送出&lt;/strong&gt;」就能遞送這次的網域名稱註冊申請。  &lt;strong&gt;要注意的是因為 Cloudflare Registrar 屬於海外刷卡、會以美元計算，信用卡部分可能會產生手續費。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="Cloudflare &amp;#35387;&amp;#20874;&amp;#32178;&amp;#22495;" height="444" src="https://free.com.tw/blog/wp-content/uploads/2020/07/Screenshot-2020-07-02-14.02.08-redacted.png" title="Screenshot 2020-07-02 14.02.08-redacted.png" width="640"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;STEP 5&lt;/p&gt;
 &lt;p&gt;提交註冊後會需要幾秒鐘時間進行註冊、設定網域名稱。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="Cloudflare &amp;#35387;&amp;#20874;&amp;#32178;&amp;#22495;" height="454" src="https://free.com.tw/blog/wp-content/uploads/2020/07/Screenshot-2020-07-02-14.02.png" title="Screenshot 2020-07-02 14.02.png" width="640"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;STEP 6&lt;/p&gt;
 &lt;p&gt;完成後網址會自動放入 Cloudflare 控制台，可以在域名設定裡調整自動續訂、DNSSEC、WHOIS 資訊變更等選項，預設情況下建議將自動續訂打開，Cloudflare 會在網址到期前一個月自動續費，以免因為過期而造成網站無法連線。&lt;/p&gt;
 &lt;p&gt;另一個好處是在 Cloudflare Registrar 註冊網址後會自動使用 Cloudflare 的 DNS 伺服器（當然使用者也可以修改為其他的伺服器），獲得 Cloudflare 各種特色功能，像是可防範惡意攻擊、網站加速或是分流等等。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="Cloudflare &amp;#35387;&amp;#20874;&amp;#32178;&amp;#22495;" height="444" src="https://free.com.tw/blog/wp-content/uploads/2020/07/Screenshot-2020-07-02-14.03.png" title="Screenshot 2020-07-02 14.03.png" width="640"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;div&gt;
  &lt;h2&gt;值得一試的三個理由：&lt;/h2&gt;
  &lt;ol&gt;
   &lt;li&gt;Cloudflare Registrar 已開放網域名稱註冊功能，目前在測試階段&lt;/li&gt;
   &lt;li&gt;支援大部分常見的網域名稱後綴（TLD），但還不支援 ccTLD&lt;/li&gt;
   &lt;li&gt;可直接整合 Cloudflare 原生 DNS、CDN 和 SSL 服務&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
 &lt;p&gt;喜歡   &lt;a href="https://free.com.tw/cloudflare-register-domain-name/" rel="nofollow"&gt;Cloudflare 註冊最便宜網域不加價，整合免費 CDN 和 SSL 憑證&lt;/a&gt; 嗎？歡迎將  &lt;a href="https://free.com.tw" rel="nofollow"&gt;免費資源網路社群&lt;/a&gt;加入書籤，以 Facebook、Twitter 或 LinkedIn 追蹤更新，獲取更多科技新知及免費資源相關介紹教學。&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>熱門主題 站長工具 編輯推薦 CDN CloudFlare</category>
      <guid isPermaLink="true">https://itindex.net/detail/60742-cloudflare-cdn-ssl</guid>
      <pubDate>Wed, 08 Jul 2020 10:00:56 CST</pubDate>
    </item>
    <item>
      <title>建议收藏！2020 年必备的几个 DevOps 工具</title>
      <link>https://itindex.net/detail/60672-%E6%94%B6%E8%97%8F-devops-%E5%B7%A5%E5%85%B7</link>
      <description>&lt;p&gt;  &lt;img alt="&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;" src="https://segmentfault.com/img/bVbIhGv" title="&amp;#22270;&amp;#29255;&amp;#25551;&amp;#36848;"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;提到 DevOps 这个词，我相信很多人一定不会陌生。作为一个热门的概念，DevOps近 年来频频出现在各大技术社区和媒体的文章中。到了 2020 年，DevOps 的革命也终于成为了一个主流，DevOps 相关工具的受欢迎程度也在激增。根据 Google 趋势，「DevOps 工具」的搜索量一直在稳定增长，并且这种趋势还在持续。&lt;/p&gt;
 &lt;p&gt;DevOps 工具越来越多，了解它们以及知道在什么时候使用他们越来越重要。由于 DevOps 涵盖了整个软件开发生命周期，因此有很多工具可供选择。让我们将其大致分为以下几类：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;开发和构建工具&lt;/li&gt;
  &lt;li&gt;自动化测试工具&lt;/li&gt;
  &lt;li&gt;部署工具&lt;/li&gt;
  &lt;li&gt;运行时 DevOps 工具&lt;/li&gt;
  &lt;li&gt;协作 DevOps 工具&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;成功且成熟地采用 DevOps 做法将始终拥有完整的渠道，其中包括适用于这五个类别的工具。确保评估您当前的工具堆栈，以确保您没有丢失 CI/CD 管道的关键部分。&lt;/p&gt;
 &lt;h2&gt;开发和构建工具&lt;/h2&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhCL" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;这是 CI/CD 管道堆栈的基础。一切都从这里开始。该类别中最好的工具可以协调多个事件流，并可以轻松地与外部工具集成。&lt;/p&gt;
 &lt;p&gt;软件开发生命周期的这一部分中的工具分为三个子类别：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;源代码控制管理（SCM）&lt;/li&gt;
  &lt;li&gt;持续集成（CI）&lt;/li&gt;
  &lt;li&gt;数据管理&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;2020年，推荐的SCM技术是GIT，因此我们的SCM工具必须具有出色的GIT支持。对于CI，绝对需要在临时容器化环境中运行和执行构建的能力。对于数据管理，我们需要能够对数据库架构进行更改并使它与应用程序版本保持一致。&lt;/p&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h3&gt;2020年排名第一的SCM + CI工具：Gitlab和Gitlab-CI&lt;/h3&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhCV" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Gitlab无疑是2020年最伟大的DevOps Lifecycle工具，它将在可预见的未来成为创新的领导者。&lt;/p&gt;
 &lt;p&gt;Gitlab的核心功能提供了一个完美的GIT存储库管理工具。它基于Web的用户界面是最冗长且易于使用的。Gitlab的免费套餐可提供您所需的一切，并且具有SaaS和On-Prem尺寸。&lt;/p&gt;
 &lt;p&gt;市场上有很多SCM工具，但是没有一种工具像Gitlab多年来所做的那样将“持续集成”直接集成到您的存储库中。称为Gitlab-CI，将.gitlab-ci.yml文件粘贴到代码库的根目录中，任何GIT事件都会根据您在此处定义的内容触发操作。他们确实是按代码进行持续集成的领导者。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;主要优势：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;成熟度 - 该产品自2013年以来一直投放市场，并且非常稳定并且得到了很好的支持。&lt;/li&gt;
  &lt;li&gt;开源 - Gitlab的免费版没有削减开发团队所需的核心功能。每个付费层都提供了附加功能，这些附加功能可根据组织的规模和需求带来极高的价值。&lt;/li&gt;
  &lt;li&gt;易用的 CI — 市场上没有其他工具可以像Gitlab-CI一样直接将持续集成直接嵌入到您的SCM中。使用Docker构建进行临时构建的能力提供了无忧的构建作业，并且内置的报告使调试构建失败变得容易。无需复杂的集成和业务流程就可以对多种工具进行编排。&lt;/li&gt;
  &lt;li&gt;无限集成 - Gitlab提供了每个核心DevOps类别中所需工具的轻松集成。这使开发人员和操作人员在任何环境中都可以使用一个真实的来源来获取与其应用程序相关的信息。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;竞争对手:&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;还有其他工具在此领域也很流行，但是它们不如Gitlab。原因如下：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;GitHub - GitHub一直是小型和早期开发商店的出色SaaS源代码管理系统。但是，对于需要在网络中保留其IP的大型企业，GitHub的唯一选择是.OVA不支持高可用性的虚拟机。这使其难以维护on-prem，并且只能在中型组织中运行，然后服务器本身才开始崩溃。它缺少GitHub Actions（直到最近，但仍不在本地版本中）或CI-as-Code，这意味着您始终需要带上自己的CI工具并管理该集成。最后，它比任何Gitlab定价都昂贵。&lt;/li&gt;
  &lt;li&gt;Jenkins — 尽管Jenkins已经成为持续集成工具的默认标准，但它始终缺少源代码控制元素。意味着，您将始终使用Jenkins 和 SCM工具。当像GitLab这样的工具同时提供这两种功能时，这简直是不必要的复杂。它可怕的UX使得现代Web应用程序有很多不足之处。&lt;/li&gt;
  &lt;li&gt;BitBucket/Bamboo — 我不得不说，这是一个自动失败者，考虑到您需要两种工具来完成Gitlab的一项工作。尽管BitBucket云支持Gitlab-CI / GitHub Action功能，但没有一家公司（规模超过一家初创公司）可以轻易采用它。用于本地的 BitBucket服务器甚至不支持BitBucket管道！&lt;/li&gt;
&lt;/ul&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h3&gt;2020年排名第一的数据管理工具：FlywayDB&lt;/h3&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhDs" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Web应用程序开发中最容易被忽视的方面是数据库的自动化需求。在应用程序的新版本中部署数据库架构更改通常是事后的想法。模式更改通常会添加或重命名列或表。如果应用程序版本与架构版本不匹配，则该应用程序可能会完全损坏。由于存在两个不同的系统，因此通过应用程序升级来协调数据库更改也可能很困难。FlyWayDB解决了所有这些问题。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;主要优势：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;数据库版本控制 - FlyWay允许您简单地创建数据库版本，跟踪数据库迁移以及轻松地前滚或后退架构更改，而无需某些定制解决方案。&lt;/li&gt;
  &lt;li&gt;二进制或内置 - 您可以选择在应用程序启动时或作为二进制可执行文件运行Flyway。在代码中使用此工具，以便它在启动时检查版本功能并运行适当的迁移，从而使数据库和应用程序版本保持同步。您也可以临时运行cmd行，从而在不重建整个应用程序的情况下为现有数据库提供了灵活性。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;竞争对手：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;这个空间中没有太多工具。但是，让我们来看几个：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;LiquiBase — Liquibase是相似的，实际上，如果有人在我的组织中工作过，那么我很乐意通过FlyWay对该工具进行标准化。&lt;/li&gt;
  &lt;li&gt;Flocker - 这可能仅适用于容器化的应用程序-在容器中运行数据库非常困难，必须精心计划才能成功执行。我建议将RDS之类的服务用于数据库，而不要尝试运行存储在容器中的关键数据。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;自动化测试工具&lt;/h2&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhDJ" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;我们必须首先将自动化工具安装到测试金字塔中，从而开始对自动测试工具进行评估。测试金字塔有4层：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;单元测试 - 这是所有自动化测试的基础。就数量而言，与其他类型相比，您应该拥有最多的单元测试。这些测试应由软件开发人员编写和运行，以确保应用程序的一部分（称为“单元”）符合其设计并按预期运行。&lt;/li&gt;
  &lt;li&gt;组件测试 — 组件测试的主要目的是验证测试对象的输入/输出行为。这样可以确保测试对象的功能按照所需规范正确运行。&lt;/li&gt;
  &lt;li&gt;集成测试 — 这是测试阶段，在此阶段中，各个软件模块被组合在一起并作为一个整体进行测试。&lt;/li&gt;
  &lt;li&gt;端到端测试 - 此层是不言自明的。我们正在研究从头到尾的应用程序流程，并使其表现出预期的效果。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;由于单元和组件层测试仅由应用程序开发人员驱动，并且通常是特定于编程语言的，因此我们不会在DevOps空间中评估这些工具。&lt;/p&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h3&gt;2020年排名第一的集成测试工具：Cucumber&lt;/h3&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhDP" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Cucumber将规范和测试文档合并为一个有凝聚力的有效文档。由于它们是由Cucumber自动测试的，因此您的规格始终是最新的。如果您想开始构建Web自动化测试框架并在Web应用程序上模拟用户行为，那么带有Java和Cucumber BDD的Selenium WebDriver是在项目中学习和实现Cucumber的好方法。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;主要优势：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;行为驱动的开发 — Cucumber用于BDD测试，它已成为一种入门测试框架（与传统的测试驱动开发相比）。&lt;/li&gt;
  &lt;li&gt;动态文档 - 记录您所做的事情总是很痛苦。由于您的测试被定义​​为代码，因此Cucumber测试会自动生成文档以进行匹配以确保它们始终保持同步。&lt;/li&gt;
  &lt;li&gt;支持 - 这里有很多工具可供选择，但是当情况变得严重时，您需要工具维护者的认真支持。黄瓜拥有足够的资金和支持结构来维持该工具的未来几年。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;竞争对手：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;在这个领域中有许多框架和特定于技术的工具，但是只有Cucumber接近于“一刀切”的解决方案。&lt;/p&gt;
 &lt;h2&gt;端到端测试工具&lt;/h2&gt;
 &lt;p&gt;进行端到端测试时，有两个重点领域需要关注：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;功能测试&lt;/li&gt;
  &lt;li&gt;负载测试&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;功能测试显然是在测试我们想要发生的事情是否实际发生。当我单击SPA上的某些页面，填写表格并单击Submit时，数据将显示在数据库中，并且屏幕显示成功！&lt;/p&gt;
 &lt;p&gt;我们还需要能够测试在相同场景下工作的x数量的用户是否可以正确处理。&lt;/p&gt;
 &lt;p&gt;如果您在这两个方面都没有进行测试，则CI / CD管道中的差距将很大。&lt;/p&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h3&gt;2020年排名第一的端到端测试工具 — 功能：SoapUI Pro&lt;/h3&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhDR" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;由于SOAP Web服务是默认的，因此SoapUI进入API测试领域已有很长时间了。尽管我们不再构建新的SOAP服务，并且该工具的名称没有更改，但这并不意味着它没有发展。SoapUI为构建后端Web服务的自动化功能测试提供了一种出色的结构。这些可以轻松地与持续集成工具集成，并且可以作为我们的CI / CD管道的一部分运行。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;主要优势：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;广泛的文档 - 此工具已经存在了一段时间，因此有许多在线资源可帮助您确定如何配置负载测试。&lt;/li&gt;
  &lt;li&gt;易于使用 — 尽管有多种API测试工具可用，但拥有一个用于多种服务的接口可以使构建测试变得简单。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;竞争对手：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;  &lt;li&gt;Selenium - Selenium是该领域的另一个出色工具。如果您正在构建和运行基于Java的应用程序，则建议使用它。但是，如果您要使用多种技术来处理一个完整的Web应用程序，那么对于非Java语言的用户来说可能会有些笨拙。&lt;/li&gt;&lt;/ul&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h3&gt;2020年排名第一的端到端测试工具 — 负载测试：LoadRunner&lt;/h3&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhDS" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;说明：在对应用程序的各个方面进行负载测试时，只有LoadRunner才能完成。是的，这很昂贵而且入门有点困难，但是它是唯一可以执行测试的工具，可以使我作为技术架构师相信新代码将在极端压力下执行。另外，我认为现在是时候让负载运行技巧从SQA资源转移到开发团队了。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;主要优势：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;广泛的文档 - 该工具已经存在了一段时间，因此有许多在线资源可以帮助您确定如何配置负载测试。&lt;/li&gt;
  &lt;li&gt;协议支持 - 从ODBC到AJAX，再到HTTPS以及您的应用程序可能在某处使用的其他晦涩协议，LoadRunner支持该协议。我们要避免串接多个负载测试工具-这只会增加复杂性。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;击败竞争对手：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;再说一次，在这个领域中没有太多的“一刀切”的工具，因此简单的解决方案是可以在任何环境中使用任何技术将其丢弃。&lt;/p&gt;
 &lt;h2&gt;部署工具&lt;/h2&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhDT" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;部署工具可能是应用程序开发中鲜为人知的方面。对于操作人员来说，如果不深入了解应用程序代码和功能，就很难使用部署工具。对于开发人员来说，管理代码部署是一项新的职责，因此他们对许多部署工具的经验很少。&lt;/p&gt;
 &lt;p&gt;首先，让我们将部署工具分为三个子类别：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;构件管理&lt;/li&gt;
  &lt;li&gt;配置管理&lt;/li&gt;
  &lt;li&gt;部署方式&lt;/li&gt;
&lt;/ul&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h3&gt;2020年排名第一的工件管理工具：Nexus&lt;/h3&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhDV" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Nexus工件存储库支持几乎所有主要技术，从Java到NPM再到Docker。我们可以使用这一工具来存储我们所有可部署的工件。通过使软件包更接近构建过程，代理远程软件包管理器的能力还大大提高了我们CI配置的速度。这样做的另一个好处是，我们可以全局查看跨多个软件项目使用的所有软件包，从而锁定不安全的开源软件包，这些软件包可能是我们代码中的攻击媒介。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;主要优势：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;技术支持 - 该产品自2013年以来一直投放市场，并且非常稳定且得到了很好的支持。&lt;/li&gt;
  &lt;li&gt;开源 - Gitlab的免费版本没有削减开发团队需要的核心功能。每个付费层均提供附加功能，这些附加功能可带来最大价值，具体取决于组织的规模和需求。&lt;/li&gt;
&lt;/ul&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h3&gt;2020年排名第一的配置管理工具：Ansible&lt;/h3&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhD5" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Ansible是这个领域的领导者，原因很简单：无国籍。较早的现场配置管理工具着重于管理配置状态。如果它与所需的配置脱离同步，它将自行修复。在新的应用程序中，我们只有无状态组件。新版本的代码是新的构件，并已部署以替换现有的构件。我们拥有短暂的短暂环境。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;主要优势：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;无状态 - Ansible剧本是从操作员机器上运行的，并命中服务器目标。我不在乎远程对象的状态，这使得使用Packer之类的工具来构建可部署对象变得更加容易。&lt;/li&gt;
  &lt;li&gt;开源 - 和CentOS一样，Ansible也由RedHat维护。该企业及其高级支持人员可以帮助维护社区，并确保高质量，易于使用的模块。&lt;/li&gt;
  &lt;li&gt;分子测试 — 因为配置管理和其他任何东西一样都是代码，所以如果不对其进行测试，我们将无所不能。用于测试Ansible角色的分子框架可以无缝地工作，以确保我们的按代码配置质量一样高，并遵循与应用程序代码相同的CI / CD管道。&lt;/li&gt;
  &lt;li&gt;YAML — 与其他工具相比，YAML更加容易使您头脑清醒。由于配置管理对于采用DevOps的任何人来说通常都是新事物，因此这使其成为关键卖点。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;竞争对手：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;OpsCode Chef - 我以厨师食谱开发人员的身份开始了DevOps生涯。露比和厨师很亲密，我的心。但是，它们根本无法解决当今无状态，云原生应用程序的问题。对于更传统的遗留应用程序来说，这是一个很好的工具，但是本文将重点放在未来。&lt;/li&gt;
  &lt;li&gt;Puppet — Puppet从未成长为一个庞大的社区，特别是与Chef and Ansible相比。它非常适合配置和裸机，但不支持Web应用程序类型的配置管理。&lt;/li&gt;
&lt;/ul&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h3&gt;2020年排名第一的部署工具：Terraform&lt;/h3&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhD9" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;Terraform解决了从网络组件到实际服务器映像定义基础架构即代码的问题。自最初发布以来，它已经走了很长一段路，并建立了庞大的插件社区和支持社区，以帮助您解决可能遇到的几乎所有部署场景。支持本地，云中或其他任何类型的环境的能力是首屈一指的。最后，最新版本在HCL中提供了许多与其他任何传统编程语言相同的逻辑功能和类，从而使开发人员可以轻松学习和学习。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;主要优势：&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;不受云/环境影响 - Terraform利用提供的资源作为Terraform代码与与基础架构提供商进行通信所需的所有API和后端逻辑之间的接口。这意味着我可以学习一种工具，并且能够在任何地方工作。&lt;/p&gt;
 &lt;ul&gt;  &lt;li&gt;开源 — 同样，很难敲响免费工具。社区支持是一流的。&lt;/li&gt;&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;竞争对手：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;  &lt;li&gt;AWS CloudFormation — 即使您仅在AWS云环境中工作，您也可能会在职业生涯中继续前进，而不是去那里。将您的技能和知识整合到一个平台中可能会有风险。此外，许多新的AWS服务通常在CloudFormation中可用之前作为Terraform模块提供。&lt;/li&gt;&lt;/ul&gt;
 &lt;h2&gt;运行时DevOps工具&lt;/h2&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhFr" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;任何开发项目的最终目标都是在生产中运行我们的应用程序。在DevOps世界中，我们希望确保我们对环境中的任何潜在问题具有可见性，并且还希望将人工干预降至最低。选择正确的运行时工具集对于实现开发必不可少的条件至关重要。&lt;/p&gt;
 &lt;p&gt;运行时工具子类别为：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;X 即服务&lt;/li&gt;
  &lt;li&gt;编排&lt;/li&gt;
  &lt;li&gt;监控方式&lt;/li&gt;
  &lt;li&gt;日志记录&lt;/li&gt;
&lt;/ul&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h3&gt;2020年排名第一的X-as-a-Service工具：Amazon Web Services&lt;/h3&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhFt" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;亚马逊一直是云计算领域的领导者。他们也不仅止步于此-他们为开发人员提供了许多新服务，以利用它可以使您旋转。将任何技术和任何模式带到AWS上，就可以构建和运行它。与在自己的数据中心中构建，管理和维护传统硬件相比，它们的成本极其合理。免费服务层使任何人都有机会在必须做出购买决定之前进行尝试，这对于尝试以正确的方式构建应用程序而不必因成本而造成损害非常有用。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;主要好处：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;行业标准 - 如果您有在AWS中构建应用程序的经验，那么您基本上可以在任何地方找到工作。企业喜欢AWS，而创业公司喜欢AWS的低成本。&lt;/li&gt;
  &lt;li&gt;Free-Tier — 与其他所有功能相比，AWS的业务确实如此。让我使用该服务并查看其工作原理，然后再决定将数千美元投入可能有巨大陷阱的事物中。我从未为POC构建的任何产品都超过免费套餐限制。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;竞争对手：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Azure – 自最初发布以来，Azure已经走了很长一段路，值得称赞。但是，区分自身的需求已导致其对服务的名称进行了怪异的命名，而这些服务要难一点了-到底什么是“ blob存储”？尽管.NET代码在Microsoft生态系统中效果更好，但不太可能仅将.NET用于应用程序的各个方面。&lt;/li&gt;
  &lt;li&gt;Heroku — 简而言之，除了在Heroku上的个人项目外，我什么都不会运行。透明度不高，企业没有理由将其用作平台。这对于在博客中演示某些内容非常有用，但对于实际应用程序来说，非常感谢！&lt;/li&gt;
&lt;/ul&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h3&gt;2020年排名第一的编排工具：OpenShift&lt;/h3&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhFu" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;您可能在应用程序堆栈中的某处使用了Docker或容器。无服务器应用程序很棒，但它们不能适合所有的架构模式。在没有业务流程平台的情况下运行容器根本行不通。从安全性和工具角度来看，Core Kubernetes带来了很多需求。OpenShift是唯一拥有Kubernetes平台的平台，它具有Source2Image构建，pod中的部署自动化以及甚至可追溯性和监视功能。它可以在本地，云中或同时在两者中运行。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;主要优势：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;内置的安全性 - 管理K8安全性几乎需要博士学位。必须仔细考虑并考虑所有细节。默认情况下，OpenShift所采用的安全机制减少了开发人员的工作量，并为他们的应用程序提供了更安全的平台。&lt;/li&gt;
  &lt;li&gt;多合一解决方案 – 与默认不包含负载平衡工具的香草K8不同，OpenShift拥有一切。我可以使用它来托管我的容器，构建容器，运行CI / CD工具，协调外部流程，管理机密等等。尽管GUI仍然需要做更多的工作，但API优先的方法意味着一切都可以编写脚本，并且与K8的其他GUI不同，它使学习Kubernetes的基础知识变得更加简单，而无需首先获得该学位！&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;竞争对手：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;  &lt;li&gt;Docker Swarm - Docker swarm尝试通过删除大量内容来简化K8。这对于较小的应用程序非常有用，但对于企业应用程序则根本不起作用。此外，AWS ECS之类的服务采用了类似的方法，但是使我可能正在与之交互的其他服务（Lambda，IAM等）的使用变得更容易。&lt;/li&gt;&lt;/ul&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h3&gt;2020年排名第一的监控工具：New Relic&lt;/h3&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhFv" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;New Relic的早期发行版确实做得非常好-APM监视。现在，它是一套完整的监视工具，使我可以监视服务器性能，容器性能，数据库性能，最终用户体验监视，当然还有APM监视。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;主要优势：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;易用性 - 我在担任系统工程师时曾使用过许多监视工具，但从未遇到过像New Relic这样易于使用的监视工具。这是一个SaaS，因此不必设置服务器组件也很不错。&lt;/li&gt;
  &lt;li&gt;端到端可见性 - 其他工具尝试监视应用程序的一个特定方面。无论是CPU利用率还是网络流量，所有这些层都可以协同工作，以使您的应用正常运行。New Relic使您能够组合所有数据以真实了解正在发生的事情。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;竞争对手：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Zabbix — Zabbix是我最喜欢的监视系统，但是由于缺乏向云原生环境和APM空间发展的能力，因此使其滞后。它仍然可以很好地监视传统的服务器基础结构，仅此而已。&lt;/li&gt;
  &lt;li&gt;DataDog - 此工具过于侧重于管理生产应用程序的过程视角，而对代码本身的关注不足。在真正的DevOps团队中，有开发人员参与生产，我们无需依靠繁琐的工具来提供世界一流的支持。&lt;/li&gt;
&lt;/ul&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h3&gt;2020年排名第一的测井工具：Splunk&lt;/h3&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhFM" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;很难反对Splunk。他们很久以来一直是日志聚合的领导者，并且他们继续做得最好。借助本地和SaaS产品，您可以在任何地方使用它。主要的缺点是，它仍然很难运行！&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;主要优势：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;行业标准 —企业喜欢Splunk，他们也有钱为此付出代价。虽然初创企业可能难以证明其成本合理，但许多概念和技能可以转移到开源替代方案中。&lt;/li&gt;
  &lt;li&gt;可支持性 -简单地说，它可以正常工作。它具有许多默认值和即用型功能，因此您不必花费大量时间阅读文档并尝试使一些没有明确说明的内容能够正常工作。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;竞争对手：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;  &lt;li&gt;ELK Stack - ElasticSearch，LogStash和Kibana，虽然似乎总是很酷，因为它们不向您收取使用费用，但随着日志集的增长和机上越来越多的应用程序的维护，它的确变得更加困难您的工具。与使用Splunk相比，我在构建任何类型的仪表板之前花了更多的时间来设置工具。&lt;/li&gt;&lt;/ul&gt;
 &lt;h2&gt;协作DevOps工具&lt;/h2&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhFQ" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;DevOps首先是组织内部的文化变革。虽然购买工具不会一夜之间改变文化，但无疑可以帮助培养与同事合作的新方法。&lt;/p&gt;
 &lt;p&gt;协作工具子类别为：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;问题跟踪&lt;/li&gt;
  &lt;li&gt;聊天操作&lt;/li&gt;
  &lt;li&gt;文献资料&lt;/li&gt;
&lt;/ul&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h3&gt;2020年排名第一的问题跟踪工具：Jira&lt;/h3&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhFV" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;吉拉（Jira）继续保持头把交椅，尽管在这个领域竞争日益激烈。但是，Jira内置的强大灵活性使开发团队和运营团队可以管理其项目工作和冲刺任务。使用敏捷术语的内置标准有助于缓解从传统工作方法到更精益流程的文化转变。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;主要优势:&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;行业标准 — 同样，就像许多工具一样，Jira到处都有使用。小型团队可以使用便宜的许可证并获得所需的一切，而企业可以为任何人负担得起许可证。&lt;/li&gt;
  &lt;li&gt;集成 - 在这个领域处于领先地位并且快速增长意味着第三方工具会选择您首先构建本机集成，而它们只会增加您工具的价值，而Jira就是这种情况。我们可以与现成的列表中的所有其他工具集成，而无需进行任何定制。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;竞争对手:&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Trello — Trello成为免费使用的看板工具，因此迅速流行。但是，一旦事情开始扩展，并且您从数十个问题扩展到数千个问题，Trello将变得难以导航，搜索和报告。&lt;/li&gt;
  &lt;li&gt;Pivotal Tracker - 在初创公司工作期间，我非常喜欢该工具。但是，他们更多地关注产品管理，而不是技术任务。尽管从Jira进行产品管理比较困难，但是仍然可以完成此过程，而不必获取完全独立的工具。&lt;/li&gt;
&lt;/ul&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h3&gt;2020年排名第一的ChatOps工具：MatterMost&lt;/h3&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhF0" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;说明：这可能是2020年这份清单上最大的惊喜，这是一件好事！MatterMost通过使用以前最好的工具，但引入了本地部署而获得了普及。对于企业来说，这是巨大的，因为它可以控制数据，还可以帮助与本地工具集成-我们不再需要为了新的事物而走出防火墙。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;主要优势:&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;开源 - MatterMost的开源版本非常适合小型或大型团队。与Slack的免费层会丢失历史记录不同，您自己运行服务器意味着您拥有数据。&lt;/li&gt;
  &lt;li&gt;集成 - 因为API几乎100％基于Slack API，所以几乎所有Slacks集成都可以直接与MatterMost一起使用。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;竞争对手:&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Slack - 松弛很棒，但是它们已经变得如此庞大，需要开始获利。他们业务的付费阶段即将到来，并且剥夺了Slack用来免费提供的许多价值，最关键的是聊天记录。&lt;/li&gt;
  &lt;li&gt;Microsoft Teams - 尝试将Microsoft产品与非Microsoft本地产品集成-祝您好运。这就是我要说的！&lt;/li&gt;
&lt;/ul&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;h3&gt;2020年排名第一的文档工具：Confluence&lt;/h3&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhF6" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;无论使用哪种工具，都很难创建和维护高质量的技术文档。尽管最近有许多SaaS文档工具进入市场，但我很难接受将有关关键应用程序的敏感技术文档存储给第三方。我需要将数据和文档保留在本地，这就是Confluence为我所做的。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;主要优势：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;易于管理 - 大多数自托管工具的启动和运行可能有些复杂，并且大规模维护它们需要一些特定知识。开箱即用的Confluence服务器非常适合10个用户或10,000个用户。&lt;/li&gt;
  &lt;li&gt;插件－ 尽管创建具有默认融合功能的漂亮且易于浏览的文档已经很不错了，但是拥有用于几乎所有内容的插件的能力释放了Wiki的潜力。&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;  &lt;strong&gt;竞争对手：&lt;/strong&gt;&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;Read the docs — 非常适合开源公共代码，但永远不会考虑在这里存储关键的应用程序知识。&lt;/li&gt;
  &lt;li&gt;MarkDown — 尽管非常适合于记录有关我的代码的内容，但很难将体系结构，过程或其他类型的文档直接放入MarkDown格式中。&lt;/li&gt;
  &lt;li&gt;Jekyll — 在记录技术知识时，我并不想简单地构建一个新的静态站点，以便在每次更改时进行部署。简单的Confluence版本管理系统使内部文档的处理变得更加容易。&lt;/li&gt;
&lt;/ul&gt;
 &lt;h2&gt;总结 2020 年最佳&lt;/h2&gt;
 &lt;p&gt;市场上实际上有数百种DevOps工具。试图浏览应使用哪些以及何时实施它们可能会令人不知所措。遵循此简单指南，为完整的CI / CD管道选择DevOps工具堆栈。&lt;/p&gt;
 &lt;p&gt;将工具分为以下五个关键领域：&lt;/p&gt;
 &lt;ul&gt;
  &lt;li&gt;开发和构建工具&lt;/li&gt;
  &lt;li&gt;自动化测试工具&lt;/li&gt;
  &lt;li&gt;部署工具&lt;/li&gt;
  &lt;li&gt;运行时工具&lt;/li&gt;
  &lt;li&gt;协作工具&lt;/li&gt;
&lt;/ul&gt;
 &lt;p&gt;最后希望大家切记：自动化所有能自动化的事情！&lt;/p&gt;
 &lt;hr&gt;&lt;/hr&gt;
 &lt;blockquote&gt;部分参考链接：  &lt;br /&gt;  &lt;a href="https://medium.com/better-programming/must-learn-devops-tools-for-2020-1a8a2675e88f" rel="nofollow noreferrer"&gt;1. 《Must Learn DevOps Tools for 2020》&lt;/a&gt;  &lt;br /&gt;  &lt;a href="https://tech.treebo.com/how-to-create-a-devops-roadmap-treebos-1-year-devops-journey-27c072001a28" rel="nofollow noreferrer"&gt;2. 《How to create a DevOps roadmap &amp;amp; Treebo’s 9 month DevOps Journey》&lt;/a&gt;  &lt;br /&gt;  &lt;a href="https://www.softwareone.com/en/solutions/publisher-advisory-services/aws/devops-with-aws" rel="nofollow noreferrer"&gt;3. 《DevOps with AWS》&lt;/a&gt;  &lt;br /&gt;  &lt;a href="https://devops.com/using-this-time-to-optimize-your-dev-teams-workflow/" rel="nofollow noreferrer"&gt;4. 《 Using this Time to Optimize Your Dev Team’s Workflow》&lt;/a&gt;
&lt;/blockquote&gt;
 &lt;p&gt;  &lt;img alt="clipboard.png" src="https://segmentfault.com/img/bVbIhGo" title="clipboard.png"&gt;&lt;/img&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>devops 工具软件</category>
      <guid isPermaLink="true">https://itindex.net/detail/60672-%E6%94%B6%E8%97%8F-devops-%E5%B7%A5%E5%85%B7</guid>
      <pubDate>Thu, 11 Jun 2020 20:59:14 CST</pubDate>
    </item>
    <item>
      <title>流量分析的瑞士军刀：Zeek</title>
      <link>https://itindex.net/detail/60577-%E6%B5%81%E9%87%8F-%E5%88%86%E6%9E%90-%E7%91%9E%E5%A3%AB%E5%86%9B%E5%88%80</link>
      <description>&lt;p&gt;  &lt;strong&gt;    Zeek (Bro) 是一款大名鼎鼎的开源网络安全分析工具。通过 Zeek 可以监测网络流量中的可疑活动，通过 Zeek 的脚本可以实现灵活的分析功能，可是实现多种协议的开相机用的分析。本文主要是将 Zeek 结合被动扫描器的一些实践的介绍，以及 Zeek 部署的踩过的一些坑。&lt;/strong&gt;&lt;/p&gt;
 &lt;h2&gt;    安装&lt;/h2&gt;
 &lt;p&gt;    Zeek 的安装还是比较简单的，笔者主要是在 Mac 上以及 Linux 上安装。这两个操作系统的安装方式还是比较类似的。对于 Linux 而言，需要安装一些依赖包：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;sudo yum install cmake make gcc gcc-c++ flex bison libpcap-devel openssl-devel python-devel swig zlib-devel&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    这里我有遇到一个问题就是可能你的 Redhat 镜像源里面没有包含 libpcap-devel，因为这个包在可选的范围内，而内网的服务器又没有互联网连接。可以通过手工下载相应版本的 libpcap 以及 libpcap-devel 即可。&lt;/p&gt;
 &lt;p&gt;    Mac 上需要的依赖更少一点，首先需要确保安装了 xcode-select，如果没有安装，可以通过 xcode-select –install 来进行安装。Mac 上只需要安装依赖 cmake, swig, openssl, bison 即可，可以通过 Homebrew 来进行安装。&lt;/p&gt;
 &lt;p&gt;    依赖包安装完毕之后就可以安装 Zeek，其实是可以通过包管理工具来进行安装的，不过这里我推荐使用基于源码的安装方式，安装比较简单而且还容易排查问题。从 Zeek 的 Github   &lt;a href="https://github.com/zeek/zeek/releases"&gt;Release&lt;/a&gt; 即可下载源码包，目前我安装的是 3.0.0 版本，注意一点是，如果使用最新的版本，可能需要 7.0 以上版本的 cmake，因为需要 C++17 的语言特性。而一般镜像源默认的 cmake 版本是4+版本，所以如果你的服务器也无法上互联网，建议可以安装 3.0.0 版本。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;./configure &amp;amp; make &amp;amp; make install&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    安装使用上面的命令就可以了，不过 make 的时间还是比较长的，这个取决于你机器的性能，不过一般安装还是需要半个小时到一个小时，这也是因为 C++ 编译速度比较慢的原因。&lt;/p&gt;
 &lt;h2&gt;    集群安装&lt;/h2&gt;
 &lt;p&gt;    集群安装的方式和单机的方式不太一样。之前在测试环境使用的都是单机模式，集群则可以管理多个实例，后来我也尝试了通过集群的方式来进行安装。如果需要配置集群的话，建议安装 PF_RING，PF_RING 可以加速网络包的速度。对于 Zeek 集群上的每个 worker 都是需要安装 PF_RING，但只需要在 manager 上安装 Zeek 就可以了，可以通过 zeekctl 在其它 worker 上安装 Zeek。不过需要确保可以通过 ssh 到其它 woker 机器上，可以通过公钥的形式来实现，将 manager 的公钥放到其它 worker 的 authorized_keys 中。&lt;/p&gt;
 &lt;p&gt;    PF_RING 的安装步骤相对来说多了一些，但也是按照说明安装即可。和上面的单机安装方式不同的是集群安装的方式的时候，安装 Zeek 需要配置前缀。&lt;/p&gt;
 &lt;p&gt;    安装 PF_RING&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;tar xvzf PF_RING-5.6.2.tar.gz
cd PF_RING-5.6.2/userland/lib
./configure --prefix=/opt/pfring
make install
cd ../libpcap
./configure --prefix=/opt/pfring
make install
cd ../tcpdump-4.1.1
./configure --prefix=/opt/pfring
make install
cd ../../kernel
make
make install
modprobe pf_ring enable_tx_capture=0 min_num_slots=32768&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    安装 Zeek&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;./configure --with-pcap=/opt/pfring
make 
make install&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    确保 Zeek 正确关联到了 PF_RING 中的 libpcap 库中&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;ldd /usr/local/zeek/bin/zeek | grep pcap
      libpcap.so.1 =&amp;gt; /opt/pfring/lib/libpcap.so.1 (0x00007fa6d7d24000)&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    接着就是通过 PF_RING 来进行 Zeekctl 的配置，Zeek 的安装路径一般都在 /usr/local/zeek。通过 /usr/local/zeek/etc/node.cfg 来进行集群结点的配置，在集群配置中，manager, proxy 以及 worker 是必须的，如果不设置 logger，默认将 manager 作为 logger。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;[worker-1]
type=worker
host=10.0.0.50
interface=eth0
lb_method=pf_ring
lb_procs=10
pin_cpus=2,3,4,5,6,7,8,9,10,11&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    接下来只需要通过 zeekctl install 就会在其它实例上来进行安装了。如果安装过程中出现了问题，可以通过 zeekctl diag woker-1 来排查具体的原因。&lt;/p&gt;
 &lt;h2&gt;    Zeek 结合被动扫描器的玩法&lt;/h2&gt;
 &lt;p&gt;    上面讲的都是 Zeek 的安装，下面聊一下 Zeek 和被动扫描器的结合。被动扫描器的效果往往取决于流量的质量和数量，在我们的实际实践中，发现通过 Zeek 获取的流量占我们被动扫描器测试流量的绝大一部分。Zeek 对于 http 解析的日志都会存储在 /usr/local/zeek/logs 中。如果 Zeek 是启动状态，那么 http.log 的路径会在 /usr/local/zeel/logs/current 中，而历史日志则会被打包。如果使用 Zeek 去捕获流量的时候，日志往往会占很大的存储，所以要记得修改 Zeek 日志的存储路径，否则很容易就把系统盘塞满。&lt;/p&gt;
 &lt;h3&gt;    通过脚本自定义 http.log&lt;/h3&gt;
 &lt;p&gt;    http.log 中其实已经包含了丰富的字段，常见的一些字段如下：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;# ts          uid          orig_h        orig_p  resp_h         resp_p
1311627961.8  HSH4uV8KVJg  192.168.1.100 52303   192.150.187.43 80&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    不过里面还有一些信息是缺失的，比如一些 http 请求头以及 POST 请求的请求体，为了添加这些字段，可以通过自定义 Zeek 脚本来实现，Zeek 脚本的能力真的非常强大，通过脚本其实有很多更高级的玩法。&lt;/p&gt;
 &lt;p&gt;      &lt;strong&gt;添加请求头&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;@load base/protocols/http/main
module HTTP;
export {
    redef record Info += {
            header_host:    string  &amp;amp;log    &amp;amp;optional;
            header_accept:  string  &amp;amp;log    &amp;amp;optional;
            header_accept_charset:  string  &amp;amp;log    &amp;amp;optional;
            header_accept_encoding:  string  &amp;amp;log    &amp;amp;optional;
            header_accept_language:  string  &amp;amp;log    &amp;amp;optional;
            header_accept_ranges:  string  &amp;amp;log    &amp;amp;optional;
            header_authorization:  string  &amp;amp;log    &amp;amp;optional;
            header_connection:  string  &amp;amp;log    &amp;amp;optional;
            header_cookie:  string  &amp;amp;log    &amp;amp;optional;
            header_content_length:  string  &amp;amp;log    &amp;amp;optional;
            header_content_type:  string  &amp;amp;log    &amp;amp;optional;
    };
}
event http_header(c: connection, is_orig: bool, name: string, value: string) &amp;amp;priority=3
        {
        if ( ! c?$http )
                return;
        if ( is_orig )
                {
                if ( log_client_header_names )
                        {
                switch ( name ) {
                                case &amp;quot;HOST&amp;quot;:
                                    c$http$header_host = value;
                                    break;
                                case &amp;quot;ACCEPT&amp;quot;:
                                    c$http$header_accept = value;
                                    break;
                                case &amp;quot;ACCEPT-CHARSET&amp;quot;:
                                    c$http$header_accept_charset = value;
                                    break;
                                case &amp;quot;ACCEPT-ENCODING&amp;quot;:
                                    c$http$header_accept_encoding = value;
                                    break;
                                case &amp;quot;ACCEPT-LANGUAGE&amp;quot;:
                                    c$http$header_accept_language = value;
                                    break;
                                case &amp;quot;ACCEPT-RANGES&amp;quot;:
                                    c$http$header_accept_ranges = value;
                                    break;
                                case &amp;quot;AUTHORIZATION&amp;quot;:
                                    c$http$header_authorization = value;
                                    break;
                                case &amp;quot;CONNECTION&amp;quot;:
                                    c$http$header_connection = value;
                                    break;
                                case &amp;quot;COOKIE&amp;quot;:
                                    c$http$header_cookie = value;
                                    break;
                                case &amp;quot;CONTENT-LENGTH&amp;quot;:
                                    c$http$header_content_length = value;
                                    break;
                                case &amp;quot;CONTENT-TYPE&amp;quot;:
                                    c$http$header_content_type = value;
                                    break;
                                }
            }
                }
        }&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;    添加 POST 请求体&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;export {
    ## The length of POST bodies to extract.
    const http_post_body_length = 200 &amp;amp;redef;
}
redef record HTTP::Info += {
    postdata: string &amp;amp;log &amp;amp;optional;
};
event log_post_bodies(f: fa_file, data: string)
    {
    for ( cid in f$conns )
        {
        local c: connection = f$conns[cid];
        if ( ! c$http?$postdata )
            c$http$postdata = &amp;quot;&amp;quot;;
        # If we are already above the captured size here, just return.
        if ( |c$http$postdata| &amp;gt; http_post_body_length )
            return;
        c$http$postdata = c$http$postdata + data;
        if ( |c$http$postdata| &amp;gt; http_post_body_length )
            {
            c$http$postdata = c$http$postdata[0:http_post_body_length] + &amp;quot;...&amp;quot;;
            }
        }
    }
event file_over_new_connection(f: fa_file, c: connection, is_orig: bool)
    {
    if ( is_orig &amp;amp;&amp;amp; c?$http &amp;amp;&amp;amp; c$http?$method &amp;amp;&amp;amp; c$http$method == &amp;quot;POST&amp;quot; )
        {
        Files::add_analyzer(f, Files::ANALYZER_DATA_EVENT, [$stream_event=log_post_bodies]);
        }
    }&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    通过上述的脚本就可以添加一些请求头以及 POST 请求的请求体，完整的脚本可以参考   &lt;a href="https://github.com/neal1991/http-custom"&gt;http-custom&lt;/a&gt;。脚本编写完毕，需要通过 zeekctl 部署才能生效，步骤也非常简单。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;mv http-custom /usr/local/bro/share/bro/base/protocols
echo &amp;apos;@load base/protocols/http-custom&amp;apos; &amp;gt;&amp;gt; /usr/local/bro/share/bro/site/local.bro
zeekctl deploy&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    对于被动扫描器，我们目前的方案是通过 Filebeat 去采集日志然后输出给 Logstash 做处理，处理完毕之后再输出到 Kafka。&lt;/p&gt;
 &lt;p&gt;      &lt;img src="https://image.3001.net/images/20200429/1588149952_5ea93ec061aba.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;    Filebeat 加 Logstash 适用于多种场景，在日常的各种日志采集场景都能派上用场。通过 Logstash 可以完成日志灵活的处理，因为 Logstash 里面包含了各种丰富的插件，几乎可以完成对于日志的任何操作。比如为了保证 POST 请求体保证传输的正确性，可以通过 base64 来进行编码。通过   &lt;a href="https://github.com/tiwilliam/logstash-filter-base64"&gt;logstash-filter-base64&lt;/a&gt; 可以遍历地实现字段的编码或者解码。通过 filter 中的 mutate 插件可以增加字段或者删除字段。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;base64 {
     field =&amp;gt; &amp;quot;postdata&amp;quot;
     action =&amp;gt; &amp;quot;encode&amp;quot;
   }&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    通过这种方案还有一个优势就是我们还可以将我们的日志输出到别的地方，比如 es，这个也可以方便后续排查日志问题。&lt;/p&gt;
 &lt;p&gt;    不过我在后面又发现了一种新的方案，可以通过 Zeek 的插件，将 http.log 直接输出到 Kafka，这个方案的优点主要是更高效，同时也节省了一些成本，毕竟 Logstash 需要的机器性能还是比较大的。对于这个方案主要是两个问题，第一个问题是首先需要处理好日志的格式，这样保证后续处理地便利性；第二个问题是如何将日志直接从 Zeek 输出到 Kafka。其实我是先解决了第一个问题再解决第二个问题的，因为第二个问题的处理的方式更灵活，得益于 Zeek 脚本的便利性，肯定是可以实现的。&lt;/p&gt;
 &lt;p&gt;      &lt;img src="https://image.3001.net/images/20200429/1588149963_5ea93ecb4cdad.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;      &lt;a href="https://github.com/apache/metron-bro-plugin-kafka"&gt;metron-bro-plugin-kafka&lt;/a&gt; 是 Apache 官方的一个 Bro 的插件，不过因为 Zeek3.0.0 是可以兼容的，所以这个插件是可以使用的。这个插件有两种安装方式，一种是通过 bro-pkg (Bro 的官方包管理工具)来进行安装，另外一种则是通过手工安装。由于网络的原因，我更推荐使用手工安装的方式，我尝试通过 bro-pkg 的方式来进行安装，速度特别慢。&lt;/p&gt;
 &lt;p&gt;安装 librdkafka&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;curl -L https://github.com/edenhill/librdkafka/archive/v0.11.5.tar.gz | tar xvz   &lt;br /&gt;cd librdkafka-0.11.5/
./configure --enable-sasl
make
sudo make install &lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;安装插件&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;./configure --bro-dist=$BRO_SRC
make
sudo make install&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    这里有一个坑就是安装文档根本就没有说 $BRO_SRC 是哪个路径，所以安装的时候总是报错，后来才弄清楚这个路径其实就是当初 Zeek 解压后的路径，即 Zeek 安装包的路径。&lt;/p&gt;
 &lt;p&gt;验证结果&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;zeek -N Apache::Kafka
Apache::Kafka - Writes logs to Kafka (dynamic, version 0.3)&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;    接着就是将 http 的日志进行处理，因为在原始的 http.log 中有还多字段是我们并不需要的。在研究了官方文档之后，可以通过   &lt;a href="https://docs.zeek.org/en/master/frameworks/logging.html#filters"&gt;Filters&lt;/a&gt; 可以定义一个新的日志文件，可以拷贝其它的日志输出到新的文件，可以自定义字段，方式比较灵活。另外还可以通过 Writer 可以将日志写入到 sqlite 数据库中。不过，这里我们主要是通过插件将日志写入到 Kafka。&lt;/p&gt;
 &lt;h2&gt;    总结&lt;/h2&gt;
 &lt;p&gt;    其实 Zeek 有很多高级玩法，你完全可以将 Zeek 改造成一个 IDS 产品。Zeek 脚本的强大能力赋予其无限的可能性，比如在流量中发现 sql 注入。本文主要就是就 Zeek 的安装部署以及结合被动扫描器的一些用法的介绍。后续如果更进一步地探索，会做更多的分享。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;*本文作者：madneal@平安银行应用安全团队，转载 请注明来自FreeBuf.COM&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;    00&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>工具 Zeek 流量 瑞士军刀</category>
      <guid isPermaLink="true">https://itindex.net/detail/60577-%E6%B5%81%E9%87%8F-%E5%88%86%E6%9E%90-%E7%91%9E%E5%A3%AB%E5%86%9B%E5%88%80</guid>
      <pubDate>Fri, 08 May 2020 16:00:18 CST</pubDate>
    </item>
    <item>
      <title>XSSFORK：新一代XSS自动扫描测试工具</title>
      <link>https://itindex.net/detail/60246-xssfork-xss-%E6%B5%8B%E8%AF%95</link>
      <description>&lt;p&gt;  &lt;strong&gt;什么是XSS漏洞呢 ？&lt;/strong&gt;&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;XSS（Cross-site scripting）译为跨站脚本攻击，在日常的web渗透测试当中，是最常见的攻击方法之一，并占有很高的地位。它是通过对网页注入可执行代码且成功地被浏览器 执行，达到攻击的目的，形成了一次有效XSS攻击，一旦攻击成功，它可以获取用户的联系人列表，然后向联系人发送虚假诈骗信息，可以删除用户的日志等等，有时候还和其他攻击方式同时实 施比如SQL注入攻击服务器和数据库、Click劫持、相对链接劫持等实施钓鱼，它带来的危害是巨 大的，是web安全的头号大敌。&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;  &lt;strong&gt;传统的 xss 探测工具：&lt;/strong&gt;&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;一般都是采用 payload in response 的方式，即在发送一次带有 payload 的 http 请求后，通过检测响应包中 payload 的完整性来判断，这种方式缺陷，很多。&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20191218/1576659332_5df9e9848ba88.jpg!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;0×001 前言&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;xssfork是一款新一代xss漏洞探测工具，其开发的目的是帮助安全从业者高效率的检测xss安全漏洞。与传统检测工具相比xssfork使用的是 webkit内核的浏览器phantomjs，其可以很好的模拟浏览器。工具分为两个部分，xssfork和xssforkapi，其中xssfork在对网站fuzz xss的时候会调用比较多的payload。话不多说，一起来研究下这款工具吧 ？&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;github地址：  &lt;a href="https://github.com/bsmali4/xssfork"&gt;https://github.com/bsmali4/xssfork&lt;/a&gt;&lt;/p&gt;
 &lt;h2&gt;0×002 环境依赖&lt;/h2&gt;
 &lt;p&gt;Python 2.x&lt;/p&gt;
 &lt;p&gt;相关python库（存在于项目requestments.txt中）&lt;/p&gt;
 &lt;h2&gt;0×003安装教程&lt;/h2&gt;
 &lt;blockquote&gt;  &lt;p&gt;git clone    &lt;a href="https://github.com/bsmali4/xssfork"&gt;https://github.com/bsmali4/xssfork&lt;/a&gt;&lt;/p&gt;
  &lt;p&gt;python2 -m pip install -rrequestments.txt&lt;/p&gt;
  &lt;p&gt;python2 xssfork.py -h&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;出现以下显示，代表安装成功&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20191218/1576661179_5df9f0bb7cb5d.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;0×004内置Payload&lt;/h2&gt;
 &lt;p&gt;工具的开发者收集了目前流行的xss payload，丰富的一批欧（目前内置存在的payload数量为70个），payload文件存在于xssfork\thirdparty\fuzz_dic\payloads.dic文件里面&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20191218/1576681249_5dfa3f21748a1.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;并且会添加上各种闭合的情况&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20191218/1576681925_5dfa41c5bfe4a.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;0×005内置编码方式&lt;/h2&gt;
 &lt;p&gt;为了进行更加智能的进行测试，作者在测试时同时加入了绕过方式，提供了四种编码方式，供大家进行使用&lt;/p&gt;
 &lt;p&gt;现阶段提供了10进制，16进制，随机大小写，关键字叠加四个脚本&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;[0]10hex_encode 10进制&lt;/p&gt;
  &lt;p&gt;[1]16hex_encode 16进制&lt;/p&gt;
  &lt;p&gt;[2]addkeywords 关键字叠加&lt;/p&gt;
  &lt;p&gt;[3]uppercase 随机大小写&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;查看命令为：python xssfork.py –list&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20191218/1576681990_5dfa42062f593.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;使用编码命令为：-t 脚本名称即可&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20191218/1576682096_5dfa427079353.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;0×006 使用场景&lt;/h2&gt;
 &lt;p&gt;场景1 反射型xss&lt;/p&gt;
 &lt;p&gt;操作命令如下：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;python2 xssfork.py -u &amp;quot;http://xssfork.codersec.net/xssdemo.php?id=23&amp;quot;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20191219/1576685018_5dfa4dda82def.gif!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;场景2 带大小写绕过&lt;/p&gt;
 &lt;p&gt;操作命令如下：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;python2 xssfork.py -u &amp;quot;http://xssfork.codersec.net/xssdemo.php?id=23&amp;quot; -t uppercase&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20191219/1576685535_5dfa4fdf1a2b7.gif!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20191219/1576685726_5dfa509ece9c9.gif!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;场景3 dom型xss&lt;/p&gt;
 &lt;p&gt;操作命令如下：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;python2 xssfork.py -u &amp;quot;http://xssfork.codersec.net/xssdemo.php?id=23&amp;quot;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20191219/1576686624_5dfa54203cc8e.gif!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;场景4 post型xss&lt;/p&gt;
 &lt;p&gt;操作命令如下：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;python2 -u &amp;quot;http://xssfork.codersec.net/xssdemo.php&amp;quot; -d &amp;quot;name=123&amp;quot;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20191219/1576685891_5dfa51437289b.gif!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;场景5 验证cookie型xss&lt;/p&gt;
 &lt;p&gt;操作命令如下：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;python2 xssfork.py -u &amp;quot;http://xssfork.codersec.net/xssdemo.php?id=23&amp;quot; -c &amp;quot;user=fdsfds;pass=123&amp;quot;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20191219/1576686371_5dfa532373fd2.gif!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20191219/1576686457_5dfa5379ac211.gif!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20191219/1576686511_5dfa53afc9667.gif!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;总结&lt;/h2&gt;
 &lt;p&gt;xssfork感觉还是很不错的工具，希望能够在工作中给你一些帮助，最后感谢工具作者！谢谢&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;*本文作者：fuckerbox，转载请注明来自FreeBuf.COM&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>工具 xss XSSFORK 扫描测试工具</category>
      <guid isPermaLink="true">https://itindex.net/detail/60246-xssfork-xss-%E6%B5%8B%E8%AF%95</guid>
      <pubDate>Thu, 26 Dec 2019 15:00:05 CST</pubDate>
    </item>
    <item>
      <title>Pfsense：免费开源的应用层防火墙</title>
      <link>https://itindex.net/detail/60082-pfsense-%E5%85%8D%E8%B4%B9-%E5%BC%80%E6%BA%90</link>
      <description>&lt;p&gt;  &lt;img src="https://image.3001.net/images/20190920/1568945470_5d84353e8d5ae.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;前言&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;一个公司的安全建设需要大量的物力、人力，由于网络安全在企业没有实际利益产出，常常在企业网络安全建设中，没有足够的投入（废话，投入没有经济效益回报，为什么要投入），但是企业网络安全建设不只是针对经济效益，它更像是一种保险（等出了事情，数据丢失，被网安抓典型处理），它更是一种对主业务的辅助（信息化辅助生产，甚至代替生产），但巧妇难为无米之炊，要对企业内网形成一套完整的纵深防御体系需要许多的商业化解决方案，无奈呼信息安全部门在一些企业中就是一个背锅的角色，为了能把锅抗好。。嗯，还是最好没锅吧！毕竟真的扛不动&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;闲话少说，纵深防护体系的第一道就是下一代防火墙，下面是下一代防火墙的简单配置介绍：&lt;/p&gt;
 &lt;h2&gt;一、说明&lt;/h2&gt;
 &lt;h3&gt;1.1 pfSense是什么&lt;/h3&gt;
 &lt;p&gt;pfSense是基于FreeBSD的、开源中最为可靠（World’s Most Trusted Open Source Firewall）的、可与商业级防火墙一战（It has successfully replaced every big name commercial firewall youcan imagine in numerous installations around the world）的防火墙。&lt;/p&gt;
 &lt;p&gt;简单点说pfSense就是一个操作系统形式的防火墙。更多介绍见：  &lt;a href="https://www.pfsense.org/getting-started/"&gt;https://www.pfsense.org/getting-started/&lt;/a&gt;&lt;/p&gt;
 &lt;h2&gt;二、pfSense安装&lt;/h2&gt;
 &lt;p&gt;整个安装过程也不是很复杂，官方安装文档见：  &lt;a href="https://www.netgate.com/docs/pfsense/install/installing-pfsense.html"&gt;https://www.netgate.com/docs/pfsense/install/installing-pfsense.html&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;在虚拟机中安装使用（操作系统）freedbs 11 进行安装&lt;/p&gt;
 &lt;h3&gt;2.1下载&lt;/h3&gt;
 &lt;p&gt;下载地址：  &lt;a href="https://www.pfsense.org/download/"&gt;https://www.pfsense.org/download/&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;下载镜像iso即可&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568893523_5d836a535d4a9.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h3&gt;2.2安装&lt;/h3&gt;
 &lt;p&gt;首选创建一个虚拟机，其他的都无所谓但是系统一定要FreeBSD。&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568895085_5d83706d58c88.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568893767_5d836b47760b1.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568895161_5d8370b947cc1.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568895203_5d8370e3d2467.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568895245_5d83710de7426.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;到这步后所有的选项都是直接点击下一步，虚拟机创建完成后点击编辑虚拟机配置，添加网卡&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568895713_5d8372e18caa6.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;因为防火墙至少需要两块网卡，所以需要创建至少2块网卡，一个网卡作为lan口，一个网卡作为wan口（也可以多个网卡），我们留一个网卡作为wan口（NAT模式），一个作为lan口（主机模式或者自定义），登录界面配置在lan口上，网卡只要启动两块就好了。&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568895825_5d8373514bccc.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568896358_5d8375662dba0.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568896222_5d8374de156db.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;将ISO文件配置到虚拟光驱中，开启电源&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568896424_5d8375a857f39.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;回车接受版权声明&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568896541_5d83761dc4ec5.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;回车确认安装&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568896634_5d83767a8301f.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;选择键盘格式，默认是美国标准键盘，直接回车&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568896685_5d8376adb5b7e.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;选择文件系统，回车选择UFS，此步之后正式安装操作系统&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568896746_5d8376ea2c38d.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568896789_5d8377156fa9e.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;直接回车选择重启完成安装&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568896827_5d83773b74793.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568896917_5d83779548648.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;三、pfSense配置&lt;/h2&gt;
 &lt;h3&gt;3.1 设置pfsense接口&lt;/h3&gt;
 &lt;p&gt;重启完成后会进入以下界面&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190920/1568943403_5d842d2ba3b94.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;不设置vlan（透明传输使用），设置wan口网卡，设置lan口网卡，最后输入Y进入系统&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190920/1568943499_5d842d8b0a2cb.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;查看自己设置的网卡的网段le0 口因为使用dhcp直接通过即可，le1口作为lan口，必须使用le1口网卡的网段地址（主机模式下的DHCP分配期间地址）&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190920/1568943720_5d842e68d181d.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;设置接口地址，设置wan和lan的地址（只需要设置一个就可以，设置完成后需要将web界面到底是使用哪一个地址进行登录），在控制界面按 2 进入地址设置界面，设置一个lan口地址（设置为192.168.192.0/24 网段的地址），关闭dhcp（避免IP地址冲突），设置lan口为web管理口地址&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190920/1568943906_5d842f22edd7d.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;设置完成后就可以登录了，通过上边14启动sshd后使用admin/pfsense也可以ssh登录操作系统&lt;/p&gt;
 &lt;p&gt;SSH账户名密码：admin/pfsense&lt;/p&gt;
 &lt;h3&gt;3.2 web管理界面&lt;/h3&gt;
 &lt;p&gt;访问设置为web登录的网卡地址，进行登录，默认用户名密码admin/pfsense&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190920/1568943970_5d842f6279c3c.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;第一次登录后有有几步主机名、dns、时区等相关初始化向导，看着填不会就直接next&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568894624_5d836ea02098e.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;最后finish&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190919/1568894638_5d836eae453f7.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;点击finish大约30秒后进入该界面&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190920/1568944506_5d84317a1b722.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;设置完成后设置中文显示，依次点击SystemGeneral&amp;gt;Setup&amp;gt;Language，在下拉框中找到Simplified,china，选择之后点击该页面最下方的save&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190920/1568944623_5d8431efb5c10.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;接下来就可以按照正常防火墙进行配置了&lt;/p&gt;
 &lt;p&gt;最后：在非常苛刻的条件下，可以使用三层路由的方式，使用各种开源的系统级防护系统，通过多个不同的主机网络将所有的设备串联起来，放置于网络的出口，以便进行内网安全防护，嗯，就是这样（说多了都是泪，都是被逼的。）&lt;/p&gt;
 &lt;p&gt;最后送上一些基础软件&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;vmware 15&lt;/strong&gt;&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;链接：   &lt;a href="https://pan.baidu.com/s/1q0janMOCtIFbiApRfEgrfA"&gt;https://pan.baidu.com/s/1q0janMOCtIFbiApRfEgrfA&lt;/a&gt;&lt;/p&gt;
  &lt;p&gt;提取码：hqou&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;  &lt;strong&gt;pfsense&lt;/strong&gt;&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;链接：   &lt;a href="https://pan.baidu.com/s/10Va4z6f69FkCjpY29X0JNA"&gt;https://pan.baidu.com/s/10Va4z6f69FkCjpY29X0JNA&lt;/a&gt;&lt;/p&gt;
  &lt;p&gt;提取码：7o1w&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;  &lt;strong&gt;*本文作者：寂静尘埃，转载请注明来自FreeBuf.COM&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>工具 PfSense 配置介绍 防火墙</category>
      <guid isPermaLink="true">https://itindex.net/detail/60082-pfsense-%E5%85%8D%E8%B4%B9-%E5%BC%80%E6%BA%90</guid>
      <pubDate>Sat, 12 Oct 2019 15:00:20 CST</pubDate>
    </item>
    <item>
      <title>分布式向量搜索系统 Vearch</title>
      <link>https://itindex.net/detail/60076-%E5%88%86%E5%B8%83-%E5%90%91%E9%87%8F-%E6%90%9C%E7%B4%A2</link>
      <description>&lt;table width="100%"&gt;
                              &lt;tr&gt;
                                                                       &lt;td valign="top" width="100"&gt;
                                            &lt;a href="https://www.oschina.net/p/vearch"&gt;     &lt;img border="0" src="https://static.oschina.net/uploads/logo/vearch_1VJ5j.png"&gt;&lt;/img&gt;&lt;/a&gt;
                                    &lt;/td&gt;
                                                                   &lt;td valign="top"&gt;Vearch 是一个分布式向量搜索系统，可以用来计算向量相似度，或用于机器学习领域，如：图像识别、视频识别或自然语言处理等各个领域。 本系统基于 Faiss 实现， 提供了快速的向量检索功能。 提供类似 Elasticsearch 的 Restful API 可以方便地对数据及表结构进行管理查询等工作。 架构&lt;/td&gt;
                            &lt;/tr&gt;
                        &lt;/table&gt;
                        &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>数据库管理工具</category>
      <guid isPermaLink="true">https://itindex.net/detail/60076-%E5%88%86%E5%B8%83-%E5%90%91%E9%87%8F-%E6%90%9C%E7%B4%A2</guid>
      <pubDate>Thu, 10 Oct 2019 16:04:26 CST</pubDate>
    </item>
    <item>
      <title>ModSecurity：一款优秀的开源WAF</title>
      <link>https://itindex.net/detail/60023-modsecurity-%E5%BC%80%E6%BA%90-waf</link>
      <description>&lt;p&gt;  &lt;img alt="" src="https://image.3001.net/images/20190814/1565774841_5d53d3f9c2643.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;  &lt;strong&gt;一、ModSecurity3.0介绍&lt;/strong&gt;&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;ModSecurity是一个开源的跨平台Web应用程序防火墙（WAF）引擎，用于Apache，IIS和Nginx，由Trustwave的SpiderLabs开发。作为WAF产品，ModSecurity专门关注HTTP流量，当发出HTTP请求时，ModSecurity检查请求的所有部分，如果请求是恶意的，它会被阻止和记录。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;优势：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;完美兼容nginx，是nginx官方推荐的WAF&lt;/p&gt;
  &lt;p&gt;支持OWASP规则&lt;/p&gt;
  &lt;p&gt;3.0版本比老版本更新更快，更加稳定，并且得到了nginx、Inc和Trustwave等团队的积极支持&lt;/p&gt;
  &lt;p&gt;免费&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;ModSecurity的功能：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;SQL Injection (SQLi)：阻止SQL注入&lt;/p&gt;
  &lt;p&gt;Cross Site Scripting (XSS)：阻止跨站脚本攻击&lt;/p&gt;
  &lt;p&gt;Local File Inclusion (LFI)：阻止利用本地文件包含漏洞进行攻击&lt;/p&gt;
  &lt;p&gt;Remote File Inclusione(RFI)：阻止利用远程文件包含漏洞进行攻击&lt;/p&gt;
  &lt;p&gt;Remote Code Execution (RCE)：阻止利用远程命令执行漏洞进行攻击&lt;/p&gt;
  &lt;p&gt;PHP Code Injectiod：阻止PHP代码注入&lt;/p&gt;
  &lt;p&gt;HTTP Protocol Violations：阻止违反HTTP协议的恶意访问&lt;/p&gt;
  &lt;p&gt;HTTPoxy：阻止利用远程代理感染漏洞进行攻击&lt;/p&gt;
  &lt;p&gt;Shellshock：阻止利用Shellshock漏洞进行攻击&lt;/p&gt;
  &lt;p&gt;Session Fixation：阻止利用Session会话ID不变的漏洞进行攻击&lt;/p&gt;
  &lt;p&gt;Scanner Detection：阻止黑客扫描网站&lt;/p&gt;
  &lt;p&gt;Metadata/Error Leakages：阻止源代码/错误信息泄露&lt;/p&gt;
  &lt;p&gt;Project Honey Pot Blacklist：蜜罐项目黑名单&lt;/p&gt;
  &lt;p&gt;GeoIP Country Blocking：根据判断IP地址归属地来进行IP阻断&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;劣势：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;不支持检查响应体的规则，如果配置中包含这些规则，则会被忽略，nginx的的sub_filter指令可以用来检查状语从句：重写响应数据，OWASP中相关规则是95X。&lt;/p&gt;
  &lt;p&gt;不支持OWASP核心规则集DDoS规则REQUEST-912-DOS- PROTECTION.conf,nginx本身支持配置DDoS限制&lt;/p&gt;
  &lt;p&gt;不支持在审计日志中包含请求和响应主体&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h2&gt;  &lt;strong&gt;二、安装部署&lt;/strong&gt;&lt;/h2&gt;
 &lt;p&gt;测试环境：centOS7.6阿里云镜像&lt;/p&gt;
 &lt;p&gt;升级软件和内核&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;yum update&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;安装nginx：   &lt;a href="http://nginx.org/en/linux_packages.html#mainline"&gt;http://nginx.org/en/linux_packages.html#mainline&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;   &lt;p&gt;yum install yum-utils&lt;/p&gt;   &lt;p&gt;vim /etc/yum.repos.d/nginx.repo&lt;/p&gt;   &lt;p&gt;[nginx-stable]&lt;/p&gt;   &lt;p&gt;name=nginx stable repo&lt;/p&gt;   &lt;p&gt;baseurl=    &lt;a href="http://nginx.org/packages/centos/$releasever/$basearch/"&gt;http://nginx.org/packages/centos/$releasever/$basearch/&lt;/a&gt;&lt;/p&gt;   &lt;p&gt;gpgcheck=1&lt;/p&gt;   &lt;p&gt;enabled=1&lt;/p&gt;   &lt;p&gt;gpgkey=    &lt;a href="https://nginx.org/keys/nginx_signing.key"&gt;https://nginx.org/keys/nginx_signing.key&lt;/a&gt;&lt;/p&gt;   &lt;p&gt;[nginx-mainline]&lt;/p&gt;   &lt;p&gt;name=nginx mainline repo&lt;/p&gt;   &lt;p&gt;baseurl=    &lt;a href="http://nginx.org/packages/mainline/centos/$releasever/$basearch/"&gt;http://nginx.org/packages/mainline/centos/$releasever/$basearch/&lt;/a&gt;&lt;/p&gt;   &lt;p&gt;gpgcheck=1&lt;/p&gt;   &lt;p&gt;enabled=0&lt;/p&gt;   &lt;p&gt;gpgkey=    &lt;a href="https://nginx.org/keys/nginx_signing.key"&gt;https://nginx.org/keys/nginx_signing.key&lt;/a&gt;&lt;/p&gt;   &lt;p&gt;yum install nginx&lt;/p&gt;   &lt;p&gt;yum install epel-release&lt;/p&gt;   &lt;p&gt;yum install gcc-c++ flex bison yajl yajl-devel curl-devel curl GeoIP-devel doxygen zlib-devel pcre pcre-devel libxml2 libxml2-devel autoconf automake lmdb-devel ssdeep-devel ssdeep-libs lua-devel libmaxminddb-devel git apt-utils autoconf automake build-essential git libcurl4-openssl-dev libgeoip-dev liblmdb-dev ibpcre++-dev libtool libxml2-dev libyajl-dev pkgconf wget zlib1g-dev&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;报错解决：Error: Cannot retrieve metalink for repository: epel. Please verify its path and try again&lt;/p&gt;
 &lt;p&gt;解决办法：一句话：把/etc/yum.repos.d/epel.repo，文件第3行注释去掉，把第四行注释掉，修改为&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;1. [epel]&lt;/p&gt;
  &lt;p&gt;2. name=Extra Packages for Enterprise Linux 6 – $basearch&lt;/p&gt;
  &lt;p&gt;3. baseurl=   &lt;a href="http://download.fedoraproject.org/pub/epel/6/"&gt;http://download.fedoraproject.org/pub/epel/6/&lt;/a&gt;$basearch&lt;/p&gt;
  &lt;p&gt;4. #mirrorlist=   &lt;a href="https://mirrors.fedoraproject.org/metalink?repo=epel-6&amp;arch="&gt;https://mirrors.fedoraproject.org/metalink?repo=epel-6&amp;amp;arch=&lt;/a&gt;$basearch&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;克隆GitHub存储库:&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;git clone --depth 1 -b v3/master --single-branch    &lt;a href="https://github.com/SpiderLabs/ModSecurity"&gt;https://github.com/SpiderLabs/ModSecurity&lt;/a&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;a href="https://github.com/SpiderLabs/ModSecurity"&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;编译源代码：&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;   &lt;p&gt;$ cd ModSecurity&lt;/p&gt;   &lt;p&gt;$ git submodule init&lt;/p&gt;   &lt;p&gt;$ git submodule update&lt;/p&gt;   &lt;p&gt;$ ./build.sh&lt;/p&gt;   &lt;p&gt;$ ./configure&lt;/p&gt;   &lt;p&gt;$ make&lt;/p&gt;   &lt;p&gt;$ make install&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;注意：安装中有报错fatal: No names found, cannot describe anything.是正常现象&lt;/p&gt;
 &lt;p&gt;下载用于ModSecurity的NGINX连接器：&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;git clone --depth 1    &lt;a href="https://github.com/SpiderLabs/ModSecurity-nginx.git"&gt;https://github.com/SpiderLabs/ModSecurity-nginx.git&lt;/a&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;a href="https://github.com/SpiderLabs/ModSecurity-nginx.git"&gt;&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;确定哪个版本的NGINX是运行在主机上的ModSecurity模块将加载:&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;   &lt;p&gt;[root@guigu ModSecurity]# nginx -v&lt;/p&gt;   &lt;p&gt;nginx version: nginx/1.17.3&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;下载与安装版本对应的源代码：&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;   &lt;p&gt;wget     &lt;a href="http://nginx.org/download/nginx-1.17.3.tar.gz"&gt;http://nginx.org/download/nginx-1.17.3.tar.gz&lt;/a&gt;&lt;/p&gt;   &lt;p&gt;tar zxvf nginx-1.17.3.tar.gz&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;编译动态模块，复制到模块标准目录:&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;   &lt;p&gt;cd nginx-1.17.3&lt;/p&gt;   &lt;p&gt;#./configure --with-compat --add-dynamic-module=../ModSecurity-nginx&lt;/p&gt;   &lt;p&gt;$ make modules&lt;/p&gt;   &lt;p&gt;cp objs/ngx_http_modsecurity_module.so /etc/nginx/modules/&lt;/p&gt;   &lt;p&gt;将以下load_module指令添加到/etc/nginx/nginx.conf的main中：&lt;/p&gt;   &lt;p&gt;load_module modules/ngx_http_modsecurity_module.so;&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;确定nginx模块加载成功：&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;nginx -t&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;h2&gt;  &lt;strong&gt;三、防护效果测试&lt;/strong&gt;&lt;/h2&gt;
 &lt;p&gt;ModSecurity 3简单示例&lt;/p&gt;
 &lt;p&gt;创建Demo web应用vim /etc/nginx/nginx.conf&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;   &lt;p&gt;server {&lt;/p&gt;   &lt;p&gt;listen 8085;    &lt;br /&gt;
location / {

    default_type text/plain;

    return 200 &amp;quot;Thank you for requesting ${request_uri}\n&amp;quot;;

    }
&lt;/p&gt;   &lt;p&gt;}&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;重新加载nginx:nginx -s reload&lt;/p&gt;
 &lt;p&gt;确认nginx正常工作:curl -D –   &lt;a href="http://localhost/"&gt;http://localhost&lt;/a&gt;&lt;/p&gt;
 &lt;p&gt;保护Demo web应用&lt;/p&gt;
 &lt;p&gt;创建/etc/nginx/modsec文件夹：mkdir /etc/nginx/modsec&lt;/p&gt;
 &lt;p&gt;下载推荐的ModSecurity配置文件&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;   &lt;p&gt;wget     &lt;a href="https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v3/master/modsecurity.conf-recommended"&gt;https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v3/master/modsecurity.conf-recommended&lt;/a&gt;&lt;/p&gt;   &lt;p&gt;mv modsecurity.conf-recommended modsecurity.conf&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;vim modsecurity.conf    #在些文件中编辑以下配置&lt;/p&gt;
 &lt;h3&gt;SecRuleEngine DetectionOnly&lt;/h3&gt;
 &lt;p&gt;SecRuleEngine On&lt;/p&gt;
 &lt;p&gt;创建ModSecurity的主配置文件&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;vim /etc/nginx/modsec/main.conf&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;h3&gt;Include the recommended configuration&lt;/h3&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;Include /etc/nginx/modsec/modsecurity.conf&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;h3&gt;A test rule&lt;/h3&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;SecRule ARGS:testparam &amp;quot;@contains test&amp;quot; &amp;quot;id:1234,deny,log,status:403&amp;quot;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;报错解决：[emerg] “modsecurity_rules_file” directive Rules error.&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;   &lt;p&gt;vim /etc/nginx/modsec/modsecurity.conf&lt;/p&gt;   &lt;p&gt;#SecUnicodeMapFile unicode.mapping 20127&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;配置nginx反向代理，vim /etc/nginx/conf.d/proxy.conf&lt;/p&gt;
 &lt;p&gt;#include /etc/nginx/conf.d/*.conf;    #把这一行注释掉，不然80端口会有冲突&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;   &lt;p&gt;server {&lt;/p&gt;   &lt;p&gt;    listen 80;    &lt;br /&gt;    &lt;br /&gt;    modsecurity on;

    modsecurity_rules_file /etc/nginx/modsec/main.conf;

    location / {

    proxy_pass [http://0.0.0.0:8085;](http://0.0.0.0:8085/)

    proxy_set_header Host $host;

    }&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;   &lt;p&gt;nginx -s reload    #重新加载nginx&lt;/p&gt;   &lt;p&gt;curl -D -     &lt;a href="http://localhost/foo?testparam=123"&gt;http://localhost/foo?testparam=123&lt;/a&gt;    #能正常返回“Thank you for requesting /foo?testparam=123”&lt;/p&gt;   &lt;p&gt;&lt;/p&gt;   &lt;p&gt;&lt;/p&gt;   &lt;p&gt;curl -D -     &lt;a href="http://localhost/foo?testparam=123"&gt;http://localhost/foo?testparam=&lt;/a&gt;test    #则返回&amp;quot;403 Forbidden&amp;quot;，说明前面配置的那条modsecuriy规则生效了，并阻拦了testparam参数中带test的请求&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;在/var/log/nginx/error.log中可以看到拦截的详细日志&lt;/p&gt;
 &lt;p&gt;部署OWASP规则–CRS（Core Rule Set）&lt;/p&gt;
 &lt;p&gt;安装运行nikto漏洞扫描工具，用于测试CRS的防御效果&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;   &lt;p&gt;&lt;/p&gt;   &lt;p&gt;git clone     &lt;a href="https://github.com/sullo/nikto"&gt;https://github.com/sullo/nikto    &lt;/a&gt;#下载nikto&lt;/p&gt;   &lt;p&gt;cd nikto &lt;/p&gt;   &lt;p&gt;perl program/nikto.pl -h localhost    #用nikto扫描nginx搭建的web系统（反向代理）&lt;/p&gt;   &lt;p&gt;扫描结果是+ 7687 requests: 0 error(s) and 308 item(s) reported on remote host    #扫描出308个问题&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;启用OWASP CRS&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;   &lt;p&gt;cd /etc/nginx/modsec/&lt;/p&gt;   &lt;p&gt;wget     &lt;a href="https://github.com/SpiderLabs/owasp-modsecurity-crs/archive/v3.0.2.tar.gz"&gt;https://github.com/SpiderLabs/owasp-modsecurity-crs/archive/v3.0.2.tar.gz    &lt;/a&gt;#下载OWASP CRS&lt;/p&gt;   &lt;p&gt;cd owasp-modsecurity-crs-3.0.2/&lt;/p&gt;   &lt;p&gt;cp crs-setup.conf.example crs-setup.conf&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;在modsecurity主配置文件中include CRS的配置和规则&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;vim /etc/nginx/modsec/main.conf&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;h3&gt;Include the recommended configuration&lt;/h3&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;Include /etc/nginx/modsec/modsecurity.conf&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;h3&gt;OWASP CRS v3 rules&lt;/h3&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;   &lt;p&gt;Include /usr/local/owasp-modsecurity-crs-3.0.2/crs-setup.conf&lt;/p&gt;   &lt;p&gt;Include /usr/local/owasp-modsecurity-crs-3.0.2/rules/*.conf&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;测试CRS&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;   &lt;p&gt;nginx -s reload    #重新加载nginx配置&lt;/p&gt;   &lt;p&gt;curl     &lt;a href="http://localhost/"&gt;http://localhost    &lt;/a&gt;#返回Thank you for requesting /&lt;/p&gt;   &lt;p&gt;curl -H &amp;quot;User-Agent: Nikto&amp;quot;     &lt;a href="http://localhost/"&gt;http://localhost    &lt;/a&gt;#返回403 Forbidden，说明WAF防护已经生效，此处匹配的规则是user-agent中不能包含漏洞扫描器名字&lt;/p&gt;   &lt;p&gt;perl nikto/program/nikto.pl -h localhost    #再次用nikto扫描nginx搭建的web系统&lt;/p&gt;   &lt;p&gt;扫描结果是+ 7687 requests: 0 error(s) and 83 item(s) reported on remote host    #扫描出83个问题，比308个少了很多&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;在安装ModSecurity时，我们将演示应用程序配置为为每个请求返回状态代码200,但实际上并没有返回这些文件,Nikto将这200个状态码解释为它请求的文件确实存在,所以报告出83个问题，为了优化nikto，去除误报，我们做如下配置&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;cp nikto/program/nikto.conf.default nikto/program/nikto.conf&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;vim nikto/program/nikto.conf    #在第76行最后加上;-sitefiles，如下所示&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;@@DEFAULT=@@ALL;-@@EXTRAS;tests(report:500);-sitefiles&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;之后再次用nikto扫描&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;perl program/nikto.pl -h localhost&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;p&gt;扫描结果是+ 7583 requests: 0 error(s) and 7 item(s) reported on remote host&lt;/p&gt;
 &lt;p&gt;可以看出问题只有7个问题，由于ModSecurity不支持响应（response）的检查，所以涉及此类的漏洞无法防御。但总体还是抵御了绝大部分的nikto的漏洞扫描。&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="" src="https://image.3001.net/images/20190814/1565774866_5d53d412b3b33.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;参考链接：&lt;/h2&gt;
 &lt;blockquote&gt;  &lt;p&gt;   &lt;a href="https://www.nginx.com/resources/library/modsecurity-3-nginx-quick-start-guide/"&gt;https://www.nginx.com/resources/library/modsecurity-3-nginx-quick-start-guide/&lt;/a&gt;&lt;/p&gt;
  &lt;p&gt;   &lt;a href="https://github.com/SpiderLabs/ModSecurity" target="_blank"&gt;https://github.com/SpiderLabs/ModSecurity&lt;/a&gt;&lt;/p&gt;
  &lt;p&gt;   &lt;a href="https://github.com/SpiderLabs/ModSecurity/tree/v3/master" target="_blank"&gt;https://github.com/SpiderLabs/ModSecurity/tree/v3/master&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;  &lt;strong&gt;*本文作者：owensky，转载请注明来自FreeBuf.COM&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>工具 ModSecurity3.0 waf web应用防火墙</category>
      <guid isPermaLink="true">https://itindex.net/detail/60023-modsecurity-%E5%BC%80%E6%BA%90-waf</guid>
      <pubDate>Tue, 10 Sep 2019 15:00:36 CST</pubDate>
    </item>
    <item>
      <title>用于监控USB设备连接事件的取证工具</title>
      <link>https://itindex.net/detail/59981-%E7%9B%91%E6%8E%A7-usb-%E8%AE%BE%E5%A4%87</link>
      <description>&lt;p&gt;  &lt;strong&gt;*本工具仅供技术分享、交流讨论，严禁用于非法用途&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img src="https://image.3001.net/images/20190809/1565320862_5d4ce69ed2127.png!small"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;usbrip（是“USB Ripper”的简写，而不是“USB R.I.P.”）是一个带有CLI接口的开源取证工具，可用于跟踪/监控Linux机器上的USB设备连接事件（即USB事件历史记录，“已连接”和“已断开连接”事件）。&lt;/strong&gt;&lt;/p&gt;
 &lt;h2&gt;描述&lt;/h2&gt;
 &lt;p&gt;usbrip是纯Python 3编写的一个小软件（使用一些外部模块，参见  &lt;a href="https://github.com/snovvcrash/usbrip#pip-packages"&gt;Dependencies/PIP&lt;/a&gt;），它会通过解析Linux的日志文件（/var/log/syslog*or/var/log/messages*取决于发行版本） 来构建USB事件历史表格，其中可能包含的内容有：“已连接”（日期和时间），“User”，“VID”（供应商ID），“PID”（产品ID），“Product”，“制造商”，“序列号”，    “端口”和“断开连接”（日期和时间）。&lt;/p&gt;
 &lt;p&gt;此外，它还可以：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;将收集到的信息导出为JSON转储文件；&lt;/p&gt;
  &lt;p&gt;生成一个授权（可信）USB设备列表作为JSON（称之为auth.json）;&lt;/p&gt;
  &lt;p&gt;基于auth.json搜索“违规事件”：显示（或生成另一个JSON）USB设备，这些设备出现在历史记录中但不会出现在auth.json中；&lt;/p&gt;
  &lt;p&gt;*当使用-s标志安装时*创建加密存储（7zip存档）以在crontab调度程序的帮助下自动备份和积累USB事件；&lt;/p&gt;
  &lt;p&gt;根据特定USB设备的VID和/或PID搜索其他详细信息。&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h2&gt;快速开始&lt;/h2&gt;
 &lt;p&gt;usbrip可在  &lt;a href="https://pypi.org/project/usbrip/"&gt;PyPI&lt;/a&gt;下载和安装：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ pip3 install usbrip&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;截图&lt;/h2&gt;
 &lt;p&gt;  &lt;img alt="40887882-e00d4d3a-6757-11e8-962c-c77331782b19.png" height="488" src="https://image.3001.net/images/20190809/1565320887_5d4ce6b709bcb.png!small" width="690"&gt;&lt;/img&gt;  &lt;img alt="40886876-46c349d6-6748-11e8-92cf-0b0790ea9505.png" height="449" src="https://image.3001.net/images/20190809/1565320898_5d4ce6c2e1401.png!small" width="499"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;Git Clone&lt;/h2&gt;
 &lt;p&gt;为简单起见，让我们同意所有出现~/usbrip$前缀的命令都在~/usbrip目录中执行，该目录是由git clone创建的：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;~$ git clone https://github.com/snovvcrash/usbrip.git usbrip &amp;amp;&amp;amp; cd usbrip
~/usbrip$&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;依赖&lt;/h2&gt;
 &lt;p&gt;usbrip仅适用于未修改的系统日志文件结构。因此，如果更改syslogs的格式（如，使用syslog-ng或rsyslog），它将无法解析USB历史记录。这就是为什么“Connected”和“Disconnected”字段的时间戳没有年份的原因。&lt;/p&gt;
 &lt;h2&gt;deb 包&lt;/h2&gt;
 &lt;blockquote&gt;  &lt;p&gt;python3.6 (或更新) interpreter&lt;/p&gt;
  &lt;p&gt;python3-venv&lt;/p&gt;
  &lt;p&gt;p7zip-full（由storages模块使用）&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;pre&gt;  &lt;code&gt;~$ sudo apt install python3-venv p7zip-full -y&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;PIP 包&lt;/h2&gt;
 &lt;p&gt;usbrip使用以下外部模块：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;   &lt;a href="https://github.com/Robpol86/terminaltables"&gt;terminaltables&lt;/a&gt;&lt;/p&gt;
  &lt;p&gt;   &lt;a href="https://pypi.org/project/termcolor"&gt;termcolor&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h2&gt;手动&lt;/h2&gt;
 &lt;p&gt;手动解析Python依赖关系（实际上并不需要pip或setup.py，可以自动化该过程，请参阅安装部分）创建虚拟环境（可选）并从内部运行pip：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;~/usbrip$ python3 -m venv venv &amp;amp;&amp;amp; source venv/bin/activate
(venv) ~/usbrip$ pip install -r requirements.txt&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;或者你可以通过下面的  &lt;a href="https://github.com/pypa/pipenv"&gt;pipenv&lt;/a&gt;单行命令为你完成所有的工作：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;~/usbrip$ pipenv install &amp;amp;&amp;amp; pipenv shell&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;之后你就可以非常轻松的运行usbrip了：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;(venv) ~/usbrip$ python -m usbrip -h
Or
(venv) ~/usbrip$ python __main__.py -h&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;安装&lt;/h2&gt;
 &lt;p&gt;有两种方法可以将usbrip安装到你的系统中：pip或setup.py。&lt;/p&gt;
 &lt;h2&gt;pip 或 setup.py&lt;/h2&gt;
 &lt;p&gt;首先，usbrip是pip可安装的。这意味着在git cloning了repo之后，你可以简单地启动pip安装过程，然后在终端的任何地方运行usbrip，如下所示： &lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;~/usbrip$ python3 -m venv venv &amp;amp;&amp;amp; source venv/bin/activate
(venv) ~/usbrip$ pip install .

(venv) ~/usbrip$ usbrip -h&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;或者，如果你想在本地解析Python依赖关系，请使用setup.py：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;~/usbrip$ python3 -m venv venv &amp;amp;&amp;amp; source venv/bin/activate
(venv) ~/usbrip$ python setup.py install

(venv) ~/usbrip$ usbrip -h&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;注意：&lt;/strong&gt;你可能希望在Python虚拟环境处于活动状态时运行安装过程（如上所示）。&lt;/p&gt;
 &lt;h2&gt;install.sh&lt;/h2&gt;
 &lt;p&gt;其次，usbrip也可以使用./installers/install.sh脚本安装到系统中。&lt;/p&gt;
 &lt;p&gt;当使用./installers/install.sh时，可以使用一些额外的功能：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;自动创建虚拟环境；&lt;/p&gt;
  &lt;p&gt;存储模块变为可用：你可以设置crontab job，按计划备份USB事件（你可以在usbrip/cron/usbrip.cron中找到crontab job的示例）。&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;  &lt;strong&gt;警告：&lt;/strong&gt;如果你使用的是crontab计划任务，则需要使用sudo crontab -e配置cron job，以强制storage update子模块以root用户身份运行，并保护USB事件存储的密码。存储密码保存在/var/opt/usbrip/usbrip.ini中。&lt;/p&gt;
 &lt;p&gt;./installers/uninstall.sh脚本会从系统中删除所有安装项。&lt;/p&gt;
 &lt;p&gt;要安装usbrip命令如下：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;~/usbrip$ chmod +x ./installers/install.sh
~/usbrip$ sudo -H ./installers/install.sh [-l/--local] [-s/--storages]
~/usbrip$ cd

~$ usbrip -h&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;启用-l开关后，将从本地.tar包（./3rdPartyTools/）而不是PyPI解析Python依赖项。&lt;/p&gt;
 &lt;p&gt;启用-s开关后，不仅会安装usbrip项目，还会创建受信任的USB设备，历史记录和违规存储列表。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;注意：&lt;/strong&gt;在安装期间使用-s选项时，请确保系统日志至少包含一个外部USB设备条目。这是usbrip成功创建受信任设备列表（并因此成功创建违规存储）的必要条件。&lt;/p&gt;
 &lt;p&gt;安装完成后，你可以删除usbrip文件夹。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;路径&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;安装后，usbrip的文件存放分布路径如下：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;/opt/usbrip/— 项目的主目录；&lt;/p&gt;
  &lt;p&gt;/var/opt/usbrip/usbrip.ini—usbrip配置文件：保存7zip存储的密码；&lt;/p&gt;
  &lt;p&gt;/var/opt/usbrip/storage/—USB事件存储：history.7z和violation.7z（在安装过程中创建）；&lt;/p&gt;
  &lt;p&gt;/var/opt/usbrip/log/— usbrip日志（建议在使用crontab时记录usbrip活动，参见usbrip/cron/usbrip.cron）；&lt;/p&gt;
  &lt;p&gt;/var/opt/usbrip/trusted/— 受信任USB设备列表（在安装过程中创建）；&lt;/p&gt;
  &lt;p&gt;/usr/local/bin/usbrip— 符号链接（symlink）到/opt/usbrip/venv/bin/usbrip脚本。&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;  &lt;strong&gt;cron&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;Cron jobs可以设置如下：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;~/usbrip$ sudo crontab -l &amp;gt; tmpcron &amp;amp;&amp;amp; echo &amp;quot;&amp;quot; &amp;gt;&amp;gt; tmpcron
~/usbrip$ cat usbrip/cron/usbrip.cron | tee -a tmpcron
~/usbrip$ sudo crontab tmpcron
~/usbrip$ rm tmpcron&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;uninstall.sh&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;卸载usbrip：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;~/usbrip$ chmod +x ./installers/uninstall.sh
~/usbrip$ sudo ./installers/uninstall.sh [-a/--all]&lt;/code&gt;&lt;/pre&gt;
 &lt;blockquote&gt;  &lt;p&gt;启用-a开关后，不仅会删除usbrip项目目录，还会删除所有存储和usbrip日志。         &lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;还有就是不要忘记删除cron job。&lt;/p&gt;
 &lt;h2&gt;使用&lt;/h2&gt;
 &lt;pre&gt;  &lt;code&gt;# ---------- BANNER ----------

$ usbrip banner
Get usbrip banner.

# ---------- EVENTS ----------

$ usbrip events history [-t | -l] [-e] [-n &amp;lt;NUMBER_OF_EVENTS&amp;gt;] [-d &amp;lt;DATE&amp;gt; [&amp;lt;DATE&amp;gt; ...]] [--user &amp;lt;USER&amp;gt; [&amp;lt;USER&amp;gt; ...]] [--vid &amp;lt;VID&amp;gt; [&amp;lt;VID&amp;gt; ...]] [--pid &amp;lt;PID&amp;gt; [&amp;lt;PID&amp;gt; ...]] [--prod &amp;lt;PROD&amp;gt; [&amp;lt;PROD&amp;gt; ...]] [--manufact &amp;lt;MANUFACT&amp;gt; [&amp;lt;MANUFACT&amp;gt; ...]] [--serial &amp;lt;SERIAL&amp;gt; [&amp;lt;SERIAL&amp;gt; ...]] [--port &amp;lt;PORT&amp;gt; [&amp;lt;PORT&amp;gt; ...]] [-c &amp;lt;COLUMN&amp;gt; [&amp;lt;COLUMN&amp;gt; ...]] [-f &amp;lt;FILE&amp;gt; [&amp;lt;FILE&amp;gt; ...]] [-q] [--debug]
Get USB event history.

$ usbrip events open &amp;lt;DUMP.JSON&amp;gt; [-t | -l] [-e] [-n &amp;lt;NUMBER_OF_EVENTS&amp;gt;] [-d &amp;lt;DATE&amp;gt; [&amp;lt;DATE&amp;gt; ...]] [--user &amp;lt;USER&amp;gt; [&amp;lt;USER&amp;gt; ...]] [--vid &amp;lt;VID&amp;gt; [&amp;lt;VID&amp;gt; ...]] [--pid &amp;lt;PID&amp;gt; [&amp;lt;PID&amp;gt; ...]] [--prod &amp;lt;PROD&amp;gt; [&amp;lt;PROD&amp;gt; ...]] [--manufact &amp;lt;MANUFACT&amp;gt; [&amp;lt;MANUFACT&amp;gt; ...]] [--serial &amp;lt;SERIAL&amp;gt; [&amp;lt;SERIAL&amp;gt; ...]] [--port &amp;lt;PORT&amp;gt; [&amp;lt;PORT&amp;gt; ...]] [-c &amp;lt;COLUMN&amp;gt; [&amp;lt;COLUMN&amp;gt; ...]] [-f &amp;lt;FILE&amp;gt; [&amp;lt;FILE&amp;gt; ...]] [-q] [--debug]
Open USB event dump.

$ usbrip events gen_auth &amp;lt;OUT_AUTH.JSON&amp;gt; [-a &amp;lt;ATTRIBUTE&amp;gt; [&amp;lt;ATTRIBUTE&amp;gt; ...]] [-e] [-n &amp;lt;NUMBER_OF_EVENTS&amp;gt;] [-d &amp;lt;DATE&amp;gt; [&amp;lt;DATE&amp;gt; ...]] [--user &amp;lt;USER&amp;gt; [&amp;lt;USER&amp;gt; ...]] [--vid &amp;lt;VID&amp;gt; [&amp;lt;VID&amp;gt; ...]] [--pid &amp;lt;PID&amp;gt; [&amp;lt;PID&amp;gt; ...]] [--prod &amp;lt;PROD&amp;gt; [&amp;lt;PROD&amp;gt; ...]] [--manufact &amp;lt;MANUFACT&amp;gt; [&amp;lt;MANUFACT&amp;gt; ...]] [--serial &amp;lt;SERIAL&amp;gt; [&amp;lt;SERIAL&amp;gt; ...]] [--port &amp;lt;PORT&amp;gt; [&amp;lt;PORT&amp;gt; ...]] [-f &amp;lt;FILE&amp;gt; [&amp;lt;FILE&amp;gt; ...]] [-q] [--debug]
Generate a list of trusted (authorized) USB devices.

$ usbrip events violations &amp;lt;IN_AUTH.JSON&amp;gt; [-a &amp;lt;ATTRIBUTE&amp;gt; [&amp;lt;ATTRIBUTE&amp;gt; ...]] [-t | -l] [-e] [-n &amp;lt;NUMBER_OF_EVENTS&amp;gt;] [-d &amp;lt;DATE&amp;gt; [&amp;lt;DATE&amp;gt; ...]] [--user &amp;lt;USER&amp;gt; [&amp;lt;USER&amp;gt; ...]] [--vid &amp;lt;VID&amp;gt; [&amp;lt;VID&amp;gt; ...]] [--pid &amp;lt;PID&amp;gt; [&amp;lt;PID&amp;gt; ...]] [--prod &amp;lt;PROD&amp;gt; [&amp;lt;PROD&amp;gt; ...]] [--manufact &amp;lt;MANUFACT&amp;gt; [&amp;lt;MANUFACT&amp;gt; ...]] [--serial &amp;lt;SERIAL&amp;gt; [&amp;lt;SERIAL&amp;gt; ...]] [--port &amp;lt;PORT&amp;gt; [&amp;lt;PORT&amp;gt; ...]] [-c &amp;lt;COLUMN&amp;gt; [&amp;lt;COLUMN&amp;gt; ...]] [-f &amp;lt;FILE&amp;gt; [&amp;lt;FILE&amp;gt; ...]] [-q] [--debug]
Get USB violation events based on the list of trusted devices.

# ---------- STORAGE ----------

$ usbrip storage list &amp;lt;STORAGE_TYPE&amp;gt; [-q] [--debug]
List contents of the selected storage (7zip archive). STORAGE_TYPE is &amp;quot;history&amp;quot; or &amp;quot;violations&amp;quot;.

$ usbrip storage open &amp;lt;STORAGE_TYPE&amp;gt; [-t | -l] [-e] [-n &amp;lt;NUMBER_OF_EVENTS&amp;gt;] [-d &amp;lt;DATE&amp;gt; [&amp;lt;DATE&amp;gt; ...]] [--user &amp;lt;USER&amp;gt; [&amp;lt;USER&amp;gt; ...]] [--vid &amp;lt;VID&amp;gt; [&amp;lt;VID&amp;gt; ...]] [--pid &amp;lt;PID&amp;gt; [&amp;lt;PID&amp;gt; ...]] [--prod &amp;lt;PROD&amp;gt; [&amp;lt;PROD&amp;gt; ...]] [--manufact &amp;lt;MANUFACT&amp;gt; [&amp;lt;MANUFACT&amp;gt; ...]] [--serial &amp;lt;SERIAL&amp;gt; [&amp;lt;SERIAL&amp;gt; ...]] [--port &amp;lt;PORT&amp;gt; [&amp;lt;PORT&amp;gt; ...]] [-c &amp;lt;COLUMN&amp;gt; [&amp;lt;COLUMN&amp;gt; ...]] [-q] [--debug]
Open selected storage (7zip archive). Behaves similary to the EVENTS OPEN submodule.

$ usbrip storage update &amp;lt;STORAGE_TYPE&amp;gt; [-a &amp;lt;ATTRIBUTE&amp;gt; [&amp;lt;ATTRIBUTE&amp;gt; ...]] [-e] [-n &amp;lt;NUMBER_OF_EVENTS&amp;gt;] [-d &amp;lt;DATE&amp;gt; [&amp;lt;DATE&amp;gt; ...]] [--user &amp;lt;USER&amp;gt; [&amp;lt;USER&amp;gt; ...]] [--vid &amp;lt;VID&amp;gt; [&amp;lt;VID&amp;gt; ...]] [--pid &amp;lt;PID&amp;gt; [&amp;lt;PID&amp;gt; ...]] [--prod &amp;lt;PROD&amp;gt; [&amp;lt;PROD&amp;gt; ...]] [--manufact &amp;lt;MANUFACT&amp;gt; [&amp;lt;MANUFACT&amp;gt; ...]] [--serial &amp;lt;SERIAL&amp;gt; [&amp;lt;SERIAL&amp;gt; ...]] [--port &amp;lt;PORT&amp;gt; [&amp;lt;PORT&amp;gt; ...]] [--lvl &amp;lt;COMPRESSION_LEVEL&amp;gt;] [-q] [--debug]
Update storage — add USB events to the existing storage (7zip archive). COMPRESSION_LEVEL is a number in [0..9].

$ usbrip storage create &amp;lt;STORAGE_TYPE&amp;gt; [-a &amp;lt;ATTRIBUTE&amp;gt; [&amp;lt;ATTRIBUTE&amp;gt; ...]] [-e] [-n &amp;lt;NUMBER_OF_EVENTS&amp;gt;] [-d &amp;lt;DATE&amp;gt; [&amp;lt;DATE&amp;gt; ...]] [--user &amp;lt;USER&amp;gt; [&amp;lt;USER&amp;gt; ...]] [--vid &amp;lt;VID&amp;gt; [&amp;lt;VID&amp;gt; ...]] [--pid &amp;lt;PID&amp;gt; [&amp;lt;PID&amp;gt; ...]] [--prod &amp;lt;PROD&amp;gt; [&amp;lt;PROD&amp;gt; ...]] [--manufact &amp;lt;MANUFACT&amp;gt; [&amp;lt;MANUFACT&amp;gt; ...]] [--serial &amp;lt;SERIAL&amp;gt; [&amp;lt;SERIAL&amp;gt; ...]] [--port &amp;lt;PORT&amp;gt; [&amp;lt;PORT&amp;gt; ...]] [--lvl &amp;lt;COMPRESSION_LEVEL&amp;gt;] [-q] [--debug]
Create storage — create 7zip archive and add USB events to it according to the selected options.

$ usbrip storage passwd &amp;lt;STORAGE_TYPE&amp;gt; [--lvl &amp;lt;COMPRESSION_LEVEL&amp;gt;] [-q] [--debug]
Change password of the existing storage.

# ---------- IDs ----------

$ usbrip ids search [--vid &amp;lt;VID&amp;gt;] [--pid &amp;lt;PID&amp;gt;] [--offline] [-q] [--debug]
Get extra details about a specific USB device by its &amp;lt;VID&amp;gt; and/or &amp;lt;PID&amp;gt; from the USB ID database.

$ usbrip ids download [-q] [--debug]
Update (download) the USB ID database.&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;Help&lt;/h2&gt;
 &lt;p&gt;获取模块名称列表：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ usbrip -h&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;获取特定模块的子模块名称列表：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ usbrip &amp;lt;module&amp;gt; -h&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;获取特定子模块的所有开关列表：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ usbrip &amp;lt;module&amp;gt; &amp;lt;submodule&amp;gt; -h&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;示例&lt;/h2&gt;
 &lt;p&gt;显示所有USB设备的事件历史记录，banner输出，信息消息和用户交互（-q，–quiet），（-l,–list）表示为列表包含最新的100个条目（ -n NUMBER，–number NUMBER）：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ usbrip events history -ql -n 100&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt; 显示外部USB设备的事件历史记录（-e，–external，实际上已断开连接），表示为包含“Connected”，“VID”，“PID”，“Disconnected”的表（-t，–table） 和“序列号”列（-c COLUMN [COLUMN],–column COLUMN [COLUMN]）按日期过滤从外部文件中获取的日志（-f FILE [FILE ...],–file FILE [FILE ...]）：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ usbrip events history -et -c conn vid pid disconn serial -d &amp;quot;Dec  9&amp;quot; &amp;quot;Dec 10&amp;quot; -f /var/log/syslog.1 /var/log/syslog.2.gz&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;构建所有USB设备的事件历史记录，并将输出重定向到文件以进一步的分析。当输出流不是终端stdout（如 | 或 &amp;gt;）时，输出中将没有ANSI转义字符，因此可以随意使用它。另外需要注意的是，usbrip使用了一些UNICODE符号，因此将生成的文件转换为UTF-8编码（如使用encov），以及将换行符更改为Windows样式会更方便（如使用awk）。&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;usbrip history events -t | awk &amp;apos;{ sub(&amp;quot;$&amp;quot;, &amp;quot;\r&amp;quot;); print }&amp;apos; &amp;gt; usbrip.out &amp;amp;&amp;amp; enconv -x UTF8 usbrip.out&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;备注：&lt;/strong&gt;即使已经将输出发送到stdout，也可以自己去掉转义字符。你只需将输出数据复制到usbrip.out并添加一条awk指令：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;awk &amp;apos;{ sub(&amp;quot;$&amp;quot;, &amp;quot;\r&amp;quot;); gsub(&amp;quot;\\x1B\\[[0-?]*[ -/]*[@-~]&amp;quot;, &amp;quot;&amp;quot;); print }&amp;apos; usbrip.out &amp;amp;&amp;amp; enconv -x UTF8 usbrip.out&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;将受信任的USB设备列表生成为json文件（trusted/auth.json），其中包含9月26日连接的前三个设备的“VID”和“PID”属性：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ usbrip events gen_auth trusted/auth.json -a vid pid -n 3 -d &amp;quot;Sep 26&amp;quot;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;警告：&lt;/strong&gt;有时不同的USB闪存驱动器可能具有相同的序列号。到目前为止，usbrip还没有办法处理这种情况，也就是说它将把一对具有相同SN（如果存在）的设备视为与可信设备列表和gen_auth模块相同的设备。&lt;/p&gt;
 &lt;p&gt;根据“PID”属性的可信USB设备列表（trusted/auth.json）搜索外部USB设备的事件历史记录，并将结果事件限定为“Bob”作为用户，“EvilUSBManufacturer”为制造商，“1234567890”为序列号，并将输出表示为具有“Connected”，“VID”和“PID”列的表：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ usbrip events violations trusted/auth.json -a pid -et --user Bob --manufact EvilUSBManufacturer --serial 1234567890 -c conn vid pid&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;通过VID（–vid VID）和PID（–pid PID）搜索特定USB设备的详细信息：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ usbrip ids search --vid 0781 --pid 5580&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;下载最新版本的usb_ids/usb.ids数据库（源码可  &lt;a href="http://www.linux-usb.org/usb.ids"&gt;在此处查看&lt;/a&gt;）：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ usbrip ids download&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;参考文献&lt;/h2&gt;
 &lt;blockquote&gt;  &lt;p&gt;   &lt;a href="https://habr.com/ru/post/352254/"&gt;Linux-форензика в лице трекинга истории подключений USB-устройств / Хабр&lt;/a&gt;&lt;/p&gt;
  &lt;p&gt;   &lt;a href="https://codeby.net/threads/usbrip-usb-forenzika-dlja-linuksov-ili-kak-alisa-stala-evoj.63644/"&gt;usbrip: USB-форензика для Линуксов, или Как Алиса стала Евой&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;  &lt;strong&gt;*参考来源：   &lt;a href="https://github.com/snovvcrash/usbrip"&gt;GitHub&lt;/a&gt;，FB小编secist编译，转载请注明来自FreeBuf.COM&lt;/strong&gt;    &lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>工具 usbrip USB设备连接事件 取证工具</category>
      <guid isPermaLink="true">https://itindex.net/detail/59981-%E7%9B%91%E6%8E%A7-usb-%E8%AE%BE%E5%A4%87</guid>
      <pubDate>Mon, 26 Aug 2019 15:00:09 CST</pubDate>
    </item>
    <item>
      <title>基于MySQL binlog的数据同步中间件 mbinlogmq</title>
      <link>https://itindex.net/detail/59859-mysql-binlog-%E6%95%B0%E6%8D%AE</link>
      <description>&lt;table width="100%"&gt;
                              &lt;tr&gt;
                                                                   &lt;td valign="top"&gt;mbinlogmq 一个基于MySQL binlog协议的数据同步中间件 什么是mbinlogmq? mbinlogmq 是一个使用C语言开发的基于 MySQL binlog 协议的一个中间件，通过模拟Slave 来实时获取 MySQL binlog日志，并将数据变更信息以及 DB 语句发送到 RabbitMQ，中间通过监听机制与校验机制来确保不宕机的情况下的100%抵达 RMQ 设计架构 安装&amp;amp;编译 mbinlog依赖于一些第三方库： 请先安装依赖软件： cURL 、mysql、rabbitmq，安装完成后记住其安装的路...&lt;/td&gt;
                            &lt;/tr&gt;
                        &lt;/table&gt;
                        &lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>数据库管理工具</category>
      <guid isPermaLink="true">https://itindex.net/detail/59859-mysql-binlog-%E6%95%B0%E6%8D%AE</guid>
      <pubDate>Wed, 24 Jul 2019 09:52:29 CST</pubDate>
    </item>
    <item>
      <title>JWT Tool：针对 JSON Web Tokens 的测试工具</title>
      <link>https://itindex.net/detail/59797-jwt-tool-json</link>
      <description>&lt;p&gt;  &lt;strong&gt;众望所归，大家期待已久的JWT渗透测试工具终于出炉啦！没错，今天给大家介绍的这款名叫JWT Tool的工具，就可以针对JSON Web Tokens进行渗透测试。&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;  &lt;img alt="aaaaaaaaaaaaaaaaaaa.jpg" height="300" src="https://image.3001.net/images/20190703/1562142582_5d1c677682b3c.jpg!small" width="600"&gt;&lt;/img&gt;    &lt;/p&gt;
 &lt;h2&gt;什么是JWT？&lt;/h2&gt;
 &lt;p&gt;JWT是JSON Web Token的缩写，它是一串带有声明信息的字符串，由服务端使用加密算法对信息签名，以保证其完整性和不可伪造性。Token里可以包含所有必要的信息，这样服务端就无需保存任何关于用户或会话的信息了。JWT可用于身份认证，会话状态维持以及信息交换等任务。&lt;/p&gt;
 &lt;p&gt;JWT由三部分构成，分别称为header，payload和signature，各部分用“.”相连构成一个完整的Token，形如xxxxx.yyyyy.zzzzz。&lt;/p&gt;
 &lt;h3&gt;Header&lt;/h3&gt;
 &lt;p&gt;使用一个JSON格式字符串声明令牌的类型和签名用的算法等，形如{“alg”:”HS256″, “typ”: “JWT”}。该字符串经过Base64Url编码后形成JWT的第一部分xxxxx。&lt;/p&gt;
 &lt;p&gt;Base64Url编码可以用这段代码直观理解：  &lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;   &lt;p&gt;from base64 import *&lt;/p&gt;   &lt;p&gt;def base64URLen(s):&lt;/p&gt;   &lt;p&gt;    t0=b64encode(s)&lt;/p&gt;   &lt;p&gt;   t1=t0.strip(&amp;apos;=&amp;apos;).replace(&amp;apos;+&amp;apos;,&amp;apos;-&amp;apos;).replace(&amp;apos;/&amp;apos;,&amp;apos;_&amp;apos;)&lt;/p&gt;   &lt;p&gt;    return t1&lt;/p&gt;   &lt;p&gt;​&lt;/p&gt;   &lt;p&gt;def base64URLde(s):&lt;/p&gt;   &lt;p&gt;    t0=s.replace(&amp;apos;-&amp;apos;,&amp;apos;+&amp;apos;).replace(&amp;apos;_&amp;apos;,&amp;apos;/&amp;apos;)&lt;/p&gt;   &lt;p&gt;    t1=t0+&amp;apos;=&amp;apos;*(4-len(t0)%4)%4&lt;/p&gt;   &lt;p&gt;    return b64decode(t1)&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;h3&gt;Payload&lt;/h3&gt;
 &lt;p&gt;使用一个JSON格式字符串描述所要声明的信息，分为registered，public，状语从句：private三类，形如{“name”: “John Doe”, “admin”: true}，具体信息可参考RFC7519的JWT要求部分。&lt;/p&gt;
 &lt;p&gt;同样的，该字符串经过Base64Url编码形成JWT的第二部分yyyyy。&lt;/p&gt;
 &lt;h3&gt;Signature&lt;/h3&gt;
 &lt;p&gt;将xxxxx.yyyyy使用alg指定的算法加密，然后再Base64Url编码得到JWT的第三部分zzzzz。所支持的算法类型取决于实现，但HS256和none是强制要求实现的。&lt;/p&gt;
 &lt;h2&gt;JWT Tool&lt;/h2&gt;
 &lt;p&gt;简而言之，Jwt_tool.py这个工具及可以用来验证、伪造和破解JWT令牌。&lt;/p&gt;
 &lt;p&gt;其功能包括：&lt;/p&gt;
 &lt;blockquote&gt;  &lt;p&gt;1、 检测令牌的有效性；&lt;/p&gt;
  &lt;p&gt;2、 测试RS/HS256公钥错误匹配漏洞；&lt;/p&gt;
  &lt;p&gt;3、 测试alg=None签名绕过漏洞；&lt;/p&gt;
  &lt;p&gt;4、 测试密钥/密钥文件的有效性；&lt;/p&gt;
  &lt;p&gt;5、 通过高速字典攻击识别弱密钥；&lt;/p&gt;
  &lt;p&gt;6、 伪造新的令牌Header和Payload值，并使用密钥创建新的签名；&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h3&gt;适用范围&lt;/h3&gt;
 &lt;p&gt;该工具专为渗透测试人员设计，可用于检测令牌的安全等级，并检测可能的攻击向量。当然了，广大研究人员也可以用它来对自己使用了JWT的项目进行安全测评以及稳定性测评。&lt;/p&gt;
 &lt;h3&gt;工具要求&lt;/h3&gt;
 &lt;p&gt;本工具采用原生Python 2.x开发，使用的都是常用Python库。大家可以在字典攻击选项中配置自定义字典文件。&lt;/p&gt;
 &lt;h2&gt;工具安装&lt;/h2&gt;
 &lt;p&gt;大家可以直接下载代码库中的jwt_tool.py文件，或使用下列命令将代码库克隆至本地：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;git clone https://github.com/ticarpi/jwt_tool.git&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;工具使用&lt;/h2&gt;
 &lt;pre&gt;  &lt;code&gt;$python jwt_tool.py &amp;lt;JWT&amp;gt; (filename)&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;第一个参数就是JWT本身，后面需要跟一个文件名或文件路径。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;样例：&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$python jwt_tool.pyeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJsb2dpbiI6InRpY2FycGkifQ.aqNCvShlNT9jBFTPBpHDbt2gBB1MyHiisSDdp8SQvgw/usr/share/wordlists/rockyou.txt&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;本工具将会验证令牌的有效性，并输出Header和Payload的值。接下来，它会给用户提供可用的菜单选项。输入值可以为标准JWT格式或url-safe模式的JWT格式。&lt;/p&gt;
 &lt;h3&gt;使用提示&lt;/h3&gt;
 &lt;p&gt;大家还可以在Burp Search中使用正则表达式来寻找JWT（请确保开启了“大小写敏感“和“正则表达式”选项）：&lt;/p&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;   &lt;p&gt;[=]ey[A-Za-z0-9_-]*\.[A-Za-z0-9._-]* - url-safe JWT version&lt;/p&gt;   &lt;p&gt;[=]ey[A-Za-z0-9_\/+-]*\.[A-Za-z0-9._\/+-]* - all JWT versions&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;&lt;/p&gt;
 &lt;h2&gt;项目地址&lt;/h2&gt;
 &lt;p&gt;JWT Tool：【  &lt;a href="https://github.com/ticarpi/jwt_tool"&gt;GitHub传送门&lt;/a&gt;】&lt;/p&gt;
 &lt;h2&gt;参考资料&lt;/h2&gt;
 &lt;blockquote&gt;  &lt;p&gt;1、    &lt;a href="https://jwt.io/introduction/"&gt;https://jwt.io/introduction/&lt;/a&gt;&lt;/p&gt;
  &lt;p&gt;2、    &lt;a href="https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/"&gt;https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/&lt;/a&gt;&lt;/p&gt;
  &lt;p&gt;3、    &lt;a href="https://www.ticarpi.com/introducing-jwt-tool/"&gt;https://www.ticarpi.com/introducing-jwt-tool/&lt;/a&gt;&lt;/p&gt;
  &lt;p&gt;4、    &lt;a href="https://pentesterlab.com/exercises/jwt"&gt;https://pentesterlab.com/exercises/jwt&lt;/a&gt;&lt;/p&gt;
  &lt;p&gt;5、    &lt;a href="https://pentesterlab.com/exercises/jwt_ii"&gt;https://pentesterlab.com/exercises/jwt_ii&lt;/a&gt;&lt;/p&gt;
  &lt;p&gt;6、    &lt;a href="https://pentesterlab.com/exercises/jwt_iii"&gt;https://pentesterlab.com/exercises/jwt_iii&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;p&gt;  &lt;strong&gt; * 参考来源：   &lt;a href="https://github.com/ticarpi/jwt_tool"&gt;ticarpi&lt;/a&gt;，FB小编Alpha_h4ck编译，转载请注明来自FreeBuf.COM&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>工具 JSON JWT Web Token 测试工具</category>
      <guid isPermaLink="true">https://itindex.net/detail/59797-jwt-tool-json</guid>
      <pubDate>Sun, 07 Jul 2019 15:00:42 CST</pubDate>
    </item>
    <item>
      <title>CyberScan：用于数据包取证的渗透工具</title>
      <link>https://itindex.net/detail/59619-cyberscan-%E6%95%B0%E6%8D%AE%E5%8C%85-%E6%B8%97%E9%80%8F</link>
      <description>&lt;p&gt;  &lt;strong&gt;CyberScan是一个开源的数据包取证渗透工具，可用于数据包的分析，解码，扫描端口，pinging以及获取IP的地理定位包括（纬度，经度，地区，国家等）。&lt;/strong&gt;&lt;/p&gt;
 &lt;h2&gt;  &lt;strong&gt;截图&lt;/strong&gt;&lt;/h2&gt;
 &lt;p&gt;  &lt;img alt="demo.png" height="376" src="https://image.3001.net/images/20190512/1557629282_5cd7896202dce.png!small" width="690"&gt;&lt;/img&gt;&lt;/p&gt;
 &lt;h2&gt;  &lt;strong&gt;支持的操作系统&lt;/strong&gt;&lt;/h2&gt;
 &lt;blockquote&gt;  &lt;p&gt;Windows XP/7/8/8.1/10&lt;/p&gt;
  &lt;p&gt;GNU/Linux&lt;/p&gt;
  &lt;p&gt;MacOSX&lt;/p&gt;
&lt;/blockquote&gt;
 &lt;h2&gt;  &lt;strong&gt;安装&lt;/strong&gt;&lt;/h2&gt;
 &lt;p&gt;你可以通过克隆Git存储库下载CyberScan：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;git clone https://github.com/medbenali/CyberScan.git
cd CyberScan/
python CyberScan.py -v&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;CyberScan支持Python版本2.6.x和2.7.x开箱即用。&lt;/p&gt;
 &lt;h2&gt;CyberScan 模块使用&lt;/h2&gt;
 &lt;p&gt;CyberScan能够发送和捕获多个协议的数据包，伪造并对它们进行解码以用于大多数网络任务，如扫描，ping，探测和攻击。&lt;/p&gt;
 &lt;p&gt;请确保你的机器已安装了CyberScan：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ git clone https://github.com/medbenali/CyberScan.git&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;查看帮助选项：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ CyberScan -h&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;Pinging 网络&lt;/h2&gt;
 &lt;p&gt;CyberScan支持使用多种协议执行ping操作，在本地以太网上发现主机的最快方法是使用ARP：&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;ARP Ping&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ CyberScan -s 192.168.1.0/24 -p arp
[*] Starting Ping ARP for 192.168.1.0/24
Begin emission:
Finished to send 256 packets.

Received 0 packets, got 0 answers, remaining 256 packets&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;ICMP Ping&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;在其他情况下，我们可以使用ICMP ping：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ CyberScan -s 192.168.1.1-254 -p icmp
[*] Starting Ping ARP for 192.168.1.0/24
Begin emission:
Finished to send 256 packets.

Received 0 packets, got 0 answers, remaining 256 packets&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;TCP Ping&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;在ICMP回应请求被阻止的情况下，我们仍然可以使用TCP：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ CyberScan -s 192.168.1.1-254 -p tcp -d 80&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;UDP Ping&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;甚至UDP（从实时主机产生ICMP端口不可达错误）。我们可以选择任何最有可能关闭的端口，例如端口0：&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ CyberScan -s 192.168.*.1-10 -p udp&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;网络扫描&lt;/h2&gt;
 &lt;p&gt;  &lt;strong&gt;端口扫描器&lt;/strong&gt;&lt;/p&gt;
 &lt;p&gt;在CyberSan Tool中，我们可以指定/不指定开始和结束端口进行扫描&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ CyberScan -s 192.168.1.1 -p scan -d 1 -t 100
WARNING: No route found for IPv6 destination :: (no default route?)
[*] CyberScan Port Scanner
[*] Scanning 192.168.1.1 From Port 1 To 100: 
[*] Starting CyberScan 1.01 at 2017-07-14 14:00 CEST
[*] Scan In Progress ...
[*] Connecting To Port :  100 
[*] Scanning Completed at 2017-07-14 14:00 CEST
[*] CyberScan done: 1IP address (1host up) scanned in 0.32 seconds
[*] Open Ports: 
  23 TELNET: Open
  53 DNS: Open
  80 HTTP: Open&lt;/code&gt;&lt;/pre&gt;
 &lt;pre&gt;  &lt;code&gt;$ CyberScan -s 8.8.8.8 -p scan
WARNING: No route found for IPv6 destination :: (no default route?)
[*] CyberScan Port Scanner
[*] Scanning For Most Common Ports On 8.8.8.8
[*] Starting CyberScan 1.01 at 2017-07-14 14:03 CEST
[*] Scan In Progress ...
[*] Connecting To Port :  10000 109 110 123 137 138 139 143 156 2082 2083 2086 2087 21 22 23 25 3306 389 546 547 69 80 8443 993 995 
[*] Scanning Completed at 2017-07-14 14:03 CEST
[*] CyberScan done: 1IP address (1host up) scanned in 13.11 seconds
[*] Open Ports: 
  53 DNS: Open
  443 HTTPS: Open&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;  &lt;strong&gt;地理定位 IP&lt;/strong&gt;&lt;/h2&gt;
 &lt;pre&gt;  &lt;code&gt;$ CyberScan -s 72.229.28.185 -p geoip
WARNING: No route found for IPv6 destination :: (no default route?)
[*] IP Address:  72.229.28.185
[*] City:  New York
[*] Region Code:  NY
[*] Area Code:  212
[*] Time Zone:  America/New_York
[*] Dma Code:  501
[*] Metro Code:  New York, NY
[*] Latitude:  40.7605
[*] Longitude:  -73.9933
[*] Zip Code:  10036
[*] Country Name:  United States
[*] Country Code:  US
[*] Country Code3:  USA
[*] Continent:  NA&lt;/code&gt;&lt;/pre&gt;
 &lt;h2&gt;分析和解码数据包&lt;/h2&gt;
 &lt;p&gt;Cyberscan可以分析PCAP文件提取和解码以太网、IP、TCP、ICMP、UDP包头。&lt;/p&gt;
 &lt;p&gt;  &lt;strong&gt;以太网包头&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ CyberScan -f test.pcap -p eth
WARNING: No route found for IPv6 destination :: (no default route?)
----------------------------------------
[*] Packet : 1
[+] ### [ Ethernet ] ###
[*] Mac Destination : 00:1f:f3:3c:e1:13
[*] Mac Source : f8:1e:df:e5:84:3a
[*] Ethernet Type : 2048&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;IP 包头&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ CyberScan -f test.pcap -p ip
WARNING: No route found for IPv6 destination :: (no default route?)
----------------------------------------
[*] Packet : 1
[+] ###[ IP ] ###
[*] IP Source : 172.16.11.12
[*] IP Destination : 74.125.19.17
[*] IP Version :  4
[*] IP Ihl :  5
[*] IP Tos :  0
[*] IP Len :  79
[*] IP Id :  56915
[*] IP Flags :  2
[*] IP Frag :  0
[*] IP Ttl :  64
[*] IP Protocol :  6
[*] IP Chksum :  18347
[*] IP Options :  []
[*] IP Dump : 
0000   45 00 00 4F DE 53 40 00  40 06 47 AB AC 10 0B 0C   E..O.S@.@.G.....
0010   4A 7D 13 11 FC 35 01 BB  C6 D9 14 D0 C5 1E 2D BF   J}...5........-.
0020   80 18 FF FF CB 8C 00 00  01 01 08 0A 1A 7D 84 2C   .............}.,
0030   37 C5 58 B0 15 03 01 00  16 43 1A 88 1E FA 7A BC   7.X......C....z.
0040   22 6E E6 32 7A 53 47 00  A7 5D CC 64 EA 8E 92      &amp;quot;n.2zSG..].d...&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;TCP 包头&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ CyberScan -f test.pcap -p tcp
WARNING: No route found for IPv6 destination :: (no default route?)
----------------------------------------
[*] Packet : 1
[+] ###[ TCP ] ###
[*] TCP Source Port :  64565
[*] TCP Destination Port :  443
[*] TCP Seq :  3336115408
[*] TCP Ack :  3307089343
[*] TCP Dataofs :  8
[*] TCP Reserved :  0
[*] TCP Flags :  24
[*] TCP Window :  65535
[*] TCP Chksum :  52108
[*] TCP Urgptr :  0
[*] TCP Options :  [(&amp;apos;NOP&amp;apos;, None), (&amp;apos;NOP&amp;apos;, None), (&amp;apos;Timestamp&amp;apos;, (444433452, 935680176))]
[*] TCP Dump : 
0000   FC 35 01 BB C6 D9 14 D0  C5 1E 2D BF 80 18 FF FF   .5........-.....
0010   CB 8C 00 00 01 01 08 0A  1A 7D 84 2C 37 C5 58 B0   .........}.,7.X.&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;UDP 包头&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ CyberScan -f test.pcap -p udp
WARNING: No route found for IPv6 destination :: (no default route?)
----------------------------------------
[*] Packet : 1
[+] ###[ UDP ] ###
[*] UDP Source Port :  54639
[*] UDP Destination Port :  53
[*] UDP Len :  47
[*] UDP Chksum :  30084
[*] UDP Dump : 
0000   D5 6F 00 35 00 2F 75 84  13 A2 01 00 00 01 00 00   .o.5./u.........
0010   00 00 00 00 04 65 38 37  32 01 67 0A 61 6B 61 6D   .....e872.g.akam
0020   61 69 65 64 67 65 03 6E  65 74 00 00 01 00 01      aiedge.net.....&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;ICMP 包头&lt;/strong&gt;&lt;/p&gt;
 &lt;pre&gt;  &lt;code&gt;$ CyberScan -f test.pcap -p icmp
WARNING: No route found for IPv6 destination :: (no default route?)
----------------------------------------
[*] Packet : 1
[+] ###[ ICMP ] ###
[*] ICMP Type :  3
[*] ICMP Code :  3
[*] ICMP Chksum :  5296
[*] ICMP Id :  None
[*] ICMP Seq :  None
[*] ICMP Dump : 
0000   03 03 14 B0 00 00 00 00  45 00 00 43 C1 80 00 00   ........E..C....
0010   40 11 4A FC AC 10 0B 01  AC 10 0B 0C 00 35 E7 E8   @.J..........5..
0020   00 2F 00 00                                        ./..&lt;/code&gt;&lt;/pre&gt;
 &lt;p&gt;  &lt;strong&gt;*参考来源：   &lt;a href="https://github.com/medbenali/cyberscan"&gt;GitHub&lt;/a&gt;，FB小编secist编译，转载请注明来自FreeBuf.COM&lt;/strong&gt;&lt;/p&gt;
&lt;div&gt; &lt;a href="https://itindex.net/"  title="IT 资讯"&gt;&lt;img src="https://itindex.net/images/iconWarning.gif" title="IT 资讯" border="0"/&gt; &lt;/a&gt;</description>
      <category>工具 CyberScan 数据包</category>
      <guid isPermaLink="true">https://itindex.net/detail/59619-cyberscan-%E6%95%B0%E6%8D%AE%E5%8C%85-%E6%B8%97%E9%80%8F</guid>
      <pubDate>Wed, 22 May 2019 15:00:49 CST</pubDate>
    </item>
  </channel>
</rss>

