在Google App Engine中实现高效的记录变更审计跟踪 - 设计模式
我遇到了一个比较常见的设计问题:我需要在Google App Engine上实现一个历史记录(审计日志),用于记录的变更。这个历史记录必须是结构化的,也就是说,我不能把所有的变更合并成一段自由格式的文本,然后存储在字符串字段里。
我考虑了几种历史记录的模型,发现第一种方案在性能上有问题,所以我选择了第三种方案。但我还是有些疑虑,这个解决方案是否高效且可扩展。例如:随着动态属性数量的增加,第三种方案的性能会不会显著下降?
你是否对每种方案的优缺点有更深入的了解,或者能否建议一些适用于Google App Engine数据库特性的其他审计日志设计模式?
- 使用经典的SQL“主从”关系
- 优点
- 对于有SQL背景的数据库开发者来说,容易理解
- 结构清晰:历史记录及其属性直接定义
- 搜索性能好:可以通过历史记录轻松搜索(可以使用索引)
- 故障排查方便:可以通过管理工具轻松访问 (_ah/admin)
- 缺点
- 在GAE数据库中,通常不推荐以这种方式实现一对多关系
- 读取性能差:为了显示长的审计日志,读取操作过多,例如在大型记录列表的详细信息面板中。
- 优点
- 将历史记录存储在BLOB字段中(序列化的Python结构)
- 优点
- 实现简单且灵活
- 读取性能好:非常高效
- 缺点
- 查询性能差:无法使用索引进行搜索
- 故障排查不方便:无法通过管理数据库查看器检查数据 (_ah/admin)
- 结构不清晰:对于SQL开发者来说,不太容易理解(他们觉得这很丑)
- 优点
- 将历史记录存储在Expando的动态属性中。例如,对于每个字段
fieldName
创建history_fieldName_n
字段(其中 n=<0..N> 是历史记录的数量)- 优点:
- 简单:实现和理解都很简单
- 故障排查方便:可以通过管理界面读取所有历史属性
- 读取性能好:一次读取操作即可获取记录
- 缺点:
- 搜索性能差:无法简单地搜索历史记录(它们有不同的名称)
- 结构不太清晰:属性数量在初看时可能会让人困惑
- 优点:
- 将历史记录存储在主记录的一组列表字段中。例如,对于每个
fieldName
创建一个fieldName_history
列表字段- 优点:
- 结构清晰:历史属性直接定义
- 简单:对于SQL开发者来说容易理解
- 读取性能好:一次读取操作即可获取记录
- 缺点:
- 搜索性能差:只能对曾经有值的记录使用索引进行搜索,无法搜索在特定时间具有某些值组合的记录;
- 故障排查困难:在管理数据库查看器中检查列表比较困难
- 优点:
1 个回答
3
如果让我选择的话,我会选择选项1。读取数据的速度和其他选项一样快(甚至更快)。而且其他选项只有在特定情况下(比如变更的数量很少或非常多)才会有速度上的优势。选择这个选项还会给你带来很多灵活性,比如可以在一定天数后清除历史记录,或者跨不同模型类型查询历史记录。确保在同一个操作中,把历史记录作为被更改实体的子项创建,这样可以保证数据的一致性。你可能会得到这样的结果:
class HistoryEventFieldLevel(db.Model):
# parent, you don't have to define this
date = db.DateTime()
model = db.StringProperty()
property = db.StringProperty() # Name of changed property
action = db.EnumProperty(['insert', 'update', 'delete'])
old = db.PickleProperty() # Old value for field, empty on insert
new = db.PickleProperty() # New value for field, empty on delete
class HistoryEventModelLevel(db.Model):
# parent, you don't have to define this
date = db.DateTime()
model = db.StringProperty()
action = db.EnumProperty(['insert', 'update', 'delete'])
change = db.PickleProperty() # Dictionary with changed fields as keys and tuples (old value, new value) as values