百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

Postgres 可以替代 Redis 作为缓存吗?

mhr18 2024-11-19 06:58 23 浏览 0 评论



近期,一篇名为“Postgres 可以替代 Redis 作为缓存吗?”的文章在Medium迅速出圈,这一新颖的话题,似乎能带来不少实际项目的启示,下面跟随着作者Raphael De Lio来解读这一疑问。





我在Twitter上询问大家了一个问题:你想到的第一个消息队列是什么?


其中一个回答引起我的注意:Postgres


“使用 Postgres 作为消息队列,并使用 SKIP LOCKED 代替 Kafka(如果你只需要一个消息队列的话)”。— Stephan Schmid


更令我惊讶的是,还有提出使用Postgres作为缓存来替代 Redis的观点。


“使用 Postgres 进行缓存,而不是 Redis。使用 UNLOGGED 表和 TEXT 作为 JSON 数据类型。存储过程可以使用 ChatGPT 编写,添加和强制执行数据的到期日期,就像在 Redis一样”。— Stephan Schmidt



在我学习 Redis 的过程中,我经常听到很多人(来自 Redis)提倡:Redis可以成为你的主要数据库。


这可能是一个好主意。Redis是一个真正的数据库,只是因为它速度非常快,可以在一秒钟内执行数百万次操作,被大家常用来作为缓存。



而当我看到最喜欢的关系型数据库 Postgres 可以取代我最喜欢的非关系型数据库 Redis 时,我的世界发生了翻天覆地的变化。我应该用Postgres取代 Redis,还是用Redis取代Postgres?


在考虑这个问题之前,我想先搞清楚:Postgres作为缓存真的是个好主意吗?它真的可以取代 Redis 吗?


Stephan Schmidt主张用 Postgres 替换 Redis(实际上他主张用 Postgres 替换一切),他认为这样做可以消除一定的复杂性。(请阅读:https://medium.com/@AmazingCTO)


“一切都用 Postgres 吧(如何降低复杂性并加快速度)” — Stephan Schmid


然而,他并不是唯一一个主张更换 Redis 的人,也有人做了同样的事情:



但首先,我为什么要用 Postgres 替换 Redis?


Stephan 已经给出了两个理由:复杂性更低和变化更快。是否还有其他驱动因素呢?


使用 Postgres 作为缓存虽不是常见的选择,但在某些情况下具有一定的优势:


统一技术栈


Postgres 是最流行的数据库之一,且开源免费,将其用作缓存可以减少管理和维护多个数据库系统的工作,从而简化技术堆栈。


熟悉的界面


Postgres 支持复杂的查询和索引,特别是对于精通 SQL的人来说,直接在缓存层内处理高级数据检索和转换任务会更加容易。


成本


某些情况下,使用现有的 Postgres 资源进行缓存,可能比部署单独的缓存解决方案(如 Redis)更具成本效益。尤其是在基础设施预算有限的环境中,将 Postgres 同时用作主存储和缓存可以提高资源利用率。


我们对缓存服务有怎样的目标?


传统缓存服务(例如 Redis)具有一系列可增强应用程序性能和可扩展性的功能,Postgres 是否真的可以取代 Redis,需要从以下几个关键层面考量:


表现


缓存服务的主要目标,是通过加快数据访问速度,来提高应用程序的性能。


高性能缓存解决方案可以处理高吞吐量工作负载,并提供亚毫秒级的响应时间,从而显著加快检索数据的进程。


删除策略


通过设置缓存数据的过期时间,让过期数据在指定时间后自动从缓存中删除。确保过期数据不会提供给应用程序。


逐出策略


缓存服务通常将其数据保存在内存中,而内存一般是有限的。因此,需要设置逐出策略让我们自动删除不常用的数据,为新数据腾出空间。


键值存储


大多数缓存服务的核心都是以键值对的形式存储数据。这种简单但功能强大的模型可以快速检索数据,从而轻松高效地存储和访问常用数据。


简而言之,缓存服务需要更快地访问数据并返回尽可能最新的数据。


怎样才能将 Postgres 变成缓存?


Stephan 和 Martin 都表示,我们可以通过使用 UNLOGGED 表将 Postgres 变成缓存服务。


结合Martin Heinz《你不需要专用的缓存服务 - PostgreSQL 作为缓存》这篇文章内容(链接:https://martinheinz.dev/blog/105),得到了这些答案:


未记录表和预写日志


Postgres 中的未记录表是一种防止特定表生成 WAL(预写日志)的方法。


反言之,WAL可确保对数据库所做的所有更改,在实际写入数据库文件之前都已记录。在系统崩溃和断电等极端情况的时候,就有助于维护数据完整性。


补充说明:Redis提供了一种类似的机制,称为仅附加文件 (AOF) ,它不仅提供了一种在 Redis中持久保存数据的机制,而且还以类似的方式运行,即记录在 Redis 中执行的所有操作。如果使用 Redis 作为主数据库,我们会启用 AOF ,而如果使用 Postgres 作为缓存,我们会关闭(在特定表上)WAL。


关闭WAL提高性能


对于每次数据修改,Postgres 必须更改写入 WAL 和数据文件。这使所需的写入操作数量加倍。


除此之外,为了确保每个已提交的事务都物理写入磁盘,WAL被设计为强制执行磁盘刷新 (fsync)。频繁的磁盘刷新操作会影响性能,因为它们会引入等待磁盘确认数据已安全写入的延迟。


放弃坚持


未记录表不是持久的。


Postgres会使用WAL来重放和应用自上次检查点以来所做的任何更改,如果我 们没有此日志记录,则无法通过重放WAL记录将数据库恢复到一致状态。但这也是缓存的一大特点。


CREATE UNLOGGED TABLE cache (
    id serial PRIMARY KEY,
    key text UNIQUE NOT NULL,
    value jsonb,
    inserted_at timestamp);


CREATE INDEX idx_cache_key ON cache (key);


存储过程的过期


Martin 和 Stephan 都表示,可以使用存储过程来实现过期,这会导致一定的复杂性。


因此,Stephan甚至更进一步建议我们使用ChatGPT来编写存储过程。


CREATE OR REPLACE PROCEDURE expire_rows (retention_period INTERVAL) AS
$
BEGIN
    DELETE FROM cache
    WHERE inserted_at < NOW() - retention_period;


    COMMIT;
END;
$ LANGUAGE plpgsql;


CALL expire_rows('60 minutes'); -- This will remove rows older than 1 hour


然而事实是,大多数现代应用程序不再依赖存储过程,而且现在很多软件开发人员都反对使用存储过程,以此避免把业务逻辑泄露到数据库中,且随着存储数据的增加,管理和理解会变得更为麻烦。


此外,我们还需要按计划调用这些存储过程。为此,我们需要使用一个扩展 pg_cron 。安装扩展后,我们仍然需要创建调度程序:


-- Create a schedule to run the procedure every hour
SELECT cron.schedule('0 * * * *', $CALL expire_rows('1 hour');$);
-- List all scheduled jobs
SELECT * FROM cron.job;


然而,复杂性还在持续增加。


存储过程逐出策略


Stephan在他的文章中没有提到逐出策略,而Martin则表示,由于过期可以保持存储大小,因此也可以作为一个选择。


但是,如果仍然想要启用逐出策略,Martin建议在我们的表中添加一个名为 last_read_timestamp的列,并偶尔运行另一个存储过程来实现“最近使用”(LRU)逐出策略。


CREATE OR REPLACE PROCEDURE lru_eviction(eviction_count INTEGER) AS
$
BEGIN
    DELETE FROM cache
    WHERE ctid IN (
        SELECT ctid
        FROM cache
        ORDER BY last_read_timestamp ASC
        LIMIT eviction_count
    );


    COMMIT;
END;
$ LANGUAGE plpgsql;
-- Call the procedure to evict a specified number of rows
CALL lru_eviction(10); -- This will remove the 10 least recently accessed rows


Redis 提供了八种现成的逐出策略(官方文档:https://redis.io/docs/latest/develop/reference/eviction/)。如果想要为“Postgres Cache”设置另一种逐出策略?问ChatGPT即可。


性能表现如何?


性能表现是缓存服务选型的决定性因素,因为我们需要缓存服务的主要原因是想更快地访问的数据。


Greg Sabino Mullane在他的文章《PostgreSQL Unlogged Tables — Look Ma, No WAL! 》中展示了测试结果(原文链接:https://www.crunchydata.com/blog/postgresl-unlogged-tables),比较了 Postgres 中 UNLOGGED 和 LOGGED 表的性能。数据显示, 写入 UNLOGGED 表的性能是在 LOGGED 表中执行相同操作的两倍。具体数据如下:


  • 未记录表

延迟:2.059 ms

TPS:485,706168


  • 记录表

延迟:5.949 ms

TPS:168,087557


读写表现如何呢?


这就是最大的问题:Postsgres 性能优化策略依赖于共享缓冲区。


共享缓冲区将经常访问的数据和索引直接存储在内存中,使其可以快速访问,并减少从磁盘读取的需要,提高已记录和未记录表的查询性能和数据访问能力。


未记录表可能留在这些缓冲区中,但如果它们变得太大或内存有限,它们则会被写入磁盘。因此,未记录表主要提高写入速度,而不是读取速度。


为了证明这一点,我使用进行了快速实验 pgbench (具体操作请见:GitHub - raphaeldelio/redis-postgres-cache-benchmark)


结果表明,记录表和未记录表的性能实际上非常相似,读取这两种类型的表平均需要大约 0.650 ms。具体数据如下:


  • 未记录表

延迟:0.679 ms

TPS:14.724,204


  • 记录表

延迟:0.627ms

TPS :15.946,025


这一结果测试进一步验证:未记录表主要增强了写入性能。


对于读取操作,未记录表的性能优势并不明显,因为记录表和未记录表都同样受益于 Postgres 的缓存和优化策略。


与 Redis 相比性能如何?


除了对 Postgres 进行基准测试之外,我还对 Redis 进行了实验。(具体操作请见:GitHub - raphaeldelio/redis-postgres-cache-benchmark)。结果显示,Redis在读写操作方面具有显著的性能优势:


  • 读取

延迟 (p50) :0.095ms

每秒请求数 (RPS) :892.857,12


  • 写入

延迟 (p50) :0.103ms

每秒请求数 (RPS) :892857,12


性能比较显示,Redis 在写入和读取操作方面都明显优于 Postgres:Redis只有 0.095ms的延迟, Postgres未记录表有0.679ms。


Redis还能处理更高的请求率,每秒 892,857.12 个请求,而 Postgres 每秒只能处理 15,946.025 个请求。


在写入操作方面,我们也可以看到Redis提供了更优异的性能,吞吐量明显更高,延迟也更低。


如果我在 RAM 中运行 Postgres 会怎样?


在审查本文的过程中,Xebia的同事Maksym Fedorov表示:


“ 如果现在在与内存映射文件对应的表空间中创建未记录表会怎么样?我猜我们会看到完全不同的数字。”


为了测试这一点,我使用保存在 RAM 中的 Postgres 数据运行了基准测试。 令人惊讶的是,结果没有任何改善。基准测试显示:


  • 读取

延迟:0.652ms

每秒请求数 (TPS) :15329,776954


经过进一步研究,我了解到,即使数据存储在 RAM 中,在Postgres 的共享缓冲区内访问数据也会产生额外成本,这些成本来自管理锁,以及数据完整性和并发访问所需的其他内部进程。


而且,Postgres总是先检查数据是否在共享缓冲区中,如果不在,它会先将数据从tmpfs文件系统复制到共享缓冲区中,然后再提供服务,即使数据库保存在 RAM中。


我应该用 Postgres 替换 Redis 吗?


综上所述,如果您需要缓存服务来提高写入性能,可以使用未记录表优化 Postgres。但是,虽然未记录表比记录表提供更好的写入性能,但与 Redis 相比仍然不足。


使用缓存服务的主要原因是缩短数据检索时间。未记录的表不会提高读取性能,而Redis则以极快的读取优势作为更优选择。


此外,Redis有助于防止大量低成本查询访问数据库,这是未记录表无法提供的优势。Redis还提供内置功能,如过期、逐出策略等,这些功能在 Postgres 中很难实现。


尽管对某些人来说,管理 Postgres 似乎更容易,但将 Postgres 变成缓存并不能提供专用缓存服务的优势。同时,Redis 的学习、部署和使用都很简单,而且很有趣。


所以为了获得更快的性能和简单性,选择像Redis这样真正的缓存服务似乎才是更明智的选择。


作者丨Raphael De Lio 编译丨Rio

来源丨https://medium.com/redis-with-raphael-de-lio/can-postgres-replace-redis-as-a-cache-f6cba13386dc


*本文为dbaplus社群编译整理,如需转载请取得授权并标明出处!欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn

相关推荐

【推荐】一个开源免费、AI 驱动的智能数据管理系统,支持多数据库

如果您对源码&技术感兴趣,请点赞+收藏+转发+关注,大家的支持是我分享最大的动力!!!.前言在当今数据驱动的时代,高效、智能地管理数据已成为企业和个人不可或缺的能力。为了满足这一需求,我们推出了这款开...

Pure Storage推出统一数据管理云平台及新闪存阵列

PureStorage公司今日推出企业数据云(EnterpriseDataCloud),称其为组织在混合环境中存储、管理和使用数据方式的全面架构升级。该公司表示,EDC使组织能够在本地、云端和混...

对Java学习的10条建议(对java课程的建议)

不少Java的初学者一开始都是信心满满准备迎接挑战,但是经过一段时间的学习之后,多少都会碰到各种挫败,以下北风网就总结一些对于初学者非常有用的建议,希望能够给他们解决现实中的问题。Java编程的准备:...

SQLShift 重大更新:Oracle→PostgreSQL 存储过程转换功能上线!

官网:https://sqlshift.cn/6月,SQLShift迎来重大版本更新!作为国内首个支持Oracle->OceanBase存储过程智能转换的工具,SQLShift在过去一...

JDK21有没有什么稳定、简单又强势的特性?

佳未阿里云开发者2025年03月05日08:30浙江阿里妹导读这篇文章主要介绍了Java虚拟线程的发展及其在AJDK中的实现和优化。阅前声明:本文介绍的内容基于AJDK21.0.5[1]以及以上...

「松勤软件测试」网站总出现404 bug?总结8个原因,不信解决不了

在进行网站测试的时候,有没有碰到过网站崩溃,打不开,出现404错误等各种现象,如果你碰到了,那么恭喜你,你的网站出问题了,是什么原因导致网站出问题呢,根据松勤软件测试的总结如下:01数据库中的表空间不...

Java面试题及答案最全总结(2025版)

大家好,我是Java面试陪考员最近很多小伙伴在忙着找工作,给大家整理了一份非常全面的Java面试题及答案。涉及的内容非常全面,包含:Spring、MySQL、JVM、Redis、Linux、Sprin...

数据库日常运维工作内容(数据库日常运维 工作内容)

#数据库日常运维工作包括哪些内容?#数据库日常运维工作是一个涵盖多个层面的综合性任务,以下是详细的分类和内容说明:一、数据库运维核心工作监控与告警性能监控:实时监控CPU、内存、I/O、连接数、锁等待...

分布式之系统底层原理(上)(底层分布式技术)

作者:allanpan,腾讯IEG高级后台工程师导言分布式事务是分布式系统必不可少的组成部分,基本上只要实现一个分布式系统就逃不开对分布式事务的支持。本文从分布式事务这个概念切入,尝试对分布式事务...

oracle 死锁了怎么办?kill 进程 直接上干货

1、查看死锁是否存在selectusername,lockwait,status,machine,programfromv$sessionwheresidin(selectsession...

SpringBoot 各种分页查询方式详解(全网最全)

一、分页查询基础概念与原理1.1什么是分页查询分页查询是指将大量数据分割成多个小块(页)进行展示的技术,它是现代Web应用中必不可少的功能。想象一下你去图书馆找书,如果所有书都堆在一张桌子上,你很难...

《战场兄弟》全事件攻略 一般事件合同事件红装及隐藏职业攻略

《战场兄弟》全事件攻略,一般事件合同事件红装及隐藏职业攻略。《战场兄弟》事件奖励,事件条件。《战场兄弟》是OverhypeStudios制作发行的一款由xcom和桌游为灵感来源,以中世纪、低魔奇幻为...

LoadRunner(loadrunner录制不到脚本)

一、核心组件与工作流程LoadRunner性能测试工具-并发测试-正版软件下载-使用教程-价格-官方代理商的架构围绕三大核心组件构建,形成完整测试闭环:VirtualUserGenerator(...

Redis数据类型介绍(redis 数据类型)

介绍Redis支持五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及Zset(sortedset:有序集合)。1、字符串类型概述1.1、数据类型Redis支持...

RMAN备份监控及优化总结(rman备份原理)

今天主要介绍一下如何对RMAN备份监控及优化,这里就不讲rman备份的一些原理了,仅供参考。一、监控RMAN备份1、确定备份源与备份设备的最大速度从磁盘读的速度和磁带写的带度、备份的速度不可能超出这两...

取消回复欢迎 发表评论: