SQLAlchemy: Sự khác biệt giữa flush () và commit () là gì?


422

Sự khác biệt là gì flush()commit()trong SQLAlchemy là gì?

Tôi đã đọc các tài liệu, nhưng không ai khôn ngoan hơn - dường như họ cho rằng một sự hiểu biết trước mà tôi không có.

Tôi đặc biệt quan tâm đến tác động của chúng đối với việc sử dụng bộ nhớ. Tôi đang tải một số dữ liệu vào cơ sở dữ liệu từ một loạt các tệp (tổng cộng khoảng 5 triệu hàng) và phiên của tôi thỉnh thoảng bị đổ - đó là một cơ sở dữ liệu lớn và một máy không có nhiều bộ nhớ.

Tôi tự hỏi nếu tôi sử dụng quá nhiều commit()và không đủ flush()cuộc gọi - nhưng không thực sự hiểu sự khác biệt là gì, thật khó để nói!

Câu trả lời:


534

Đối tượng Phiên về cơ bản là một giao dịch liên tục thay đổi cơ sở dữ liệu (cập nhật, chèn, xóa). Các hoạt động này không tồn tại trong cơ sở dữ liệu cho đến khi chúng được cam kết (nếu chương trình của bạn hủy bỏ vì một số lý do trong giao dịch giữa phiên, mọi thay đổi không được cam kết trong đó sẽ bị mất).

Đối tượng phiên đăng ký các hoạt động giao dịch với session.add(), nhưng chưa giao tiếp chúng với cơ sở dữ liệu cho đến khi session.flush()được gọi.

session.flush()truyền đạt một loạt các hoạt động đến cơ sở dữ liệu (chèn, cập nhật, xóa). Cơ sở dữ liệu duy trì chúng như các hoạt động chờ xử lý trong một giao dịch. Các thay đổi không tồn tại vĩnh viễn trên đĩa hoặc hiển thị cho các giao dịch khác cho đến khi cơ sở dữ liệu nhận được CAM KẾT cho giao dịch hiện tại (đó là những gì session.commit()thực hiện).

session.commit() cam kết (vẫn tồn tại) những thay đổi đối với cơ sở dữ liệu.

flush()được luôn gọi như một phần của một cuộc gọi đến commit()( 1 ).

Khi bạn sử dụng một đối tượng Phiên để truy vấn cơ sở dữ liệu, truy vấn sẽ trả về kết quả cả từ cơ sở dữ liệu và từ các phần bị xóa của giao dịch không được cam kết mà nó giữ. Theo mặc định, các đối tượng phiênautoflush các hoạt động của chúng, nhưng điều này có thể bị vô hiệu hóa.

Hy vọng ví dụ này sẽ làm cho điều này rõ ràng hơn:

#---
s = Session()

s.add(Foo('A')) # The Foo('A') object has been added to the session.
                # It has not been committed to the database yet,
                #   but is returned as part of a query.
print 1, s.query(Foo).all()
s.commit()

#---
s2 = Session()
s2.autoflush = False

s2.add(Foo('B'))
print 2, s2.query(Foo).all() # The Foo('B') object is *not* returned
                             #   as part of this query because it hasn't
                             #   been flushed yet.
s2.flush()                   # Now, Foo('B') is in the same state as
                             #   Foo('A') was above.
print 3, s2.query(Foo).all() 
s2.rollback()                # Foo('B') has not been committed, and rolling
                             #   back the session's transaction removes it
                             #   from the session.
print 4, s2.query(Foo).all()

#---
Output:
1 [<Foo('A')>]
2 [<Foo('A')>]
3 [<Foo('A')>, <Foo('B')>]
4 [<Foo('A')>]

Chỉ một điều nữa: bạn có biết việc gọi commit () làm tăng bộ nhớ được sử dụng hay giảm nó không?
AP257

2
Điều này cũng sai đối với các công cụ db không hỗ trợ các giao dịch như myisam. Vì không có giao dịch liên tục, tuôn ra thậm chí còn ít hơn để phân biệt với cam kết.
underrun

1
@underrun Vậy nếu tôi làm session.query() sau session.flush(), tôi sẽ thấy những thay đổi của mình chứ? Cho rằng tôi đang sử dụng MyISAM.
Frozen Flame

1
Là nó tốt hay kém phong cách đến việc sử dụng flush()commit(), hay tôi nên rời khỏi rằng có đến Alchemy. Tôi đã sử dụng flush()trong một số trường hợp vì các truy vấn tiếp theo cần thiết để nhận dữ liệu mới.
Jens

1
@Jens Sử dụng autoflush( Truetheo mặc định). Nó sẽ tự động xóa trước tất cả các truy vấn, vì vậy bạn không cần phải nhớ mỗi lần.
Kiran Jonnalagadda

24

Như @snapshoe nói

flush() gửi các câu lệnh SQL của bạn đến cơ sở dữ liệu

commit() cam kết giao dịch.

Khi nào session.autocommit == False:

commit()sẽ gọi flush()nếu bạn đặt autoflush == True.

Khi nào session.autocommit == True:

Bạn không thể gọi commit()nếu bạn chưa bắt đầu giao dịch (điều mà bạn có thể chưa thực hiện vì có lẽ bạn chỉ sử dụng chế độ này để tránh quản lý giao dịch theo cách thủ công).

Trong chế độ này, bạn phải gọi flush()để lưu các thay đổi ORM của mình. Việc xả hiệu quả cũng cam kết dữ liệu của bạn.


24
"commit () sẽ gọi flush () nếu autoflush của bạn == True." không hoàn toàn chính xác, hoặc chỉ gây hiểu nhầm. Cam kết luôn luôn xả, bất kể cài đặt tự động xóa.
Ilja Everilä

3
Thông số autoflushkiểm soát xem sqlalchemy trước tiên sẽ phát hành lệnh xóa nếu có lệnh ghi đang chờ xử lý trước khi đưa ra truy vấn và không liên quan gì đến việc kiểm soát luồng không thể tránh khỏi trên cam kết.
SuperShoot

4

Tại sao phải xả nếu bạn có thể cam kết?

Là một người mới làm việc với cơ sở dữ liệu và sqlalchemy, các câu trả lời trước đó - flush()gửi các câu lệnh SQL đến DB và commit()vẫn tồn tại chúng - không rõ ràng với tôi. Các định nghĩa có ý nghĩa nhưng nó không rõ ràng ngay lập tức từ các định nghĩa tại sao bạn sẽ sử dụng tuôn ra thay vì chỉ cam kết.

Vì một cam kết luôn tuôn ra ( https://docs.sqlalchemy.org/en/13/orm/session_basics.html#commting ) những âm thanh này thực sự giống nhau. Tôi nghĩ vấn đề lớn cần làm nổi bật là việc xả nước không phải là vĩnh viễn và có thể được hoàn tác, trong khi đó một cam kết là vĩnh viễn, theo nghĩa là bạn không thể yêu cầu cơ sở dữ liệu hoàn tác cam kết cuối cùng (tôi nghĩ)

@snapshoe nhấn mạnh rằng nếu bạn muốn truy vấn cơ sở dữ liệu và nhận kết quả bao gồm các đối tượng mới được thêm vào, bạn cần phải xóa trước (hoặc đã cam kết, sẽ xóa cho bạn). Có lẽ điều này hữu ích cho một số người mặc dù tôi không chắc tại sao bạn lại muốn tuôn ra thay vì cam kết (ngoài câu trả lời tầm thường mà nó có thể được hoàn tác).

Trong một ví dụ khác, tôi đã đồng bộ hóa tài liệu giữa DB cục bộ và máy chủ từ xa và nếu người dùng quyết định hủy, tất cả các bổ sung / cập nhật / xóa sẽ được hoàn tác (nghĩa là không đồng bộ hóa một phần, chỉ đồng bộ hóa hoàn toàn). Khi cập nhật một tài liệu duy nhất, tôi quyết định chỉ cần xóa hàng cũ và thêm phiên bản cập nhật từ máy chủ từ xa. Nó chỉ ra rằng do cách viết sqlalchemy, thứ tự các hoạt động khi cam kết không được đảm bảo. Điều này dẫn đến việc thêm một phiên bản trùng lặp (trước khi cố gắng xóa phiên bản cũ), dẫn đến DB không có một ràng buộc duy nhất. Để giải quyết vấn đề này, tôi đã sử dụng flush()để duy trì trật tự, nhưng tôi vẫn có thể hoàn tác nếu sau đó quá trình đồng bộ hóa không thành công.

Xem bài đăng của tôi về điều này tại: Có bất kỳ thứ tự nào để thêm so với xóa khi cam kết trong sqlalchemy

Tương tự, ai đó muốn biết liệu thứ tự thêm có được duy trì khi cam kết hay không, tức là nếu tôi thêm object1rồi thêm object2, có object1được thêm vào cơ sở dữ liệu trước khi object2 SQLAlchemy lưu thứ tự khi thêm đối tượng vào phiên không?

Một lần nữa, ở đây có lẽ việc sử dụng tuôn ra () sẽ đảm bảo hành vi mong muốn. Vì vậy, tóm lại, một lần sử dụng cho tuôn ra là để cung cấp đảm bảo đơn hàng (tôi nghĩ), một lần nữa trong khi vẫn cho phép mình tùy chọn "hoàn tác" mà cam kết không cung cấp.

Autoflush và Autocommit

Lưu ý, autoflush có thể được sử dụng để đảm bảo các truy vấn hoạt động trên cơ sở dữ liệu được cập nhật vì sqlalchemy sẽ xóa trước khi thực hiện truy vấn. https://docs.sqlalchemy.org/en/13/orm/session_api.html#sqlalchemy.orm.session.Session.params.autoflush

Autocommit là một thứ khác mà tôi không hoàn toàn hiểu nhưng có vẻ như việc sử dụng nó không được khuyến khích: https://docs.sqlalchemy.org/en/13/orm/session_api.html#sqlalchemy.orm.session.Session.params. tự động

Sử dụng bộ nhớ

Bây giờ câu hỏi ban đầu thực sự muốn biết về tác động của tuôn ra so với cam kết cho mục đích bộ nhớ. Vì khả năng tồn tại hay không là điều mà cơ sở dữ liệu cung cấp (tôi nghĩ), chỉ cần xả nước là đủ để giảm tải cho cơ sở dữ liệu - mặc dù cam kết không nên làm tổn thương (thực sự có thể giúp - xem bên dưới) nếu bạn không quan tâm đến việc hoàn tác .

sqlalchemy sử dụng tham chiếu yếu cho các đối tượng đã bị xóa: https://docs.sqlalchemy.org/en/13/orm/session_state_manler.html#session-references-behavior

Điều này có nghĩa là nếu bạn không có một đối tượng rõ ràng được giữ ở đâu đó, như trong danh sách hoặc chính tả, sqlalchemy sẽ không giữ nó trong bộ nhớ.

Tuy nhiên, sau đó bạn có cơ sở dữ liệu của những điều phải lo lắng. Có lẽ xả nước mà không cam kết đi kèm với một số hình phạt bộ nhớ để duy trì giao dịch. Một lần nữa, tôi mới biết điều này nhưng đây là một liên kết dường như gợi ý chính xác điều này: https://stackoverflow.com/a/15305650/764365

Nói cách khác, các cam kết sẽ làm giảm việc sử dụng bộ nhớ, mặc dù có lẽ có sự đánh đổi giữa bộ nhớ và hiệu năng ở đây. Nói cách khác, có lẽ bạn không muốn cam kết mỗi lần thay đổi cơ sở dữ liệu, mỗi lần một (vì lý do hiệu suất), nhưng chờ đợi quá lâu sẽ làm tăng mức sử dụng bộ nhớ.


1

Điều này không trả lời đúng câu hỏi ban đầu nhưng một số người đã đề cập rằng với session.autoflush = Truebạn không phải sử dụng session.flush()... Và điều này không phải lúc nào cũng đúng.

Nếu bạn muốn sử dụng id của một đối tượng mới được tạo ở giữa một giao dịch , bạn phải gọi session.flush().

# Given a model with at least this id
class AModel(Base):
   id = Column(Integer, primary_key=True)  # autoincrement by default on integer primary key

session.autoflush = True

a = AModel()
session.add(a)
a.id  # None
session.flush()
a.id  # autoincremented integer

Điều này là do autoflushkhông KHÔNG tự động điền id (mặc dù một truy vấn của đối tượng sẽ, mà đôi khi có thể gây nhầm lẫn như trong "lý do tại sao công trình này ở đây nhưng không có?" Nhưng snapshoe đã bao phủ phần này).


Một khía cạnh liên quan có vẻ khá quan trọng đối với tôi và không thực sự được đề cập:

Tại sao bạn không cam kết tất cả thời gian? - Câu trả lời là nguyên tử .

Một từ ưa thích để nói: tất cả các hoạt động phải được thực hiện thành công HOẶC không ai trong số chúng sẽ có hiệu lực.

Ví dụ: nếu bạn muốn tạo / cập nhật / xóa một số đối tượng (A) và sau đó tạo / cập nhật / xóa một (B) khác, nhưng nếu (B) không thành công, bạn muốn hoàn nguyên (A). Điều này có nghĩa là 2 hoạt động đó là nguyên tử .

Do đó, nếu (B) cần kết quả của (A), bạn muốn gọi flushsau (A) và commitsau (B).

Ngoài ra, nếu session.autoflush is True, ngoại trừ trường hợp mà tôi đã đề cập ở trên hoặc những người khác trong câu trả lời của Jimbo , bạn sẽ không cần phải gọi flushthủ công.

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.