引言
如果你正在运行一个使用 SQLite 数据库的 Rails 应用程序(生产环境),那么很可能也在将 SQLite 用作缓存数据库。相较于 Redis 或 Memcached,SQLite 能够提供更大的缓存空间和更高的读取性能,这两点在缓存处理中至关重要。此外,基于 SQLite 的缓存功能还有一个重要优势,那就是它能利用底层文件系统(filesystem)或卷管理器(volume manager )的特性,比如 transparent compression 功能。
本文将探讨两款基于 SQLite 的 Rails 缓存存储方案:Litecache(Litestack SQLite 数据组件库的一部分);以及 SolidCache(一种新的、基于 ActiveRecord 的 Rails 缓存存储方案,支持配置为使用 SQLite)。
Litecache
Litecache 是基于 Litestack 提供的抽象层构建在 SQLite 之上的,因此拥有一个经过精细调整的、直接的 SQLite 连接,能够自动检测并适应 thread/fiber 环境,并且具备良好的 fork resilience 能力(译者注:在 Unix-like 系统中,进程可以通过 fork 操作创建子进程,而 Litecache 能够在这样的操作后保持稳定运行,不会因为进程分支而产生问题。)。
要将 Litecache 配置到 Rails 应用中,需要往 Gemfile 文件里添加以下这行代码:
gem “litestack”
接着执行以下命令:
bundle update
然后,在 environments/production 配置文件里,加入以下配置行:
config.cache_store = :litecache
此外,我们还可以选择运行安装程序来配置 litestack 的其他组件:
rails generate litestack:install
一旦上述配置步骤完成并正确设置,就可以在 Rails 应用程序中使用缓存功能了。而且,这个缓存功能将会通过 Litecache 来实现,而 Litecache 是利用 SQLite 作为其存储机制的。
Solid Cache
Solid Cache 为 Rails 提供了一个基于 ActiveRecord 构建的缓存存储方案,因此它继承了 ActiveRecord 的所有功能,比如支持多种数据库系统(包括 PostgreSQL、SQLite、MySQL 和 MongoDB)。此外,它还提供了支持数据库分片和使用主从数据库架构所需的基础设施。这些高级功能可以直接在 Solid Cache 中得以应用,对于大规模部署尤为有用。
要启用 Solid Cache,首先需要将相应的 gem 添加到 Gemfile 中:
gem “solid_cache”
接着执行以下命令进行更新:
bundle update
需要确保已经为数据库配置了 SQLite(可以通过 sqlite3 适配器或 Litestack 的 Litedb 适配器完成):
production: adapter: sqlite3
接下来,为目标环境生成必要的数据库迁移文件:
RAILS_ENV=production rails solid_cache:install:migrations
然后,运行迁移命令:
RAILS_ENV=production rails db:migrate 在config/environments/production
配置文件中,设置配置内容如下:
config.cache_store = :solid_cache_store
完成以上步骤后,就可以直接使用 Solid Cache 了。有关 Solid Cache 的更多信息和更多配置选项,请参阅 github 上的 Solid Cache gem 页面。
并非所有的 SQLite 缓存方案都是一样的
尽管 Litestack 和 Solid Cache 都采用了 SQLite,但它们在功能集合和性能表现上却大相径庭。是因为它们各自采用了不同的方式来抽象化处理底层的物理缓存存储,这些抽象层决定了它们如何与底层的物理存储(在这个案例中是 SQLite)交互。抽象化的方式会影响到缓存的效率、性能以及可用的功能。
Litecache,追求极致效率的直连策略
Litecache 尽可能以最高效的方式使用原始数据库连接,尽量避免创建不必要的显式事务(explicit transactions),并尽可能依赖预处理语句(prepared statements)来降低延迟。因此,Litecache 性能表现卓越,读取缓存的速度甚至比 Redis 和其他基于网络的解决方案还要快。
Solid Cache,站在巨人的肩膀上
如前文所述,Solid Cache 是在 ActiveRecord 的基础上深度构建而成的。这使得它成为一个非常灵活的解决方案,可以将缓存操作映射到可能非常复杂的分片、分布式和复制的数据库设置中。然而,这种灵活性是以在缓存操作和物理存储之间增加多层抽象层为代价的。
可以这么说,在生产环境中使用 SQLite 的应用程序,通常会被限制在单个服务器上。这样的应用程序需要采用复杂的缓存设置,使用多个 SQLite 数据库,以规模换取延迟的可能性非常小,甚至可以说是微乎其微。
性能基准比较
接下来,我们将对比Litecache与Solid Cache这两种方案在不同数据负载下的读写操作表现。
所有基准测试均采用各库的默认配置进行,但为了提升Solid Cache中SQLite连接的读写效率,我们对相关参数进行了优化。具体调整的参数如下:
PRAGMA journal_mode = WAL; PRAGMA synchronous = 1; PRAGMA mmap_size = 134,217,728;
这些测试在一台配置了AMD Ryzen 8740HS处理器的设备上执行。
从结果中可以看出,Litecache的写入速度显著快于Solid Cache,最快时可达到后者速度的5倍。当然,在高并发测试环境下,两种方案的表现都会有所影响,但Litecache由于操作更快,相较于需要长时间持有写入锁的Solid Cache,其出现写入冲突的可能性会更低。
在执行读取操作时,Litecache依旧保持着对Solid Cache的领先优势,尽管这种优势不像在写入操作中那么突出,但依旧显著,最高可达3倍以上。随着数据负载的增加,这一优势有所下降,降至大约2倍。这一变化是可以理解的,因为随着数据量的增加,ActiveRecord的额外开销相对于大数据记录的传输开销而言,变得不再那么突出。
总结
Litecache和Solid Cache均为基于SQLite的Rails应用程序提供了适用于生产环境的缓存解决方案。由于设计理念的不同,Litecache在性能上显著优于Solid Cache。尽管Solid Cache基于ActiveRecord带来了众多便利功能,但这些功能在采用SQLite进行生产的Rails应用中,其实用性并不高。