3. 加密模式(ECB、CBC、GCM等)对吞吐量的影响
加密模式这玩意,说白了就是决定「怎么把明文切成块,再一块块扔进算法里」。刚开始接触字段级加密的时候,我习惯用ECB,因为简单。但后来在压测环境里跑了一轮,才发现不同模式之间的吞吐量差距能到一个数量级。你想想看,同样一颗CPU,选错模式可能白白浪费几十倍的性能。
3.1 ECB模式:快,但有硬伤
ECB(电子密码本模式)是所有模式里最简单的——每个16字节块独立加密,互不影响。这种「各扫门前雪」的设计让它天然支持并行。我在项目里用OpenSSL跑过AES-128-ECB,单线程吞吐量能到800 MB/s左右,四线程直接飙到2.5 GB/s。
个人习惯:如果只是做吞吐量基线测试,我通常先用ECB摸一下硬件的上限,因为它的并行瓶颈最小。但千万别在生产用——块之间没有关联,同样的明文块会生成同样的密文块,太容易泄露模式了。
不过ECB有一个致命问题:它不抵抗重放攻击。我记得有一次帮一个金融客户做审计,他们的老系统用了ECB加密交易流水号,结果攻击者通过观察密文块就能判断哪些交易金额相同……嗯,相当危险。
3.2 CBC模式:串行依赖让吞吐量打折
CBC(密码分组链接模式)引入了「链式依赖」——每个块的加密都要等前一个块的密文算出来才能开始。这种前后依赖导致它几乎没法并行。我在压测中看到的数据是:AES-128-CBC单线程吞吐量大概只有ECB的70%~80%,而且加了解密更惨,解密虽然可以并行(因为知道所有密文块),但实际实现中很多库也没做并行优化。
为什么会这样?因为加密时每个块都要等前一个块的IV或密文。你想想看,CPU在等数据的时候只能干洗流水线,吞吐量自然上不去。我曾经在百万QPS的支付场景里硬上了CBC,结果CPU持续100%,头都大了。
避坑指南:我曾经在32核服务器上用单线程跑CBC加密,吞吐量只有80 MB/s。后来改成CTR模式,直接翻了8倍。记住:CBC适合数据量小、并行为次要的场景,比如单个字段偶尔加密,不适合批量流水线。
3.3 GCM模式:认证加密的吞吐量真相
GCM(Galois/Counter Mode)是当前最常用的认证加密模式。它把CTR模式的并行能力和GMAC消息认证码结合。理论上,GCM的加密核心是CTR,可以完全并行。但是——这个「但是」很重要——GMAC部分要求乘法运算,并且GCM的认证标签计算对数据顺序有依赖。
我在实测中发现:Intel的AES-NI + PCLMULQDQ指令对GCM有硬件加速,吞吐量非常可观。AES-128-GCM在核显加速下能到2 GB/s,跟ECB差不太多。但要是没有硬件支持,纯软件GCM的GMAC乘法会拖慢很多,可能只有CBC的水平。
我记得有一次在ARM架构的低功耗设备上测试,没有PCLMULQDQ指令,GCM吞吐量只有ECB的40%——这个差距太大了。所以GCM的性能好不好,依赖硬件加速的程度远超你的想象。
3.4 其他模式速览
除了上面三个,还有几个常见模式:
- CTR(计数器模式):跟ECB一样可以并行,而且没有ECB的安全隐患。我一般把它当作「更安全的ECB」来用。
- CCM(Counter with CBC-MAC):认证加密,但计算量比GCM大,吞吐量通常只有GCM的60%~70%。
- OCB(Offset Codebook Mode):理论上最快,但专利问题导致很多库不支持生产环境。
3.5 吞吐量对比:实测数据
下面这张表是我在3.5 GHz Xeon上用AVX2加速得出的结果(AES-256,16KB数据块,单线程):
| 加密模式 | 加密吞吐量 (MB/s) | 解密吞吐量 (MB/s) | 并行度 | 是否认证 |
|---|---|---|---|---|
| ECB | 1032 | 1028 | 完全并行 | 否 |
| CBC | 712 | 689 | 串行(加密) | 否 |
| GCM(硬件加速) | 985 | 978 | 几乎并行 | 是 |
| CTR | 1020 | 1015 | 完全并行 | 否 |
| CCM | 420 | 405 | 半串行 | 是 |
关键结论:如果只是需要机密性(不要求认证),CTR是最好的并行模式。如果需要认证加密且硬件支持AES-NI + PCLMULQDQ,GCM是首选。ECB虽然快但不要在生产用——安全团队会来找你麻烦的。
3.6 模式选择对字段级加密的影响
字段级加密有个特点:每个字段长度通常很小,比如手机号11字节、身份证18字节。小数据块模式下,模式的开销占比会更大。我遇到过的情况:用GCM加密16字节的字段,认证标签和初始向量的额外开销甚至比加密本身还大。这时CTR模式就很有优势——不需要额外认证,而且可以复用初始向量(只要保证每个字段的计数器唯一)。
另外要注意:有些云厂商的加密SDK默认用CBC,我调优时第一件事就是把它改成GCM或CTR。一次优化就能把字段加密延迟从2ms降到0.5ms以下,效果非常明显。
// 性能测试代码片段(伪代码)
for each模式 in [ECB, CBC, GCM, CTR] {
time = measure_encrypt(mode, data_size, thread_count)
print(mode + ": " + time + " MB/s")
}
3.7 核心逻辑一图流
下面这张图总结了我刚才讲的不同模式在并行度、安全性和吞吐量上的取舍关系:
看到这张图你应该能快速定位:如果字段级加密追求极致吞吐量,且不介意额外做数据完整性校验,CTR是最省心的选择。如果必须用GCM,一定要确认目标平台的指令集支持情况——我吃过这个亏,在旧服务器上GCM跑得比CBC还慢,查了半天才发现是没硬件加速。
我的建议:压测阶段不要只测默认模式。把ECB、CTR、GCM都跑一遍,画出吞吐量-数据块大小的曲线,你会发现有些模式在小块(< 64字节)下效率急剧下降。这个曲线的拐点,就是你实际业务里最应该避开的陷阱。