Chỉ chạy PostgreSQL trong bộ nhớ


102

Tôi muốn chạy một cơ sở dữ liệu PostgreSQL nhỏ chỉ chạy trong bộ nhớ, cho mỗi bài kiểm tra đơn vị tôi viết. Ví dụ:

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

Lý tưởng nhất là tôi sẽ kiểm tra một tệp thực thi postgres duy nhất vào kiểm soát phiên bản, kiểm tra đơn vị sẽ sử dụng.

Một cái gì đó giống như HSQL, nhưng cho postgres. Làm thế nào tôi có thể làm điều đó?

Tôi có thể nhận được phiên bản Postgres như vậy không? Làm thế nào tôi có thể hướng dẫn nó không sử dụng đĩa?

Câu trả lời:


48

Điều này là không thể với Postgres. Nó không cung cấp một công cụ trong quy trình / trong bộ nhớ như HSQLDB hoặc MySQL.

Nếu bạn muốn tạo một môi trường độc lập, bạn có thể đặt các tệp nhị phân Postgres vào SVN (nhưng nó không chỉ là một tệp thực thi duy nhất).

Bạn sẽ cần chạy initdb để thiết lập cơ sở dữ liệu thử nghiệm của mình trước khi bạn có thể làm bất cứ điều gì với điều này. Điều này có thể được thực hiện từ một tệp hàng loạt hoặc bằng cách sử dụng Runtime.exec (). Nhưng lưu ý rằng initdb không phải là một thứ gì đó nhanh. Bạn chắc chắn sẽ không muốn chạy điều đó cho mỗi bài kiểm tra. Tuy nhiên, bạn có thể chạy nó trước khi có bộ thử nghiệm.

Tuy nhiên, trong khi điều này có thể được thực hiện, tôi khuyên bạn nên cài đặt Postgres chuyên dụng, nơi bạn chỉ cần tạo lại cơ sở dữ liệu thử nghiệm của mình trước khi chạy thử nghiệm.

Bạn có thể tạo lại cơ sở dữ liệu thử nghiệm bằng cách sử dụng cơ sở dữ liệu mẫu giúp tạo nó khá nhanh ( nhanh hơn rất nhiều so với chạy initdb cho mỗi lần chạy thử nghiệm)


8
Có vẻ như câu trả lời thứ hai của Erwin dưới đây sẽ được đánh dấu là câu trả lời đúng
vfclists

3
@vfclists Thực ra, một vùng bảng trên đĩa ram là một ý tưởng thực sự tồi. Đừng làm vậy. Xem postgresql.org/docs/devel/static/manage-ag-tablespaces.html , stackoverflow.com/q/9407442/398670
Craig Ringer

1
@CraigRinger: Để làm rõ cho câu hỏi cụ thể này: Đó là một ý tưởng tồi khi trộn với dữ liệu có giá trị (và cảm ơn vì đã cảnh báo). Để kiểm tra đơn vị với một cụm DB chuyên dụng, đĩa ram là tốt.
Erwin Brandstetter,

1
Với việc sử dụng docker đã trở nên phổ biến, một số người đã thành công với một công cụ như testcontainers, về cơ bản cho phép khởi động thử nghiệm của bạn một cách ngẫu nhiên, dockezed, postgres. Xem github.com/testcontainers/testcontainers-java/blob/master/…
Hans Westerbeek

1
@ekcrisp. đó không phải là phiên bản được nhúng thực sự của Postgres. Nó chỉ là một thư viện trình bao bọc để giúp bắt đầu một cá thể Postgres (trong một quy trình riêng) dễ dàng hơn. Postgres sẽ vẫn chạy "bên ngoài" ứng dụng Java và không "nhúng" trong cùng một quy trình chạy JVM
a_horse_with_no_name

77

(Chuyển câu trả lời của tôi từ Sử dụng PostgreSQL trong bộ nhớ và tổng quát hóa nó):

Bạn không thể chạy Pg trong quá trình, trong bộ nhớ

Tôi không thể tìm ra cách chạy cơ sở dữ liệu Postgres trong bộ nhớ để thử nghiệm. Nó có khả thi không?

Không, no không thể. PostgreSQL được triển khai bằng C và được biên dịch sang mã nền tảng. Không giống như H2 hoặc Derby, bạn không thể chỉ tảijar và kích hoạt nó như một DB trong bộ nhớ.

Không giống như SQLite, cũng được viết bằng C và được biên dịch sang mã nền tảng, PostgreSQL cũng không thể được tải trong quá trình. Nó yêu cầu nhiều quy trình (một quy trình trên mỗi kết nối) vì đó là kiến ​​trúc đa xử lý, không phải đa luồng. Yêu cầu đa xử lý có nghĩa là bạn phải khởi chạy quản trị viên bưu điện như một quy trình độc lập.

Thay vào đó: định cấu hình trước một kết nối

Tôi khuyên bạn chỉ cần viết các bài kiểm tra của mình để mong đợi một tên máy chủ / tên người dùng / mật khẩu cụ thể hoạt động và có bài kiểm tra khai thác CREATE DATABASEmột cơ sở dữ liệu hữu ích, sau đóDROP DATABASE vào cuối quá trình chạy. Lấy chi tiết kết nối cơ sở dữ liệu từ tệp thuộc tính, xây dựng thuộc tính đích, biến môi trường, v.v.

Thật an toàn khi sử dụng phiên bản PostgreSQL hiện có mà bạn đã có cơ sở dữ liệu mà bạn quan tâm, miễn là người dùng bạn cung cấp cho các bài kiểm tra đơn vị của mình không phải là superuser, chỉ là người dùng có CREATEDBquyền. Tệ nhất là bạn sẽ tạo ra các vấn đề về hiệu suất trong các cơ sở dữ liệu khác. Tôi thích chạy một bản cài đặt PostgreSQL hoàn toàn biệt lập để thử nghiệm vì lý do đó.

Thay vào đó: Khởi chạy một phiên bản PostgreSQL mới để thử nghiệm

Ngoài ra, nếu bạn thực sự quan tâm, bạn có thể yêu cầu bộ khai thác thử nghiệm của mình xác định vị trí initdbpostgresmã nhị phân, chạy initdbđể tạo cơ sở dữ liệu, sửa đổi pg_hba.confthành trust, chạy postgresđể khởi động nó trên một cổng ngẫu nhiên, tạo người dùng, tạo DB và chạy thử nghiệm . Bạn thậm chí có thể gói các tệp nhị phân PostgreSQL cho nhiều kiến ​​trúc trong một jar và giải nén các tệp cho kiến ​​trúc hiện tại vào một thư mục tạm thời trước khi chạy thử nghiệm.

Cá nhân tôi nghĩ đó là một nỗi đau lớn cần phải tránh; thật dễ dàng hơn khi chỉ cần cấu hình một DB thử nghiệm. Tuy nhiên, nó trở nên dễ dàng hơn một chút với sự ra đời của include_dirhỗ trợ trong postgresql.conf; bây giờ bạn chỉ có thể nối một dòng, sau đó viết tệp cấu hình đã tạo cho tất cả phần còn lại.

Kiểm tra nhanh hơn với PostgreSQL

Để biết thêm thông tin về cách cải thiện an toàn hiệu suất của PostgreSQL cho mục đích thử nghiệm, hãy xem câu trả lời chi tiết mà tôi đã viết về chủ đề này trước đó: Tối ưu hóa PostgreSQL để kiểm tra nhanh

Phương ngữ PostgreSQL của H2 không phải là một sự thay thế thực sự

Thay vào đó, một số người sử dụng cơ sở dữ liệu H2 ở chế độ phương ngữ PostgreSQL để chạy thử nghiệm. Tôi nghĩ điều đó cũng tệ như những người Rails sử dụng SQLite để thử nghiệm và PostgreSQL để triển khai sản xuất.

H2 hỗ trợ một số phần mở rộng PostgreSQL và mô phỏng phương ngữ PostgreSQL. Tuy nhiên, nó chỉ là - một giả lập. Bạn sẽ tìm thấy những nơi H2 chấp nhận một truy vấn nhưng PostgreSQL không, nơi khác hành vi, vv . Bạn cũng sẽ tìm thấy rất nhiều nơi mà PostgreSQL hỗ trợ thực hiện điều gì đó mà H2 không thể - như các hàm cửa sổ, tại thời điểm viết bài.

Nếu bạn hiểu những hạn chế của phương pháp này và truy cập cơ sở dữ liệu của bạn đơn giản, H2 có thể ổn. Nhưng trong trường hợp đó, bạn có thể là ứng cử viên tốt hơn cho một ORM tóm tắt cơ sở dữ liệu vì dù sao thì bạn cũng không sử dụng các tính năng thú vị của nó - và trong trường hợp đó, bạn không còn phải quan tâm đến tính tương thích của cơ sở dữ liệu nữa.

Bàn phím không phải là câu trả lời!

Đừng không sử dụng một bảng để tạo ra một "trong bộ nhớ" cơ sở dữ liệu. Nó không chỉ là không cần thiết vì dù sao thì nó cũng sẽ không giúp hiệu suất đáng kể mà còn là một cách tuyệt vời để làm gián đoạn quyền truy cập vào bất kỳ thứ gì khác mà bạn có thể quan tâm trong cùng một bản cài đặt PostgreSQL. Tài liệu 9.4 hiện có cảnh báo sau :

CẢNH BÁO

Mặc dù nằm bên ngoài thư mục dữ liệu PostgreSQL chính, không gian bảng là một phần không thể thiếu của cụm cơ sở dữ liệu và không thể được coi là một tập hợp các tệp dữ liệu tự trị. Chúng phụ thuộc vào siêu dữ liệu có trong thư mục dữ liệu chính và do đó không thể được đính kèm vào một cụm cơ sở dữ liệu khác hoặc được sao lưu riêng lẻ. Tương tự, nếu bạn mất một vùng bảng (xóa tệp, hỏng đĩa, v.v.), cụm cơ sở dữ liệu có thể không đọc được hoặc không thể khởi động. Đặt một vùng bảng trên một hệ thống tệp tạm thời như đĩa ram có nguy cơ ảnh hưởng đến độ tin cậy của toàn bộ cụm.

bởi vì tôi nhận thấy có quá nhiều người đang làm điều này và gặp rắc rối.

(Nếu bạn đã làm điều này, bạn có thể mkdirthư mục không gian bảng bị thiếu để PostgreSQL bắt đầu lại, sau đó DROPcác cơ sở dữ liệu, bảng bị thiếu, v.v. Tốt hơn là không nên làm điều đó.)


1
Tôi không rõ về cảnh báo được cung cấp ở đây. Nếu tôi đang cố gắng chạy Unit Test nhanh chóng, tại sao lại có một cụm tham gia? Không phải tất cả điều này chỉ là phiên bản PG cục bộ của tôi? Nếu cụm (của một) bị hỏng tại sao điều đó lại quan trọng, tôi đã định xóa nó bằng mọi cách.
Gates VP

1
@GatesVP PostgreSQL sử dụng thuật ngữ "cụm" theo cách hơi kỳ lạ, để chỉ phiên bản PostgreSQL (thư mục dữ liệu, bộ sưu tập cơ sở dữ liệu, quản trị viên bưu điện, v.v.). Vì vậy, nó không phải là một "cluster" theo nghĩa "cụm tính toán". Vâng, điều đó thật khó chịu và tôi muốn thấy thuật ngữ đó thay đổi. Và nếu nó bị vứt đi thì tất nhiên không thành vấn đề, nhưng mọi người thường cố gắng tạo ra một vùng bảng trong bộ nhớ vứt đi trên một bản cài đặt PostgreSQL có chứa dữ liệu mà họ quan tâm. Đó là một vấn đề.
Craig Ringer,

OK, đó là cả "những gì tôi nghĩ""rất đáng sợ" , giải pháp RAMDrive rõ ràng chỉ thuộc về một DB cục bộ không chứa dữ liệu hữu ích. Nhưng tại sao mọi người lại muốn chạy các bài kiểm tra đơn vị với một máy không phải là máy của họ? Dựa trên câu trả lời của bạn, Tablespaces + RamDisk nghe có vẻ hoàn toàn hợp pháp đối với phiên bản Unit Test thực tế của PGSQL chỉ chạy trên máy cục bộ của bạn.
Gates VP

1
@GatesVP Một số người giữ những thứ họ quan tâm trên máy cục bộ của họ - điều này là tốt, nhưng sau đó sẽ hơi ngớ ngẩn khi chạy thử nghiệm đơn vị với cùng một bản cài đặt DB. Mọi người thật ngớ ngẩn. Một số người trong số họ cũng không giữ các bản sao lưu thích hợp. Sau đó là những mối họa.
Craig Ringer,

Trong mọi trường hợp, nếu bạn đi đến tùy chọn đĩa ram, bạn cũng thực sự muốn WAL trên đĩa ram, vì vậy bạn cũng initdbcó thể cài đặt Pg hoàn toàn mới ở đó. Nhưng thực sự, có rất ít sự khác biệt giữa Pg được tinh chỉnh để kiểm tra nhanh trên bộ nhớ thông thường (fsync = tắt và các tính năng an toàn / độ bền dữ liệu khác bị tắt) so với chạy trên đĩa ram, ít nhất là trên Linux.
Craig Ringer,

66

Hoặc bạn có thể tạo TABLESPACE trong ramfs / tempfs và tạo tất cả các đối tượng của bạn ở đó.
Gần đây tôi đã được chỉ đến một bài báo về cách làm chính xác điều đó trên Linux .

Cảnh báo

Điều này có thể gây nguy hiểm cho tính toàn vẹn của toàn bộ cụm cơ sở dữ liệu của bạn .
Đọc cảnh báo đã thêm trong sách hướng dẫn.
Vì vậy, đây chỉ là một lựa chọn cho dữ liệu có thể sử dụng.

Đối với thử nghiệm đơn vị, nó sẽ hoạt động tốt. Nếu bạn đang chạy các cơ sở dữ liệu khác trên cùng một máy, hãy đảm bảo sử dụng một cụm cơ sở dữ liệu riêng (có cổng riêng) để an toàn.


4
Tôi thực sự nghĩ rằng đây là một lời khuyên tồi. Đừng làm điều này. Thay vào đó, initdbmột phiên bản postgres mới trong tempfs hoặc ramdisk. Đừng không sử dụng một bảng trong một tempfs vv, đó là mong manh và vô nghĩa. Tốt hơn hết bạn nên sử dụng một vùng bảng bình thường và tạo UNLOGGEDcác bảng - nó sẽ hoạt động tương tự. Và nó sẽ không giải quyết các yếu tố hiệu suất WAL và fsync trừ khi bạn thực hiện các hành động sẽ gây rủi ro cho tính toàn vẹn của toàn bộ DB (xem stackoverflow.com/q/9407442/398670 ). Đừng làm vậy.
Craig Ringer

29

Giờ đây, bạn có thể chạy phiên bản trong bộ nhớ của PostgreSQL trong các bài kiểm tra JUnit của bạn thông qua Thành phần PostgreSQL được nhúng từ OpenTable: https://github.com/opentable/otj-pg-embedded .

Bằng cách thêm phần phụ thuộc vào thư viện otj-pg-nhúng ( https://mvnrepository.com/artifact/com.opentable.components/otj-pg-embedded ), bạn có thể bắt đầu và dừng phiên bản PostgreSQL của riêng mình trong @Before và @Afer móc:

EmbeddedPostgres pg = EmbeddedPostgres.start();

Họ thậm chí còn cung cấp một quy tắc JUnit để JUnit tự động khởi động và dừng máy chủ cơ sở dữ liệu PostgreSQL của bạn cho bạn:

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

1
Trải nghiệm của bạn với gói này sáu tháng sau như thế nào? Hoạt động tốt hay bị lỗi?
oligofren

@Rubms Bạn đã di chuyển sang JUnit5 chưa? Làm thế nào để bạn sử dụng thay thế của @Rulevới @ExtendWith? Chỉ cần sử dụng .start()trong @BeforeAll?
Frankie Drake

Tôi chưa chuyển sang JUnit5, vì vậy tôi chưa thể trả lời câu hỏi của bạn. Lấy làm tiếc.
Rubms

Điều này đã hoạt động tốt. Cảm ơn. Sử dụng thông tin sau để tạo nguồn dữ liệu trong cấu hình mùa xuân của bạn nếu bạn thích:DataSource embeddedPostgresDS = EmbeddedPostgres.builder().start().getPostgresDatabase();
Sacky San

12

Bạn có thể sử dụng TestContainers để tạo một vùng chứa docker PosgreSQL cho các thử nghiệm: http://testcontainers.viewdocs.io/testcontainers-java/usage/database_containers/

TestContainers cung cấp một JUnit @ Rule / @ ClassRule : chế độ này bắt đầu một cơ sở dữ liệu bên trong một vùng chứa trước khi bạn kiểm tra và chia nhỏ nó sau đó.

Thí dụ:

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);
    }
}

7

Hiện đã có phiên bản trong bộ nhớ của PostgreSQL từ công ty Tìm kiếm của Nga có tên Yandex: https://github.com/yandex-qatools/postgresql-embedded

Nó dựa trên quy trình nhúng của Flapdoodle OSS.

Ví dụ về cách sử dụng (từ trang 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));");

Tôi đang sử dụng nó một thời gian. Nó hoạt động tốt.

CẬP NHẬT : dự án này không còn được duy trì tích cực nữa

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.

1
Điều đó phải bùng nổ theo đủ loại cách mới và thú vị nếu bạn sử dụng nhiều luồng, nhúng thời gian chạy JVM hoặc Mono, fork () các quy trình con của riêng bạn hoặc bất cứ thứ gì tương tự. Chỉnh sửa : Nó không thực sự được nhúng, nó chỉ là một trình bao bọc.
Craig Ringer

3

Bạn cũng có thể sử dụng cài đặt cấu hình PostgreSQL (chẳng hạn như cài đặt chi tiết trong câu hỏi và câu trả lời được chấp nhận ở đây ) để đạt được hiệu suất mà không nhất thiết phải sử dụng cơ sở dữ liệu trong bộ nhớ.


Vấn đề chính của OP là quay một phiên bản Postgres trong bộ nhớ, không phải vì hiệu suất, mà là vì sự đơn giản trong các bài kiểm tra đơn vị khởi động trong môi trường dev và CI.
ba người. Đi

0

Nếu bạn đang sử dụng NodeJS, bạn có thể sử dụng pg-mem (từ chối trách nhiệm: Tôi là tác giả) để mô phỏng các tính năng phổ biến nhất của db postgres.

Bạn sẽ có một cơ sở dữ liệu đầy đủ trong bộ nhớ, cô lập, nền tảng bất khả tri sao chép hành vi PG (thậm chí nó còn chạy trong trình duyệt ).

Tôi đã viết một bài báo để hướng dẫn cách sử dụng nó cho các bài kiểm tra đơn vị của bạn tại đây .

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.