SQL行存储与列存储的根本区别到底是什么

来源:PHP编程网作者:越南程序员头衔:程序员
导读:本期聚焦于小伙伴创作的《SQL行存储与列存储的根本区别到底是什么》,敬请观看详情。为什么同样的SQL查询在数据分析场景下列存储比行存储快几十倍。根本差异在于磁盘读写单元和组织方式:行存储按整行连续存放,适合点查和事务;列存储把同字段值压在一起,大幅减少扫描量并提升压缩率。本文从页结构、谓词下推、向量化执行三个角度说明二者在扫描、过滤、聚合时的真实表现,并给出OLTP与OLAP选型时的判断依据,帮你避免把报表查询压在行存表上导致IO暴涨的误区。

在关系型数据库内部,数据落盘方式直接决定了查询引擎该如何取数。SQL行存储与列存储并不是简单的“换个格式”,而是从页分配、缓存命中到执行算子的全链路差异。理解这两种存储模型的底层机制,才能在建表时选对结构,避免线上分析语句把磁盘IO打满。

SQL行存储与列存储的根本区别到底是什么

物理布局与读写单元的本质差异

行存储(row store)以行为最小连续单元,一条记录的所有字段值按顺序写入同一个数据页。以传统堆表或B+树叶子节点为例,页内先放第一行的id、name、age、city,紧接着第二行同字段。这种布局让“根据主键拿整行”非常高效,因为一次页读就能拿到全部列,非常适合OLTP里的增删改和点查。事务提交时,整行写入回滚段与日志,一致性控制也以行为粒度。

列存储(column store)则把每个字段单独成组,同一列的所有行值连续存放。比如先存完全部行的id,再存全部行的name。这样当查询只关心age大于30的人数时,引擎只需读取age这一串连续块,完全跳过name、city等无关列。由于同列数据类型一致,压缩算法(字典、游程、位图)能获得极高比率,进一步减少物理读。下面用伪代码展示两种格式在内存中的抽象表达:

-- 行存储逻辑视图
CREATE TABLE user_row (
  id INT,
  name VARCHAR(50),
  age INT,
  city VARCHAR(50)
) ENGINE=ROW_STORE;

-- 列存储逻辑视图(以列式引擎为例)
CREATE TABLE user_col (
  id INT,
  name VARCHAR(50),
  age INT,
  city VARCHAR(50)
) ENGINE=COLUMN_STORE;

从缓存角度看,行存储的缓冲池常因宽表而迅速被整行占满,分析查询扫描大表时会频繁淘汰页;列存储因每次只加载需要的列,缓冲命中率在分析负载下明显更高。这也是为什么混合负载数据库会引入行列混存或列存副本。

查询执行与谓词下推的不同路径

在执行SELECT city, COUNT(*) FROM t WHERE age > 30这类语句时,行存储引擎通常先按页扫描,逐行取出age做判断,再提取city。即便有索引,若过滤后还需投影多列,随机回表成本不可忽视。谓词下推在行存中更多依赖索引结构,而非存储格式本身。

列存储天然支持“列裁剪”与“谓词下推到存储层”。引擎在打开段文件时已知各列的上下界(min/max),可跳过整块不满足age范围的页;同列连续布局也让比较操作能批量进行。以下示例展示列存过滤时的批量判断逻辑:

// 列存块级跳过示例
for (auto& block : age_column.blocks) {
  if (block.max < 30) continue; // 整块跳过
  for (int i = 0; i < block.count; i++) {
    if (block.values[i] > 30) {
      result_city.append(city_column.at(block.offset + i));
    }
  }
}

此外,列存储更容易配合向量化执行器,一次处理上千个同列值,减少函数调用与分支预测失败。行存储若要做向量化,需先拆行再拼列,开销更大。因此在聚合、扫描密集型报表中,列存的执行效率优势来自“少读+批量算”的双重叠加。

事务能力与适用场景的边界

行存储在MVCC、行级锁、高频更新上积累数十年工程优化。更新某行某列时,引擎定位到页内偏移直接改写或追加版本,事务隔离实现直观。列存储因同列跨行集中,单行更新会牵动多个列文件,通常采取“增量行存+后台合并”或只允许批量载入,因而不适合作为核心交易表。

实际选型应看负载轮廓:订单、账户、会话等需要低延迟读写与强一致的场景,继续使用行存;用户行为、日志、BI大宽表等以扫描和聚合为主、写入多为追加的场景,采用列存可获得数量级性能提升。很多系统提供行列双写或HTAP架构,在写侧保留行存事务性,在读侧同步列存副本服务分析流量。

-- 典型HTAP:行存写,列存读
INSERT INTO orders_row VALUES (1, 'pay', 99);
-- 异步同步到列存视图
SELECT region, SUM(amount)
FROM orders_col
WHERE ts > '2023-01-01'
GROUP BY region;

归根结底,SQL行存储与列存储的根本区别不在语法层,而在“数据怎样落在磁盘”以及“引擎怎样取它”。认清这点,才能在表设计阶段就规避把分析查询压在行存结构上的典型误区。

row_storecolumn_storeSQL_engine修改时间:2026-08-15 17:12:35

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。