当前位置:首页 > 健康 > 正文

365天未删减在线视频,用Go语言实现永不过期的视频管理策略

  • 健康
  • 2026-07-09 07:23:49
  • 340
摘要: 为什么我非要跟“删除”过不去?你有没有过这种经历——半夜刷到一个好视频,心里想着“明天再整理”,结果第二天想找的时候,它已经被平...

为什么我非要跟“删除”过不去?

你有没有过这种经历——半夜刷到一个好视频,心里想着“明天再整理”,结果第二天想找的时候,它已经被平台清理了。365dni未减删除(365天未删除)这个说法,最早是从一些海外视频站点来的,意思就是“一年内的视频都不会被系统自动清理”,听起来很爽对吧?但实现起来,特别是用Golang来写一套这样的视频管理逻辑,远比你想的要麻烦一点

我最初的想法特别简单:不就是设个定时器,365天后才删吗?后来发现,真正的难点不在“删不删”,而在“未减删除”这四个字背后的数据一致性、状态标记和用户交互,你想想,一个视频上传后,如果用户自己点删除,你得尊重吧?系统判定违规,你得配合吧?但“未减删除”的意思是说:只要不是用户主动要求、不是法律红线,系统就无权私自清理

Go语言能干什么?——把“365天”变成一条条可靠的记录

在Go里实现这个逻辑,一般会走这么几步:

视频元数据模型的“时间锚点”

我们得在数据库里存两个关键时间:`upload_time`(上传时间)和`expire_time`(过期时间),对于“365dni未减删除”策略,`expire_time`默认是`upload_time + 365天`,但注意,这个“过期”不是“立即删除”,而是“允许清理”。真正决定视频生死的,是一个叫`status`的字段
字段名 类型 说明
video_id string 唯一标识
upload_time timestamp 上传时间
expire_time timestamp 系统允许清理的时间
status int 1=正常, 2=用户删除, 3=违规删除, 4=过期可清理

你看,Go的结构体定义大概是这样的:

type Video struct {
    VideoID    string    `json:"video_id"`
    UploadTime time.Time `json:"upload_time"`
    ExpireTime time.Time `json:"expire_time"`
    Status     int       `json:"status"`
}

定时任务:别搞“真删除”,先做“标记清理”

我一开始脑子发热,想直接写一个`cron`任务,每天凌晨扫一遍数据库,碰到`expire_time < now()`且`status == 4`的视频就`DELETE`,结果被运维大哥骂了一顿——万一误删呢?用户投诉怎么办?

所以后来改成:每天扫描status == 1expire_time < now()的视频,把它们的status改成4(可清理),然后另起一个手动确认的接口,或者等用户访问时再提示“该视频即将清理”,这样就把“系统强制”变成了“用户知情”,核心Go代码大概是这样的:

func CheckExpiredVideos(db *sql.DB) {
    rows, _ := db.QueryContext(ctx, "UPDATE videos SET status=4 WHERE status=1 AND expire_time < NOW()")
    // 然后发个通知或者写日志
}

用户主动删除 vs 系统未减删除

用户在前端点“删除”时,后端不是物理删除,而是把`status`改成`2`,然后系统扫到`status==2`的视频,隔7天再真正删掉——这是给用户留后悔药,而“365dni未减删除”策略强调的是:只要用户没动手,系统就不能主动把`status`从1改成2或3,唯一能触发状态变化的,只能是`expire_time`到期后的`1→4`转换。

一个很容易踩的坑:时间的“未减”到底怎么算?

“365天”是指自然日还是工作日?是按上传时间算还是按最后访问时间算?我见过有的站点,你点开视频一次,它就把到期时间往后推24小时——这叫“滚动过期”。真正的“365dni未减删除”通常不走滚动逻辑,因为那样会让清理任务永远清不掉“热门视频”,服务器存储会炸,但如果你真想实现“只要用户还在看,视频就不删”,那可以在视频表里加一个last_access_time字段,每次用户观看时更新,然后定时任务判断时,改为:

if expire_time < now() && last_access_time < now() - 30天 {
    // 才标记为可清理
}

这样既保证了“未减删除”的承诺,又避免了死文件占坑,怎么在Go里优雅地写这个判断?我一般用time.Since()

if time.Since(v.ExpireTime) > 0 && time.Since(v.LastAccessTime) > 30*24*time.Hour {
    // 标记清理
}

性能问题:每天扫全表会不会慢?

会啊!如果你是百万级视频库,全表扫描肯定慢到哭,这时候要配合索引,在Go里写迁移脚本时,可以给expire_timestatus建联合索引:

CREATE INDEX idx_expire_status ON videos (expire_time, status)

然后查询语句写成:

SELECT * FROM videos WHERE status = 1 AND expire_time < NOW()

这样走索引,百万数据也就毫秒级,如果你用的是GORM,可以这么写:

db.Where("status = ? AND expire_time < ?", 1, time.Now()).Find(&videos)

注意别忘了加.Limit(1000),分批次处理,防止一次性拉爆内存。

那些“未减删除”背后的法律与人性

说实话,写代码是简单的,但“365天不删”这个承诺其实很烫手,用户上传了一个视频,365天后他忘了,你还留着,万一视频内容后来被举报违规,你删不删?按照“未减删除”的原始逻辑,只要不是用户主动删、不是法院来函,系统就不能删,但实际操作中,我们通常会留一个“管理员强制删除”的后门,这个在Go里就是加一个status=3的枚举值。

用户真的在乎这365天吗?我访谈过几个重度用户,他们说:“我不在乎系统啥时候删,我只要在我需要的时候它还在就行。”所以后来我们把“365天”改成了“无特殊情况不删,但保留随时调整的权利”,并在前端用个小字标出来,代码上其实没变,只是把expire_time设成了一个很大的值,比如9999-01-01这其实就是“未减删除”的终极形态——根本不设过期时间

我踩过的一个真实bug:时区问题

Go的time.Now()默认是本地时间,如果你的服务器在UTC,用户在中国,那expire_time在数据库里存的是UTC,但用户看到的“365天后”其实是北京时间。有个视频,用户看着还差1天才过期,但服务器已经把它标记为可清理了,这问题怎么修?所有时间在存入数据库时统一转成UTC,查询时再转回用户时区,Go里用time.Now().UTC()就很稳。

expireTime := time.Now().UTC().Add(365 * 24 * time.Hour)

聊聊这个功能的真实使用场景

如果你是做个人视频站、兴趣社区、或是家庭相册备份,365dni未减删除是个特别好的卖点,用户会对“你帮我存着”这件事产生信任感,我见过一个例子,有个爸爸把小孩的成长视频传上去,两年后回来找,发现还在,直接在社区写了篇感谢帖,这就是技术带来了情感价值

但如果你做的是流量平台、UGC内容站,那就要慎重了——存储成本、审核风险、版权纠纷,每一条都有可能让“不删”变成负担,这时候,用Go写一个分层存储策略会更好:热视频放SSD,冷视频放廉价对象存储,过期标记的视频先挪到冷存储,而不是直接删,这样既保持了“未减删除”的体验,又控制了成本,代码上就是加一个storage_tier字段,定期用Go的goroutine做迁移任务。

最后的几句话

写这篇文章的时候,我自己的Go项目里其实就有一个视频模块在跑“365dni未减删除”的逻辑,数据库里现在躺着几万个视频,有些已经超过两年了——因为我把过期时间改成了NULL,代表永久保留,系统每天早上有个goroutine起来检查,发现status还是1而且expire_time不为NULL的视频,才做标记。

不过说实话,我到现在也没完全想明白“理想中的未减删除”到底该长什么样,每个用户对“保留”的定义都不太一样,有人希望系统永不删,有人希望系统定期提醒他“你的视频要过期了”,可能最好的做法是,在视频上传页面上多放一个下拉菜单,让用户自己选:3天、30天、365天、永久,后端用Go的switch语句处理不同的expire_time逻辑,干净又灵活。

哦对了,如果你真的打算用Go写这个功能,记得别忘了加个数据库事务——更新status和写入日志要在一个事务里完成,不然崩了之后你都不知道视频到底是被删了还是被转移了,我就因为这个挂过一次,被用户追着问了一下午。

365天未删减在线视频,用Go语言实现永不过期的视频管理策略