เป็นไปได้ไหมที่จะมีพูลการเชื่อมต่อฐานข้อมูลหลายตัวในรางเพื่อสลับไปมา?


12

พื้นหลังเล็กน้อย

ฉันใช้พลอยอพาร์ทเม้นท์เพื่อเรียกใช้แอพหลายผู้เช่ามาหลายปีแล้ว ตอนนี้เมื่อเร็ว ๆ นี้ความต้องการในการขยายฐานข้อมูลออกไปยังโฮสต์ที่แยกต่างหากได้มาถึงแล้วเซิร์ฟเวอร์ db ก็ไม่สามารถติดตามได้อีกต่อไป (ทั้งการอ่านและการเขียนมีมากเกินไป) - และใช่ฉันปรับขนาดฮาร์ดแวร์ให้สูงสุด ฮาร์ดแวร์ 64 คอร์, 12 Nvm-e ไดรฟ์ในการโจมตี 10, 384Gb ram เป็นต้น)

ฉันกำลังพิจารณาที่จะทำสิ่งนี้ต่อผู้เช่า (1 tenant = 1 การเชื่อมต่อฐานข้อมูล config / pool) เพราะนั่นจะเป็นวิธีที่ "ง่าย" และมีประสิทธิภาพในการเพิ่มnumber-of-tenantsความจุได้มากขึ้นโดยไม่ต้องเปลี่ยนรหัสแอปพลิเคชัน

ตอนนี้ฉันกำลังเรียกใช้ rails 4.2 atm. และอีกไม่นานจะอัพเกรดเป็น 5.2 ฉันเห็นว่า Rails 6 เพิ่มการสนับสนุนสำหรับคำจำกัดความการเชื่อมต่อต่อโมเดลอย่างไรก็ตามนั่นไม่ใช่สิ่งที่ฉันต้องการจริงๆเพราะฉันมี schema ฐานข้อมูลแบบมิเรอร์ทั้งหมดสำหรับผู้เช่า 20 คนของฉัน โดยทั่วไปฉันจะสลับ "ฐานข้อมูล" ต่อคำขอ (เป็นมิดเดิลแวร์) หรือต่อแบ็คกราวน์ (มิดเดิลแวร์ด้านข้าง) อย่างไรก็ตามนี่เป็นเรื่องเล็กน้อยและจัดการ ny gem อพาร์ทเม้นท์เนื่องจากมันเพิ่งตั้งค่าsearch_pathใน Postgresql และไม่เปลี่ยนการเชื่อมต่อจริง เมื่อเปลี่ยนเป็นกลยุทธ์การโฮสต์ต่อผู้เช่าฉันจะต้องสลับการเชื่อมต่อทั้งหมดตามคำขอ

คำถาม:

  1. ฉันเข้าใจว่าฉันสามารถทำงานActiveRecord::Base.establish_connection(config)ตามคำขอ / แบ็คกราวน์ได้ - แต่ตามที่ฉันเข้าใจแล้วนั่นทำให้เกิดการจับมือการเชื่อมต่อฐานข้อมูลใหม่ทั้งหมดและจะสร้าง db pool ใหม่เพื่อวางไข่ในราง - ใช่ไหม? ฉันเดาว่าจะเป็นการฆ่าตัวตายของการแสดงเพื่อให้ได้ค่าใช้จ่ายในทุก ๆ การร้องขอของฉัน
  2. ฉันจึงสงสัยว่าถ้าใครสามารถเห็นตัวเลือกที่มีรางของเช่นการสร้างการเชื่อมต่อ / พูลฐานข้อมูลหลาย ๆ อัน (รวมทั้งหมด 20) จากจุดเริ่มต้น (เช่นเมื่อบูตแอพพลิเคชั่น) แล้วสลับระหว่างพูลเหล่านั้นตามคำขอ เพื่อให้เขาเชื่อมต่อฐานข้อมูลเรียบร้อยแล้วและพร้อมใช้งาน
  3. ทั้งหมดนี้เป็นเพียงความคิดที่ไม่ดีและฉันควรจะมองหาวิธีการที่แตกต่างออกไปหรือไม่? เช่น 1 แอปอินสแตนซ์ = หนึ่งการเชื่อมต่อเฉพาะกับผู้เช่าหนึ่งราย หรืออย่างอื่น.

2
guide.rubyonrails.org/active_record_multiple_databases.htmlฉันคิดว่ามันอาจช่วยคุณได้
Alex Golubenko

1
คุณอาจสนใจPRนี้ในที่เก็บ GitHub ของ Railsที่เพิ่งเพิ่มฟีเจอร์ที่คุณต้องการในmasterสาขาRails ปัจจุบัน Rails Egde นั้นจะใช้เป็นตัวเลือกหรือไม่สนับสนุนคุณสมบัตินั้นกับ Rails ปัจจุบันของคุณ?
spickermann

@spickermann ActiveRecord::Base.connected_to(shard: :shard_one) do ... endหมายความว่าจะใช้พูล (re-) แทนการสร้างการเชื่อมต่อใหม่ทั้งหมดทุกครั้งหรือไม่
เบ็น

คำตอบ:


4

อย่างที่ฉันเข้าใจมี 4 รูปแบบสำหรับแอพที่มีผู้เช่าหลายคน:

1. รูปแบบเฉพาะ / สภาพแวดล้อมการผลิตที่หลากหลาย

แต่ละอินสแตนซ์หรือฐานข้อมูลจะโฮสต์แอปพลิเคชันผู้เช่าที่แตกต่างกันโดยสิ้นเชิง

นี่คือแอปอินสแตนซ์ 1 รายการและฐานข้อมูล 1 รายการสำหรับผู้เช่า 1 ราย การพัฒนาจะง่ายเหมือนคุณให้บริการ 1 ผู้เช่าเท่านั้น แต่จะฝันร้ายหากคุณมี devops พูด 100 ผู้เช่า

2. การแยกทางกายภาพของผู้เช่า

1 อินสแตนซ์แอปสำหรับผู้เช่าทั้งหมด แต่ 1 ฐานข้อมูลสำหรับ 1 ผู้เช่า นี่คือสิ่งที่คุณกำลังค้นหา คุณสามารถใช้ActiveRecord::Base.establish_connection(config)หรือใช้พลอยหรืออัปเดตเป็น Rails 6 ตามที่คนอื่นแนะนำ ดูคำตอบสำหรับ (2) ด้านล่าง

3. แบบจำลองสคีแยก / การแบ่งแบบลอจิคัล

ใน Schema Isolated ตารางผู้เช่าหรือส่วนประกอบฐานข้อมูลเป็นกลุ่มภายใต้สคีตรรกะหรือชื่อพื้นที่และแยกออกจาก schema ผู้เช่าอื่น ๆ แต่สคีมาจะโฮสต์ในอินสแตนซ์ฐานข้อมูลเดียวกัน

แอปอินสแตนซ์ 1 ตัวและฐานข้อมูล 1 ฐานสำหรับผู้เช่าทุกรายเช่นที่คุณทำกับอพาร์ทเมนท์อัญมณี

4. ส่วนประกอบที่แยกได้บางส่วน

ในรุ่นนี้ส่วนประกอบที่มีฟังก์ชั่นทั่วไปจะถูกใช้ร่วมกันระหว่างผู้เช่าในขณะที่ส่วนประกอบที่มีฟังก์ชั่นที่ไม่ซ้ำกันหรือไม่เกี่ยวข้องจะถูกแยกออก ที่ชั้นข้อมูลข้อมูลทั่วไปเช่นข้อมูลที่ระบุผู้เช่าจะถูกจัดกลุ่มหรือเก็บไว้ในตารางเดียวในขณะที่ข้อมูลเฉพาะของผู้เช่าจะถูกแยกออกจากตารางหรือชั้นของอินสแตนซ์


สำหรับ (1) ActiveRecord::Base.establish_connection(config)อย่าจับมือกับ db ต่อคำขอหากคุณใช้อย่างถูกต้อง คุณสามารถตรวจสอบที่นี่และอ่านทุกความคิดเห็นที่นี่

สำหรับ (2) ถ้าคุณไม่ต้องการใช้establish_connectionคุณสามารถใช้ gem multiverse (ใช้ได้กับราง 4.2) หรืออัญมณีอื่น ๆ หรือตามที่แนะนำอื่น ๆ คุณสามารถอัปเดตเป็น Rails 6

แก้ไข: establish_connectionอัญมณีลิขสิทธิ์ใช้ มันจะผนวกdatabase.ymlและสร้างคลาสพื้นฐานเพื่อให้คลาสย่อยแต่ละคลาสใช้การเชื่อมต่อ / พูลเดียวกันร่วมกัน โดยทั่วไปจะช่วยลดความพยายามในการใช้ของestablish_connectionเรา

สำหรับ (3) คำตอบ:

หากคุณไม่มีผู้เช่าจำนวนมากและแอปพลิเคชันของคุณค่อนข้างซับซ้อนฉันขอแนะนำให้คุณใช้รูปแบบ Dedicated Model ดังนั้นคุณจะไปสำหรับ 1 แอปอินสแตนซ์ = หนึ่งการเชื่อมต่อเฉพาะกับผู้เช่ารายหนึ่ง คุณไม่จำเป็นต้องทำให้แอพซับซ้อนขึ้นด้วยการเพิ่มการเชื่อมต่อฐานข้อมูลหลายรายการ

แต่ถ้าคุณมีผู้เช่าจำนวนมากฉันขอแนะนำให้คุณใช้การแยกทางกายภาพของผู้เช่าหรือส่วนประกอบแยกบางส่วนขึ้นอยู่กับกระบวนการทางธุรกิจของคุณ

ไม่ว่าจะด้วยวิธีใดคุณต้องอัปเดต / เขียนแอปพลิเคชันของคุณใหม่เพื่อให้สอดคล้องกับสถาปัตยกรรมใหม่


สวัสดีขอบคุณสำหรับคำตอบ ฉันจะต้องใช้เวลาสักครู่เพื่อทดสอบข้อเสนอแนะจริง ๆ ก่อนที่ฉันจะได้รับรางวัลคำตอบอย่างใดอย่างหนึ่งถ้าพวกเขาเป็นทางออกที่ดี
Niels Kristian

ฉันมีคำถามสองสามข้อเกี่ยวกับ 1 และ 2 1: ฉันไม่แน่ใจว่าฉันเข้าใจการอ้างอิงของคุณหรือไม่ เป็นสิ่งที่คุณพูดหรือไม่ที่ฉันสามารถเรียกว่า. asablish_connection (config) โดยไม่ต้องทำ db handshake / สร้างแบบสำรวจความคิดเห็น db ใหม่ ในกรณีนี้ฉันไม่แน่ใจว่าลิงก์ทั้งสองอธิบายได้อย่างไร 2: สำหรับลิขสิทธิ์นั่นไม่ใช่การสลับฐานข้อมูลต่อโมเดลแทนที่จะเป็น db ทั้งหมดสำหรับแอปทั้งหมดหรือไม่ ฉันรู้สึกว่าเอกสารของพวกเขาค่อนข้างคลุมเครือ
Niels Kristian

ฉันคิดว่าฉันเข้าใจผิด คุณตั้งใจจะทำประโยคเหล่านี้ให้ละเอียดหรือไม่? ผมเข้าใจว่าผมสามารถทำ ActiveRecord :: Base.establish_connection (config) ต่อคำขอของงานพื้นหลัง / - แต่เป็นฉันยังเข้าใจทริกเกอร์ที่จับมือเชื่อมต่อฐานข้อมูลใหม่ทั้งหมดที่จะทำและสระว่ายน้ำฐานข้อมูลใหม่ที่จะวางไข่ในรางมัน แนะนำว่าหนึ่งคำขอสร้างหนึ่ง db pool?
KSD Putra

ฉันหมายถึง: (1) ฉันกังวลเกี่ยวกับค่าใช้จ่ายด้านประสิทธิภาพ / เครือข่ายเมื่อต้องเรียกใช้ ActiveRecord :: Base.establish_connection (config) ในทุกคำขอเพียงเพื่อสลับระหว่างฐานข้อมูล / ประเทศที่แตกต่างกัน
Niels Kristian

คุณไม่ต้องกังวลเกี่ยวกับค่าใช้จ่าย ตอนนี้ถ้าคุณใช้ฐานข้อมูลเดียวคุณจะมีกลุ่มการเชื่อมต่อหนึ่ง (คุณสามารถตรวจสอบลิงค์เกี่ยวกับการเชื่อมต่อในคำตอบของ (1) ด้านบน) ถ้าคุณใช้establish_connectionในโมเดลเช่นนี้: class SecondTenantUser < ActiveRecord::Base; establish_connection(DB_SECOND_TENANT); endและถ้าคุณมี 5 โมเดลคุณจะสร้างพูลการเชื่อมต่อ 5 อันไปยัง DB_SECOND_TENANT และแต่ละสระได้รับการปฏิบัติอย่างเท่าเทียมกัน ดังนั้นคุณไม่ได้สร้างสระว่ายน้ำตามคำขอ establish_connectionแต่ต่อ
KSD Putra

3

จากสิ่งที่ฉันเข้าใจ (2) ควรเป็นไปได้ด้วยการสลับการเชื่อมต่อด้วยตนเองใน Rails 6


ขอบคุณ แต่นี่ดูเหมือนจะค่อนข้างไกลจากกรณีการใช้งานของฉัน มันบอกเป็นนัยถึงการเขียนแอพทั้งหมดสำหรับการใช้ขั้นตอนนี้ทุกที่
Niels Kristian

3

เพียงไม่กี่วันที่ผ่านมาการเพิ่มการเรียงแนวนอนถูกเพิ่มลงในmasterสาขาของ Ruby on Rails ใน GitHub ปัจจุบันคุณสมบัตินี้ยังไม่เปิดตัวอย่างเป็นทางการ แต่ขึ้นอยู่กับรุ่น Rails ของแอปพลิเคชั่นที่คุณอาจต้องการพิจารณาใช้ Rails masterโดยเพิ่มสิ่งนี้ในGemfile:

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

ด้วยคุณสมบัติใหม่นี้คุณสามารถใช้ประโยชน์จากพูลการเชื่อมต่อฐานข้อมูลของ Rails และสลับฐานข้อมูลตามเงื่อนไข

ฉันไม่ได้ใช้คุณสมบัติใหม่นี้ แต่ดูเหมือนจะตรงไปตรงมามาก:

# 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

คุณไม่ได้เพิ่มรายละเอียดมากนักเกี่ยวกับวิธีการกำหนดหมายเลขผู้เช่าหรือวิธีการอนุญาตในแอปพลิเคชันของคุณ แต่ผมจะพยายามที่จะกำหนดจำนวนผู้เช่าเร็วที่สุดเท่าที่เป็นไปได้ในในapplication_controller around_actionบางสิ่งเช่นนี้อาจเป็นจุดเริ่มต้น:

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

นั่นเป็นเหตุผลที่จะเปลี่ยนกลับไปใช้การเชื่อมต่อเริ่มต้นในกรณีนั้นด้วยหรือไม่ github.com/inflamatic/apartment#middleware-considerations
Ben

1
เมื่อคุณออกจากActiveRecord::Base.connected_to ... doบล็อกมันจะใช้การเชื่อมต่อเริ่มต้นอีกครั้ง
spickermann

@spickermann ฉันอ่านอัญมณีนี้ไม่ได้เป็นแค่สำหรับ Rails6 เท่านั้น?
7urkm3n

@ 7urkm3n มันรวมอยู่ในmasterสาขาRails ปัจจุบัน
spickermann

สวัสดีขอบคุณสำหรับคำตอบ ฉันจะต้องใช้เวลาสักครู่เพื่อทดสอบข้อเสนอแนะจริง ๆ ก่อนที่ฉันจะได้รับรางวัลคำตอบอย่างใดอย่างหนึ่งถ้าพวกเขาเป็นทางออกที่ดี
Niels Kristian
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.