仅在内存中运行 PostgreSQL

我想运行一个小的 PostgreSQL 数据库,它只在内存中运行,对于我编写的每个单元测试。例如:

@Before
void setUp() {
String port = runPostgresOnRandomPort();
connectTo("postgres://localhost:"+port+"/in_memory_db");
// ...
}

在理想情况下,我将在版本控制中检入一个 postgres 可执行文件,单元测试将使用这个版本控制。

类似 HSQL的东西,但是是给邮差用的,我怎么才能做到呢?

我能得到这样一个 Postgres 版本吗? 我怎样才能指示它不使用磁盘?

138243 次浏览

或者您可以在 ramfs/temfs 中创建一个 TABLESPACE,并在那里创建所有对象。
最近有一篇文章指出,在 Linux 上正是这样做的。原始链接断了。但它被归档了(由 Arsinclair 提供) :

警告

这可能会危及你的 整个数据库集群的完整性。
阅读手册中添加的警告。
所以这只是消耗性数据的一种选择。

对于 单元测试来说,应该可以正常工作。如果在同一台计算机上运行其他数据库,请确保使用单独的数据库集群(它有自己的端口)是安全的。

Postgres 不可能做到这一点,它不提供 HSQLDB 或 MySQL 这样的进程内/内存内引擎。

如果您想创建一个自包含的环境,那么 可以将 Postgres 二进制文件放入 SVN (但它不仅仅是一个单独的可执行文件)。

您将需要运行 Initdb来设置您的测试数据库,然后才能使用它进行任何操作。这可以通过批处理文件或使用 Runtime.exec ()来完成。但是请注意,initdb 并不是一个快速的东西。您肯定不希望在每个测试中都运行它。不过,您可能会在测试套件之前运行它。

然而,尽管可以做到这一点,我还是建议安装一个专用的 Postgres,在运行测试之前,只需要简单地重新创建测试数据库。

可以通过使用模板数据库重新创建测试数据库,这使得创建速度非常快(对于每次测试运行,使用 很多比运行 initdb 更快)

您也可以使用 PostgreSQL 配置设置(例如问题和接受的答案 给你中详细说明的设置)来实现性能,而不必求助于内存数据库。

(将我的答案从 使用内存中的 PostgreSQL中移出并归纳出来) :

不能在内存中运行 Pg

我不知道如何运行内存中的 Postgres 数据库进行测试。这可能吗?

不,这不可能。PostgreSQL 是用 C 实现的,并编译成平台代码。与 H2或 Derby 不同,您不能只是加载 jar并将其作为一个一次性内存数据库启动。

它的存储是基于文件系统的,它没有任何内置的存储抽象,允许您使用纯内存中的数据存储。但是,您可以将 可以指向一个 ramdisk、 temfs 或其他临时文件系统存储。

与 SQLite (也是用 C 编写并编译成平台代码的)不同,PostgreSQL 也不能在进程中加载。它需要多个进程(每个连接一个) ,因为它是一个多进程,而不是多线程体系结构。多处理需求意味着 必须的以独立进程的形式启动邮局主管。

使用一次性容器

自从我最初编写这篇文章以来,容器的使用已经变得广泛、易于理解和容易。

在 Docker 容器中配置一个用于测试用途的一次性 postgres 实例,然后在最后将其删除,这应该是不需要动脑筋的。你可以通过像 LD_PRELOADinglibeatmydata这样的黑客技术来加快速度,禁用那个讨厌的“不要在崩溃时严重破坏我的数据”特性;)。

对于您想要的任何测试套件、语言或工具链,都有许多包装器可以自动完成这项工作。

备选方案: 预先配置连接

(在简单集装箱化之前写的,不再推荐)

我建议简单地编写您的测试,以期望特定的主机名/用户名/密码能够工作,并让测试工具 CREATE DATABASE成为一个一次性数据库,然后在运行结束时使用 DROP DATABASE。从属性文件中获取数据库连接详细信息,构建目标属性,环境变量等等。

使用现有的 PostgreSQL 实例是安全的,只要您提供给单元测试的用户是超级用户 没有,只有拥有 CREATEDB权限的用户。在最坏的情况下,您将在其他数据库中创建性能问题。出于这个原因,我更喜欢运行一个完全隔离的 PostgreSQL 安装来进行测试。

相反: 启动一个一次性的 PostgreSQL 实例进行测试

或者,如果你是 真的热衷你可以 让测试工具定位 ABC0和 postgres二进制文件,运行 ABC0创建数据库,将 ABC3修改为 ABC4,运行 postgres在随机端口启动它,创建用户,创建数据库,并运行测试。您甚至可以将用于多个体系结构的 PostgreSQL 二进制文件捆绑在一个 jar 中,并在运行测试之前将用于当前体系结构的二进制文件解压缩到一个临时目录中。

就我个人而言,我认为这是一个应该避免的主要痛苦; 配置一个测试数据库要容易得多。但是,随着 postgresql.conf中支持 include_dir的出现,这就变得容易了一些; 现在您只需要添加一行,然后为其余的代码编写一个生成的配置文件。

使用 PostgreSQL 进行更快的测试

有关如何为测试目的提高 PostgreSQL 性能的更多信息,请参见我之前就此主题写的详细答案: 优化 PostgreSQL 以进行快速测试

H2的 PostgreSQL 方言并不是真正的替代品

有些人使用 PostgreSQL 方言模式下的 H2数据库来运行测试。我认为这几乎和 Rails 人员使用 SQLite 进行测试和使用 PostgreSQL 进行生产部署一样糟糕。

H2支持一些 PostgreSQL 扩展并模拟 PostgreSQL 方言。然而,这只是一种模仿。您会发现 H2接受查询但 PostgreSQL 不接受查询的区域,以及行为不同的区域等等.在编写本文时,您还会发现在很多地方,PostgreSQL 支持做一些 H2不能做的事情——比如窗口函数。

如果您了解这种方法的局限性,并且您的数据库访问很简单,那么 H2可能没有问题。但是在这种情况下,您可能是抽象数据库的 ORM 的更好候选者,因为您无论如何都不会使用它的有趣特性——在这种情况下,您不必再那么关心数据库兼容性。

桌面空间不是解决办法!

使用 没有表空间创建“内存中”数据库。它不仅没有必要,因为它无论如何都不会显著提高性能,而且它还是破坏对同一 PostgreSQL 安装中您可能关心的任何其他内容的访问的一个很好的方法。返回文章页面

警告

即使位于主 PostgreSQL 数据目录之外, 表空间是数据库集群的一个组成部分,不能是 视为一个独立的数据文件集合。它们是相互依赖的 在主数据目录中包含的元数据上,因此不能 附加到不同的数据库集群或单独备份。 类似地,如果您丢失了一个表空间(文件删除,磁盘故障, 等) ,数据库集群可能变得不可读或无法启动。 将表空间放置在临时文件系统上,就像存储磁盘风险一样 整个集群的可靠性。

因为我注意到有太多人这样做,而且遇到了麻烦。

(如果你已经这样做了,你可以 mkdir缺失的表空间目录让 PostgreSQL 再次启动,然后 DROP缺失的数据库,表等。最好不要这么做。)

您可以使用 TestContainer 测试容器旋转 PosgreSQL docker 容器进行测试: Http://testcontainers.viewdocs.io/testcontainers-java/usage/database_containers/

TestContainers 提供了一个 JUnit@Rule/@ClassRule: 这种模式在测试之前在容器中启动一个数据库,然后将其拆除。

例如:

public class SimplePostgreSQLTest {


@Rule
public PostgreSQLContainer postgres = new PostgreSQLContainer();


@Test
public void testSimple() throws SQLException {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setJdbcUrl(postgres.getJdbcUrl());
hikariConfig.setUsername(postgres.getUsername());
hikariConfig.setPassword(postgres.getPassword());


HikariDataSource ds = new HikariDataSource(hikariConfig);
Statement statement = ds.getConnection().createStatement();
statement.execute("SELECT 1");
ResultSet resultSet = statement.getResultSet();


resultSet.next();
int resultSetInt = resultSet.getInt(1);
assertEquals("A basic SELECT query succeeds", 1, resultSetInt);
}
}

现在可以通过 OpenTable 中的嵌入式 PostgreSQL 组件在 JUnit 测试中运行 PostgreSQL 的内存实例: https://github.com/opentable/otj-pg-embedded

通过向 otj-pg 嵌入式库(https://mvnrepository.com/artifact/com.opentable.components/otj-pg-embedded)添加依赖项,您可以在@Before 和@After 挂钩中启动和停止自己的 PostgreSQL 实例:

EmbeddedPostgres pg = EmbeddedPostgres.start();

它们甚至提供了一个 JUnit 规则来自动让 JUnit 启动和停止 PostgreSQL 数据库服务器:

@Rule
public SingleInstancePostgresRule pg = EmbeddedPostgresRules.singleInstance();

现在有一个来自俄罗斯搜索公司 Yandex: https://github.com/yandex-qatools/postgresql-embedded的 PostgreSQL 内存版本

它基于 Flapdodle OSS 的嵌入进程。

使用示例(来自 github 页面) :

// starting Postgres
final EmbeddedPostgres postgres = new EmbeddedPostgres(V9_6);
// predefined data directory
// final EmbeddedPostgres postgres = new EmbeddedPostgres(V9_6, "/path/to/predefined/data/directory");
final String url = postgres.start("localhost", 5432, "dbName", "userName", "password");


// connecting to a running Postgres and feeding up the database
final Connection conn = DriverManager.getConnection(url);
conn.createStatement().execute("CREATE TABLE films (code char(5));");

我用了一段时间,效果很好。

更新 : 该项目不再被积极维护

Please be adviced that the main maintainer of this project has successfuly
migrated to the use of Test Containers project. This is the best possible
alternative nowadays.

如果使用 NodeJS,可以使用 Pg-mem(免责声明: 我是作者)来模拟 postgres db 最常见的特性。

您将拥有一个完整的内存中、隔离的、与平台无关的数据库,可以复制 PG 行为(甚至是 在浏览器中运行)。

我写了一篇文章来展示如何在单元测试 给你中使用它。

如果您可以使用 docker,您可以在内存中挂载 postgreql 数据目录以进行测试

docker run --tmpfs=/data -e PGDATA=/data postgres

如果你正在使用 java,我看到有一个库被有效地使用了,它提供了一个内存中的“嵌入式”postgres 环境,主要用于单元测试。

Https://github.com/opentable/otj-pg-embedded

如果您来到这个搜索结果寻找答案,这也许能够解决您的用例。