坑边闲话:Jellyfin 作为一个开源免费且声誉良好的媒体服务器一直被玩家追捧,然而 Jellyfin 早期与 SQlite 绑定非常深,导致无法丝滑迁移到其他的高级数据库。在数据非常多且数据库查询插入操作很频繁的情况下,SQlite 显现出了巨大的弊端。

此前我 follow 这个版本已经很久了,终于,等到了正式版。

图 1. Jellyfin 10.11 发行公告。

Jellyfin 脱胎于 Emby,因此前期的很多代码与 Emby 重复很多,不过这些陈年历史就不讲了。下面单纯围绕媒体服务器的一些特点进行讲解。

此前 Jellyfin 使用的后端数据库是 sqlite,这是一个超级强大的本地数据库,据说其源代码的测试框架有 9000 多万行代码(仅仅是为了测试 sqlite),基本实现了百分百代码覆盖率测试。

然而 sqlite 有个致命问题,那就是无法扩展。注意,不是扩展很难,而且没有任何办法横向扩展。因为 sqlite 基于单个文件。一旦某个进程占有了这个数据库文件并上锁,别人就没办法使用这个数据库。

可怕的是前期的 Jellyfin 完全基于 sqlite 开发,换言之,你的电影元数据、演员元数据全都存在这个 sqlite 里,一旦扫描器开始工作,sqlite 就会面临大量 I/O 请求,界面非常卡顿。这个问题的核心在于 sqlite 在任意时刻只允许一个线程写 (单写者模型),读写混合时,读操作必然被阻塞,然后就卡顿了。扫描器是写的主力,而 UI 浏览 (加载媒体库 加载首页、搜索演员) 是读操作。唯一方案就是等,等它处理完数据同步逻辑,一切就都正常了。

此外,更可怕的是 Jellyfin 的源代码里直接硬嵌入了 sqlite 的 SQL 语句,导致无法简单地替换为 PostgreSQL/MySQL 等更高级的数据库,这一度让用户非常崩溃!

刚刚提到的卡顿问题只是其中之一,另一个问题是没办法横向扩展以及迁移到 K8S 平台,而原因依然是数据库问题。正常情况下,Jellyfin 作为一个媒体服务器,要做的就是接受用户请求,反馈、写入元数据,因此数据库和后端应该分开。

v10.11.0 是第一个做到这一点的版本。通过 EF Core 这个 C# 框架,程序员可以把 SQL 操作用一种更抽象的方案 (表达式树 LINQ) 写出来,然后 EF Core 框架会把数据库操作翻译为某种具体的数据库操作方言,比如 PostgreSQL/MySQL/SQL Server 等。以 PostgreSQL 为例,它不仅允许多个线程/事务同时写,而且在写的同时也不会被阻塞读(大多数情况下)。

虽然这是一个小版本号更新,但是其背后经历了接近 10 个 RC 版本,开发者们付出的努力可谓是艰苦卓绝。期待未来某一天,Jellyfin 官方支持更强大的数据库。我相信随着 10.11 的到来,这一天不会太远。

升级体验·

图 2. 升级体验并没有很顺利,由于数据库需要迁移,因此这是一个高风险操作,强烈建议在升级到该版本之前备份所有数据!

由于升级之后,笔者的一个巨大的仓库(含有 6 万部 STRM 电影)无法打开,而且日志也没有正在进行的工作,界面也异常卡顿。一气之下,笔者就把 Jellyfin 数据库会滚到 10.10.7 版本了。再观望一下吧。