我为什么要写这篇安装笔记
说实话,第一次接触Apache Arrow这个概念,我是在处理一个3GB的CSV文件时被逼的。当时用read.csv()读完那个文件,内存直接爆炸,RStudio卡了将近两分钟才缓过来。后来换成arrow包之后,同样的数据用open_dataset()加collect()的组合,读取时间从"泡杯咖啡等结果"缩到了"眨个眼的功夫",从那时起我才意识到,这个R包arrow远不止一个简单的数据读入工具,它背后是整套跨语言内存数据格式的设计思路。
这篇文章我不打算讲那种"一行命令装完就完事"的教程,因为arrow这个包在安装层面真的没有表面上那么简单。CRAN上虽然可以直接装二进制版本,但如果你在Linux服务器上、或者想用GitHub上的最新开发版、或者需要开启S3和GCS的支持,那踩坑的概率会翻好几倍。这篇笔记会把arrow包的安装逻辑、多平台方案、常见报错、以及装完之后怎么验证和怎么和其他生态打通,全部梳理一遍,当作一份可以直接照着抄作业的完整记录。
1. arrow包的定位:它到底解决了什么问题
1.1 从Parquet和Feather说起
arrow这个R包的核心功能简单说就是两件事:第一,让你能用R直接读写Parquet、Feather(现在叫Feather V2,其实就是IPC格式)这类列式存储格式;第二,提供一套零拷贝的跨语言内存数据交换机制。它背后的底层实现是Apache Arrow C++库,R包只是一个封装层。
在没接触arrow之前,很多人的数据工作流是这样的:从数据库导出CSV → 用R读入 → 处理 → 又写成CSV → 传给Python那边继续弄。每一次CSV读写都是全量扫描、字符串解析、内存翻倍,数据量一旦上了GB级别就非常痛苦。而arrow的处理方式完全不一样:数据以列式内存格式存放,直接和文件系统里的Parquet格式对应,不用反复做序列化和反序列化。Parquet文件本身就是列式压缩存储,arrow的R包可以直接把这个文件映射进内存做查询,只加载你需要的列和行组。
我用一个具体例子说明它的价值。假设你有一个订单表,一共200个字段、5000万行,整个文件大概6GB。传统读法是把整个文件全部load进内存,机器内存小一点的直接OOM。但用open_dataset()之后,arrow会建立一个惰性查询的DataSet对象,然后你可以用dplyr的语法做筛选、聚合,真正执行collect()的时候它只扫描需要的字段和行,内存峰值可能只有原来的十分之一。
1.2 为什么安装它比装普通R包更容易出问题
arrow不是那种纯R语言写成的包,它的大部分性能核心来自C++库。CRAN上的预编译二进制版本实际上是维护者预先用特定版本的C++库编译好的。这个设计带来一个麻烦:不同平台的预编译版本不一定完全同步,某些新功能只有源码编译才能开启。
举例来说,CRAN上的Windows和macOS版本二进制包,通常会默认开启S3支持(s3=TRUE)和GCS支持,但Linux上的二进制包往往没有。如果你想在Linux上跑通S3读取,光靠install.packages("arrow")是不够的,要么用arrow::s3_bucket()的时候发现报错,要么就得老老实实从源码编译并且指定相关参数。
还有一点,arrow的依赖链中有不少需要系统级的库,比如curl、openssl、libxml2等。在Windows上这些依赖打包在Rtools里,问题不大;但到了Linux上,如果没有安装对应的-dev包,编译到一半就可能会报"找不到头文件"之类的错误。这就是为什么我强烈建议,装arrow之前先把系统环境检查一遍,而不是直接上手install.packages()然后干等。
2. 快速安装:先走通最简单的路径
2.1 CRAN预编译版本安装
如果你的需求很常规,只是想在本机的RStudio里读Parquet文件,那直接敲这一行就够了:
r复制install.packages("arrow")
这一步在Windows和macOS上一般不会出问题,大概一两分钟就能装完。装完以后验证一下版本和可用功能:
r复制library(arrow)
packageVersion("arrow")
# 检查当前安装支持的特性
arrow_info()
arrow_info()会输出一大段诊断信息,重点看几个字段:features里是否包含s3、gcs、utf8proc等;build_info里会显示C++库的版本号。如果features里没有s3,那后面你要是想直接读S3上的数据,就会碰到Error: Unsupported: S3 not enabled这类报错。
需要提醒的是,CRAN版本有一个特点:它为了保持稳定,往后退了特性。有些最新的性能优化或者新的格式版本支持,可能要等一段时间才能进CRAN渠道。所以如果你对版本有要求,或者需要用gcs、dataset、json这些新特性,建议直接走下面的方式。
2.2 用r-universe源装最新版
R社区有一个r-universe平台,Apache Arrow项目在r-universe上维护了独立的仓库,里面的包更新频率比CRAN快很多。想装最新版的arrow,可以指定仓库地址安装:
r复制install.packages("arrow", repos = "https://apache.r-universe.dev")
这个方案我之前在项目里用过一次,当时CRAN上的arrow版本还是4.x,而r-universe上已经到5.0了,新版本修复了一个关于read_parquet()在处理嵌套列表时的性能bug。如果你就是为了某个特性才用arrow,建议先到官方GitHub的Release页面看看版本情况,再决定用CRAN还是r-universe。
2.3 通过GitHub安装开发版
追求最新功能可以装GitHub上的开发版。注意,开发版可能没有预编译好的二进制包,这时R会尝试从源码编译。如果你本机没有装好编译环境,就很容易卡在编译阶段。
r复制# 如果还没安装remotes
install.packages("remotes")
# 从GitHub安装arrow开发版
remotes::install_github("apache/arrow", subdir = "r")
装开发版最好设置两个环境变量,一个是告诉R不要自动下载依赖,另一个是指定使用pkg-config来查找系统的Arrow C++库(如果系统已经装过的话):
r复制Sys.setenv(ARROW_USE_PKG_CONFIG = "TRUE")
Sys.setenv(NOT_CRAN = "true")
remotes::install_github("apache/arrow", subdir = "r")
不过实话讲,非必要不建议普通用户直接装开发版,毕竟开发版偶尔会有接口变动,你写好的代码可能过两个月就跑不了了。我的建议是:CRAN版保底,r-universe版尝新,GitHub开发版只给那些确实需要修复bug或新格式支持的场景。
3. 从源码编译:Linux服务器上的完整流程
3.1 环境准备与依赖安装
提到Linux服务器,arrow的安装难度会明显上一个台阶。原因在于,CRAN上目前没有为Linux提供统一预编译的二进制包,R在Linux上安装时一般会直接走源码编译,而arrow的C++库又很大,完整编一次可能需要二三十分钟。
我在Ubuntu 20.04、22.04上都试过,也帮朋友排查过CentOS 7(旧版本)的问题。先列一下Ubuntu上需要提前准备的依赖:
bash复制sudo apt update
sudo apt install -y \
curl \
libcurl4-openssl-dev \
libssl-dev \
libxml2-dev \
libfontconfig1-dev \
libfreetype6-dev \
libpng-dev \
libtiff5-dev \
libjpeg-dev \
cmake \
g++ \
pkg-config
这些依赖里面,cmake和g++是编译C++库的核心,libcurl、libssl、libxml2分别对应arrow在联网、加密和解析层面的依赖。如果这些没装齐,编译过程中会有各种奇葩报错。我遇到过最典型的一个是缺少libcurl导致报错 cannot find -lcurl,当时第一反应是arrow装坏了,后来排查了半天才发现是系统里根本没有libcurl的开发头文件。
3.2 源码安装的两种方式
第一种是让R自己下载并编译捆绑的C++源文件。这种方式最简单,但耗时长:
r复制install.packages("arrow", type = "source")
第二种是先编译安装Arrow的C++库,再让R包通过pkg-config方式去链接。这种方式适合你在同一台机器上还要用Python的pyarrow或者其他语言绑定的场景,可以避免重复编译。步骤大概是这样的:
bash复制# 下载Arrow源码
wget https://www.apache.org/dyn/closer.lua/arrow/arrow-14.0.2/apache-arrow-14.0.2.tar.gz
tar -xzf apache-arrow-14.0.2.tar.gz
cd apache-arrow-14.0.2/cpp
# 编译安装C++库
mkdir build && cd build
cmake .. -DCMAKE_INSTALL_PREFIX=/usr/local \
-DARROW_PARQUET=ON \
-DARROW_DATASET=ON \
-DARROW_S3=ON \
-DARROW_CSV=ON \
-DARROW_JSON=ON \
-DARROW_WITH_SNAPPY=ON
make -j$(nproc)
sudo make install
之后再回R环境里跑:
r复制Sys.setenv(ARROW_USE_PKG_CONFIG = "TRUE")
install.packages("arrow", type = "source")
这种方式有个好处,编译出来的arrow R包体积很小,因为核心逻辑都链接到了系统的共享库上。代价是你得维护C++库的版本和R包的版本对应关系,如果C++库太新而R包太旧,可能反而会因为API不匹配而编译失败。
3.3 无sudo权限的服务器怎么装
很多公司的服务器是有堡垒机管控的,你根本拿不到root权限。这种情况下源码编译就需要指定一个用户目录作为安装前缀。给个参考方案:
r复制# 指定个人目录安装C++库
cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/arrow_install \
-DARROW_PARQUET=ON \
-DARROW_DATASET=ON \
-DARROW_S3=ON
make -j$(nproc)
make install
# 告诉R去哪找库
Sys.setenv(ARROW_HOME = "~/arrow_install")
Sys.setenv(ARROW_USE_PKG_CONFIG = "TRUE")
install.packages("arrow", type = "source")
这个方法我实测过,唯一需要注意的是,编译C++库之前要确认cmake能找到依赖头文件,比如libcurl的-dev包,如果没有root装不了,那就只能找管理员帮忙提前装好基础依赖,这一关绕不过去。
4. 装完怎么验证:功能与性能双向检查
4.1 功能特性验证
装好arrow以后,我习惯跑一圈功能验证脚本,确保关键特性都是开启的。尤其是parquet、dataset、s3这三个能力,属于日常高频场景,缺一个后面都会出问题。
r复制library(arrow)
# 1. 检查特性开关
arrow_info()$features
# 2. 写一个临时parquet文件再读回来
tmp <- tempfile(fileext = ".parquet")
df <- data.frame(x = 1:100000, y = rnorm(100000))
write_parquet(df, tmp)
read_parquet(tmp) |> head()
# 3. 测试dataset API
ds <- open_dataset(tmp)
ds |>
dplyr::filter(x > 50000) |>
dplyr::summarise(avg_y = mean(y)) |>
dplyr::collect()
arrow_info()$features这段如果输出里s3是FALSE,而你后面又计划直接从S3上取数,那我建议先解决S3的问题再继续。解决方式要么换r-universe版本,要么源码编译时带ARROW_S3=ON。
4.2 性能验证
我自己在项目里常用的一个性能测试方式是,构造一个大一点的数据集,对比data.table::fread()和arrow::read_parquet()的读取效率:
r复制library(data.table)
library(arrow)
# 构造500万行、20列的数据
set.seed(42)
n <- 5e6
df <- data.frame(
id = seq_len(n),
group = sample(letters, n, replace = TRUE),
value1 = rnorm(n),
value2 = runif(n),
value3 = rpois(n, lambda = 3)
)
# 分别写出csv和parquet
fwrite(df, "/tmp/test.csv")
write_parquet(df, "/tmp/test.parquet")
# 对比读取时间
system.time(fread("/tmp/test.csv"))
system.time(read_parquet("/tmp/test.parquet"))
以我实测的数据来看,Parquet的读取时间通常仅为CSV的1/3甚至更少,而且内存占用差距更加明显。这个对比其实就是arrow为什么值得折腾安装的最好说明。
5. 典型安装问题与排查实录
5.1 Windows系统下的常见报错
报错1:安装时提示Rtools版本过旧
arrow的源码编译依赖Rtools,如果Rtools没装或者版本对不上,一般会看到Rtools is required to build R packages的提示。解决方法是到Rtools官网下载对应R版本的Rtools,安装时勾选"Add to PATH"。
不过说实话,Windows上大多数情况下直接装CRAN版就好了,没必要折腾源码编译。
报错2:版本升级后包加载失败
如果你之前装过旧版arrow,后来升级R或者升级包,可能出现package or namespace load failed。通常原因是安装目录里的旧版本文件没有清干净。我的建议是:
r复制remove.packages("arrow")
然后重启RStudio,再重新安装。如果还不行,把R的library目录下的arrow文件夹手动删掉再试。
5.2 Linux系统下的常见报错
报错3:Cannot find -lcurl或curl/curl.h: No such file
这个很直白,就是系统缺libcurl的开发包。Ubuntu上执行:
bash复制sudo apt install libcurl4-openssl-dev
CentOS/RHEL上则是:
bash复制sudo yum install libcurl-devel
这里有个容易忽略的地方:有些基础镜像里libcurl.so存在,但curl.h头文件不存在,R包编译时需要的其实是头文件而不是动态库,所以光有运行库是不够的,必须装-dev或-devel版本。
报错4:编译过程中内存不足被Killed
arrow的C++库编译非常吃内存,我之前在2GB的小机器上编过一次,make -j4直接触发OOM。解决方式是把并行编译的线程数调低:
bash复制make -j2
如果还是OOM,就把ARROW_SIMD_LEVEL关掉,因为有些CPU的SIMD指令集编译会显著增加内存开销:
bash复制cmake .. -DARROW_SIMD_LEVEL=NONE
5.3 从报错信息反推原因的思路
排查编译错误,我一直遵循"从上往下看、从第一处error入手"的原则。编译日志动辄几百行,但真正的错误往往在最开始出现的那一两条。用tail虽然方便,但有时候真正的错误在中间而不是尾部。另外一个经验是,把install.packages()的输出的configure部分单独保存下来,用grep -i "error"去过滤,效率会高很多。
6. 装好arrow之后:R、Python与大数据生态的互联
6.1 R与Python的Arrow内存零拷贝互通
arrow装好之后最惊艳的一点,是R和Python之间可以做到内存级别的数据交换。虽然R和Python各有一套arrow绑定(R的arrow包、Python的pyarrow),但它们的内存格式是统一的。
比如在R里拿到一个数据对象,不想耗时间写CSV再让Python读,可以直接保存为Feather格式,Python这边一个pd.read_feather()就搞定了。
r复制write_feather(df, "data.feather")
Python那边:
python复制import pandas as pd
df = pd.read_feather("data.feather")
这个流程我现在已经替换掉了之前"R写CSV、Python读CSV、存在中文字段就乱码"的土办法。Feather格式不仅保留数据类型,而且读写速度快得不是一点半点。
6.2 Arrow类型和Flink/Doris生态的对接
在用Flink或Doris做数据同步的场景里,Arrow经常作为内存列的传输格式被用于减少序列化开销。有一个典型问题可能很多同学在报错日志里都见过:flink type is datev2, but arrow type is dateday。这其实是Doris的Flink连接器在做类型映射时,发现Doris侧的DATEV2类型对应不到Arrow的date32/date64类型的标准映射上。
遇到这种跨系统的类型不匹配,我给出的排查思路是这样的:先确认源头表和目标表的字段类型定义,Arrow的类型系统比较严格,date32表示天数精度的日期,date64表示毫秒精度的日期,如果你的上游字段其实是一个字符串"2024-01-01",那Arrow读进来映射成什么类型就取决于你的schema定义。对Flint/Doris这类场景,建议在ETL脚本里先显式把日期字段统一cast成Arrow的date32类型,再做写入。
这个例子说明,arrow不只是R生态内部的一个包,它还是很多大数据组件之间数据对接的公共语言。你理解了它的类型系统和安装开关,排起错来会顺畅很多。
6.3 结合glmm统计建模的大数据预处理思路
说实话,glmm可以用什么r包做这个问题很多做生态学、医学统计的同学都问过。常用的R包有lme4、glmmTMB、rstanarm这些,但它们的共同缺点是:原始数据一旦到了几GB的量级,处理起来就特别吃力,因为lme4和glmmTMB在拟合之前要把完整的data.frame全部读进内存。
arrow的定位正好能作为这些统计包的"前端":先用open_dataset()做数据筛选、聚合、抽样,把真正建模需要的那部分数据reduce到几百MB以内,再用collect()取回R,最后喂给glmmTMB或者lme4。我之前处理过一个包含几千万行观测的生态数据集,分组变量几百个,随机效应结构比较复杂,直接read.csv()根本跑不动。用arrow做了按年份和地点过滤之后,数据量只剩十分之一,建模速度也从原来的一小时提到了五分钟以内。
顺便提一句,glmmTMB现在也支持直接读取一些外部矩阵的索引方式,但本质上它还是需要data.frame。所以arrow的作用,是为这些经典统计模型打通"大数据到R内存"的最后一公里。
7. 安装过程里容易被忽略的细节
我发现很多人装arrow,卡住的地方不在技术难度,而是一些特别基础的细节。这里列几个我踩过的坑。
第一,install.packages("arrow")之前要先更新R到最新版。老版本的R可能会因为缺少某些C级API的支持,导致二进制包装不上。你如果看到ERROR: this R is version 3.x, package 'arrow' requires R >= 4.0,那只能升级R。
第二,.libPaths()里面的路径权限问题。公司电脑上R的默认包目录如果不在用户目录,而C盘又开了写保护,安装会有一部分文件写不进去。我的习惯是把自己的包目录设置到用户目录下,避免每次装包都弹权限提示。
第三,如果之前装过arrow的一些依赖包,比如vctrs、rlang、tidyselect,版本太旧也可能触发一系列兼容性问题。一个比较稳妥的办法,是先去CRAN页面看看arrow的Imports依赖要求,用update.packages(ask = FALSE)先升级相关依赖。
第四,arrow这个包体积大,安装时不要在内存很低的虚拟机里搞,至少预留2GB以上的内存给编译过程比较稳。尤其是源码编译时,内存不足的报错特别容易和别的报错混淆。
8. 我自己的一套装包流程模板
说了这么多,最后总结一下我日常装arrow的标准操作流程,供你直接参考。
如果你是在自己的电脑上而且只要常规功能:
r复制# 第一步:升级R到最新版
# 第二步:安装CRAN版
install.packages("arrow")
# 第三步:验证功能
library(arrow)
arrow_info()$features
如果发现features里面s3是FALSE,或者某些你需要的功能没开:
r复制# 换r-universe仓库
install.packages("arrow", repos = "https://apache.r-universe.dev")
如果公司服务器上需要完整功能:
r复制# 1. 先让管理员装好编译依赖
# 2. 配置缓存目录
Sys.setenv(ARROW_USE_PKG_CONFIG = "TRUE")
# 3. 源码编译
install.packages("arrow", type = "source")
装完以后不要急着删安装日志,保存一份arrow_info()的输出,后面如果出现奇怪的问题,无论是去GitHub提issue还是自己排查,都能省很多时间。
我个人在实际项目里的习惯是,把arrow的安装版本记录在项目的renv.lock或者requirements.txt里,和Python环境一样做锁版本管理。因为arrow这个包升级频率不低,不同版本在一些API细节上会有变化,团队协作时如果不锁版本,很容易出现"你那边能跑、我这边报错"的尴尬。
希望这篇安装记录能帮你少走点弯路。arrow这个东西,装好它只是第一步,后面真正用好它的数据集、Parquet、内存交换这些能力,才是让它发挥价值的地方。如果你后续在安装过程中遇到什么新的报错,欢迎拿着报错信息再来聊。
