Có thể có nhiều nhóm kết nối cơ sở dữ liệu trong đường ray để chuyển đổi giữa không?


12

Một chút nền tảng

Tôi đã sử dụng đá quý Căn hộ để chạy một ứng dụng nhiều người thuê trong nhiều năm. Gần đây, nhu cầu mở rộng cơ sở dữ liệu thành các máy chủ riêng biệt đã xuất hiện, máy chủ db đơn giản là không thể theo kịp nữa (cả đọc và viết đang trở nên quá nhiều) - và vâng, tôi đã tăng tỷ lệ phần cứng lên mức tối đa (dành riêng phần cứng, 64 lõi, ổ đĩa 12 Nvm-e trong cuộc đột kích 10, 384Gb ram, v.v.).

Tôi đã xem xét thực hiện công việc cho mỗi người thuê này (1 người thuê = 1 cấu hình / nhóm kết nối cơ sở dữ liệu) vì đó sẽ là một cách "đơn giản" và hiệu quả để tăng number-of-tenantsthêm dung lượng mà không phải thực hiện nhiều thay đổi mã ứng dụng.

Bây giờ, tôi đang chạy rails 4.2 atm., Sớm nâng cấp lên 5.2. Tôi có thể thấy rằng rails 6 bổ sung hỗ trợ cho các định nghĩa kết nối theo mô hình, tuy nhiên đó không thực sự là điều tôi cần, vì tôi có một lược đồ cơ sở dữ liệu được nhân đôi hoàn toàn cho mỗi trong số 20 người thuê của tôi. Thông thường tôi chuyển đổi "cơ sở dữ liệu" theo yêu cầu (trong phần mềm trung gian) hoặc trên mỗi công việc nền (phần mềm trung gian bên cạnh), tuy nhiên điều này hiện không quan trọng và xử lý ny đá quý Căn hộ, vì nó chỉ đặt search_pathtrong Postgresql và không thực sự thay đổi kết nối thực tế. Khi chuyển sang chiến lược lưu trữ cho mỗi người thuê, tôi sẽ cần chuyển toàn bộ kết nối cho mỗi yêu cầu.

Câu hỏi:

  1. Tôi hiểu rằng tôi có thể thực hiện một ActiveRecord::Base.establish_connection(config)yêu cầu / công việc nền - tuy nhiên, như tôi cũng hiểu, điều đó kích hoạt một bắt tay kết nối cơ sở dữ liệu hoàn toàn mới được thực hiện và một nhóm db mới sinh ra trong đường ray - phải không? Tôi đoán đó sẽ là một vụ tự sát hiệu suất để thực hiện loại chi phí đó cho mỗi yêu cầu đối với ứng dụng của tôi.
  2. Do đó, tôi tự hỏi liệu có ai có thể thấy tùy chọn với các đường ray ví dụ như thiết lập trước nhiều (tổng số 20) kết nối / nhóm cơ sở dữ liệu ngay từ đầu (ví dụ: khi khởi động ứng dụng), và sau đó chỉ chuyển đổi giữa các nhóm đó theo yêu cầu không? Vì vậy, các kết nối db của anh ấy đã được thực hiện và sẵn sàng để được sử dụng.
  3. Có phải tất cả chỉ là một ý tưởng nghèo nàn, và thay vào đó tôi nên tìm kiếm một cách tiếp cận khác? Ví dụ: 1 ứng dụng = một kết nối cụ thể đến một người thuê cụ thể. Hoặc một cái gì đó khác.


1
Bạn có thể quan tâm đến PR này trong kho GitHub của Rails mà gần đây đã thêm chính xác tính năng bạn cần cho masternhánh Rails hiện tại . Chạy Rails Egde sẽ là một tùy chọn hoặc sao lưu tính năng đó cho phiên bản Rails hiện tại của bạn?
spickermann

@spickermann ActiveRecord::Base.connected_to(shard: :shard_one) do ... endcó nghĩa là pool sẽ được (re-) sử dụng, thay vì tạo ra một kết nối hoàn toàn mới mọi lúc?
Ben

Câu trả lời:


4

Theo tôi hiểu, có 4 mẫu cho ứng dụng nhiều người thuê:

1. Mô hình chuyên dụng / Nhiều môi trường sản xuất

Mỗi phiên bản hoặc trường hợp cơ sở dữ liệu hoàn toàn lưu trữ ứng dụng người thuê khác nhau và không có gì được chia sẻ giữa những người thuê.

Đây là 1 ứng dụng và 1 cơ sở dữ liệu cho 1 người thuê. Sự phát triển sẽ dễ dàng như thể bạn chỉ phục vụ 1 người thuê nhà. Nhưng sẽ là cơn ác mộng đối với các tín đồ nếu bạn có 100 người thuê nhà.

2. Phân chia vật lý của người thuê nhà

1 ứng dụng cho tất cả người thuê nhưng 1 cơ sở dữ liệu cho 1 người thuê. Đây là những gì bạn đang tìm kiếm. Bạn có thể sử dụng ActiveRecord::Base.establish_connection(config)hoặc sử dụng đá quý hoặc cập nhật lên Rails 6 như các gợi ý khác. Xem câu trả lời cho (2) dưới đây.

3. Mô hình lược đồ biệt lập / Phân đoạn hợp lý

Trong Lược đồ biệt lập, các bảng đối tượng thuê hoặc các thành phần cơ sở dữ liệu được nhóm theo một lược đồ logic hoặc không gian tên và được phân tách khỏi các lược đồ đối tượng thuê khác, tuy nhiên lược đồ được lưu trữ trong cùng một thể hiện cơ sở dữ liệu.

1 ứng dụng và 1 cơ sở dữ liệu cho tất cả người thuê, giống như bạn làm với đá quý căn hộ.

4. Thành phần biệt lập một phần

Trong mô hình này, các thành phần có chức năng chung được chia sẻ giữa những người thuê trong khi các thành phần có chức năng duy nhất hoặc không liên quan được tách biệt. Ở lớp dữ liệu, dữ liệu phổ biến như dữ liệu xác định người thuê được nhóm hoặc giữ trong một bảng trong khi dữ liệu cụ thể của người thuê được phân lập tại lớp hoặc lớp đối tượng.


Đối với (1), ActiveRecord::Base.establish_connection(config)không bắt tay với db theo yêu cầu nếu bạn sử dụng đúng cách. Bạn có thể kiểm tra ở đâyđọc tất cả các bình luận ở đây .

Đối với (2), nếu bạn không muốn sử dụng establish_connection, bạn có thể sử dụng đa vũ khí đá quý (nó hoạt động cho đường ray 4.2) hoặc các loại đá quý khác. Hoặc, như đề xuất khác, bạn có thể cập nhật lên Rails 6.

Chỉnh sửa: Multiverse gem đang sử dụng establish_connection. Nó sẽ nối thêm database.ymlvà tạo một lớp cơ sở để mỗi lớp con chia sẻ cùng một kết nối / nhóm. Về cơ bản, nó làm giảm nỗ lực của chúng tôi để sử dụngestablish_connection .

Đối với (3), câu trả lời:

Nếu bạn không có nhiều người thuê và ứng dụng của bạn khá phức tạp, tôi khuyên bạn nên sử dụng mẫu Mô hình chuyên dụng. Vì vậy, bạn đi cho 1 ứng dụng = một kết nối cụ thể đến một người thuê cụ thể. Bạn không cần phải làm cho ứng dụng của mình phức tạp hơn bằng cách thêm nhiều kết nối cơ sở dữ liệu.

Nhưng nếu bạn có nhiều người thuê, tôi khuyên bạn nên sử dụng Phân chia vật lý của người thuê hoặc Thành phần biệt lập một phần tùy thuộc vào quy trình kinh doanh của bạn.

Dù bằng cách nào, bạn phải cập nhật / viết lại ứng dụng của mình để tuân thủ kiến ​​trúc mới.


Xin chào, cảm ơn trả lời của bạn. Tôi sẽ cần một ít thời gian để thực sự kiểm tra đề xuất trước khi tôi có thể thưởng một trong những câu trả lời nếu tiền thưởng là những giải pháp tốt.
Niels Kristian

Tôi có một vài câu hỏi liên quan đến 1 và 2. 1: Tôi không chắc là tôi hiểu tài liệu tham khảo của bạn. Là những gì bạn nói, mà tôi có thể gọi .est Thiết_connection (config) mà không cần thực hiện bắt tay db / tạo lại cuộc thăm dò db? Trong trường hợp đó, tôi không chắc hai liên kết giải thích điều đó như thế nào? 2: Đối với đa vũ trụ, không phải là chuyển đổi cơ sở dữ liệu theo mô hình chứ không phải là toàn bộ chuyển đổi db cho toàn bộ ứng dụng? Tôi cảm thấy tài liệu của họ khá mơ hồ
Niels Kristian

Tôi nghĩ rằng tôi đã hiểu lầm. Bạn có phiền để xây dựng những câu này? Tôi hiểu rằng tôi có thể thực hiện ActiveRecord :: Base.estabase_connection (config) cho mỗi yêu cầu / công việc nền - tuy nhiên, như tôi cũng hiểu, điều đó kích hoạt một bắt tay kết nối cơ sở dữ liệu hoàn toàn mới và một nhóm db mới sinh ra trong đường ray Nó đề nghị một yêu cầu tạo một nhóm db?
KSD Putra

Ý tôi là: (1) Tôi lo lắng về hiệu suất / chi phí mạng khi phải gọi ActiveRecord :: Base.est trai_connection (config) theo mọi yêu cầu, chỉ để chuyển đổi giữa các cơ sở dữ liệu / quốc gia khác nhau
Niels Kristian

Bạn không phải lo lắng về chi phí. Bây giờ, nếu bạn sử dụng DB đơn, bạn có một nhóm kết nối (bạn có thể kiểm tra liên kết về kết nối trong câu trả lời của (1) ở trên). Nếu bạn sử dụng establish_connectiontrong mô hình như mô hình này: class SecondTenantUser < ActiveRecord::Base; establish_connection(DB_SECOND_TENANT); endvà giả sử bạn có 5 mô hình, bạn tạo 5 nhóm kết nối tới DB_SECOND_TENANT. Và mỗi hồ bơi được đối xử bình đẳng. Vì vậy, bạn không tạo nhóm theo yêu cầu, nhưng mỗi establish_connection.
KSD Putra


3

Chỉ một vài ngày trước, shending ngang đã được thêm vào masterchi nhánh của Ruby on Rails trên GitHub. Hiện tại, tính năng này chưa được phát hành chính thức nhưng tùy thuộc vào phiên bản Rails của ứng dụng, bạn có thể muốn xem xét sử dụng Rails masterbằng cách thêm tính năng này vào Gemfile:

gem "rails", github: "rails/rails", branch: "master"

Với tính năng mới này, bạn có thể tận dụng nhóm kết nối cơ sở dữ liệu của Rails và chuyển đổi cơ sở dữ liệu dựa trên các điều kiện.

Tôi chưa sử dụng tính năng mới này, nhưng có vẻ khá đơn giản:

# in your config/database.yml
production:
  primary:
    database: my_database
    # other config: user, password, etc
  primary_tenant_1:
    database: tenant_1_database
    # other config: user, password, etc

# in your controller for example when updating a tenant
ActiveRecord::Base.connected_to(shard: "primary_tenant_#{tenant.database_shard_number}") do
  tenant.save
end

Bạn đã không thêm nhiều chi tiết về cách bạn xác định số người thuê hoặc cách thực hiện ủy quyền trong ứng dụng của bạn. Nhưng tôi sẽ cố gắng xác định số người thuê càng sớm càng tốt application_controllertrong một around_action. Một cái gì đó như thế này có thể là một điểm khởi đầu:

around_filter :determine_database_connection

private

def determine_database_connection
  # assuming you have a method to determine the current_tenant and that tenant
  # has a method that returns the number of the shard to use or even the 
  # full shard identifier
  shard = current_tenant.database_shard # returns for example `:primary_tenant_1` 

  ActiveRecord::Base.connected_to(shard: shard) do
    yield
  end
end

Điều đó cũng có ý nghĩa tương tự để chuyển trở lại kết nối mặc định trong trường hợp đó chứ? github.com/inflearning/apidor#middleware-considerations
Ben

1
Khi bạn rời khỏi ActiveRecord::Base.connected_to ... dokhối, nó sẽ sử dụng lại kết nối mặc định.
spickermann

@spickermann Tôi đã đọc ab gem này, Không phải chỉ dành cho rails6 sao?
7urkm3n

@ 7urkm3n Nó được bao gồm trong masternhánh Rails hiện tại .
spickermann

Xin chào, cảm ơn trả lời của bạn. Tôi sẽ cần một ít thời gian để thực sự kiểm tra đề xuất trước khi tôi có thể thưởng một trong những câu trả lời nếu tiền thưởng là những giải pháp tốt.
Niels Kristian
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.