引言
juicefs是一款面向云原生设计的高性能分布式文件系统,其有如下特点:
- 数据存储和元数据存储分离,可以适配多种数据和元数据存储引擎。
- 后端存储可以直接对接各种对象存储,使用起来更方便,更加适配云服务趋势。
相关技术架构可直接参考:https://juicefs.com/docs/zh/community/architecture
源码分析
读同步线程处理
func (fs *fileSystem) Read(
// 获取filehandle
func (v *VFS) findHandle(
// filehandle加读锁
func (h *handle) Rlock(
// fileWriter刷盘
func (w *dataWriter) Flush(
func (f *fileWriter) flush(
// fileWriter加mutex锁
func (m *Mutex) Lock(
// 遍历所有chunk和chunk上的slice,进行slice刷盘(异步)
func (s *sliceWriter) flushData(
// 等待所有chunk刷盘完成
// fileWriter释放mutex锁
func (m *Mutex) Unlock(
// fileReader读数据
func (f *fileReader) Read(
// fileReader加mutex锁
func (m *Mutex) Lock(
// 遍历fileReader上所有的slice,根据与读取数据范围相互覆盖,生成一个rangemap
func (f *fileReader) splitRange(
// 根据rangemap,生成一组读盘request
func (f *fileReader) prepareRequests(
// 遍历每一个range,遍历每一个slice
// 1、若slice数据包含range,生成一个request,表示slice已经由之前的io缓存在内存里
// 2、若slice数据不包含range,创建一个slice,生成一个request
func (f *fileReader) newSlice(
// 创建一个slice后,创建线程读数据(异步),具体流程见slice读数据章节
func (s *sliceReader) run(
// 等待所有request完成,拷贝数据到返回的内存
func (f *fileReader) waitForIO(
// fileReader释放mutex锁
func (m *Mutex) Unlock(
// filehandle释放读锁
func (h *handle) Runlock(
slice读数据
func (s *sliceReader) run(
// fileReader加mutex锁
func (m *Mutex) Lock(
// 设置state为busy
s.state = BUSY
// fileReader释放mutex锁
func (m *Mutex) Unlock(
// 读元数据
func (m *dbMeta) Read(
// 从内存openfiles中找到slice,直接返回
func (o *openfiles) ReadChunk(
// 从数据库中获取chunk对应的所有slice,遍历所有slice,从前到后覆盖,获取一组需要读的slice
func (m *dbMeta) roTxn(
// 缓存chunk到openfiles中
func (o *openfiles) CacheChunk(
// 如果返回的slice过多,说明有一定的覆盖关系或有很多小的slice,触发compact(异步)
func (m *dbMeta) compactChunk(
// 从对象存储读数据
func (r *dataReader) Read(
// 若slice超过16个,转异步(异步)
func (r *dataReader) readManySlices(
// 若slice小于16个,遍历所有slice读
func (r *dataReader) readSlice(
func (s *rSlice) ReadAt(
// 根据sliceid,chunkidx,blocksize生成key,用于对象存储文件名
func (s *rSlice) key(
// 读磁盘cache,若命中直接返回
func (cache *cacheStore) load(
// 从对象存储读取数据
in, err := s.store.storage.Get(key, int64(boff), int64(len(p)))
// 将key加入预取队列,进行异步预取(异步)
func (p *prefetcher) fetch(
异步预取
func (p *prefetcher) do(
// 遍历每个预取key,进行预取,执行函数在newPrefetcher时传入,即func(key string) {...
func(key string) {
// 读取数据
func (store *cachedStore) load(
// 从对象存储读数据
in, err = store.storage.Get(key, 0, -1)
// 数据存入cache
func (cache *cacheStore) cache(
// 加cache锁
cache.Lock()
// 将key和数据插入内存cache
// 解除cache锁
cache.Unlock()
淘汰
// 当cache空间不足时,后台cache check线程,触发淘汰
func (cache *cacheStore) checkFreeSpace(
// 当cache空间不足时,后台cache check线程,触发淘汰
// 加cache锁
func (cache *cacheStore) cleanup(
// 遍历内存cache中的kv,根据atime淘汰数据
// 将内存cache中的kv删除
delete(cache.keys, lastKey)
// 解除cache锁
// 删除磁盘cache中的数据
_ = os.Remove(cache.cachePath(key))
// 加cache锁
// 解除cache锁
关键总结
- 磁盘cache中缓存干净数据,其中文件名为对象存储文件名
- 内存cache中指缓存key,不缓存干净数据
- 内存cache的预取和淘汰通过cache锁互斥
- 磁盘cache中的数据通过后台预取获得,预取的条件是读命中的key
- 读和读的互斥点在fileReader上,如果读遇到正在预取的slice,等待slice预取完成后直接读数据
参考文献
https://juicefs.com/docs/zh/community/architecture
https://github.com/juicedata/juicefs