数据保留策略和自动化:保留您需要的内容,删除您必须的内容

根据 GDPR、SOX 和 HIPAA 的法律要求、保留计划、自动执行和合规性验证来构建数据保留策略。

E
ECOSIRE Research and Development Team
|2026年3月16日4 分钟阅读764 字数|

属于我们的Compliance & Regulation系列

阅读完整指南

数据保留策略和自动化:保留您需要的内容,删除您必须的内容

公司保留的数据平均比合法需要的数据多 33%,从而增加了存储成本和泄露风险。 数据保留是法律合规性、成本优化和安全性的融合点。保留太多会增加攻击面。保留太少就会违反法律保留要求。

本指南涵盖保留策略设计、数据类型的法律要求以及无需人工干预即可保持组织合规的自动执行。

要点

  • 保留期限由法律要求、业务需求和隐私义务的交叉点决定
  • 自动执行至关重要 --- 手动删除流程的失败率为 40%
  • 同一系统内不同数据类型可能有不同的保留期限
  • 当预计发生诉讼时,法律保留必须优先于标准保留政策

法规的保留期限要求

最低保留要求

数据类型通用数据保护条例SOX(美国)税法(可变)健康保险流通与责任法案行业标准
财务记录无最低限度(目的限制)7 年3-10 年(按国家/地区)不适用7 年
员工记录工作期限+ 2-6 年不适用终止后 3-7 年不适用终止后 7 年
客户 PII只要目的有必要不适用不适用不适用当关系结束 + 2 年时删除
健康档案不适用不适用不适用自创建/最后生效日期起 6 年因州而​​异(最长 30 年)
税务文件不适用7 年3-10 年(按国家/地区)不适用7 年
合同不适用持续时间 + 7 年不适用不适用持续时间 + 6 年(诉讼时效)
招聘资料最长 6 个月(最佳实践)不适用不适用不适用6-24 个月
支持票只要需要不适用不适用不适用关闭后 3 年
审核日志只要需要7 年不适用6 年7 年
营销许可同意期限不适用不适用不适用直至撤回 + 2 年

特定国家/地区的税收保留

国家保留期限笔记
美国3-7年IRS:一般监禁 3 年,欺诈罪判处 7 年
英国6 年英国税务海关总署要求
德国10年欧盟最严格
法国6 年(有些人 10 年)商业代码
荷兰7 年Fiscale bewaarplicht
澳大利亚5 年ATO 要求
加拿大6 年CRA 要求
印度8 年所得税法
阿联酋5 年联邦税务局

设计保留计划

第 1 步:库存数据类别

类别系统业主数据示例
客户资料CRM、电子商务、支持销售姓名、电子邮件、订单历史记录
员工资料HRIS、薪资、福利人力资源SSN、薪资、绩效评估
财务数据会计、ERP、银行业金融发票、收据、税务申报
营销数据电子邮件平台、分析营销活动数据、同意记录
产品数据ERP、电子商务运营产品规格、定价、库存
操作日志应用服务器、数据库信息技术访问日志、错误日志、审计跟踪

第 2 步:指定保留期

对于每个类别,使用以下层次结构确定保留期:

  1. 法定最低限度:法律要求您保留什么?
  2. 法律最高限度:法律要求您删除什么? (GDPR 目的限制)
  3. 业务需求:您实际需要多长时间?
  4. 风险承受能力:保留它与删除它的成本是多少?

保留期限为:最长(法定最低期限,业务需要),但不得长于法定最长期限(如果适用)。

第 3 步:定义处置操作

行动描述何时使用
删除永久删除所有副本过期数据的默认设置
匿名删除识别元素,保持聚合分析、研究
档案转向受限的加密存储合法保留,合规存档
聚合用摘要替换个别记录财务报告

自动保留执行

PostgreSQL 实现

-- Retention policy table
CREATE TABLE retention_policies (
    id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
    table_name VARCHAR(100) NOT NULL,
    date_column VARCHAR(100) NOT NULL,
    retention_days INTEGER NOT NULL,
    action VARCHAR(20) NOT NULL, -- 'DELETE', 'ANONYMIZE', 'ARCHIVE'
    condition TEXT, -- Optional WHERE clause
    enabled BOOLEAN DEFAULT TRUE,
    last_run TIMESTAMP,
    records_affected INTEGER DEFAULT 0
);

-- Insert policies
INSERT INTO retention_policies (table_name, date_column, retention_days, action, condition) VALUES
('support_tickets', 'closed_at', 1095, 'ANONYMIZE', 'status = ''closed'''),
('recruitment_candidates', 'applied_at', 730, 'DELETE', 'status != ''hired'''),
('session_logs', 'created_at', 90, 'DELETE', NULL),
('newsletter_unsubscribed', 'unsubscribed_at', 365, 'DELETE', NULL),
('audit_logs', 'created_at', 2555, 'ARCHIVE', NULL);

-- Enforcement function
CREATE OR REPLACE FUNCTION enforce_retention_policies()
RETURNS TABLE(policy_id UUID, table_name TEXT, records_affected BIGINT) AS $$
DECLARE
    policy RECORD;
    affected BIGINT;
    query TEXT;
BEGIN
    FOR policy IN
        SELECT * FROM retention_policies WHERE enabled = TRUE
    LOOP
        IF policy.action = 'DELETE' THEN
            query := format(
                'DELETE FROM %I WHERE %I < NOW() - INTERVAL ''%s days''',
                policy.table_name, policy.date_column, policy.retention_days
            );
            IF policy.condition IS NOT NULL THEN
                query := query || ' AND ' || policy.condition;
            END IF;
        ELSIF policy.action = 'ANONYMIZE' THEN
            -- Anonymization requires table-specific logic
            -- This is a simplified example
            query := format(
                'UPDATE %I SET email = ''[email protected]'', name = ''Anonymized'' WHERE %I < NOW() - INTERVAL ''%s days''',
                policy.table_name, policy.date_column, policy.retention_days
            );
            IF policy.condition IS NOT NULL THEN
                query := query || ' AND ' || policy.condition;
            END IF;
        END IF;

        EXECUTE query;
        GET DIAGNOSTICS affected = ROW_COUNT;

        UPDATE retention_policies
        SET last_run = NOW(), records_affected = affected
        WHERE id = policy.id;

        RETURN QUERY SELECT policy.id, policy.table_name::TEXT, affected;
    END LOOP;
END;
$$ LANGUAGE plpgsql;

日程安排

# Run retention enforcement daily at 3 AM
# /etc/cron.d/retention-enforcement
0 3 * * * postgres psql -d ecosire -c "SELECT * FROM enforce_retention_policies();" >> /var/log/retention-enforcement.log 2>&1

合法保留

当合法保留适用时

当诉讼预期、未决或正在进行时,合法保留会暂停正常的保留政策。无论保留时间表如何,所有潜在相关数据都必须保留。

实施

-- Legal hold table
CREATE TABLE legal_holds (
    id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
    matter_name VARCHAR(255) NOT NULL,
    description TEXT,
    custodians TEXT[], -- Users whose data is held
    tables_affected TEXT[], -- Tables where retention is suspended
    created_at TIMESTAMP DEFAULT NOW(),
    released_at TIMESTAMP,
    created_by UUID REFERENCES users(id)
);

-- Modified retention enforcement respects legal holds
CREATE OR REPLACE FUNCTION enforce_retention_with_holds()
RETURNS void AS $$
BEGIN
    -- Skip tables under active legal hold
    DELETE FROM session_logs
    WHERE created_at < NOW() - INTERVAL '90 days'
    AND 'session_logs' NOT IN (
        SELECT UNNEST(tables_affected)
        FROM legal_holds
        WHERE released_at IS NULL
    );
END;
$$ LANGUAGE plpgsql;

验证和合规性

每月保留审核

  • 所有保留政策均已记录并处于最新状态
  • 自动执行成功运行(检查日志)
  • 不存在超出保留期限的数据(样本检查)
  • 法律保留对所有未决诉讼均有效
  • 新数据类别已分配保留期限
  • 第三方处理器符合保留要求

常见问题

如果我们保留数据的时间超过 GDPR 规定的必要时间,会发生什么情况?

保留个人数据超过其必要的保留期限违反了 GDPR 的存储限制原则(第 5(1)(e) 条)。监管机构可处以最高 2000 万欧元或全球年营业额 4% 的罚款。更实际的是,过多的数据会增加您遭受泄露的风险——您要对不应该拥有的数据负责。

我们如何处理备份副本的保留?

备份使保留变得复杂,因为它们包含特定时间点的数据快照。选项:(1) 按照与最长保留期一致的计划轮换备份,(2) 加密备份并在保留到期时销毁加密密钥(“加密粉碎”),(3) 接受备份可能包含过期数据,但将其记录为具有补偿控制的技术限制。

我们如何在 Odoo 中实现保留?

Odoo 没有内置的保留自动化功能。使用以下方式实现它:(1) 对旧记录进行归档或匿名化的计划操作(cron 作业),(2) 在特定模型上强制执行保留规则的自定义模块,(3) 用于批量操作的数据库级函数。 ECOSIRE 提供 Odoo 定制 来实现自动化数据生命周期管理。


接下来会发生什么

数据保留是完整治理计划的组成部分之一。将其与新系统的隐私设计、HR 数据的员工数据隐私 以及第三方处理器的供应商合同管理 相结合。

联系 ECOSIRE 获取数据保留策略设计和实施咨询。


由 ECOSIRE 发布——帮助企业在整个生命周期中管理数据。

E

作者

ECOSIRE Research and Development Team

在 ECOSIRE 构建企业级数字产品。分享关于 Odoo 集成、电商自动化和 AI 驱动商业解决方案的洞见。

通过 WhatsApp 聊天