原文链接 https://wxaxiaoyao.cn/article/105
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链, 也是提交历史.
文件回退流程:
- 遍历 commit 链, 选择需要的版本提交点.
- 通过 commit 记录的根tree索引, 获取指定版本的文件树, 递归遍历查找指定文件项
- 根据文件项记录的记录的文件索引去读取文件内容.
详细原理分析可读此篇文章: 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
- 其它拆库方案
- 单库文件数过多时, 程序是否可以自动拆库, 降低客户端心智负担.