Tôi đang thêm câu trả lời của riêng mình vì tôi không đồng ý với câu trả lời hiện được chấp nhận. Nó nói rằng hoạt động không an toàn với luồng, nhưng điều này là không đúng - SQLite uses file locking thích hợp với nền tảng hiện tại của nó để đảm bảo rằng tất cả các truy cập tuân thủ ACID.
Trên hệ thống Unix, khóa này sẽ là khóa fcntl()
hoặc flock()
, đây là khóa cho mỗi tệp. Kết quả là, mã được đăng mà tạo ra một kết nối mới mỗi lần sẽ luôn luôn phân bổ một tập tin mới và do đó khóa riêng của SQLite sẽ ngăn chặn tham nhũng cơ sở dữ liệu. Một hệ quả của việc này là thường là một ý tưởng tồi khi sử dụng SQLite trên một chia sẻ NFS hoặc tương tự, thường thì chúng không cung cấp khóa đặc biệt đáng tin cậy (tuy nhiên nó phụ thuộc vào việc thực hiện NFS của bạn).
Như @abernert đã chỉ ra trong nhận xét, SQLite has had issues with threads, nhưng điều này liên quan đến việc chia sẻ một kết nối duy nhất giữa các chuỗi. Như ông cũng đề cập, điều này có nghĩa nếu bạn sử dụng một hồ bơi ứng dụng rộng, bạn sẽ nhận được lỗi thời gian chạy nếu luồng thứ hai kéo ra một kết nối tái chế từ hồ bơi. Đây cũng là loại lỗi kích thích mà bạn có thể không nhận thấy trong thử nghiệm (tải nhẹ, có lẽ chỉ là một sợi đơn được sử dụng), nhưng có thể dễ dàng gây đau đầu sau này. Đề nghị sau này của Martijn Pieters về một hồ bơi địa phương có thể hoạt động tốt.
Như đã nêu trong SQLite FAQ như các phiên bản 3.3.1 nó thực sự an toàn để vượt qua các kết nối giữa các chủ đề miễn là họ không nắm giữ bất kỳ ổ khóa - đây là một sự nhượng bộ mà tác giả của SQLite thêm mặc dù là chỉ trích việc sử dụng các chủ đề nói chung.Bất kỳ việc thực hiện gộp kết nối hợp lý nào sẽ luôn đảm bảo rằng mọi thứ đã được cam kết hoặc cuộn lại trước khi thay thế kết nối trong nhóm, do đó, thực sự một hồ bơi toàn cầu ứng dụng sẽ có thể an toàn nếu nó không được kiểm tra Python. , mà tôi tin rằng vẫn còn tại chỗ ngay cả khi một phiên bản mới hơn của SQLite được sử dụng. Chắc chắn hệ thống Python 2.7.3 của tôi có mô-đun sqlite3
với sqlite_version_info
báo cáo 3.7.9, nhưng nó vẫn ném một số RuntimeError
nếu bạn truy cập nó từ nhiều luồng.
Trong mọi trường hợp, trong khi kiểm tra tồn tại thì các kết nối không thể được chia sẻ hiệu quả ngay cả khi thư viện SQLite bên dưới hỗ trợ nó.
Đối với câu hỏi ban đầu của bạn, chắc chắn tạo kết nối mới mỗi lần kém hiệu quả hơn là giữ một nhóm kết nối, nhưng đã được đề cập đến điều này sẽ cần phải là một hồ bơi địa phương. . Chi phí của việc tạo một kết nối mới đến cơ sở dữ liệu về cơ bản là mở tệp và đọc tiêu đề để đảm bảo đó là tệp SQLite hợp lệ. Chi phí thực thi câu lệnh cao hơn vì nó cần đưa ra vẻ ngoài và thực hiện khá nhiều tập tin I/O, do đó phần lớn công việc thực sự bị trì hoãn cho đến khi thực thi câu lệnh và/hoặc commit.
Thật thú vị, tuy nhiên, ít nhất là trên các hệ thống Linux, tôi đã xem mã để thực thi câu lệnh lặp lại các bước đọc tiêu đề tệp - kết quả là việc mở kết nối mới sẽ không tệ kể từ khi đọc ban đầu khi mở kết nối sẽ kéo tiêu đề vào bộ đệm hệ thống tập tin của hệ thống. Vì vậy, nó nắm xuống phần trên của việc mở một filehandle duy nhất.
Tôi cũng nên thêm rằng nếu bạn đang mong đợi mã của bạn để mở rộng đến đồng thời cao thì SQLite có thể là một lựa chọn không tốt. Như their own website points out nó không thực sự thích hợp cho đồng thời cao như các hit hiệu suất của việc phải ép tất cả truy cập thông qua một khóa toàn cầu duy nhất bắt đầu cắn như số lượng các chủ đề đồng thời tăng lên. Sẽ ổn nếu bạn đang sử dụng các chủ đề để thuận tiện, nhưng nếu bạn thực sự mong đợi một mức độ đồng thời cao thì tôi sẽ tránh SQLite.
Tóm lại, tôi không nghĩ rằng cách tiếp cận của bạn mở mỗi lần thực sự là tất cả những điều xấu. Có thể một hồ bơi thread-local cải thiện hiệu suất? Chắc là đúng. Liệu hiệu suất này có thể nhận thấy được không? Theo tôi, không trừ khi bạn nhìn thấy tỷ lệ kết nối khá cao, và tại thời điểm đó bạn sẽ có rất nhiều chủ đề vì vậy bạn có thể muốn di chuyển ra khỏi SQL anyway anyway bởi vì nó không xử lý đồng thời terribly tốt. Nếu bạn quyết định sử dụng, hãy đảm bảo rằng nó xóa kết nối trước khi trả lại hồ bơi - SQLAlchemy có một số chức năng connection pooling mà bạn có thể thấy hữu ích ngay cả khi bạn không muốn tất cả các lớp ORM ở trên cùng.
EDIT
Như khá hợp lý chỉ ra tôi nên đính kèm timings thực. Đây là từ một VPS được hỗ trợ khá thấp:
>>> timeit.timeit("cur = conn.cursor(); cur.execute('UPDATE foo SET name=\"x\"
WHERE id=3'); conn.commit()", setup="import sqlite3;
conn = sqlite3.connect('./testdb')", number=100000)
5.733098030090332
>>> timeit.timeit("conn = sqlite3.connect('./testdb'); cur = conn.cursor();
cur.execute('UPDATE foo SET name=\"x\" WHERE id=3'); conn.commit()",
setup="import sqlite3", number=100000)
16.518677949905396
Bạn có thể thấy hệ số chênh lệch khoảng 3x, không đáng kể. Tuy nhiên, thời gian tuyệt đối vẫn còn dưới một phần nghìn giây, do đó, trừ khi bạn thực hiện nhiều truy vấn cho mỗi yêu cầu thì có thể có những nơi khác để tối ưu hóa trước tiên. Nếu bạn thực hiện nhiều truy vấn, thỏa hiệp hợp lý có thể là kết nối mới theo yêu cầu (nhưng không có sự phức tạp của một nhóm, chỉ cần kết nối lại mỗi lần).
Để đọc (tức là SELECT) thì chi phí liên quan của việc kết nối mỗi lần sẽ cao hơn, nhưng chi phí tuyệt đối trong đồng hồ treo tường phải nhất quán.
Như đã được thảo luận ở đâu đó về câu hỏi này, bạn nên thử nghiệm với các truy vấn thực, tôi chỉ muốn ghi lại những gì tôi đã làm để đi đến kết luận của mình.
Vui lòng đăng mã thực, với các chức năng phù hợp, như 'connect' và' fetchall' thay vì 'open' và' findall'. – abarnert
Bạn gặp phải vấn đề gì với "các mã khác nhau"? Nếu bạn đang cố gắng viết nhiều đồng thời, hãy sử dụng một RDBMS thực sự http://www.sqlite.org/faq.html#q5 – msw