Truy cập đồng thời SQLite


177

SQLite3 có xử lý an toàn truy cập đồng thời bằng nhiều quá trình đọc / ghi từ cùng một DB không? Có bất kỳ ngoại lệ nền tảng cho điều đó?


3
Tôi quên đề cập đến tiền thưởng : hầu hết các câu trả lời đều ổn: "SQLite đủ nhanh", "SQLite xử lý đồng thời tốt" v.v., nhưng, imho, không trả lời chi tiết / không giải thích rõ ràng điều gì xảy ra nếu hai thao tác ghi sẽ đến chính xác cùng một lúc (trường hợp lý thuyết rất hiếm). 1) Nó có gây ra lỗi và làm gián đoạn chương trình không? hoặc 2) Thao tác ghi thứ hai sẽ đợi cho đến khi thao tác ghi đầu tiên kết thúc? hoặc 3) Một trong các thao tác ghi sẽ bị loại bỏ (mất dữ liệu!)? 4) Cái gì khác? Biết những hạn chế của việc viết đồng thời có thể hữu ích trong nhiều tình huống.
Basj

7
@Basj Tóm lại, 2) nó sẽ chờ và thử lại nhiều lần (Có thể định cấu hình), 1) gây ra lỗi, SQLITE_BUSY.3) bạn có thể đăng ký Gọi lại để xử lý lỗi SQLITE_BUSY.
obgnaw

Câu trả lời:


112

Nếu hầu hết các truy cập đồng thời đó là đọc (ví dụ CHỌN), SQLite có thể xử lý chúng rất tốt. Nhưng nếu bạn bắt đầu viết đồng thời, sự tranh chấp khóa có thể trở thành một vấn đề. Rất nhiều thứ sẽ phụ thuộc vào hệ thống tập tin của bạn nhanh như thế nào, vì bản thân công cụ SQLite cực kỳ nhanh và có nhiều tối ưu hóa thông minh để giảm thiểu sự tranh chấp. Đặc biệt là SQLite 3.

Đối với hầu hết các ứng dụng máy tính để bàn / máy tính xách tay / máy tính bảng / điện thoại, SQLite đủ nhanh vì không đủ đồng thời. (Firefox sử dụng SQLite rộng rãi cho dấu trang, lịch sử, v.v.)

Đối với các ứng dụng máy chủ, một số người trước đây đã nói rằng bất cứ điều gì ít hơn 100 nghìn lượt xem trang mỗi ngày đều có thể được xử lý hoàn hảo bởi cơ sở dữ liệu SQLite trong các tình huống điển hình (ví dụ: blog, diễn đàn) và tôi chưa thấy bằng chứng nào ngược lại. Trong thực tế, với các đĩa và bộ xử lý hiện đại, 95% các trang web và dịch vụ web sẽ hoạt động tốt với SQLite.

Nếu bạn muốn truy cập đọc / ghi thực sự nhanh, hãy sử dụng cơ sở dữ liệu SQLite trong bộ nhớ . RAM là một số đơn đặt hàng có cường độ nhanh hơn đĩa.


16
OP không hỏi về hiệu quả và tốc độ, mà về truy cập đồng thời. Máy chủ web không có gì để làm với nó. Tương tự về cơ sở dữ liệu bộ nhớ.
Jarekczek

1
Bạn đúng ở một mức độ nào đó nhưng hiệu quả / tốc độ đóng một vai trò. Truy cập nhanh hơn có nghĩa là thời gian chờ đợi khóa thấp hơn, do đó làm giảm nhược điểm của hiệu suất đồng thời của SQLite. Đặc biệt, nếu bạn có ít và viết nhanh, DB dường như sẽ không có bất kỳ vấn đề tương tranh nào với người dùng.
Caboose

1
Làm thế nào bạn có thể quản lý truy cập đồng thời vào cơ sở dữ liệu sqlite trong bộ nhớ?
P-Gn

42

Đúng, SQLite xử lý đồng thời tốt, nhưng nó không phải là tốt nhất từ ​​góc độ hiệu suất. Từ những gì tôi có thể nói, không có ngoại lệ cho điều đó. Các chi tiết có trên trang web của SQLite: https://www.sqlite.org/lockingv3.html

Tuyên bố này được quan tâm: "Mô-đun máy nhắn tin đảm bảo tất cả các thay đổi xảy ra cùng một lúc, rằng tất cả các thay đổi xảy ra hoặc không có thay đổi nào xảy ra, rằng hai hoặc nhiều quá trình không cố gắng truy cập cơ sở dữ liệu theo cách không tương thích cùng một lúc"


2
Dưới đây là một số nhận xét về các sự cố trên các nền tảng khác nhau , cụ thể là hệ thống tệp NFS và Windows (mặc dù nó chỉ liên quan đến các phiên bản cũ của Windows ...)
Nate

1
Có thể tải cơ sở dữ liệu SQLite3 vào RAM để sử dụng cho tất cả người dùng trong PHP không? Tôi đoán là không có thủ tục
Anon343224user

1
@foxyfennec .. một điểm khởi đầu, mặc dù SQLite có thể không phải là db tối ưu cho trường hợp sử dụng này. sqlite.org/inmemorydb.html
kingPuppy

37

Có nó làm. Hãy tìm hiểu tại sao

SQLite là giao dịch

Tất cả các thay đổi trong một giao dịch trong SQLite đều xảy ra hoàn toàn hoặc không xảy ra

Hỗ trợ ACID như cũng như đồng thời đọc / ghi được cung cấp theo 2 cách - bằng cách sử dụng cái gọi là nhật ký (cho phép gọi nó là “ cách cũ ”) hoặc ghi-trước khai thác gỗ (cho phép gọi nó là “ phương pháp mới ”)

Nhật ký (cách cũ)

Trong chế độ này, SQLite sử dụng khóa DATABASE-LEVEL . Đây là điểm quan trọng để hiểu.

Điều đó có nghĩa là bất cứ khi nào nó cần đọc / ghi một cái gì đó, lần đầu tiên nó sẽ có được một khóa trên tệp cơ sở dữ liệu ENTIRE . Nhiều người đọc có thể cùng tồn tại và đọc một cái gì đó song song

Trong quá trình viết, nó đảm bảo có được một khóa độc quyền và không có quá trình nào khác là đọc / ghi đồng thời và do đó việc ghi là an toàn.

Đây là lý do tại sao ở đây họ nói cụ SQLite serializable giao dịch

Rắc rối

Vì nó cần phải khóa toàn bộ cơ sở dữ liệu mỗi lần và mọi người chờ đợi một quá trình xử lý việc viết đồng thời bị ảnh hưởng và việc ghi / đọc đồng thời như vậy có hiệu suất khá thấp

Rollback / cúp

Trước khi viết một cái gì đó vào tệp cơ sở dữ liệu, trước tiên SQLite sẽ lưu đoạn dữ liệu được thay đổi trong một tệp tạm thời. Nếu một cái gì đó gặp sự cố khi viết vào tệp cơ sở dữ liệu, nó sẽ chọn tệp tạm thời này và hoàn nguyên các thay đổi từ nó

Ghi nhật ký trước hoặc WAL (Cách mới)

Trong trường hợp này, tất cả các ghi được thêm vào một tệp tạm thời ( nhật ký ghi trước ) và tệp này được định kỳ hợp nhất với cơ sở dữ liệu gốc. Khi SQLite đang tìm kiếm thứ gì đó, trước tiên, nó sẽ kiểm tra tệp tạm thời này và nếu không tìm thấy gì, hãy tiếp tục với tệp cơ sở dữ liệu chính.

Do đó, độc giả không cạnh tranh với các nhà văn và hiệu suất tốt hơn nhiều so với Cách cũ.

Hãy cẩn thận

SQlite phụ thuộc rất nhiều vào chức năng khóa hệ thống tập tin cơ bản vì vậy nó nên được sử dụng một cách thận trọng, chi tiết hơn ở đây

Bạn cũng có khả năng chạy vào cơ sở dữ liệu bị khóa , đặc biệt là ở chế độ ghi nhật ký, vì vậy ứng dụng của bạn cần được thiết kế với lỗi này.


34

Dường như không ai đề cập đến chế độ WAL (Write Ahead Log). Đảm bảo các giao dịch được tổ chức hợp lý và với chế độ WAL được bật, không cần phải khóa cơ sở dữ liệu trong khi mọi người đang đọc mọi thứ trong khi cập nhật đang diễn ra.

Vấn đề duy nhất là tại một số điểm, WAL cần được kết hợp lại vào cơ sở dữ liệu chính và nó thực hiện điều này khi kết nối cuối cùng với cơ sở dữ liệu đóng lại. Với một trang web rất bận rộn, bạn có thể thấy mất vài giây để tất cả các kết nối được đóng lại, nhưng 100 nghìn lượt truy cập mỗi ngày không phải là vấn đề.


Thú vị, nhưng chỉ hoạt động trên một máy duy nhất, không hoạt động trên cơ sở dữ liệu được truy cập mạng onver.
Bobík

Điều đáng nói là thời gian chờ mặc định để một nhà văn chờ đợi là 5 giây và sau đó, database is lockedlỗi đó sẽ được đưa ra bởi nhà văn
mirhossein

16

Năm 2019, có hai tùy chọn ghi đồng thời mới chưa được phát hành nhưng có sẵn trong các nhánh riêng biệt.

"Tạp chí PRAGMA_mode = wal2"

Ưu điểm của chế độ nhật ký này so với chế độ "wal" thông thường là người viết có thể tiếp tục viết vào một tệp wal trong khi tệp kia được kiểm tra.

BEGIN CONCURRENT - liên kết đến tài liệu chi tiết

Cải tiến BEGIN CONCURRENT cho phép nhiều người viết xử lý các giao dịch ghi đồng thời nếu cơ sở dữ liệu ở chế độ "wal" hoặc "wal2", mặc dù hệ thống vẫn tuần tự hóa các lệnh CAMIT.

Khi một giao dịch ghi được mở bằng "BEGIN CONCURRENT", thực tế việc khóa cơ sở dữ liệu sẽ được hoãn lại cho đến khi một CAMIT được thực thi. Điều này có nghĩa là bất kỳ số lượng giao dịch nào được bắt đầu với BEGIN CONCURRENT đều có thể tiến hành đồng thời. Hệ thống sử dụng khóa cấp độ trang lạc quan để ngăn chặn các giao dịch đồng thời xung đột được thực hiện.

Cùng nhau, chúng có mặt trong start-concallel-wal2 hoặc mỗi cái trong một nhánh riêng .


1
Chúng ta có ý tưởng gì khi những tính năng đó sẽ được đưa vào phiên bản phát hành không? Họ thực sự có thể có ích cho tôi.
Peter Moore

2
Không ý kiến. Bạn có thể xây dựng dễ dàng từ các chi nhánh. Đối với .NET Tôi có một thư viện với giao diện cấp thấp & WAL2 + bắt đầu đồng thời + FTS5: github.com/Spreads/Spreads.SQLite
VB

Ồ, chắc chắn cảm ơn. Tôi đang tự hỏi nhiều hơn về sự ổn định. Điểm nổi bật hàng đầu của SQLite khi nói đến các bản phát hành của họ nhưng tôi không biết việc sử dụng một nhánh trong mã sản xuất sẽ nguy hiểm như thế nào.
Peter Moore

2
Xem chủ đề này github.com/Expensify/Bedrock/issues/65 và Bedrock nói chung. Họ sử dụng nó trong sản xuất và đẩy những begin concurrentthứ đó.
VB

13

SQLite có khóa trình đọc- ghi ở cấp cơ sở dữ liệu. Nhiều kết nối (có thể thuộc sở hữu của các quy trình khác nhau) có thể đọc dữ liệu từ cùng một cơ sở dữ liệu cùng một lúc, nhưng chỉ một kết nối có thể ghi vào cơ sở dữ liệu.

SQLite hỗ trợ số lượng người đọc đồng thời không giới hạn, nhưng nó sẽ chỉ cho phép một người viết bất cứ lúc nào. Đối với nhiều tình huống, đây không phải là một vấn đề. Nhà văn xếp hàng. Mỗi ứng dụng làm cơ sở dữ liệu của nó hoạt động nhanh chóng và di chuyển, và không có khóa nào tồn tại trong hơn vài chục mili giây. Nhưng có một số ứng dụng đòi hỏi đồng thời nhiều hơn và những ứng dụng đó có thể cần tìm kiếm một giải pháp khác. - Sử dụng phù hợp cho SQLite @ SQLite.org

Khóa độc giả-nhà văn cho phép xử lý giao dịch độc lập và nó được thực hiện bằng cách sử dụng các khóa độc quyền và chia sẻ ở cấp cơ sở dữ liệu.

Phải có khóa độc quyền trước khi kết nối thực hiện thao tác ghi trên cơ sở dữ liệu. Sau khi có được khóa độc quyền, cả thao tác đọc và ghi từ các kết nối khác đều bị chặn cho đến khi khóa được giải phóng lại.

Chi tiết thực hiện cho trường hợp viết đồng thời

SQLite có một bảng khóa giúp khóa cơ sở dữ liệu càng muộn càng tốt trong quá trình ghi để đảm bảo đồng thời tối đa.

Trạng thái ban đầu là UNLOCKED và ở trạng thái này, kết nối chưa được truy cập cơ sở dữ liệu. Khi một quá trình được kết nối với cơ sở dữ liệu và thậm chí một giao dịch đã được bắt đầu với BEGIN, kết nối vẫn ở trạng thái UNLOCKED.

Sau trạng thái UNLOCKED, trạng thái tiếp theo là trạng thái CHIA SẺ. Để có thể đọc (không ghi) dữ liệu từ cơ sở dữ liệu, trước tiên, kết nối phải vào trạng thái CHIA SẺ, bằng cách lấy khóa CHIA SẺ. Nhiều kết nối có thể có được và duy trì các khóa CHIA SẺ cùng một lúc, vì vậy nhiều kết nối có thể đọc dữ liệu từ cùng một cơ sở dữ liệu cùng một lúc. Nhưng miễn là chỉ có một khóa CHIA SẺ vẫn chưa được phát hành, không có kết nối nào có thể hoàn thành ghi vào cơ sở dữ liệu.

Nếu một kết nối muốn ghi vào cơ sở dữ liệu, trước tiên, nó phải nhận được khóa RESERVED.

Chỉ một khóa RESERVED có thể hoạt động cùng một lúc, mặc dù nhiều khóa CHIA SẺ có thể cùng tồn tại với một khóa RESERVED duy nhất. RESERVED khác với PENDING ở chỗ khóa SHARED mới có thể có được trong khi có khóa RESERVED. - Khóa tệp và đồng thời trong SQLite Phiên bản 3 @ SQLite.org

Khi một kết nối có được khóa RESERVED, nó có thể bắt đầu xử lý các hoạt động sửa đổi cơ sở dữ liệu, mặc dù những sửa đổi này chỉ có thể được thực hiện trong bộ đệm, thay vì thực sự được ghi vào đĩa. Các sửa đổi được thực hiện cho nội dung đọc được lưu trong bộ nhớ đệm. Khi một kết nối muốn gửi một sửa đổi (hoặc giao dịch), cần phải nâng cấp khóa RESERVED thành khóa ĐỘC QUYỀN. Để có được khóa, trước tiên bạn phải nâng khóa lên khóa PENDING.

Khóa PENDING có nghĩa là quá trình giữ khóa muốn ghi vào cơ sở dữ liệu càng sớm càng tốt và chỉ chờ trên tất cả các khóa CHIA SẺ hiện tại để xóa để có thể nhận được khóa ĐỘC QUYỀN. Không có khóa SHARED mới nào được phép đối với cơ sở dữ liệu nếu khóa PENDING đang hoạt động, mặc dù các khóa SHARED hiện tại được phép tiếp tục.

Một khóa ĐỘC QUYỀN là cần thiết để ghi vào tệp cơ sở dữ liệu. Chỉ có một khóa ĐỘC QUYỀN được cho phép trên tệp và không có khóa nào khác được phép cùng tồn tại với khóa ĐỘC QUYỀN. Để tối đa hóa đồng thời, SQLite hoạt động để giảm thiểu lượng thời gian mà các khóa EXCLUSIVE được giữ. - Khóa tệp và đồng thời trong SQLite Phiên bản 3 @ SQLite.org

Vì vậy, bạn có thể nói SQLite xử lý truy cập đồng thời một cách an toàn bằng nhiều quy trình ghi vào cùng một DB đơn giản vì nó không hỗ trợ nó! Bạn sẽ nhận được SQLITE_BUSYhoặc SQLITE_LOCKEDcho nhà văn thứ hai khi nó đạt đến giới hạn thử lại.


Cảm ơn bạn. Một ví dụ về mã với 2 người viết sẽ là siêu tuyệt vời để hiểu cách thức hoạt động của nó.
Basj

1
@Basj nói ngắn gọn, sqlite có Khóa Đọc-Ghi trên tệp cơ sở dữ liệu. Nó giống như ghi đồng thời một tệp. Và với WAL, vẫn không thể viết đồng thời, nhưng WAL có thể tăng tốc độ viết và đọc và viết có thể đồng thời. Và WAL giới thiệu khóa mới như WAL_READ_LOCK, WAL_WRITE_LOCK.
obgnaw

2
Bạn có thể sử dụng Hàng đợi và có nhiều luồng cung cấp Hàng đợi và chỉ một luồng ghi vào DB bằng cách sử dụng Báo cáo SQL trong Hàng đợi. Một cái gì đó như thế này
Gabriel

7

Chủ đề này đã cũ nhưng tôi nghĩ sẽ rất tốt khi chia sẻ kết quả các thử nghiệm của mình được thực hiện trên sqlite: tôi đã chạy 2 phiên bản của chương trình python (các quy trình khác nhau cùng một chương trình) thực thi các lệnh CHỌN và CẬP NHẬT các lệnh sql trong giao dịch với khóa EXCLUSIVE và thời gian chờ được đặt thành 10 giây để có được một khóa, và kết quả là bực bội. Mỗi trường hợp đã thực hiện trong vòng 10000 bước:

  • kết nối với db bằng khóa độc quyền
  • chọn trên một hàng để đọc bộ đếm
  • cập nhật hàng với giá trị mới bằng với bộ đếm tăng thêm 1
  • kết nối chặt chẽ với db

Ngay cả khi sqlite được cấp khóa độc quyền trong giao dịch, tổng số chu kỳ thực hiện không bằng 20 000 nhưng ít hơn (tổng số lần lặp qua bộ đếm đơn được tính cho cả hai quy trình). Chương trình Python gần như không ném bất kỳ ngoại lệ nào (chỉ một lần trong khi chọn trong 20 lần thực thi). sửa đổi sqlite tại thời điểm thử nghiệm là 3.6.20 và python v3.3 CentOS 6.5. Theo ý kiến ​​của tôi, tốt hơn là tìm sản phẩm đáng tin cậy hơn cho loại công việc này hoặc hạn chế ghi vào sqlite cho quy trình / chủ đề duy nhất.


5
Có vẻ như bạn cần phải nói một số từ thần kỳ để có được khóa trong python, như đã thảo luận ở đây: stackoverflow.com/a/12848059/1048959 Điều này mặc dù thực tế là tài liệu sqlite python khiến bạn tin rằng nó with conlà đủ.
Dan Stahlke
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.