如何在同一个 table 中高效处理冷热数据?
How to handle hot and cold data efficiently in same table?
我们有大量 table 的记录,这些记录在一周内更新很多,然后它们被冻结并以只读形式存储两年。该应用程序是用休眠制作的。
每条记录都有一个唯一的 ID 和一个在记录被冻结时设置为 0 的状态字段。 ID 是按时间顺序排列的,因为它们来自一个序列。
我们希望以最有效的方式存储热记录:在 SSD 驱动器上,填充因子为 45%,因此更新将是热更新(索引不会改变,因为新元组与前一个元组位于同一页上)。冻结的记录将存储在填充因子为 100 的 HDD 上,因为它们永远不会被修改。注意有外键tables。它们也必须以相同的方式进行分区,因此当我们分离主记录的分区时,我们也可以分离链接项的相应分区。
我们必须只在数据库上处理,在应用程序端不做任何更改(hibernate 映射除外)。
我尝试了几种解决方案:
二级分区
对 ID 和状态使用范围分区。每周一个新分区分为两个子分区,用于状态热和状态冷。
The primary key must include all partition fields both ID and status. This require to add those fields in all linked items and the application has to be rewritten.
使用 odd/even ID
我想过只对新记录(热门记录)使用奇数 ID。并在状态更新上添加一个触发器,该触发器会增加 id,以便它甚至可以用于冻结记录。奇数记录在热子分区上,偶数记录在冷分区上。为了拆分奇数和偶数,我想到了使用 modulus/remainder 分区。
This would work fine if the modulus was applied to the raw ID field, unfortunately it is calculated on a hash. So the distribution of record is random. Is there a way to choose the hash function ?
使用否定 ID
我现在正在考虑对固定记录使用负 ID。还没有测试。
有官方的方法吗?有大量应用程序具有这种行为。
如果您想让旧行过期,请不要使用生成的序列 id
来确定“旧”的含义。相反,添加时间戳列。
然后在该时间戳列上按范围分区。将相同的时间戳列引入所有 table 与大 table.
具有外键关系的列
大 table 的每个分区都是按状态列表分区的:0 的分区位于存储速度较慢的 table 空间上,包含所有其他状态的另一个分区位于 table具有快速存储空间。然后行在冻结时将自动移动到慢速存储。
不要在分区的table上定义外键,否则你将无法删除分区。相反,定义各个分区之间的外键约束。
“快速”子分区将随着年龄的增长而变空。您可能无法删除它(如果您在分区级别上有外键),但是您可以 VACUUM
子分区一旦为空就可以缩小它。
然后您只需删除适当的分区即可使旧数据过期。
我们有大量 table 的记录,这些记录在一周内更新很多,然后它们被冻结并以只读形式存储两年。该应用程序是用休眠制作的。
每条记录都有一个唯一的 ID 和一个在记录被冻结时设置为 0 的状态字段。 ID 是按时间顺序排列的,因为它们来自一个序列。
我们希望以最有效的方式存储热记录:在 SSD 驱动器上,填充因子为 45%,因此更新将是热更新(索引不会改变,因为新元组与前一个元组位于同一页上)。冻结的记录将存储在填充因子为 100 的 HDD 上,因为它们永远不会被修改。注意有外键tables。它们也必须以相同的方式进行分区,因此当我们分离主记录的分区时,我们也可以分离链接项的相应分区。
我们必须只在数据库上处理,在应用程序端不做任何更改(hibernate 映射除外)。
我尝试了几种解决方案:
二级分区
对 ID 和状态使用范围分区。每周一个新分区分为两个子分区,用于状态热和状态冷。
The primary key must include all partition fields both ID and status. This require to add those fields in all linked items and the application has to be rewritten.
使用 odd/even ID
我想过只对新记录(热门记录)使用奇数 ID。并在状态更新上添加一个触发器,该触发器会增加 id,以便它甚至可以用于冻结记录。奇数记录在热子分区上,偶数记录在冷分区上。为了拆分奇数和偶数,我想到了使用 modulus/remainder 分区。
This would work fine if the modulus was applied to the raw ID field, unfortunately it is calculated on a hash. So the distribution of record is random. Is there a way to choose the hash function ?
使用否定 ID
我现在正在考虑对固定记录使用负 ID。还没有测试。
有官方的方法吗?有大量应用程序具有这种行为。
如果您想让旧行过期,请不要使用生成的序列 id
来确定“旧”的含义。相反,添加时间戳列。
然后在该时间戳列上按范围分区。将相同的时间戳列引入所有 table 与大 table.
具有外键关系的列大 table 的每个分区都是按状态列表分区的:0 的分区位于存储速度较慢的 table 空间上,包含所有其他状态的另一个分区位于 table具有快速存储空间。然后行在冻结时将自动移动到慢速存储。
不要在分区的table上定义外键,否则你将无法删除分区。相反,定义各个分区之间的外键约束。
“快速”子分区将随着年龄的增长而变空。您可能无法删除它(如果您在分区级别上有外键),但是您可以 VACUUM
子分区一旦为空就可以缩小它。
然后您只需删除适当的分区即可使旧数据过期。