JuiceFS:读流程源码分析 + 预取 + 淘汰

文章目录

  • 引言
  • 源码分析
    • 读同步线程处理
    • slice读数据
    • 异步预取
    • 淘汰
  • 关键总结
  • 参考文献

引言

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

你可能感兴趣的:(JuiceFS,分布式,大数据,云计算)