2012-01-20 35 views
19

Tôi có một ứng dụng web Flask, SQLAlchemy sử dụng một máy chủ mysql duy nhất. Tôi muốn mở rộng thiết lập cơ sở dữ liệu để có một máy chủ phụ thuộc chỉ đọc để tôi có thể truyền bá các lần đọc giữa cả chủ và phụ trong khi tiếp tục ghi vào máy chủ db chủ.đọc slave, đọc-ghi thiết lập tổng thể

Tôi đã xem xét một số tùy chọn và tôi tin rằng tôi không thể làm điều này với SQLAlchemy thuần túy. Thay vào đó, tôi lập kế hoạch tạo 2 xử lý cơ sở dữ liệu trong webapp của tôi, mỗi cái cho các máy chủ db chủ và phụ. Sau đó, sử dụng một giá trị ngẫu nhiên đơn giản, sử dụng db chủ/phụ xử lý cho các hoạt động "SELECT".

Tuy nhiên, tôi không chắc liệu đây có phải là cách phù hợp để sử dụng SQLAlchemy hay không. Bất kỳ gợi ý/lời khuyên về làm thế nào để kéo này đi? Cảm ơn trước.

Trả lời

26

Tôi có ví dụ về cách thực hiện việc này trên blog của mình tại http://techspot.zzzeek.org/2012/01/11/django-style-database-routers-in-sqlalchemy/. Về cơ bản bạn có thể nâng cao Phiên để nó chọn từ chủ hoặc phụ thuộc vào truy vấn theo truy vấn. Một trục trặc tiềm tàng với cách tiếp cận đó là nếu bạn có một giao dịch gọi sáu truy vấn, bạn có thể sẽ sử dụng cả hai yêu cầu trong một yêu cầu .... nhưng chúng tôi chỉ đang cố bắt chước tính năng của Django :)

A hơi tiếp cận ít kỳ diệu mà còn thiết lập phạm vi của việc sử dụng một cách rõ ràng hơn, tôi đã sử dụng là một trang trí trên quan điểm callables (bất kể chúng được gọi trong Flask), như thế này:

@with_slave 
def my_view(...): 
    # ... 

with_slave sẽ làm một cái gì đó như thế này, giả sử bạn có phiên và một số công cụ được thiết lập:

master = create_engine("some DB") 
slave = create_engine("some other DB") 
Session = scoped_session(sessionmaker(bind=master)) 

def with_slave(fn): 
    def go(*arg, **kw): 
     s = Session(bind=slave) 
     return fn(*arg, **kw) 
    return go 

Ý tưởng là gọi số Session(bind=slave) gọi sổ đăng ký để lấy đối tượng Session thực tế cho chuỗi hiện tại, tạo nó nếu nó không tồn tại - tuy nhiên vì chúng ta đang chuyển đối số, scoped_session sẽ khẳng định rằng Session chúng ta làm ở đây chắc chắn là hoàn toàn mới.

Bạn trỏ nó vào "nô lệ" cho tất cả SQL tiếp theo. Sau đó, khi yêu cầu kết thúc, bạn sẽ đảm bảo rằng ứng dụng Flask của bạn đang gọi Session.remove() để xóa sổ đăng ký cho chuỗi đó. Khi đăng ký được sử dụng tiếp theo trên cùng một luồng, nó sẽ là một Session mới được gắn kết với "master".

Hoặc một biến thể, bạn muốn sử dụng các "nô lệ" chỉ dành riêng cho cuộc gọi đó, đây là "an toàn hơn" ở chỗ nó phục hồi bất kỳ ràng buộc hiện trở lại với phiên:

def with_slave(fn): 
    def go(*arg, **kw): 
     s = Session() 
     oldbind = s.bind 
     s.bind = slave 
     try: 
      return fn(*arg, **kw) 
     finally: 
      s.bind = oldbind 
    return go 

Đối với mỗi một trong các trang trí bạn có thể đảo ngược mọi thứ, có phiên được ràng buộc với một "nô lệ", nơi trang trí đặt nó trên "chủ" cho các hoạt động viết. Nếu bạn muốn một nô lệ ngẫu nhiên trong trường hợp đó, nếu Flask có một số loại "yêu cầu bắt đầu" sự kiện bạn có thể thiết lập nó tại thời điểm đó.

+2

Thnx zzzeek này sẽ giúp rất nhiều. Kudos cho tất cả các công việc tuyệt vời trên sqlalchemy. –

+0

Bình luận Rad, ví dụ mã tuyệt vời quá! Sẽ tốt hơn nếu sqlalchemy có cách nào đó để phân tích truy vấn và định tuyến tự động, nhưng trong một thế giới mà truy vấn có thể gây ra bảng tmp hoặc hoạt động ghi khác do kết quả của những gì có lẽ thường được đọc chỉ yêu cầu thứ gì đó như yêu cầu kế hoạch truy vấn từ chương trình phụ trợ trước khi gửi truy vấn và sẽ phức tạp hơn nó sẽ có giá trị đối với hầu hết các trường hợp. –

+1

chúng tôi có tùy chọn "phân tích truy vấn", mặc dù nó yêu cầu bạn tự viết phân tích. Hệ thống sharding ngang minh họa một ví dụ về loại kỹ thuật này, xem http://docs.sqlalchemy.org/en/rel_0_7/orm/extensions/horizontal_shard.html. – zzzeek

0

Hoặc, chúng tôi có thể thử cách khác. Chẳng hạn như chúng ta có thể khai báo hai lớp khác nhau với tất cả các thuộc tính instance giống nhau nhưng thuộc tính lớp __bind__ là khác nhau. Do đó, chúng tôi có thể sử dụng các lớp rw để đọc/ghi và r lớp chỉ đọc. :)

Tôi nghĩ cách này dễ dàng hơn và đáng tin cậy hơn. :)

Chúng tôi khai báo hai mô hình db vì chúng tôi có thể có các bảng trong hai db khác nhau có cùng tên. Bằng cách này, chúng tôi cũng có thể bỏ qua lỗi 'extend_existing' khi hai mô hình có cùng một số __tablename__.

Dưới đây là một ví dụ:

app = Flask(__name__) 
app.config['SQLALCHEMY_BINDS'] = {'rw': 'rw', 'r': 'r'} 
db = SQLAlchemy(app) 
db.Model_RW = db.make_declarative_base() 

class A(db.Model): 
    __tablename__ = 'common' 
    __bind_key__ = 'r' 

class A(db.Model_RW): 
    __tablename__ = 'common' 
    __bind_key__ = 'rw'  
+0

bạn có thể cải thiện câu trả lời của mình bằng cách cung cấp ví dụ về tạo, xác định và sử dụng hai cơ sở dữ liệu với khả năng đọc và ghi khác nhau –