- Nếu gói OpenDocument Presentation (ODP) trong container SQLite thay vì kho lưu trữ ZIP, có thể thiết kế cách lưu, khởi động và khôi phục tài liệu an toàn hơn và nhanh hơn
- Hiện nay ODP có cấu trúc gói kho lưu trữ ZIP chứa các tệp XML và hình ảnh, và tệp trình chiếu ví dụ gồm 49 slide được tạo thành từ tổng cộng 78 mục như
content.xml, styles.xml, meta.xml, settings.xml cùng các hình ảnh
- Với cấu trúc dựa trên ZIP, ngay cả thay đổi nhỏ cũng dễ dẫn đến phải ghi lại toàn bộ kho lưu trữ nên khó thực hiện cập nhật gia tăng, kéo theo độ trễ File/Save và tăng lượng ghi lên SSD
- Khi chuyển sang SQLite, có thể lưu tệp dưới dạng các hàng trong bảng, và xa hơn nữa là tách nội dung cùng phiên bản theo từng slide để chỉ đọc slide đầu tiên hoặc chỉ lưu các slide đã thay đổi
- Đây không phải là đề xuất chỉ trích hay thay đổi chính OpenDocument, mà là một ví dụ cho thấy SQLite có thể giúp tạo ra lưu nguyên tử, khả năng truy cập, quản lý phiên bản và chức năng khôi phục trong định dạng tệp ứng dụng một cách dễ dàng
Phạm vi và đối tượng của thí nghiệm tư duy
- Đối tượng là ODP (OpenDocument Presentation), tức định dạng tài liệu trình chiếu trong OpenDocument
- Mục đích không phải là thay đổi OpenDocument trong thực tế, mà là xem xét cách dùng SQLite làm container trong thiết kế định dạng tệp về sau
- Hiệu quả kỳ vọng là tài liệu nhỏ hơn, File/Save nhanh hơn, khởi động nhanh hơn, dùng ít bộ nhớ hơn, quản lý phiên bản tài liệu và trải nghiệm người dùng tốt hơn
Cấu trúc tệp ODP hiện nay
- Tệp ODP là một kho lưu trữ ZIP chứa các tệp XML và tài nguyên hình ảnh
- Ví dụ, tệp trình chiếu 49 slide liên quan đến SQLite tại SouthEast LinuxFest 2014 có tổng cộng 78 mục trong đầu ra
zip -l
- Bốn tệp XML
content.xml, styles.xml, meta.xml, settings.xml định nghĩa bố cục slide, nội dung văn bản và kiểu dáng
- Tệp trình chiếu lưu 62 hình ảnh thành các tệp riêng, từ ảnh toàn màn hình đến các biểu tượng nhỏ
- Tệp
mimetype chứa một dòng application/vnd.oasis.opendocument.presentation
- Tệp xử lý văn bản và bảng tính OpenDocument cũng có cấu trúc tương tự, nhưng đối tượng được phân tích ở đây là ODP
Hạn chế của ODP dựa trên ZIP
- Kho lưu trữ ZIP gần với một cơ sở dữ liệu key/value được tối ưu cho ghi một lần, đọc nhiều lần, và phù hợp với cấu trúc có ít key nhưng giá trị BLOB lớn
- Vì khó cập nhật từng mục riêng lẻ, khi người dùng chọn
File/Save thường toàn bộ kho ZIP sẽ được ghi lại
- Dù vẫn có thể cập nhật từng mục để tránh làm hỏng toàn bộ tài liệu trong lúc mất điện hay sự cố, việc này đủ khó để trên thực tế hầu như không được dùng
- Với tệp trình chiếu 50MB, chỉ thay một ký tự cũng có thể dẫn đến phải ghi lại toàn bộ 50MB
- Thời gian khởi động có thể chậm
- ODP lưu toàn bộ nội dung slide trong một tệp XML lớn là
content.xml
- LibreOffice phải đọc và phân tích toàn bộ tệp này để hiển thị slide đầu tiên
- Có vẻ mọi hình ảnh cũng được đọc hết vào bộ nhớ, nên khi nhấp đúp vào tệp, người dùng thấy thanh tiến trình thay vì slide đầu tiên
- Mức dùng bộ nhớ tăng cao
- Cấu trúc ZIP dễ dẫn đến cách triển khai là đọc toàn bộ tài liệu vào bộ nhớ khi khởi động, chỉnh sửa trong bộ nhớ rồi ghi lại toàn bộ xuống đĩa khi lưu
- Một tệp trình chiếu 50MB có thể dùng hơn 200MB RAM
- Nếu mở nhiều tệp trình chiếu cùng lúc, lại còn dùng trình duyệt và ứng dụng desktop, hiện tượng swapping có thể xảy ra
- Khôi phục sau sự cố trở nên phiền phức
- Dòng OpenOffice định kỳ sao lưu tài liệu đang nằm trong bộ nhớ để đề phòng crash
- Trong lúc sao lưu, ứng dụng có thể đứng vài giây, và sau khi khởi động lại còn phải qua một hộp thoại khôi phục riêng
- Khả năng truy cập nội dung thấp
- Có thể trích xuất hình ảnh bằng công cụ ZIP, nhưng rất khó trích xuất hoặc chỉnh sửa văn bản slide bằng các công cụ thông thường
- Trong tệp ví dụ,
content.xml có dòng đầu là khai báo XML, còn dòng thứ hai chứa 211.792 ký tự XML trên đúng một dòng
Cải tiến đầu tiên: thay ZIP bằng SQLite
- Bước đầu tiên là một cấu trúc đơn giản, thay các mục ZIP bằng các hàng trong bảng SQLite
CREATE TABLE OpenDocTree(
filename TEXT PRIMARY KEY,
filesize BIGINT,
content BLOB
);
- Ở giai đoạn này, phần còn lại của cấu trúc định dạng tệp không thay đổi
- Vẫn là cấu trúc “đống tệp”, nhưng mỗi tệp trở thành một hàng trong cơ sở dữ liệu SQLite thay vì một entry ZIP
- So sánh kích thước tệp SQLite được đóng gói lại bằng tiện ích SQLAR với nội dung giống
self2014.odp do NeoOffice tạo ra như sau
self2014.odp: 10.514.994 byte
self2014.sqlar: 10.464.256 byte
zip.odp được nén lại bằng lệnh zip: 10.416.644 byte
- Tệp SQLite nhỏ hơn khoảng 0,5% so với ODP do NeoOffice tạo ra
- Tệp ZIP được nén tốt bằng lệnh
zip lại nhỏ hơn SQLite thêm khoảng 0,5%
- Cơ sở dữ liệu SQLite có thể cạnh tranh với kho ZIP về mặt kích thước
- SQLite cung cấp ghi nguyên tử, nên có thể lưu các thay đổi gia tăng mà không có nguy cơ làm hỏng tài liệu trong lúc crash hay mất điện
- Hạn chế là vẫn phải ghi lại toàn bộ
content.xml
- Dù vậy, 77 tệp còn lại có thể giữ nguyên, giúp File/Save nhanh hơn và giảm lượng ghi lên SSD
Cải tiến thứ hai: chia nội dung thành các mảnh nhỏ
- SQLite có thể lưu hiệu quả không chỉ các khối lớn mà cả nhiều mảnh nhỏ, nên có thể tạo bảng nội dung theo từng slide
CREATE TABLE slide(
pageNumber INTEGER,
slideContent TEXT
);
CREATE INDEX slide_pgnum ON slide(pageNumber);
- Khi hiển thị màn hình đầu tiên, ứng dụng chỉ cần đọc slide đầu tiên
SELECT slideContent FROM slide WHERE pageNumber=1;
- Với cấu trúc này, chỉ nội dung của slide đầu tiên mới cần được lấy ra, phân tích và hiển thị nhanh, nên không cần đọc toàn bộ
content.xml khi khởi động
- Cũng có thêm nhiều lựa chọn triển khai
- Sau khi hiển thị slide đầu tiên, có thể dùng luồng nền để đọc các trang còn lại
- Có thể chỉ giữ một slide hiện tại trong bộ nhớ
- Hoặc chỉ giữ slide hiện tại và slide tiếp theo trong bộ nhớ để chuyển trang nhanh
- Việc lưu cũng nhanh hơn vì chỉ cần ghi lại các trang đã thay đổi
- Với các mảnh văn bản ngắn, hiệu quả nén sẽ kém hơn nên kích thước tài liệu có thể tăng
- Tuy vậy, phần lớn dung lượng tài liệu là hình ảnh, nên suy giảm hiệu quả nén văn bản có thể xem là cái giá nhỏ so với cải thiện trải nghiệm người dùng
Cải tiến thứ ba: quản lý phiên bản
- Khi lưu slide như các đối tượng riêng lẻ, có thể chứa lịch sử phiên bản ngay trong cùng một tài liệu
CREATE TABLE slide(
slideId INTEGER PRIMARY KEY,
derivedFrom INTEGER REFERENCES slide,
content TEXT
);
CREATE TABLE version(
versionId INTEGER PRIMARY KEY,
priorVersion INTEGER REFERENCES version,
checkinTime DATETIME,
comment TEXT,
manifest TEXT
);
- Mỗi slide có
slideId riêng thay vì số trang, còn thứ tự được xác định bằng danh sách slideId lưu trong manifest của bảng version
- Khi khởi động, ứng dụng trước tiên chọn phiên bản cần hiển thị, thường là lấy phiên bản mới nhất
SELECT manifest, versionId FROM version ORDER BY versionId DESC LIMIT 1;
- Cũng có thể dùng truy vấn lấy phiên bản mới nhất theo
checkinTime
SELECT manifest, versionId, max(checkinTime) FROM version;
- Trong SQLite, truy vấn
max(checkinTime) ở trên trả về kết quả được xác định, nhưng ở nhiều cơ sở dữ liệu SQL khác có thể trả về kết quả không xác định hoặc báo lỗi
- Khi người dùng thực hiện
File/Save, chỉ các slide đã sửa mới cần được thêm thành hàng mới trong bảng slide, rồi tạo một hàng version mới với manifest đã cập nhật
- Bảng
version lưu thời điểm check-in, bình luận của người dùng và phiên bản cha để giữ lại lịch sử thay đổi
- Cũng có thể lưu nhiều bộ slide trình chiếu trong cùng một tài liệu
- Thay vì tệp sao lưu riêng, có thể dùng một phiên bản
pending đặc biệt để ghi lại thường xuyên và âm thầm các thay đổi chưa lưu
- Vì chỉ ghi phần thay đổi chứ không phải toàn bộ tài liệu, lượng ghi có thể là vài KB thay vì vài MB
- Thời gian lưu có thể là mili giây thay vì vài giây
- Ngay cả sau khi crash và khởi động lại, phần lớn hoặc gần như toàn bộ công việc của người dùng vẫn có thể được giữ lại
- Nếu muốn bỏ các thay đổi chưa lưu, người dùng chỉ cần quay lại phiên bản trước đó
Những tính năng còn có thể làm thêm với định dạng tệp SQLite
- Chỉ với ba bảng, container SQLite đã có thể bổ sung các tính năng quan trọng cho định dạng tệp ứng dụng
- Ngoài ra còn có thể tận dụng schema, index, trigger, view và constraint để tăng hiệu năng, tính tiện dụng và tính nhất quán
- Một số ý tưởng mở rộng gồm
- Lưu ngăn xếp undo/redo tự động trong bảng cơ sở dữ liệu để có thể hoàn tác cả các phiên chỉnh sửa trước
- Thêm tìm kiếm toàn văn cho một bộ slide hoặc nhiều bộ slide
- Tách
settings.xml thành các bảng SQL để ứng dụng khác có thể xem và chỉnh sửa dễ hơn
- Tách ghi chú người thuyết trình của từng slide sang bảng riêng để ứng dụng bên thứ ba hoặc script truy cập dễ dàng
- Hỗ trợ cấu trúc trình bày vượt ra ngoài thứ tự slide tuyến tính đơn giản, với nhiều nhánh hoặc đường vòng tùy phản ứng của khán giả
Sự e dè thường gặp với SQLite và phản biện
- Vì kinh nghiệm với cơ sở dữ liệu SQL cấp doanh nghiệp, nhiều người có thể cảm thấy e dè khi dùng SQLite làm định dạng tệp ứng dụng
- Nhiều cơ sở dữ liệu doanh nghiệp khuyên không nên đưa chuỗi lớn hay BLOB vào cơ sở dữ liệu mà nên lưu thành tệp riêng, nhưng SQLite thì khác
- Bất kỳ cột nào của SQLite cũng có thể lưu chuỗi hoặc BLOB tới khoảng 1GB
- Với chuỗi và BLOB dưới 100KB, hiệu năng I/O còn tốt hơn so với tệp riêng
- Quan niệm rằng mọi schema SQL đều phải theo chuẩn hóa mức ba (3NF) và chỉ lưu kiểu nguyên thủy nhỏ cũng có thể trở thành rào cản
- Lý thuyết quan hệ rất quan trọng, nhưng trong định dạng tệp thực tế, lưu thông tin phức tạp như XML hay JSON trong trường văn bản cũng là một lựa chọn chấp nhận được
SQLite như một định dạng tệp ứng dụng
- Tệp cơ sở dữ liệu SQLite có kích thước gần như tương đương kho ZIP chứa cùng thông tin, và trong một số trường hợp còn có thể nhỏ hơn
- Nhờ cập nhật nguyên tử, các thay đổi nhỏ có thể được ghi an toàn vào tài liệu để giảm I/O đĩa và cải thiện hiệu năng File/Save
- Ứng dụng có thể chỉ đọc phần nội dung cần cho màn hình đầu tiên để rút ngắn thời gian khởi động
- Chỉ cần giữ nội dung liên quan đến phần đang hiển thị trong bộ nhớ, còn lại để trên đĩa, nên mức dùng bộ nhớ có thể giảm mạnh
- Schema SQL có thể biểu diễn thông tin trực tiếp và cô đọng hơn so với cấu trúc key/value kiểu ZIP
- Khả năng truy cập từ ứng dụng bên thứ ba và script sẽ tốt hơn
- Việc triển khai các tính năng cao cấp như quản lý phiên bản tài liệu tích hợp và khôi phục công việc sau crash sẽ dễ hơn
- OpenDocument vốn đã là một định dạng được thiết lập tốt và thiết kế tốt; vì SQLite xuất hiện sau OpenDocument nên đây không phải là lời chỉ trích lựa chọn trước đó
- Tài liệu Application File Format đưa ra thêm nhiều ý tưởng về việc dùng SQLite làm định dạng tệp ứng dụng
1 bình luận
Ý kiến trên Hacker News
Tôi đang làm một ứng dụng dùng SQLite làm định dạng tệp
Vì muốn giữ luồng thông thường là tệp chỉ thay đổi khi người dùng chỉnh sửa tài liệu rồi lưu, nên khi mở tệp tôi sao chép nó vào cơ sở dữ liệu
:memory:: https://www.sqlite.org/inmemorydb.htmlNgười dùng thao tác tùy ý, còn ứng dụng phản ánh trực tiếp vào định dạng cơ sở dữ liệu mà không cần mô hình tài liệu riêng. Khi lưu thì xử lý bằng cách ghi lại ra tệp cơ sở dữ liệu bằng
VACUUM: https://www.sqlite.org/lang_vacuum.htmlCách này hoạt động tốt với các tệp có kích thước vừa phải, và trong ứng dụng của tôi thì luôn nằm trong phạm vi đó
Thà tự động lưu trực tiếp vào cơ sở dữ liệu và bỏ nút lưu còn hơn. Cách này chịu va chạm tốt, chỉ có một cơ sở dữ liệu nên code và bug ít hơn, còn thao tác ghi của SQLite thì hoặc thành công hoặc thất bại chứ không có trạng thái lưng chừng. Ngược lại, như tài liệu được trích dẫn,
VACUUM INTOcó thể để lại cơ sở dữ liệu đầu ra không hoàn chỉnh hoặc bị hỏng nếu thoát bất ngờ hay mất điệnNếu dùng SQLite đúng theo cách vốn được thiết kế, bạn sẽ không phải lo phần này trong suốt vòng đời của SQLite
Tốt hơn nhiều là sau mỗi thao tác, lưu vào một vị trí tạm thời, chẳng hạn theo thư mục XDG là
~/.local/share/application/yourapp, rồi khi người dùng bấm lưu thì sao chép tệp tới vị trí họ muốn. Sau khi mất điện, mở lại ứng dụng vẫn có thể khôi phục gần như đúng chỗ cũ, nhiều nhất chỉ mất vài giây cuốiKhi người dùng lưu, thực hiện checkpoint để gộp nội dung WAL vào cơ sở dữ liệu chính là được
VACUUMsao chép nội dung sang một tệp cơ sở dữ liệu tạm thời rồi ghi đè lên bản gốc; khi ghi đè, nó dùng rollback journal hoặc WAL như một transaction thông thường. Vì vậy cần dung lượng trống tối đa khoảng gấp đôi kích thước bản gốcVACUUM INTOdùng tệp được chỉ định trongINTOthay cho cơ sở dữ liệu tạm thời, và bỏ qua bước sao chép ngược lên bản gốc. Điều quan trọng là thứ thực sự đang dùng làVACUUMchịu được mất điện, hayVACUUM INTOcó vẻ dễ bị lỗi nếu mất điện trong lúc ghi và có thể làm hỏng nếu đó là tên tệp hiện cóVấn đề của SQLite là nó không phải định dạng tệp được chuẩn hóa
Nó được tài liệu hóa tốt và được hiểu rộng rãi, nhưng ISO không định nghĩa chi tiết cách diễn giải tệp SQLite. Các triển khai thay thế cũng vậy
Zip và XML có bề mặt API nhỏ hơn SQLite rất nhiều. API của SQLite không chỉ là vài hàm C mà là cả ngôn ngữ SQL; việc triển khai parser SQL, bộ tối ưu truy vấn, trình biên dịch, máy ảo bytecode, công cụ tìm kiếm toàn văn, v.v. mà không làm hỏng dữ liệu là việc lớn hơn parser XML rất nhiều
Nếu là một ứng dụng đóng, chuyên biệt theo miền, nơi khả năng tương tác hoặc chuẩn hóa ISO không quan trọng, thì SQLite là một định dạng tệp tốt, nhưng tôi hiểu rằng với OpenOffice thì những lo ngại đó thực sự đã tồn tại
Thư viện C của SQLite cũng thuộc public domain nên mã nguồn hoàn toàn mở, xử lý định dạng tệp, và mức độ tài liệu hóa còn cao hơn phần lớn tiêu chuẩn ISO. Hầu như ngôn ngữ phổ biến nào cũng có binding
Nếu vấn đề là một định dạng OpenDocument nào đó sẽ được lưu bên trong tệp SQLite còn chưa được tạo ra và tài liệu hóa, thì đó là chuyện khác. Tiêu chuẩn ISO thì tốt, nhưng nếu phải chờ ISO định nghĩa định dạng tệp thì sẽ có quá ít thứ có thể dùng được
Cũng giống như không cần triển khai mọi tính năng bảng tính để đọc bảng tính LibreOffice. Thứ cần có là khả năng tái dựng các bảng; sau đó, thông tin mong muốn có thể được lấy bằng cách duyệt qua bằng mã mệnh lệnh viết bằng ngôn ngữ bạn chọn
Việc triển khai lại thư viện đó sẽ là chuyện lớn, nhưng cũng cùng loại với việc triển khai lại code dùng định dạng tệp OpenDocument. Bản thân định dạng tệp thì khá đơn giản
Nếu lo về tính tương thích, có thể làm cho tài liệu cũng truy cập được bằng cơ sở dữ liệu khác như MySQL
Tôi từng nghĩ nếu Audacity áp dụng SQLite thì chức năng lưu tệp sẽ cải thiện đáng kể, nhưng thực tế lại có nhiều cạm bẫy
Trên Linux, nếu lưu thành tệp mới vào một phân vùng NTFS do root sở hữu nhưng cho phép mọi người ghi, được mount bằng
/etc/fstab, thì thao tác thất bại vì các lý do kiểu lỗi quyền; còn lưu vào tệp đã có thì hoạt động bình thườngNgay khi chỉnh sửa dự án, tệp trên đĩa đã bị sửa đổi, nên nếu đưa dự án Audacity vào Git như một khối nhị phân thì sẽ sinh ra các khác biệt Git không cần thiết. Ngay cả khi lưu, dữ liệu cũ hoặc đã xóa vẫn còn trong tệp SQLite cho đến khi đóng cửa sổ dự án, nên nếu không đóng cửa sổ trước khi commit thì chúng có thể lọt vào kho. Tôi nhớ trước đây phải tự
VACUUMtệp.aup3, còn bây giờ chỉ cần đóng cửa sổ là đủ. Có cảm giác giống Fast Save của Word 2003Khi một dự án lẽ ra chỉ vài trăm MB phình lên thành vài GB và cần tiết kiệm dung lượng đĩa, với các tác vụ một track đơn giản thì Mix and Render là giải pháp. Nó không thay đổi âm thanh, nhưng có thể dọn rác khi lưu và thoát
Đây rõ ràng không phải vấn đề của bản thân SQLite mà là vấn đề ở tầng ứng dụng. Tôi nhớ Audacity 2 dường như có khái niệm không gian làm việc tạm thời, còn Audacity 3 có vẻ dùng chính tệp
.aup3làm không gian làm việcTôi đã xem qua định dạng Audacity 3 và thấy rất khó hiểu: dữ liệu dự án, vốn tương ứng với tệp
.aupcũ, được lưu dưới dạng XML trong một bảng chỉ có một hàng, nhưng lại không ghi nguyên văn văn bản mà mã hóa bằng một bộ mã hóa từ điển đơn giản. Điều này làm khả năng tương tác và kiểm tra khó hơn nhiều, cũng gây hại dù rất nhỏ cho hiệu năng, còn dung lượng tiết kiệm được chắc chỉ là sai số làm tròn vài KB trong các tệp âm thanh hàng trăm MBXét từ góc nhìn Git, dùng định dạng văn bản dễ diff và merge sẽ có lợi hơn. Tôi không rõ SQLite dump dễ đến mức nào ở khía cạnh này
Nếu quan trọng thì có thể sửa thủ công, nhưng thường chỉ cần bỏ tệp đó đi là lại hoạt động
Bài viết hay. Tuy vậy, tôi thích việc OpenDocument là một bó các tệp XML bên trong một kho lưu trữ Zip
Ngay cả khi không có thư viện nặng hiểu định dạng tài liệu, vẫn có thể tạo các tài liệu kiểu bảng tính khá dễ dàng
Đôi khi người dùng dịch vụ web muốn dùng dữ liệu được xuất ra dưới dạng các hàng bảng trong nhiều công cụ khác nhau. UTF-8 CSV thì mở, theo thông lệ và dùng được, nhưng ai từng cung cấp CSV cho người dùng cuối đều biết nỗi đau khi các ứng dụng bảng tính gặp trục trặc với nó
Tôi đã lưu một bảng tính mẫu thành ODS của OpenDocument và XLSX của OOXML, con quái vật XML của Microsoft, rồi chỉ nắm các điểm cơ bản của định dạng XML. Tôi rút gọn kho Zip chỉ còn các thành phần thiết yếu, đánh dấu vị trí sẽ đặt nội dung, và tạo tệp bảng tính mới khi có yêu cầu. Giờ có thể xuất cùng dữ liệu đó dưới dạng CSV, ODS, XLSX, JSON
SQLite cũng có thể làm được, nhưng sẽ phức tạp hơn một chút và tốc độ phát triển chậm hơn. Việc có thể tạo tài liệu mẫu bằng bộ ứng dụng văn phòng rồi đào vào XML của tệp lưu là một tính năng tuy ngách nhưng tốt
Đặc biệt Excel ở các locale như
nl_NLlà vấn đề, vì nó hành xử như thể dấu phân cách cột trong tệp CSV được hard-code là dấu chấm phẩy. Lý do là Microsoft đã khét tiếng quyết định rằng người Hà Lan không dùng dấu phẩy trong các tệp comma separated valueslocaleconv()->decimal_point. Nếu giá trị là,, Excel sẽ dùng dấu chấm phẩy trong cả tệp CSV lẫn ngôn ngữ biểu diễn công thứcTrước đây có thể cấu hình khi mở CSV/TXT trong Excel, và LibreOffice hiện vẫn làm được, nhưng trong quá trình đơn giản hóa UI nói chung, tùy chọn đó đã bị chuyển đâu đó vào menu/thẻ ribbon
Data. Bạn phải mở một workbook mới rồi tìm đúng tùy chọn; nếu muốn tiết kiệm thời gian thì nên dùng LibreOfficePhần này thật sự khiến tôi ngạc nhiên. Khó tin là không cần truy vấn lồng nhau
SELECT manifest, versionId, max(checkinTime) FROM version;Người ta nói trong SQLite, truy vấn thứ hai dùng
max(checkinTime)này thực sự hoạt động tốt và trả về một câu trả lời đã được định nghĩa. Các engine cơ sở dữ liệu SQL khác sẽ trả về câu trả lời không xác định hoặc báo lỗi, nhưng trong SQLite, nó trả vềmanifestvàversionIdcủa mục cócheckinTimelớn nhấtTrong trường hợp này không cần truy vấn lồng nhau; chỉ cần sắp xếp theo
checkinTimerồi giới hạn một hàng:select manifest, versionId, checkinTime from version order by checkinTime desc limit 1Ít nhất thì nó sẽ hoạt động trong SQLite và PostgreSQL. Tôi nhớ trong Oracle phải dùng
where rownum=1, nên khi đó cần truy vấn lồng nhauGROUP BY manifest, versionId ORDER BY 3 DESC LIMIT 1, hoặc của một CTE tìmcheckinTimelớn nhất rồi join lạiTuy nhiên nếu có nhiều hàng có cùng
checkinTimelớn nhất thì sẽ có tính ngẫu nhiên, trở thành khẩu súng chĩa vào chân, nên tôi không mấy khi dùng tính năng riêng của SQLite3 này. Để chọn hàng tốt nhất một cách xác định, cần cách viết tường minh tương tự như trênTrong Postgres có thể làm điều tương tự bằng truy vấn
DISTINCT ON. Trong SQL, đây là một trong những việc trông có vẻ đơn giản nhưng tôi thấy khó nhấtmanifestvàversionIdkhông phụ thuộc hàm vàomax(checkinTime)Ví dụ, có thể có hai hàng cùng giá trị
checkinTime, và giá trị đó là giá trị lớn nhấtTừng có lúc phát hành một sản phẩm dùng cả SQLite lẫn tệp XML
Một trong những phần được cải thiện là chuyển một số bảng có ít dữ liệu sang tệp XML. Vì tệp nhỏ và hầu như không được dùng, lớp truy cập dữ liệu và việc chẩn đoán trở nên đơn giản hơn, và XML được tạo với thụt lề tab nhiều dòng
Việc yêu cầu nhân sự kỹ thuật cần chẩn đoán sản phẩm mở cơ sở dữ liệu SQLite ra xem là một gánh nặng lớn. Nhưng ở các phần chính của sản phẩm, SQLite vượt trội hơn hẳn so với tệp XML. Phiên bản trước dùng tệp XML, nhưng XML không có cách cập nhật tăng dần tốt nên gặp vấn đề về khả năng mở rộng
Ưu điểm của XML là định dạng dễ đọc với con người chỉ thật sự phát huy khi tệp nhỏ và thiết kế schema được điều chỉnh cho XML dễ đọc. Việc phải ghi lại toàn bộ tệp XML mỗi lần, cùng độ phức tạp phát sinh khi có thêm nhiều tính năng, nhanh chóng bào mòn ưu điểm lớn nhất của XML
Việc người dùng thông thường phải trực tiếp chỉnh sửa bên trong tài liệu office là đủ hiếm, nên mức rào cản như học cách dùng trình đọc SQLite là chấp nhận được. Giới hạn của XML+Zip đối với ghi ngẫu nhiên ở giữa tệp là thứ ngay cả định luật Moore cũng không vượt qua được
TEXThayBLOBcủa SQLite có được nén không, hay giả định rằng bên gọi sẽ nén BLOB trước khi ghiODT được thiết kế với tiêu chuẩn hóa trong tâm trí. Định dạng trước đó cũng rất giống, nhưng phụ thuộc nhiều vào các tiêu chuẩn hiện có như XHTML, SVG, CSS
Nếu không thể tham chiếu các tiêu chuẩn hiện có, bản đặc tả ODT tự nó sẽ đột nhiên trở nên khổng lồ. Nỗ lực cập nhật tiêu chuẩn cũng có vẻ đáng kể, và vài năm gần đây không có nhiều tiến triển
Thực tế thì định dạng SQLite có thể được cung cấp như một lựa chọn, nhưng con thuyền về định dạng tài liệu office có lẽ đã rời bến. Tuy vậy, đây là một căn cứ tốt cho lập luận nên biên soạn đặc tả SQLite thành tiêu chuẩn chính thức
Ngoại trừ một vài khiếm khuyết, chẳng hạn vấn đề thuộc tính
ooo:rsidlàm bùng nổ số lượng style cục bộ và phạm vi văn bản, bảng tính không thưa, các cơ chế kỳ lạ trong styling bảng, đây là một markup được thiết kế rất tốt cho loại dữ liệu tài liệu này. Nó cân bằng tốt giữa markup ngữ nghĩa và cách biểu đạt mà người dùng thực sự muốnNgược lại, Office OpenXML có các thẻ rỗng dùng cho định dạng mang trạng thái, và trong DOCX chúng bật/tắt việc văn bản theo sau có được hiển thị đậm hay không
Việc gắn một định dạng tệp với SQLite có vẻ hơi sai sai
SQLite thì tốt, nhưng trong lĩnh vực này nó khá đặc thù. Vì nó làm rất nhiều việc nên khó sao chép y nguyên
Nhưng trong trường hợp này có cần nhiều tính năng đến vậy không thì không. Chỉ cần ngữ nghĩa giao dịch cơ bản, an toàn và khả năng lưu cấu trúc bảng đơn giản; không cần đến toàn bộ chuẩn SQL hay cả bộ tối ưu hóa truy vấn
Có thể có định dạng tệp tốt hơn, nhưng sẽ tốt hơn nếu đó là một định dạng tách biệt với SQLite
Kích thước dưới 1MB, và https://sqlite.org/footprint.html, ngay cả khi bật mọi tính năng cũng chỉ 750KB: https://www.sqlite.org/about.html
Khi biên dịch có thể bỏ khá nhiều tính năng, và dường như cũng có tùy chọn điều chỉnh hoặc thu gọn bộ lập kế hoạch truy vấn: https://www.sqlite.org/compile.html
Hơn nữa còn có câu “SQLite không cạnh tranh với cơ sở dữ liệu client/server. SQLite cạnh tranh với
fopen()”: https://www.sqlite.org/whentouse.htmlRốt cuộc thứ cần thiết không phải là bản thân cơ sở dữ liệu, mà là một thư viện cung cấp API và hành vi của cơ sở dữ liệu
SQLITE_BUSYkhá vất vảTôi biết rằng trong xử lý giao dịch, lỗi tuần tự hóa là điều có thể dự kiến, nhưng trong SQLite rất khó phân biệt giữa lỗi kéo dài kiểu tự deadlock và vấn đề cập nhật đồng thời tạm thời. Nếu là lỗi tạm thời thì chỉ cần chạy lại closure định nghĩa công việc của giao dịch, nhưng nếu là lỗi kéo dài thì vô nghĩa
Một phần vấn đề là
sqlite3_stmtgộp cả tính chất của câu lệnh đã chuẩn bị và tập kết quả. Vì muốn cache bytecode đã biên dịch nên thường giữ nó lâu, nhưng nếu dừng giữa chừng khi đang lặp thì tại thời điểm đó nó có thể đang giữ khóa. Điều này có thể dẫn đến lỗi nâng cấp khóa ngoài dự kiếnCuối cùng tôi đã loại bỏ vấn đề bằng cách tạo báo cáo lỗi chi tiết với
sqlite3_next_stmt,sqlite3_stmt_busy,sqlite3_sql. Dù chỉ dùng cho cá nhân, mã retry giao dịch vẫn đầy logging tùy chọn và chú thích. Logic retry giao dịch cho PostgreSQL dễ hơn nhiềuMột điểm nữa khiến tôi ngạc nhiên là tài liệu nói rằng ở chế độ WAL với
synchronous=NORMAL, một giao dịch đã commit có thể bị rollback sau khi mất điện hoặc hệ thống crash: https://sqlite.org/pragma.html#pragma_synchronousĐiều này không liên quan đến ứng dụng của tôi
Nếu Richard Hipp và công ty có thể đưa ra một chuẩn SQLite ISO/IEC/ANSI/ETSI mà họ tuyệt đối không đi chệch, một rà soát pháp lý xác nhận không có bằng sáng chế ảnh hưởng, và nhiều hiện thực SQLite tương thích vẫn giữ nguyên mọi ưu điểm, thì khi đó mới có thể bàn chuyện khuyến nghị nó làm định dạng tệp. Nếu không, đó chỉ là yêu cầu đặt sự phụ thuộc mạnh vào một hiện thực từ một nguồn duy nhất và đẩy cả sự phụ thuộc đó sang người dùng
XML, ASN.1, JFIF là các chuẩn chính thức, và ZIP cũng là chuẩn chính thức được chấp nhận thành ISO/IEC 21320-1:2015 trong quá trình chuẩn hóa OpenDocument
Điều quan trọng nhất với tài liệu là mọi người khác phải đọc được. Giảm thời gian cập nhật đĩa chỉ là chuyện thứ yếu. Không thể không rút ra bài học từ việc Microsoft bóp méo các cơ quan tiêu chuẩn hóa để duy trì sự phụ thuộc: https://arstechnica.com/uncategorized/2008/10/norwegian-standards-body-implodes-over-ooxml-controversy/
Đó không thể là một lựa chọn quá sai
Một ví dụ khác là tile bản đồ raster. Thực chất là các ảnh vuông nhỏ có thể lên tới hàng triệu
Tôi đã thử Zip, tar, hệ thống tệp và SQLite, và SQLite là nhanh nhất, nhỏ nhất, thậm chí còn tốt hơn cả archive thông thường không có overhead
SQLite có một nhược điểm lớn. BLOB lấy từ cơ sở dữ liệu không thể
mmap, nên phải sao chép sang nơi khác. Tệp Zip, nếu không được nén hoặc được nén bằng kiểu mã hóa đặc biệt như PVRTC, thì có thểmmaptrực tiếpOpenDocument là ảnh đã nén và XML. Rốt cuộc điều đó có nghĩa là phải phân tích cú pháp toàn bộ định dạng rồi đưa lên bộ nhớ
Tôi không rõ SQLite cải thiện việc này như thế nào. XML không lý tưởng, nhưng vì được nén bằng Zip nên mức phạt về kích thước cũng không lớn
Tất cả các ưu điểm được liệt kê trong bài về SQLite đều có thể triển khai nếu dùng SQLite làm mô hình runtime của tài liệu. Có thể làm cả trên đĩa lẫn trong bộ nhớ, nhưng SQLite không nhất thiết phải là định dạng truyền tải
Ngược lại, SQLite có thể lớn hơn định dạng hiện tại. Sau các thay đổi, có thể xuất hiện khoảng trống không dùng đến, bị phân mảnh và trở nên thưa. Nếu lần nào cũng phải tối ưu hóa thì những ưu điểm như lưu nhanh cũng biến mất
Những định dạng cần cập nhật delta và tra cứu chỉ mục nhanh, đồng thời không nên nạp toàn bộ tệp vào bộ nhớ, thực tế thường dùng SQLite làm định dạng tệp. Tuy nhiên, tôi cảm thấy OpenDocument là một ví dụ không hay để chọn làm đối tượng áp dụng SQLite trong kịch bản giả định này
Nếu dùng SQLite làm định dạng trên đĩa và triển khai ứng dụng đúng cách, có thể tránh kết thúc ở trạng thái bị hỏng
XML/Zip cũng có thể đạt được điều tương tự bằng mẹo đổi tên tệp, nhưng SQLite cung cấp việc đó trong một tệp đĩa duy nhất. Nếu đã dùng SQLite làm mô hình bộ nhớ thì không có lý do gì để không dùng nó làm định dạng trên đĩa/truyền tải. Đến thời điểm đó thì gần như là miễn phí
Vấn đề kích thước tệp có lẽ có thể xử lý bằng
VACUUM