清华主页 - 清华新闻 - 综合时讯 - 正文

JuiceFS:编写流程源码分析 刷盘 数据一致性分析

引言。

juicefs是为云原生设计的高性能分布式文件系统,xff1的特点如下a;

  • 分离数据存储和元数据存储c;可适应多种数据和元数据存储引擎。

  • 后端存储可以直接连接到各种对象存储和xff0c;使用起来更方便,更适合云服务趋势。

直接参考相关技术架构:https://juicefs.com/docs/zh/community/architecture。

写流程cache简要说明。

cache分为两层󿀌磁盘cache和内存cache。

  • 磁盘cache:刷数据时,首先写入磁盘cache即本地文件系统,然后将文件上传到对象存储󿀌然后删除本地文件系统文件。

  • 内存cache:内存cache只记录key和datasize。

源码分析。

编写同步线程处理。

func (fs *fileSystem) Write(cancel <-chan struct{ }, in *fuse.WriteIn, data []byte) (written uint32, code fuse.Status)    func (v *VFS) Write(ctx Context, ino Ino, buf []byte, off, fh uint64) (err syscall.Errno)        // filehandle获得        func (v *VFS) findHandle(inode Ino, fh uint64) *handle        // 加文件写锁,如果有其他client并发写,等待        func (h *handle) Wlock(ctx Context) bool        // filewriter编写数据        func (f *fileWriter) Write(ctx meta.Context, off uint64, data []byte) syscall.Errno            // 目前file使用了1000多个slice,或者使用的buffersize大于预留,等待            // 加filewriter mutex锁,使用cas操作            f.Lock()            // 等待正在进行的flush完成            // offset和length󿀌拆分chunk,单独写每个chunk            func (f *fileWriter) writeChunk(ctx meta.Context, indx uint32, off uint32, data []byte) syscall.Errno                // 根据文件的chunk idx找到相应的chunkwriter                func (f *fileWriter) findChunk(i uint32) *chunkWriter                // 在chunkwriter中找到slicewriter,如果没有合适的slice,申请新写作                func (c *chunkWriter) findWritableSlice(pos uint32, size uint32) *sliceWriter                    // 每一个slice￰遍历c;发现未覆盖的slice直接使用,如果没有可用的slice,再在外面申请一个                // 每个chunk󿼌当第一个slice生成时,启动异步线程,等待数据写盘,刷slice到对象存储的映射。                func (c *chunkWriter) commitThread()                    // slicewriter写数据                func (s *sliceWriter) write(ctx meta.Context, off uint32, data []uint8) syscall.Errno                    // 将数据写入buffer                    func (s *wSlice) WriteAt(p []byte, off int64) (n int, err error)                    // 如果只是写满chunk,异步刷盘#xff08;异步)                    func (s *sliceWriter) flushData()。 

slice刷盘。

func (s *sliceWriter) flushData(    // 生成sliceid    func (s *sliceWriter) prepareID(    // 刷盘    func (s *wSlice) Finish(        func (s *wSlice) FlushTo(            // 遍历每一个block刷盘            func (s *wSlice) upload(            // sliceid󰀌chunkidx,blocksize生成key󿀌用于对象存储文件名                func (s *rSlice) key(                // slice数据写盘(异步)                go func() {     // 标记slice刷盘完成    func (s *sliceWriter) markDone(。

slice数据写盘。

func (s *wSlice) upload(    go func()        // 写磁盘cache,存储在加速盘中        func (cache *cacheStore) stage            // 生成路径󿀌与对象存储相关的路径一致,将数据写到本地文件系统目录            func (cache *cacheStore) flushPage(                // 将数据写入磁盘cache༌即使用os本地文件系统写文件                func (f *File) Write(            // 插入keymap,将key、datasize、atime,插入内存cache            func (cache *cacheStore) add(            // 如果内存cache空间不足,触发淘汰                    // 数据上传到对象存储        func (store *cachedStore) upload(            // 存储上传对象            func (store *cachedStore) put(                // 更新内存cache中的key,与data相对应        func (cache *cacheStore) uploaded(        // 删除本地文件系统cache的文件        os.Remove(stagingPath)。

关键总结。

  • 关键总结

  • 多客户端如何保证数据一致性:不同的客户端在阅读和写作时需要先调用setlk、SetLkw、Flock接口󿼌确保客户端之间的相互排斥。

  • 并发读写如何保证数据一致性:使用filehandle上的读写锁来保证数据的一致性。

  • 并写如何保证内存原子性:使用filewriter上的mutex锁保证原子性。

  • 如何保证元数据和数据的一致性:在写数据到盘的过程中,使用追加写,未修改元数据时,不会更新数据,保证原子性。inode信息(不会更新;如size等),block到存储对象的映射只会更新#xff0c;所以inode信息的更新不会并发。

  • 如何确保inode数据和文件的一致性:不能保证󿀌只有close文件后,inode信息࿰将更新c;因此,juicefs只能保证最终inode和文件信息的一致性。

slice中的连续小io将在slice中聚合,等待触发刷slice。

参考文献。

https://juicefs.com/docs/zh/community/internals。

2025-06-24 11:41:52

相关新闻

清华大学新闻中心版权所有,清华大学新闻网编辑部维护,电子信箱: news@tsinghua.edu.cn
Copyright 2001-2020 news.tsinghua.edu.cn. All rights reserved.