<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>列式 on Yeqown</title>
    <link>https://www.yeqown.xyz/tags/%E5%88%97%E5%BC%8F/</link>
    <description>Recent content in 列式 on Yeqown</description>
    <generator>Hugo</generator>
    <language>en-US</language>
    <lastBuildDate>Tue, 31 Mar 2026 14:44:37 +0800</lastBuildDate>
    <atom:link href="https://www.yeqown.xyz/tags/%E5%88%97%E5%BC%8F/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>列式数据库是怎么炼成的</title>
      <link>https://www.yeqown.xyz/2026/03/31/%E5%88%97%E5%BC%8F%E6%95%B0%E6%8D%AE%E5%BA%93%E6%98%AF%E6%80%8E%E4%B9%88%E7%82%BC%E6%88%90%E7%9A%84/</link>
      <pubDate>Tue, 31 Mar 2026 14:44:37 +0800</pubDate>
      <guid>https://www.yeqown.xyz/2026/03/31/%E5%88%97%E5%BC%8F%E6%95%B0%E6%8D%AE%E5%BA%93%E6%98%AF%E6%80%8E%E4%B9%88%E7%82%BC%E6%88%90%E7%9A%84/</guid>
      <description>&lt;p&gt;列式数据库与行式数据库最大的区别在于数据的存储方式，也就是它们在磁盘上的组织方式不同。传统的行式数据库常用于 &lt;code&gt;OLTP (Online Transaction Processing)&lt;/code&gt; 场景，在这个场景下需要频繁的进行数据的插入、更新、删除操作，操作的对象往往是单行数据。而列式数据库常用于 &lt;code&gt;OLAP (Online Analytical Processing)&lt;/code&gt; 场景，对于数据的聚合查询更为常见，往往需要扫描某一列的大量数据进行计算。&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;  &lt;img src=&#34;https://www.yeqown.xyz/images/columnar-database/storage-difference.png&#34; alt=&#34;Storage Difference&#34;&gt;&#xA;  &lt;figcaption&gt;Storage Difference&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;p&gt;如果同时用过两种类型的数据库，就会发现：&lt;/p&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;这里列举的优化原则只是冰山一角，仅用于说明两种数据库最显眼的差异。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;p&gt;使用行式数据库过程中，最简单常见的优化原则就是 &lt;strong&gt;尽可能命中索引、降低 B+ 树高度、减少扫描行数&lt;/strong&gt;，如：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;优先对区分度高的列建立索引&lt;/li&gt;&#xA;&lt;li&gt;覆盖索引（索引中包含查询所需的所有列，避免回表）&lt;/li&gt;&#xA;&lt;li&gt;索引下推（在存储引擎层提前过滤不满足条件的数据）&lt;/li&gt;&#xA;&lt;li&gt;最左前缀匹配原则&lt;/li&gt;&#xA;&lt;li&gt;避免使用函数或者隐式类型转换（如：&lt;code&gt;where date(create_time) = &#39;2022-01-01&#39;&lt;/code&gt;），会导致索引失效&lt;/li&gt;&#xA;&lt;li&gt;避免在索引列上使用 &lt;code&gt;!=&lt;/code&gt;、&lt;code&gt;&amp;lt;&amp;gt;&lt;/code&gt; 等操作符，会导致索引失效&lt;/li&gt;&#xA;&lt;li&gt;避免深度分页&lt;/li&gt;&#xA;&lt;li&gt;分库分表（提出这一优化方向，也是基于单表数据量过大，索引维护的开销会增加，性能也会退化）&lt;/li&gt;&#xA;&lt;li&gt;等等&amp;hellip;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;减少扫描行数这一思路对于列式数据库同样适用（如分区裁剪），但列式数据库还有另一个很重要的优化方向，那就是 &lt;strong&gt;减少列&lt;/strong&gt;，如：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;行存特性（如果是点查询，列数据库 I/O 反而会增加，这一点和行式数据库正好相悖）&lt;/li&gt;&#xA;&lt;li&gt;只读取查询涉及的列（行存也提倡避免 SELECT *，但由于行存以行为单位读取磁盘，主要减少的是网络传输量而非磁盘 I/O；而列存中每列独立存储，少读一列就直接少一份磁盘 I/O）&lt;/li&gt;&#xA;&lt;li&gt;等等&amp;hellip;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;当然，任何数据库的优化，都逃不开 &lt;strong&gt;减少 I/O&lt;/strong&gt; 这一核心目的。说得更白话一点，如果有一种完美的存储介质，它没有I/O延迟，也不会丢失数据，那么这些优化也就不再需要了。&lt;/p&gt;&#xA;&lt;!-- more --&gt;&#xA;&lt;h2 id=&#34;1-clickhouse-的设计&#34;&gt;1. ClickHouse 的设计&lt;a class=&#34;anchor&#34; href=&#34;#1-clickhouse-%e7%9a%84%e8%ae%be%e8%ae%a1&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;11-整体组件&#34;&gt;1.1 整体组件&lt;a class=&#34;anchor&#34; href=&#34;#11-%e6%95%b4%e4%bd%93%e7%bb%84%e4%bb%b6&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;从 ClickHouse 的架构图来看，列式数据库包含以下核心组件：&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;    &lt;img src=&#34;https://www.yeqown.xyz/images/columnar-database/clickhouse-architecture.png&#34; alt=&#34;ClickHouse Architecture&#34;&gt;&#xA;    &lt;figcaption&gt;ClickHouse Architecture&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;查询处理层&lt;/strong&gt;&lt;/em&gt;： 查询处理遵循传统范式：解析入站查询、构建并优化逻辑与物理查询计划，然后执行&#xA;&lt;ul&gt;&#xA;&lt;li&gt;SQL Parser&lt;/li&gt;&#xA;&lt;li&gt;SQL Planner&lt;/li&gt;&#xA;&lt;li&gt;Physical Plan Builder&lt;/li&gt;&#xA;&lt;li&gt;Plan Executor&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;存储层&lt;/strong&gt;&lt;/em&gt;：由不同的表引擎组成，这些表引擎封装了表数据的格式和位置&#xA;&lt;ul&gt;&#xA;&lt;li&gt;MergeTree* Family Tables Engines： 代表了 ClickHouse 中的主要持久化格式&lt;/li&gt;&#xA;&lt;li&gt;Special-Purpose Tables Engines：用于加速或分布查询执行的专用表引擎&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Dictionary&lt;/li&gt;&#xA;&lt;li&gt;Memory&lt;/li&gt;&#xA;&lt;li&gt;Distributed (Data Sharding) 处理分布式&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;集成层&lt;/strong&gt;&lt;/em&gt;：用于与外部系统进行双向数据交换的虚拟表引擎，例如关系型数据库 (如 PostgreSQL、MySQL) 、发布/订阅系统 (如 Kafka、RabbitMQ) ，或键值存储 (如 Redis) 。还可以与数据湖 (如 Iceberg) 或对象存储中的文件 (如 AWS S3、Google GCP) 交互&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Virtual Tables Engines&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;正交组件&lt;/strong&gt;&lt;/em&gt;：提供辅助功能&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Thread pools&lt;/li&gt;&#xA;&lt;li&gt;Caches&lt;/li&gt;&#xA;&lt;li&gt;RBAC (Role-Based Access Control)&lt;/li&gt;&#xA;&lt;li&gt;Backups&lt;/li&gt;&#xA;&lt;li&gt;Monitoring&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;&lt;strong&gt;访问层&lt;/strong&gt;&lt;/em&gt;：通过不同协议管理用户会话并与应用程序通信&#xA;&lt;ul&gt;&#xA;&lt;li&gt;User Session&lt;/li&gt;&#xA;&lt;li&gt;Wire protocols&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;我们更进一步，丢掉分布式特性、集成和监控，只保留最影响 OLAP 查询性能的核心设计，如下所示：&lt;/p&gt;</description>
    </item>
    <item>
      <title>VictoriaLogs 设计与实现</title>
      <link>https://www.yeqown.xyz/2025/12/11/%E7%90%86%E8%A7%A3VictoriaLogs%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0/</link>
      <pubDate>Thu, 11 Dec 2025 14:05:17 +0800</pubDate>
      <guid>https://www.yeqown.xyz/2025/12/11/%E7%90%86%E8%A7%A3VictoriaLogs%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%AE%9E%E7%8E%B0/</guid>
      <description>&lt;p&gt;Victoria Logs 简单易上手，非常适合中小团队使用，同时也适用于存储大量数据且成本敏感的场景，比如日志存储。&lt;/p&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;官方建议：如果可以接受在单节点上垂直扩展来满足业务需求，那么就不必使用集群模式。&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;p&gt;本文包含大量源码片段，若不关注细节，可只阅读 &lt;a href=&#34;#%e6%9e%b6%e6%9e%84%e6%a6%82%e8%a7%88&#34;&gt;架构概览&lt;/a&gt; 和 &lt;a href=&#34;#%e5%ad%98%e5%82%a8%e6%a8%a1%e5%9e%8b&#34;&gt;存储模型&lt;/a&gt;。&lt;/p&gt;&#xA;&lt;!-- more --&gt;&#xA;&lt;h2 id=&#34;架构概览&#34;&gt;架构概览&lt;a class=&#34;anchor&#34; href=&#34;#%e6%9e%b6%e6%9e%84%e6%a6%82%e8%a7%88&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;Victoria Logs 的集群架构如下：&lt;/p&gt;&#xA;&lt;figure&gt;&#xA;    &lt;img src=&#34;https://www.yeqown.xyz/images/victorialogs/vl-architecture.svg&#34; alt=&#34;Victoria Logs 架构图&#34;&gt;&#xA;    &lt;figcaption&gt;Victoria Logs 架构图&lt;/figcaption&gt;&#xA;&lt;/figure&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;通过 vlagent 完成类似 Replica 的功能，将日志数据复制到多个 vlstorage 组件中，实现高可用和数据冗余。&lt;/li&gt;&#xA;&lt;li&gt;通过 vlinsert 执行日志数据的写入策略。&lt;/li&gt;&#xA;&lt;li&gt;通过 vlselect 来聚合多个 vlstorage 组件中的日志数据，实现查询功能。&lt;/li&gt;&#xA;&lt;li&gt;通过 vlstorage 可以无缝的实现扩展存储容量，而不用考虑数据迁移（rebalance）。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;vlstorage 的缩容稍微复杂一些，这里提供一种方案：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;先将需要缩容的 vlstorage 节点从 vlinsert 的 &lt;code&gt;storageNode&lt;/code&gt; 列表中移除，vlselect 中保留；&lt;/li&gt;&#xA;&lt;li&gt;利用 Victoria Logs 的数据保留功能，让数据随逐步过期，直到待删除节点上的数据全部过期；&lt;/li&gt;&#xA;&lt;li&gt;最后将其从 vlselect 中移除，并释放 vlstorage 节点。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;也可以参考 &lt;a href=&#34;https://docs.victoriametrics.com/victorialogs/#partitions-lifecycle&#34;&gt;Partitions lifecycle&lt;/a&gt;，手动执行数据迁移。&lt;/p&gt;&#xA;&lt;h3 id=&#34;vlagent&#34;&gt;vlagent&lt;a class=&#34;anchor&#34; href=&#34;#vlagent&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;&lt;code&gt;vlagent&lt;/code&gt; 是 Victoria Logs 的代理组件（类似 OpenTelemetry 的 Agent），位置更靠近日志源，同时也是一层缓冲区，负责接收日志数据并将其发送到 &lt;code&gt;vlinsert&lt;/code&gt; 组件。&lt;/p&gt;&#xA;&lt;p&gt;它可以将数据写入到不同的 Victoria Logs 实例中，实现数据的多写（复制），是实现高可用的必要组件。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
