加密集合
字段级加密伴随着性能和存储成本。您选择的每个要加密的字段
都会增加插入和更新操作的写入。
需要额外的存储空间,因为MongoDB维护一个加密字段的索引以改进查询性能。
本节总结了加密集合的存储和写入影响,并解释了如何压缩加密集合索引以最小化这些成本。如果您想加密字段并为其配置查询,请参阅加密字段和启用查询.
概述
可查询加密引入了使用随机加密加密文档中的敏感字段的能力,同时仍然能够查询加密字段。
使用可查询加密,给定的明文字值始终加密为不同的密文,同时仍然可查询。为了启用此功能,可查询加密使用三种数据结构
两个元数据集合
加密集合中每个文档中的一个字段,称为
__safeContent__
警告
这些数据结构不得修改或删除,否则查询结果将不正确。
元数据集合
当您创建一个加密集合时,MongoDB会创建两个元数据集合
enxcol_.<collectionName>.esc
,称为ESC
enxcol_.<collectionName>.ecoc
,称为ECOC
示例
如果您创建一个名为“patients”的集合,MongoDB会创建以下元数据集合
enxcol_.patients.esc
enxcol_.patients.ecoc
当您插入带有可查询加密字段的文档时,MongoDB会更新元数据集合以维护一个允许查询的索引。该字段变为“索引字段”。每个此类字段都会在存储和写入速度上付出代价。
重要
当您删除加密集合时,立即删除相关的元数据集合
enxcol_.<collectionName>.esc
enxcol_.<collectionName>.ecoc
否则,重新创建具有相同名称的集合会使元数据集合处于冲突状态,消耗额外的存储空间并降低CRUD性能。
等值查询和范围查询影响
等值查询在存储和写入操作上具有固定的额外成本。范围查询成本取决于可查询字段的参数。紧密绑定这些查询可以大大降低其性能影响。
等值可查询字段的写入成本
插入操作
在插入文档时,每个索引字段需要向元数据集合写入两次。
一次写入
ESC
一次写入
ECOC
示例
插入具有两个索引字段的文档需要
一次写入加密集合。
四次写入元数据集合。
更新操作
在更新文档时,每个索引字段需要向元数据集合写入两次。
一次写入
ESC
一次写入
ECOC
示例
更新具有两个索引字段的文档需要
一次写入加密集合。
四次写入元数据集合。
删除操作
在删除文档时,索引字段不需要额外的写入。
等价查询字段存储费用
在进行任何元数据压缩之前,ESC
和 ECOC
为每个索引字段的每个字段/值对包含一个元数据文档。在一个加密的字段/值对上索引 1000 个文档需要在 ESC
中占用 1000 个文档,并在 ECOC
中占用 1000 个文档。
注意
加密每个字段的可查询加密集合可能需要 2-3 倍的存储空间,以考虑元数据集合。例如,1 GB 的集合可能需要 2-3 GB 的存储空间。
最佳实践
元数据收集压缩
当您插入或更新文档时,元数据集合会发生变化并增长。元数据集合压缩会清空 ECOC
并减小 ESC
的大小。
安排元数据压缩
重要
在最坏的情况下,运行元数据压缩会揭示自上次压缩以来插入的所有文档中的唯一字段/值对数量。
有关元数据压缩揭示的准确信息,请参阅MongoDB的“第6节:理论分析”和“第9节:指南”可查询加密技术论文.
作为最佳实践,记录以下信息
encfields
:每个文档的加密字段数量。docinserts
:自上次压缩以来插入的文档数量。valinserts
:自上次压缩以来插入的唯一字段/值对数量。
为了通过至少 t
个文档减小 ESC
元数据集合的大小,请在以下公式满足时运行元数据压缩
(encfields
· docinserts
) - valinserts
≥ t
每个文档的加密字段数量乘以自上次压缩以来的插入数量,减去自上次压缩以来插入的所有文档中的唯一字段/值对数量,应大于或等于要从 ESC
中删除的文档数量。
示例
例如,在一个包含六个加密字段的集合中,当插入的总文档数和自上次压缩以来插入的唯一字段/值对满足以下条件时,可以至少减少1000个文档的ESC
大小
(6 · docinserts
) - valinserts
≥ 1000
例如,如果自上次压缩以来插入了200个文档,其中所有文档中包含总共200个唯一的字段/值对,或者如果自上次压缩以来插入了400个文档,其中包含700个唯一的字段/值对,则该公式将得到满足。
运行元数据压缩
您必须手动运行元数据压缩。使用 mongosh
并运行 db.collection.compactStructuredEncryptionData()
命令
示例
const eDB = "encryption" const eKV = "__keyVault" const secretDB = "records" const secretCollection = "patients" const localKey = fs.readFileSync("master-key.txt") const localKeyProvider = { key: localKey } const queryableEncryptionOpts = { kmsProviders: { local: localKeyProvider }, keyVaultNamespace: `${eDB}.${eKV}`, } const encryptedClient = Mongo("localhost:27017", queryableEncryptionOpts) const encryptedDB = encryptedClient.getDB(secretDB) const encryptedCollection = encryptedDB.getCollection(secretCollection) encryptedCollection.compactStructuredEncryptionData()
{ "stats": { ... }, "ok": 1, ... }
您可以使用 mongosh
和运行 db.collection.totalSize()
命令来检查元数据集合的大小。
示例
在这个示例中,加密集合被命名为 "patients"。
db.enxcol_.patients.esc.totalSize()
1407960328