传统分布式存储系统大多沿用POSIX文件语义,元数据服务器和锁管理很容易在高并发下变成性能短板。DAOS(Distributed Asynchronous Object Storage)选择了完全不同的路线:它把数据抽象为对象,放弃全局POSIX命名空间,让客户端通过异步API直接访问存储节点。这种设计大幅减少了元数据交互次数,更适合现代NVMe设备和RDMA网络。

在DAOS中,一个关键概念是“持久内存优先”。DAOS的元数据并不落盘到传统文件系统,而是存放在Intel Optane这类持久内存中。这样做的好处是持久内存的读写延迟远低于SSD,而且支持字节级寻址,元数据操作不再需要经过页缓存和块设备层。数据本身则分布在多个存储节点上的NVMe SSD中,通过DAOS引擎统一管理。
DAOS的存储模型与对象寻址方式
DAOS把存储单元叫做对象,每个对象属于一个容器,容器又属于一个池。对象内部可以按dkey和akey组织数据,dkey可以理解为分布键,akey是属性键。一个对象可以有多个dkey,每个dkey下可以有多个akey,每个akey对应一个值,值可以是单值也可以是数组。这种双层键的设计让DAOS既支持类似键值存储的简单访问,也能表达更复杂的数据布局。
对象在集群中的位置由一致性哈希决定。DAOS使用可扩展的哈希环,将对象映射到具体的存储目标上。每个存储目标由一个存储节点上的一个NVMe设备加一块持久内存区域组成。当客户端写入对象时,会先计算出目标节点,然后通过RDMA网络直接把数据发送到目标设备的DMA缓冲区。这种跳过服务器CPU参与数据路径的方式,大幅降低了存储访问延迟。
下面是一段使用DAOS C API创建容器并写入单值对象的简化代码。它展示了DAOS异步模型的典型用法:先创建事件队列,再发起异步请求,最后等待完成。
daos_handle_t poh; /* 池句柄 */
daos_handle_t coh; /* 容器句柄 */
daos_handle_t oh; /* 对象句柄 */
daos_event_t ev;
daos_handle_t eq;
int rc;
/* 初始化DAOS,连接池和容器 */
rc = daos_init();
rc = daos_pool_connect("pool1", NULL, DAOS_PC_RW, &poh, NULL, NULL);
rc = daos_cont_open(poh, "cont1", DAOS_COO_RW, &coh, NULL, NULL);
/* 创建事件队列 */
rc = daos_eq_create(&eq);
/* 打开或创建对象 */
daos_obj_id_t oid;
daos_obj_generate_oid(coh, &oid, DAOS_OT_KV_HASHED, OC_S1, 0);
rc = daos_obj_open(coh, oid, DAOS_OO_RW, &oh, NULL);
/* 准备写入数据 */
char *buffer = "hello daos";
daos_size_t len = strlen(buffer);
d_iov_t iov;
d_sg_list_t sgl;
iov.iov_buf = buffer;
iov.iov_buf_len = len;
iov.iov_len = len;
sgl.sg_nr = 1;
sgl.sg_iovs = &iov;
/* 异步写入单值对象 */
rc = daos_obj_update(oh, DAOS_TX_NONE, 0, &sgl, &ev);
rc = daos_eq_poll(eq, 1, DAOS_EQ_WAIT, NULL, NULL);
if (rc == 0) {
printf("async update completed\n");
}
这段代码省略了错误处理细节,但能看出DAOS API的几个特征:对象ID由客户端生成,写入操作带事件参数,用户通过事件队列轮询完成状态。这种设计允许同一个线程同时发起大量I/O,不需要为每个请求创建线程,从而支撑起百万级IOPS的高并发场景。
DAOS引擎内部结构与异步事务机制
DAOS服务端运行一个叫做daos_engine的进程,它直接管理持久内存和NVMe设备。引擎内部按照存储目标划分多个VOS(Versioning Object Store)实例。VOS是DAOS的本地对象存储层,负责维护对象的版本化索引和数据布局。每个VOS实例拥有独立的持久内存日志和NVMe数据区,避免实例之间的锁竞争。
DAOS的事务模型不是传统数据库的ACID事务,它提供的是带版本号的乐观并发控制。客户端每次更新对象时可以选择创建新版本,旧版本保留到显式删除。这样读操作可以始终拿到一致快照,而不会阻塞写操作。对于需要原子性的多对象更新,DAOS提供分布式事务接口,但代价是引入跨节点协调。实际应用中,许多AI训练场景只需要单对象原子性,因此直接使用DAOS_TX_NONE就能获得最佳性能。
异步I/O的完成通知方式也很灵活。除了事件队列,DAOS还支持回调函数和进度上下文。回调方式适合把存储I/O嵌入到事件驱动框架中。例如在数据预处理流水线中,加载完一个批次的数据后立即触发下一个批次的读取,可以避免CPU空转等待存储。
DAOS与Lustre、Ceph的对比及适用场景
Lustre是传统的并行文件系统,强项在于POSIX兼容和成熟的HPC生态,但元数据集中式管理导致小文件和高频元数据操作延迟较高。Ceph的RADOS对象存储具备良好的扩展性和自愈能力,但其数据路径涉及多次网络跳转和副本写入,在低延迟场景下不如DAOS直接。DAOS利用持久内存和RDMA,将单次对象更新延迟控制在个位数微秒,这是前两者难以做到的。
不过DAOS并不是万能替代品。它放弃了POSIX语义,现有大量依赖文件接口的应用需要改造才能接入。DAOS的部署也假设了高速网络和持久内存硬件,硬件成本较高。因此DAOS最适合新建的、面向海量非结构化数据的高性能应用,例如深度学习训练集缓存、检查点加速、基因分析中间结果存储等。
从架构思考角度看,DAOS把一个存储请求的数据路径压缩到极致:客户端绕过服务器操作系统,直接把数据写入远端NVMe。这种设计牺牲了部分通用性,换来了可预测的微秒级延迟和高并发吞吐。对于正在设计下一代数据平台的团队,DAOS提供了一个值得借鉴的异步存储引擎范式。