10 万条记录改一条:文本文件 737 毫秒,数据库 0.2 毫秒
同样 10 万条数据改一条:文件 737 毫秒,数据库 0.2 毫秒。

我本来只想给网页换个中文字体,结果发现字体包 18.6 MB。按页面实际用到的字裁一遍,发出去的是 232.7 KB,是原来的 1.22%。
站上的正文一直用系统默认字体。我想换成思源黑体(Noto Sans CJK),服务器上这个字体放在 /usr/share/fonts/opentype/noto/,文件 19,484,784 字节,也就是 18.6 MB。
这一个文件里装了 10 套字体:简体、繁体、日文、韩文,每套还带一个等宽版本。我只要简体那套。
现在文章页整页 10,138 字节,压缩后传输 4,694 字节。挂上原字体,访客看一篇文章要先下 18.6 MB。就像为了查一个字,买下整本字典。
先把简体那一套从大包里拆出来。这种「一个文件装多套字体」的格式叫 ttc(字体合集)。用 Python 的 fontTools 拆:
from fontTools.ttLib import TTCollection
c = TTCollection('/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc')
print([f['name'].getDebugName(4) for f in c.fonts]) # 先看 10 套都叫什么
c.fonts[2].save('NotoSansSC-full.ttf') # 第 3 套才是简体
拿到单套简体:16,437,340 字节(15.7 MB),44,810 个字符,拆包花了 0.85 秒。还是太大。
再数页面到底用到多少字。我直接查站点数据库里的 17 篇文章正文:
cur.execute('SELECT body FROM articles WHERE published=1')
txt = ''.join(r[0] for r in cur)
print(len(txt), len(set(txt))) # 35656 1024
35,656 个字,不重复的只有 1,024 个(汉字 894 个)。整本 44,810 个字,我站上只用得到这一千个。
按这份字表裁剪。这个动作叫子集化(只留下用到的字,其余扔掉)。工具是 fontTools 自带的小程序,一条命令,跑 3.5 秒:
python3 -m fontTools.subset NotoSansSC-full.ttf \
--text-file=site-chars.txt \
--output-file=site-sub.woff2 \
--flavor=woff2 --no-hinting
--flavor=woff2 指输出成 woff2,这是网页专用的字体格式,浏览器直接认。--no-hinting 是去掉屏幕上微调字形的小指令,网页上基本看不出来。
同一次裁剪,换个存法体积差很多:
| 存法 | 大小 | 说明 |
|---|---|---|
| 原始 ttc | 18.6 MB | 10 套字体,不能直接给网页 |
| 单套简体 ttf | 15.7 MB | 44,810 个字符 |
| 整包转 woff2 | 10.9 MB | 压掉 41%,还是 10 MB 级 |
| 子集 ttf | 318.4 KB | 只留 1,022 个字 |
| 子集 ttf 再 gzip | 264.1 KB | 比原样小 17% |
| 子集 woff2 | 232.7 KB | 整包的 1.22% |
| 子集 woff2 关排版特性 | 141.0 KB | 再省 39.4% |
「整包转 woff2」是把整个 15.7 MB 的 ttf 直接存成 woff2,花了 71.8 秒,出来 10.9 MB。光靠压缩救不了,得裁字。
末行的 141.0 KB 是加了 --layout-features='',把连字、标点挤压这些排版特性一起删掉。横排正文看不出区别,竖排和精细标点就丢了,页面用不上再删。
单篇文章单独裁更小:最新那篇正文 2,549 字、282 个不重复汉字,裁出来 76.9 KB,是整站子集的三分之一。但文章一多就得一篇一个文件,浏览器缓存反而用不划算,我这次还是共用一份。
文件变小不等于浏览器用上了。我用浏览器调试接口(CDP)把字体挂到测试页,量了三组:
Video CRF 23:子集字体宽 240.53 像素,系统衬线字体 230.97 像素,差 4.14%,字确实换上了。再看慢网。把网速限到 125 KB/s(大约 1 Mbps),在 0.6 秒、1.2 秒、2.0 秒各截一次图,数深色像素:
font-display: swap 的页面:0.6 秒就有 14,631 个深色像素(先用系统字体顶上),2.0 秒字体到货变成 14,650,字换了、位置没跳。232.7 KB 是整包的 1.22%,比一张手机照片还小。裁一次 3.5 秒,这步别省。
四个坑:
--text-file 重裁。font-display: swap。下次复用按这个顺序:数用字 → 裁子集 → 存 woff2 → 限速看一眼。
同样 10 万条数据改一条:文件 737 毫秒,数据库 0.2 毫秒。
同一张照片 10 种存法,4 种把定位带出去,涂黑也白涂。
同一段 63 秒视频 9 种压法实测:设得越清楚,文件越大画质没变好。