作者:  逍遥 更新于: 2019-09-29 09:09 访问量: 22

# GIT 存储服务性能分析与优化

Git 本质是一套内容寻址 (content-addressable) 文件系统, 但由于其将内容哈希成一个 sha 作为索引, 故也称对象存储或键值存储(key-value-store).

# 背景介绍

现在很多产品都带有历史回退功能, 笔记产品尤为常见, 这也是非常重要的, 特别对于一些文字工作相关的, 经常会写一些东西然后改来改去, 最后发现还是之前的写的好, 这时想回退旧版本, 就必须记录历史.

# GIT 历史实现原理

git 有三类对象blob, tree, commit, 这三类会以文件形式存储在 git 根目录下 objects 目录中, 每个对象都会有个对应的索引哈希值(sha), 哈希值(sha) 几乎等同于其文件名(前两位为目录, 后面部分为文件名).

  • blob 对应仓库中的文件.
  • tree 对应目录, 包含blob索引值和以及子tree索引值. 因此根 tree 便记录整个仓库的文件信息.
  • commit 对应版本, 其内容包含当前版本根tree索引, 以及上次commit索引, 以及其它提交信息. 由于commit存在上次commit索引, 便可形成整个commit链, 也是提交历史.

文件回退流程:

  1. 遍历 commit 链, 选择需要的版本提交点.
  2. 通过 commit 记录的根tree索引, 获取指定版本的文件树, 递归遍历查找指定文件项
  3. 根据文件项记录的记录的文件索引去读取文件内容.

详细原理分析可读此篇文章: GIT 原理分析

# index 文件

以根tree为起点是可以递归获取整个仓库的文件索引信息的, 但每个tree都是一个文件, 通过tree去获取会不停的读文件, 性能很差. 为此git 会将整个仓库的文件索引信息单独记录在一个index文件中, 每次通过读取index文件内容来获取整个仓库的文件记录.

# git 服务性能问题

由于git通过index文件记录仓库的文件信息. 因此当仓库的文件数越多, index 文件大小就越大, git 服务程序越占内存. 同时由于要保证index文件正确性, git 服务是没法支持并发写同一个仓库的多个文件. 当并发写多个仓库的文件时, 程序会同时加载多个仓库的index文件到内存. 而硬件内容是有限的, 因此index文件大小也直接影响到并发写多个仓库的数量. 由此可见仓库文件数过多确实会影响git服务的性能.

# git 服务性能优化

git 本职功能是代码管理, 若 git 服务仅用于代码管的话, 其性能问题其实还好, 因为没有并发写同一仓库的问题存在. 但是将 git服务 作为普通的存储服务, 那么并发写同一仓库的可能性会很高(仓库内容越多, 可能性也越高). 也许有人会说 git 不适合此种场景, 本文暂不讨论此说法, 是建立 git 做存储服务上讨论. 解决 git 性能最好的办法便是降低单仓库的文件数, 而作为普通的存储服务关心的是单文件的历史, 并不关心整个仓库的整体的历史. 所以降低单仓库的思路是可行的. 而降低单仓库的文件数又便于管理直接办法便是: 目录即仓库, 将所有目录都初始化为仓库, 同时限制单目录(仓库)的文件数.

# TODO

  • 其它拆库方案
  • 单库文件数过多时, 程序是否可以自动拆库, 降低客户端心智负担.