时间:2024年08月24日
作者:小蒋聊技术
邮箱:wei_wei10@163.com
微信:wei_wei10
音频:https://xima.tv/1_Gtthca?_sonic=0
希望大家帮个忙!如果大家有工作机会,希望帮小蒋内推一下,小蒋希望遇到一个认真做事的团队,一起努力。需要简历可以加我微信。
大家好,欢迎来到小蒋聊技术,小蒋准备和大家一起聊聊技术的那些事。
今天小蒋准备和大家一起聊的这个技术就厉害了!那就是“数据中的完全版本记录”设计。
昨天小蒋和大家一起分析了一个证券交易系统中的一个ER图。这个时候,有一个新的需求:请记录“数据中的完全版本记录”,小蒋来试着分析一下。
当业务要求数据有完全版本记录时,系统需要确保每一笔数据变更都被详细记录下来,这样不仅可以在出现问题时回溯,还可以满足合规性和风险管理的需求。通常在金融和证券交易系统,医疗信息系统,交易支付系统,都会有这样的要求。这些系统对数据的准确性、可追溯性以及合规性有着严格的要求。为了实现这一目标,数据模型需要能够支持以下功能:
- 所有数据变更的记录和追踪:每次数据的更新、插入或删除操作都需要被记录,并保留每一个版本的数据。
- 高效的查询和数据恢复能力:系统必须能够高效地查询历史数据,并能够在必要时恢复到指定的历史状态。
- 性能优化:在高并发环境下,系统应尽量减少记录数据变更对性能的影响。
数据模型设计
核心业务表和历史记录表设计
我们在系统里基于ER图中的核心业务表(这些是原有的表),并为数据的完全版本记录引入了历史记录表。以下是具体设计:
核心业务表(ER图中的原有表)
- BOND_TR 表:记录债券交易的核心信息,包含每一笔交易的最新数据。
- 字段:
- TradeId: 交易唯一标识,作为主键,用于标识每笔交易。
- 其他字段:如交易类型、交易金额、交易时间等,存储最新的交易数据。
- 字段:
- BOND 表:记录债券的详细交易信息,包括债券的种类、金额、期限等。
- 字段:
- BondId: 债券唯一标识,作为主键。
- TradeId: 外键,引用 BOND_TR 表中的 TradeId,用于关联具体的交易记录。
- 字段:
- BD_PTSETL 表:记录交易的结算信息,保存结算金额、日期等关键数据。
- 字段:
- SettleId: 结算唯一标识,作为主键。
- TradeId: 外键,引用 BOND_TR 表中的 TradeId。
- 字段:
历史记录表(新增的表)
为了确保每次数据变更都被保存,我们引入以下历史记录表。这些表专门用于记录每次数据修改前的状态,保留所有版本的信息:
- BOND_TR_History 表
- 用途:存储 BOND_TR 表的历史版本。每次 BOND_TR 表中的数据发生变更时,旧版本会被复制到这个表中。
- 字段:
- TradeId: 交易唯一标识,引用 BOND_TR 表中的 TradeId。
- VersionNumber: 版本号,每次修改后递增,确保每个版本的数据唯一。
- IsCurrent: 标识当前版本是否为最新版本(True 表示当前版本,False 表示历史版本)。
- EffectiveDate: 版本生效时间,标识该版本数据何时生效。
- EndDate: 版本结束时间,标识该版本何时被新的版本取代。
解释:
- CreateTime 记录的是数据的创建时间,也就是第一次写入数据库的时间点。
- EffectiveDate 则记录的是某个版本的数据开始生效的时间,通常在数据被修改时更新。
所以,尽管两个字段都涉及时间信息,但 EffectiveDate 是版本控制中的时间概念,而 CreateTime 是记录创建时的时间概念,它们的意义在数据生命周期中各有侧重,不可互换。
- BOND_History 表
- 用途:存储 BOND 表的历史版本。记录每次债券信息的变化。
- 字段:
- BondId: 债券唯一标识,引用 BOND 表中的 BondId。
- TradeId: 外键,引用 BOND_TR_History 表中的 TradeId。
- VersionNumber: 版本号,和 BOND_TR_History 表中的 VersionNumber 保持同步。
- IsCurrent: 标识当前版本是否为最新版本。
- EffectiveDate: 版本生效时间。
- EndDate: 版本结束时间。
- BD_PTSETL_History 表
- 用途:存储 BD_PTSETL 表的历史版本,确保所有结算信息的变化都能被追溯。
- 字段:
- SettleId: 结算唯一标识,引用 BD_PTSETL 表中的 SettleId。
- TradeId: 外键,引用 BOND_TR_History 表中的 TradeId。
- VersionNumber: 版本号,与 BOND_TR_History 表中的 VersionNumber 保持一致。
- IsCurrent: 标识当前版本是否为最新版本。
- EffectiveDate: 版本生效时间。
- EndDate: 版本结束时间。
设计细节的深入优化
为了确保数据完全版本记录模型在高并发场景下的有效性和性能,我们还需要进一步优化设计:
1. 乐观锁机制
为了确保数据一致性,我们采用乐观锁机制。在数据更新时,系统会检查当前记录的版本号是否匹配,如果版本号一致,则允许更新;如果版本号不一致,说明有其他操作修改过数据,这时系统会拒绝更新并提示用户重新提交。
2. 分区与分表策略
为了优化历史记录表的性能,我们可以按时间段(如季度或年度)对历史记录表进行分区,这样可以减少单个表的数据量,提升查询和写入效率。此外,对于数据量巨大的表,我们还可以采用分表策略,将数据按业务类型或其他规则分散存储在不同的表中。
3. 异步处理与缓存机制
为了降低历史数据写入对系统主业务流程的影响,我们可以使用异步处理机制。通过消息队列(如 Kafka)将旧版本数据发送到后台处理模块,异步写入历史记录表。这样主线程不必等待历史数据写入完成,可以提高系统响应速度。同时,使用 Redis 等缓存系统来存储常用的查询结果,可以进一步减轻数据库压力。
4. 异常情况下的数据一致性保证
使用异步处理机制时,最重要的是在异常情况下如何保证数据的一致性。我们可以通过以下方法来确保这一点:
- 事务管理与回滚机制:通过将异步处理纳入事务管理,确保主事务提交时,异步任务也成功执行。如果异步处理失败,可以触发回滚机制,保持数据的一致性。
- 消息队列的确认机制:像 Kafka 这样的消息队列提供了确认机制,只有当消息被成功处理后,才会被标记为“已消费”。如果处理失败,消息会保留在队列中等待重新处理。
- 幂等性设计:确保异步操作具有幂等性,即多次执行同一操作不会影响最终结果。通过在写入历史数据时检查是否已经存在相同的版本,避免重复写入,从而确保数据一致性。
- 定期数据对账与补偿机制:通过定期对账,检查主业务表与历史记录表之间的数据是否一致。如果发现不一致,可以通过补偿机制重新处理未成功的操作。
5. 短事务管理与批量处理
在高并发环境下,短事务管理非常重要。尽量缩短每个事务的生命周期,减少数据库锁的持有时间,降低并发操作之间的冲突。同时,对于大量的数据更新操作,可以使用批量处理的方式,减少数据库连接和提交的开销,从而提升系统的整体性能。
总结
通过以上深入的设计,我们可以在确保系统高效运行的同时,满足数据完全版本记录的业务需求。这个数据模型设计能够为系统提供强大的数据追溯能力,并确保在高并发环境下的稳定性和性能表现。通过乐观锁机制、分区与分表、异步处理和短事务管理等策略,我们能够有效管理数据的历史版本,同时保持系统的高效性和可靠性。尤其在异常情况下,通过事务管理、消息队列确认、幂等性设计和补偿机制,我们可以确保数据的一致性,进一步提升系统的可靠性。