只在内存中运行PostgreSQL
我想运行一个只在内存中运行的小型PostgreSQL数据库,用于我编写的每个unit testing。 例如:
@Before void setUp() { String port = runPostgresOnRandomPort(); connectTo("postgres://localhost:"+port+"/in_memory_db"); // ... }
理想情况下,我将有一个postgres可执行文件签入版本控制,unit testing将使用。
像HSQL
,但对于Postgres。 我怎样才能做到这一点?
我能得到这样一个Postgres版本吗? 我如何指示它不要使用磁盘?
这对于Postgres来说是不可能的。 它不提供像HSQLDB或MySQL这样的进程内/内存引擎。
如果你想创build一个自包含的环境,你可以把Postgres二进制文件转换成SVN(但不仅仅是一个可执行文件)。
您需要运行initdb来设置您的testing数据库,然后才能对此做任何事情。 这可以通过batch file或使用Runtime.exec()来完成。 但请注意,initdb不是那么快。 你绝对不会想要为每个testing运行。 尽pipe如此,你可能会在你的testing套件之前跑掉它。
然而,虽然这可以完成,我build议有一个专门的Postgres安装,您只需在运行testing之前重新创build您的testing数据库。
您可以通过使用模板数据库来重新创buildtesting数据库,这使得创buildtesting数据库的速度非常快(比运行每个testing运行的initdb快得多)
或者你可以在ramfs / tempfs中创build一个TABLESPACE ,并在那里创build所有的对象。
最近我曾经指出过一篇关于在Linux上做这件事的文章 。
警告
这可能会危及整个数据库集群的完整性。
阅读手册中的添加警告。
所以这只是消耗性数据的一个select。
对于unit testing,它应该工作得很好。 如果您在同一台计算机上运行其他数据库,请确保使用单独的数据库群集(具有自己的端口)以确保安全。
( 使用内存中的PostgreSQL移动我的答案并将其推广):
您无法在进程内运行内存
我不知道如何运行内存Postgres数据库进行testing。 可能吗?
不,这是不可能的。 PostgreSQL在C中实现并编译为平台代码。 与H2或Derby不同的是,你不能只加载jar
并将其作为一次性内存数据库进行启动。
不同于SQLite,也是用C语言编写的,编译成平台代码,PostgreSQL也不能在进程中加载。 它需要多个进程(每个连接一个进程),因为这是一个多进程,而不是multithreading架构。 多处理要求意味着您必须启动postmaster作为一个独立的过程。
相反:预先configuration一个连接
我build议简单地写你的testing,以期望一个特定的主机名/用户名/密码正常工作,让testing工具CREATE DATABASE
成为一次性数据库,然后在运行结束时使用DROP DATABASE
。 从属性文件获取数据库连接详细信息,构build目标属性,环境variables等
只要您提供给您的unit testing的用户不是超级用户,只使用具有CREATEDB
权限的用户,那么使用已经拥有您关心的数据库的现有PostgreSQL实例是安全的。 最糟糕的是,你会在其他数据库中创build性能问题。 我宁愿运行一个完全隔离的PostgreSQL安装来testing这个原因。
相反:启动一个一次性的PostgreSQL实例进行testing
或者,如果您真的热衷于使用testing工具findinitdb
和postgres
二进制文件,运行initdb
创build一个数据库,修改pg_hba.conf
来trust
,运行postgres
在一个随机的端口上启动它,创build一个用户,创build一个数据库,并运行testing 。 您甚至可以将PostgreSQL二进制文件捆绑到一个jar中的多个体系结构中,并在运行testing之前将当前体系结构的那些解压缩到临时目录。
就我个人而言,这是一个应该避免的重大难题。 只需要configurationtesting数据库就简单多了。 但是,在postgresql.conf
中include_dir
支持的出现使得它变得容易一些; 现在你可以追加一行,然后为其余的编写一个生成的configuration文件。
使用PostgreSQL更快地进行testing
有关如何安全地提高PostgreSQL的性能以便进行testing的更多信息,请参阅我之前就此主题撰写的详细答案: 优化PostgreSQL以进行快速testing
H2的PostgreSQL方言不是真正的替代品
有些人在PostgreSQL方言模式下使用H2数据库来运行testing。 我认为这几乎和使用SQLite进行testing和使用PostgreSQL进行生产部署的Rails人一样糟糕。
H2支持一些PostgreSQL扩展并模拟PostgreSQL方言。 但是,这只是一个模拟。 你会发现H2接受查询的地方,但是PostgreSQL不会,行为不同等等 。 你也可以find很多PostgreSQL支持的地方,在编写本文的时候,H2并不能像窗口函数那样做一些事情。
如果您了解这种方法的局限性,并且您的数据库访问很简单,那么H2可能是正确的。 但是在这种情况下,对于一个抽象数据库的ORM来说,你可能是一个更好的select,因为不pipe用什么有趣的特性 – 在这种情况下,你不必再关心数据库的兼容性了。
表空间不是答案!
不要使用表空间来创build“内存中”数据库。 这不仅是没有必要的,因为它无论如何不会对性能有很大的帮助,但它也是一个很好的方式来破坏在同一个PostgreSQL安装中你可能关心的其他任何其他的访问。 9.4文档现在包含以下警告 :
警告
即使位于主PostgreSQL数据目录之外,表空间也是数据库集群的组成部分,不能被视为数据文件的自治集合。 它们依赖于主数据目录中包含的元数据,因此无法附加到不同的数据库群集或单独备份。 同样,如果您丢失了一个表空间(文件删除,磁盘故障等),数据库集群可能变得不可读或无法启动。 将表空间放置在临时文件系统(如虚拟磁盘)上会影响整个集群的可靠性。
因为我注意到有太多的人这样做了,而且遇到了麻烦。
(如果你已经这样做了,你可以使用缺less的表空间目录来让PostgreSQL重新启动,然后DROP
丢失的数据库,表格等。最好不要这样做。)
您可以使用TestContainers启动一个PosgreSQL docker容器进行testing: http ://testcontainers.viewdocs.io/testcontainers-java/usage/database_containers/
TestContainers提供了一个JUnit @ Rule / @ ClassRule :这个模式在testing之前在容器内启动一个数据库,并在之后将其closures。
例:
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中的Embedded PostgreSQL组件在您的JUnittesting中运行PostgreSQL的内存实例: https : //github.com/opentable/otj-pg-embedded 。
通过向otj-pgembedded式库( https://mvnrepository.com/artifact/com.opentable.components/otj-pg-embedded )添加依赖关系,您可以在@Before中启动和停止自己的PostgreSQL实例, @Afer钩子:
EmbeddedPostgres pg = EmbeddedPostgres.start();
他们甚至提供了一个JUnit规则来自动让JUnit为你启动和停止你的PostgreSQL数据库服务器:
@Rule public SingleInstancePostgresRule pg = EmbeddedPostgresRules.singleInstance();
您还可以使用PostgreSQLconfiguration设置(如问题中的详细信息和此处接受的答案)来实现性能,而不必采用内存数据库。