1 背景Curve(https://github.com/opencurve/... )是网易数帆自主设计研发的高性能、易运维、全场景反对的云原生软件定义存储系统,旨满足Ceph自身架构难以撑持的一些场景的需要,于2020年7月正式开源。以后由CurveBS和CurveFS两个子项目形成,别离提供分布式块存储和分布式文件存储两种能力。其中CurveBS曾经成为开源云原生数据库PolarDB for PostgreSQL的分布式共享存储底座,撑持其存算拆散架构。
在CurveBS的设计中,数据服务器ChunkServer数据一致性采纳基于raft的分布式一致性协定去实现的。
典型的基于raft一致性的写Op实现如下图所示:
以常见的三正本为例,其大抵流程如下:
首先client 发送写op(步骤1),写op达到Leader后(如果没有Leader,先会进行Leader选举,写Op总是先发送给Leader),Leader首先会接管写Op,生成WAL(write ahead log),将WAL长久化到本地存储引擎(步骤2), 并同时并行将WAL通过日志发送rpc发送给两个Follower(步骤3)。两个Follower在收到Leader的日志申请后,将收到的日志长久化到本地存储引擎(步骤4)后,向Leader返回日志写入胜利(步骤5)。一般来说,Leader日志总是会先实现落盘,此时再收到其余一个Follower的日志胜利的回复后,即达成了大多数条件,就开始将写Op提交到状态机,并将写Op写入本地存储引擎(步骤6)。实现上述步骤后,即示意写Op曾经实现,能够向client返回写胜利(步骤7)。在稍晚一些工夫,两个Follower也将收到Leader日志提交的音讯,将写Op利用到本地存储引擎(步骤9)。在目前CurveBS的实现中,写Op是在raft apply 到本地存储引擎(datastore)时,应用了基于O_DSYNC关上的sync写的形式。实际上,在基于raft曾经写了日志的状况下,写Op不须要sync就能够平安的向client端返回,从而升高写Op的时延,这就是本文所述的写时延的优化的原理。
其中的代码如下,在chunkfile的Open函数中应用了O_DSYNC的标记。
CSErrorCode CSChunkFile::Open(bool createFile) { WriteLockGuard writeGuard(rwLock_); string chunkFilePath = path(); // Create a new file, if the chunk file already exists, no need to create // The existence of chunk files may be caused by two situations: // 1. getchunk succeeded, but failed in stat or load metapage last time; // 2. Two write requests concurrently create new chunk files if (createFile && !lfs_->FileExists(chunkFilePath) && metaPage_.sn > 0) { std::unique_ptr<char[]> buf(new char[pageSize_]); memset(buf.get(), 0, pageSize_); metaPage_.version = FORMAT_VERSION_V2; metaPage_.encode(buf.get()); int rc = chunkFilePool_->GetFile(chunkFilePath, buf.get(), true); // When creating files concurrently, the previous thread may have been // created successfully, then -EEXIST will be returned here. At this // point, you can continue to open the generated file // But the current operation of the same chunk is serial, this problem // will not occur if (rc != 0 && rc != -EEXIST) { LOG(ERROR) << "Error occured when create file." << " filepath = " << chunkFilePath; return CSErrorCode::InternalError; } } int rc = lfs_->Open(chunkFilePath, O_RDWR|O_NOATIME|O_DSYNC); if (rc < 0) { LOG(ERROR) << "Error occured when opening file." << " filepath = " << chunkFilePath; return CSErrorCode::InternalError; }...}2 问题剖析先前之所以应用O_DSYNC,是思考到raft的快照场景下,数据如果没有落盘,一旦开始打快照,日志也被Truncate掉的场景下,可能会丢数据,目前批改Apply写不sync首先须要解决这个问题。 首先须要剖析分明Curve ChunkServer端打快照的过程,如下图所示:
...