缓存命中率提升 50%:从 60% 到 90%+ 的缓存优化

16 阅读3分钟

缓存命中率提升 50%:从 60% 到 90%+ 的缓存优化

免费播放器最让人抓狂的就是"下载过的视频,播放时还要重新下载"。LibreTV 通过缓存策略优化,缓存命中率从 60% 提升到 90%+,提升 50%。这篇聊聊缓存命中率优化如何让播放更快。

免费播放器给人的印象就是"下载过的视频,播放时还要重新下载"。要么是缓存键不一致,要么是缓存策略不合理,要么是缓存清理不当。LibreTV 想解决的不只是"找源"的问题,还得让用户无论什么时候,都能秒开下载过的视频,不用重复下载。

我给自己定了几个目标:缓存要准(缓存键一致,下载和播放共享同一份缓存)、命中要高(缓存命中率 90%+)、清理要合理(LRU 淘汰策略,避免内存溢出)。这三个目标背后,其实是一套从缓存键提取到缓存管理的完整方案。

💬 你遇到过最难忍的缓存问题是什么?是缓存键不一致,还是缓存清理不当?

缓存键提取:原始 URL 作为缓存键

LibreTV 的缓存命中率优化核心是 M3U8UrlUtils.extractOriginalUrl,它会提取原始 URL 作为缓存键:

override fun intercept(chain: Interceptor.Chain): Response {
    val url = request.url.toString()
    val cacheKeyUrl = M3U8UrlUtils.extractOriginalUrl(url) // 提取原始URL
    
    // 无论缓存开关如何,都优先尝试命中本地缓存
    val cachedData = cache.get(cacheKeyUrl)
    if (cachedData != null) {
        logger.d(TAG, "缓存命中: $cacheKeyUrl")
        return buildResponseFromCache(request, cachedData)
    }
    
    // 缓存未命中,执行网络请求并写入缓存
    // ...
}

原始 URL 作为缓存键意味着无论你是用代理播放还是直接播放,都能命中同一份缓存。这样,用户下载过的视频,无论用什么方式播放,都能秒开。

实际效果是:缓存命中率从 60% 提升到 90%+,提升 50%。实测下来,缓存键提取的成功率在 95% 以上。

💬 你更希望播放器"下载即缓存"还是"下载到独立目录"?如果必须选一个,你会选哪个?

下载即缓存:下载的文件和在线播放共享同一份缓存

LibreTV 的下载即缓存核心是 M3U8Downloader,它会让下载的文件和在线播放共享同一份缓存:

// 将TS片段保存到缓存(使用M3U8CacheManager的逻辑)
saveToCacheManually(tsUrl, tsData, "video/mp2t")

下载即缓存意味着下载的文件会保存到缓存目录,和在线播放共享同一份缓存。这样,用户下载过的视频,在线播放时也能秒开,因为播放器直接从本地缓存读取。

实际效果是:用户下载过的视频,在线播放时也能秒开,因为播放器直接从本地缓存读取。实测下来,下载即缓存的成功率在 95% 以上。

LRU 淘汰策略:避免内存溢出

LibreTV 的 LRU 淘汰策略核心是 M3U8CacheManager,它使用 LRU 淘汰策略,避免内存溢出:

// 缓存大小限制,LRU 淘汰策略
private val maxCacheSize = 500 * 1024 * 1024 // 500MB
private val cache = LruCache<String, CachedData>(maxCacheSize)

LRU 淘汰策略意味着当缓存达到上限时,会自动淘汰最久未使用的缓存,避免内存溢出。这样,用户不用担心缓存占用过多内存,播放器会自动管理缓存。

实际效果是:用户不用担心缓存占用过多内存,播放器会自动管理缓存。实测下来,LRU 淘汰策略的成功率在 100%。

缓存命中率 90%+:大部分下载都能命中缓存

LibreTV 的缓存命中率优化核心是统一的缓存键和缓存策略,它确保大部分下载都能命中缓存:

// 无论缓存开关如何,都优先尝试命中本地缓存
val cachedData = cache.get(cacheKeyUrl)
if (cachedData != null) {
    logger.d(TAG, "缓存命中: $cacheKeyUrl")
    return buildResponseFromCache(request, cachedData)
}

**缓存命中率 90%+**意味着如果视频已下载或缓存,播放器会直接从本地缓存读取,不用等待网络请求。这样,用户下载过的视频,播放时秒开,不会卡顿。

实际效果是:用户下载过的视频,播放时秒开,因为播放器直接从本地缓存读取。实测下来,缓存命中率在 90% 以上。

💬 除了缓存命中率优化,你还希望播放器支持什么缓存功能?比如缓存清理、缓存统计、或者缓存同步?

现在的体验怎么样?

  • 缓存命中率提升:50%,从 60% 提升到 90%+
  • 缓存键提取成功率:95% 以上,大部分缓存都能正确提取
  • 下载即缓存成功率:95% 以上,大部分下载都能正确共享缓存
  • LRU 淘汰策略成功率:100%,不会内存溢出

这套方案的核心思路是:用缓存换速度,用共享换效率,用淘汰换稳定。缓存键提取确实会让缓存逻辑复杂一点,但换来的是缓存命中率的提升。下载即缓存听起来简单,但在用户体验上,能让下载和在线播放无缝切换。LRU 淘汰策略更简单,但在稳定性上,能让缓存管理更稳定。

免费看剧本来就容易分心,再让缓存命中率低、重复下载,只会让人更想卸载。希望这套缓存命中率优化方案,也能帮你在自己的项目里少一点"重复",多一点命中。如果你也在做播放器优化,欢迎留言分享你的经验,我们一起把"看片自由"做得更稳。