- Ben Johnson, người tạo ra BoltDB (DB key-value nhúng), hiện đang phát triển Litestream tại FlyIO
- Cấu trúc hợp lý thông thường của một ứng dụng full-stack là n-tier: máy chủ ứng dụng + máy chủ DB
→ Trong kiến trúc này, SQLite trước đây chỉ được dùng cho unit test, nhưng giờ đã hoàn toàn có thể dùng làm lớp dữ liệu và lưu trữ bền vững - Litestream là mã nguồn mở giúp SQLite có thể dùng trong ứng dụng full-stack thông qua replication
Lược sử ngắn của cơ sở dữ liệu ứng dụng
-
50 năm không phải là khoảng thời gian quá dài, nhưng cách phần mềm quản lý dữ liệu đã thay đổi rất nhiều
→ Những năm 70 có “quy tắc của Codd” định nghĩa cơ sở dữ liệu quan hệ
→ Mọi dữ liệu nằm trong bảng, có CRUD, schema, ngôn ngữ SQL, v.v.
→ Sang thập niên 80 và 90, các cơ sở dữ liệu SQL như Oracle/DB2/Postgres/MySQL bùng nổ
→ Cơ sở dữ liệu XML của những năm 2000 không mấy hiệu quả, trong khi các column DB xuất sắc cũng xuất hiện cùng thời
→ Những năm 2010 chứng kiến sự ra đời của các dự án DB phân tán mã nguồn mở quy mô lớn, và giờ ai cũng có thể dựng cluster rồi truy vấn dữ liệu ở mức terabyte -
Khi cơ sở dữ liệu tiến hóa, chiến lược kết nối DB với ứng dụng cũng phát triển theo
→ Sau Codd là sự phân tách theo tier
→ Lúc đầu là tier cơ sở dữ liệu
→ Sau đó là tier cache với memcached và Redis
→ Tier background job (Sidekiq), tier routing (PgBouncer), tier phân phối, v.v.
→ Nhiều tutorial nói như thể chỉ có 3-tier, nhưng vì không biết sẽ có bao nhiêu tier nữa nên gọi là “n-tier” -
Trong 50 năm qua, CPU, bộ nhớ và ổ đĩa đã nhanh hơn và rẻ hơn hàng trăm lần
→ Từ khóa thực sự định hình đổi mới cơ sở dữ liệu trong thập niên 2010 là “big data”
→ Nhưng nhờ phần cứng được cải thiện, đến năm 2020 khái niệm đó cũng khó còn giữ nguyên ý nghĩa
→ Năm 1996, quản lý DB 1GB là chuyện rất lớn, nhưng đến 2022 thì chạy trên laptop hay t3.micro cũng đã là quá đủ -
Khi nghĩ về kiến trúc DB mới, chúng ta thường bị ám ảnh bởi giới hạn mở rộng
→ Nếu không xử lý được dữ liệu ở mức petabyte, hoặc ít nhất là terabyte, thì dường như không đủ tư cách để tham gia cuộc thảo luận
→ Nhưng phần lớn ứng dụng, kể cả khi thành công, cũng khó chạm tới lượng dữ liệu mức terabyte
→ Chúng ta đang dùng búa khoan phá bê tông (JackHammer) chỉ để đóng một cái đinh
Bản phát hành ngọt ngào của SQLite
- Có một cơ sở dữ liệu phản ánh rất rõ xu hướng này
- Đây là một trong những SQL DB nổi tiếng nhất thế giới, là định dạng lưu trữ chính thức của Thư viện Quốc hội Mỹ, nổi tiếng về độ tin cậy, bộ test suite khổng lồ khó mà hình dung nổi, và hiệu năng cũng cực kỳ xuất sắc
- Đến mức này thì chắc chẳng cần nói tên nữa... nhưng dành cho ai đang giơ tay ở phía sau... đó chính là SQLite
- SQLite là DB nhúng. Nó không tồn tại như một tier riêng trong kiến trúc thông thường, mà chỉ là một thư viện được link vào tiến trình máy chủ ứng dụng của bạn
→ Một “ứng dụng đơn tiến trình” chạy độc lập mà không phụ thuộc vào máy chủ khác
- Vì tôi là người làm cơ sở dữ liệu nên tôi quan tâm đến kiểu ứng dụng này
- Tôi đã tạo ra BoltDB, DB key/value nhúng nổi tiếng trong hệ sinh thái Go
- BoltDB ổn định và cho hiệu năng như chiếc xe đồ chơi gắn nitro, đúng như điều ta kỳ vọng ở một DB chạy trong cùng tiến trình
- Nhưng BoltDB có giới hạn
→ Vì schema được định nghĩa bằng mã Go nên migration DB rất khó. Bạn phải tự làm công cụ cho mình. Thậm chí còn không có REPL
- Nếu bạn cẩn thận, dùng loại DB này có thể mang lại hiệu năng khổng lồ
- Nhưng cho mục đích phổ thông, có lẽ bạn sẽ không muốn vận hành loại DB như vậy
- Tôi đã suy nghĩ xem cần làm gì để BoltDB dùng được cho nhiều ứng dụng hơn, và kết luận tôi đi đến là “SQLite được tạo ra chính xác cho việc đó”
- Tất nhiên SQLite cũng có giới hạn. Lớn nhất là ứng dụng đơn tiến trình có SPOF (Single Point of Failure): mất máy chủ là mất luôn cơ sở dữ liệu. Đây không phải lỗi của SQLite mà là đặc tính thiết kế vốn có
Litestream xuất hiện
- Hai lý do lớn khiến nhiều người không dùng SQLite làm mặc định là
→ Thứ nhất là khả năng chống chịu với lỗi lưu trữ (Resilience)
→ Thứ hai là khả năng đồng thời (Concurrency) khi quy mô tăng lên - Litestream có điều muốn nói về cả hai vấn đề này
-
Litestream hoạt động bằng cách điều khiển journaling của chế độ WAL (Write Ahead Log) của SQLite
-
Trong chế độ WAL, các thao tác ghi được thêm vào một tệp log riêng, tách biệt với tệp DB chính của SQLite
-
Reader sẽ kiểm tra cả tệp WAL lẫn DB chính để đáp ứng truy vấn
-
Thông thường SQLite sẽ tự động checkpoint các page từ WAL sang DB chính
-
Litestream xen vào ở giai đoạn này bằng cách mở một giao dịch đọc vô hạn để ngăn auto-checkpoint, tự bắt và sao chép các cập nhật WAL, rồi tự kích hoạt checkpoint
Điều quan trọng nhất cần hiểu về Litestream là nó đơn giản chỉ là SQLite. Ứng dụng dùng SQLite tiêu chuẩn, không thêm dependency, không phân tích truy vấn, không hoạt động như proxy. Nó chỉ tận dụng cơ chế journaling và concurrency mà SQLite vốn đã có. Trong hầu hết trường hợp, mã của bạn thậm chí có thể không hề biết Litestream tồn tại
- Nghe có vẻ phức tạp, nhưng trên thực tế lại cực kỳ đơn giản. Dùng thử là sẽ thấy kiểu “just works”
$ litestream replicate fruits.db s3://my-bukkit:9000/fruits.db
$ litestream restore -o fruits-replica.db s3://my-bukkit:9000/fruits.db
- Thông thường người ta dùng nó để sao chép DB SQLite và lưu lên S3
- Điều này mang lại lợi ích vận hành rất lớn. DB của bạn trở nên linh hoạt, dễ di chuyển hoặc migration hơn
- Nhưng với Litestream còn có thể làm được nhiều hơn thế
- Ở phiên bản tiếp theo, sẽ có replication thời gian thực giữa các DB SQLite, giúp có thể thiết lập Read Replica phân tán và DB Write-Leader
→ Read Replica có thể bắt các thao tác ghi rồi redirect về Leader
→ Nhiều ứng dụng thiên về đọc nên thiết lập này có thể mang đến cho ứng dụng một DB mở rộng toàn cầu
Bạn nên nghiêm túc hơn với lựa chọn này: dùng SQLite làm DB ứng dụng
- Một trong những công việc IT đầu tiên của tôi là làm Oracle DBA vào đầu những năm 2000
- Tôi đã dành rất nhiều thời gian đọc vô số sách và tài liệu để học về Oracle
- Chỉ riêng hướng dẫn quản trị viên đã gần một nghìn trang, và đó chỉ là một trong hàng trăm tài liệu
- Hồi đó, học cách tối ưu truy vấn hay cải thiện ghi chép thực sự tạo ra khác biệt rất lớn
- Ổ cứng chỉ đọc được vài chục megabyte mỗi giây, nên tận dụng index tốt hơn có thể biến truy vấn 5 phút thành truy vấn 30 giây
- Nhưng việc tối ưu DB ngày càng bớt quan trọng với ứng dụng phổ thông
- Nếu bạn có DB 1GB, ổ NVMe có thể nạp toàn bộ vào bộ nhớ trong chưa đầy 1 giây
- Tôi rất thích tối ưu truy vấn SQL, nhưng với nhiều lập trình viên ứng dụng thì đây đang trở thành một kỹ năng dần mai một
- Ngay cả truy vấn chưa được tinh chỉnh đúng mức cũng có thể chạy trong vòng chưa đến 1 giây trên nhiều DB
- Postgres hiện đại là một kỳ tích. Tôi đã học được rất nhiều khi đọc mã nguồn của nó suốt nhiều năm
- Trình tối ưu truy vấn, chính sách bảo mật theo hàng, 6 loại index, v.v.
- Nếu bạn cần những tính năng đó thì tất nhiên là đáng giá, nhưng phần lớn thì không
- Và nếu bạn không cần các tính năng đó của Postgres, sẽ có trách nhiệm vận hành đi kèm
- Dù không dùng nhiều tài khoản, bạn vẫn phải cấu hình xác thực theo host và mở firewall
- Nhiều tính năng hơn đồng nghĩa với nhiều tài liệu hơn, nên thực tế lại khó hiểu rõ phần mềm mình đang vận hành
- Tài liệu của Postgre14 dài gần 3.000 trang
- SQLite là tập con tính năng của Postgres. Nhưng với tôi, nó đáp ứng 99,9% những gì tôi thường cần
- Hỗ trợ SQL tuyệt vời, window function, CTE, full-text search, hỗ trợ JSON, v.v.
- Nếu có thiếu tính năng nào đi nữa thì dữ liệu nằm ngay cạnh ứng dụng, nên lấy ra xử lý cũng không tốn nhiều overhead
- Trong khi đó, những bài toán phức tạp thực sự cần giải quyết lại không được giải bằng tính năng cốt lõi của cơ sở dữ liệu
- Thay vào đó, tôi chỉ muốn tối ưu hai thứ: độ trễ và trải nghiệm của lập trình viên
- Vì vậy, một lý do để nghiêm túc cân nhắc SQLite là vì vận hành của nó cực kỳ đơn giản
- Bạn có thể không cần thiết kế tầng cơ sở dữ liệu mà chỉ tập trung viết mã ứng dụng
- Nhưng vẫn còn một vấn đề khác
Ánh sáng quá chậm: The Light is Too Damn Slow
- Chúng ta bắt đầu chạm tới giới hạn vật lý. Trong chân không, ánh sáng đi được 186 dặm trong 1 mili giây (quãng đường khứ hồi từ Philadelphia đến New York)
- Nếu thêm switch mạng, firewall và các lớp giao thức ứng dụng thì còn chậm hơn nữa
- Trong cùng một AWS region, phần overhead độ trễ cho truy vấn Postgres có thể lên tới gần 1 mili giây
- Không phải vì Postgres chậm, mà vì ta đã chạm tới giới hạn tốc độ di chuyển của dữ liệu
- Ứng dụng hiện đại xử lý HTTP request, và trước cả khi chạy nhiều truy vấn DB cùng business logic hoặc render, đã tiêu tốn sẵn 10ms rồi
- Có một con số ma thuật với độ trễ ứng dụng: phản hồi dưới 100ms sẽ cho cảm giác gần như tức thì
- Ứng dụng phản hồi nhanh nhạy (snappy) tạo ra người dùng hài lòng
- 100ms nghe có vẻ nhiều nhưng rất dễ bị tiêu hết mà không để ý
- Vì ngưỡng 100ms quá quan trọng, người ta prerender trang rồi đưa lên CDN để giảm độ trễ
- Chúng ta nên đưa dữ liệu đến gần ứng dụng hơn. Gần đến mức nào? Thật sự rất gần
- SQLite không chỉ ở cùng máy với ứng dụng, mà còn nằm ngay bên trong tiến trình ứng dụng của bạn
- Khi đặt dữ liệu cạnh ứng dụng, độ trễ mỗi truy vấn có thể giảm xuống còn 10~20 micro giây (μ)
- Tức là nhanh hơn 50~100 lần so với truy vấn Postgres trong cùng region
- Nhưng còn hơn thế nữa. Ta gần như loại bỏ được độ trễ trên mỗi truy vấn. Ứng dụng vừa nhanh hơn lại còn đơn giản hơn
- Bạn có thể chia các truy vấn lớn thành các truy vấn nhỏ dễ quản lý hơn, và dành nhiều thời gian hơn để xây tính năng mới thay vì săn lùng mẫu N+1 query
- Tối thiểu hóa độ trễ không chỉ dành cho production. Việc test tích hợp với DB client/server truyền thống thường dễ kéo dài thành vài phút ngay trên máy local, và đẩy lên CI thì nỗi đau vẫn y nguyên
- Rút ngắn vòng phản hồi từ lúc sửa mã đến lúc test xong sẽ tiết kiệm thời gian và giúp bạn giữ được sự tập trung khi phát triển
- Với SQLite, chỉ một thay đổi một dòng cũng có thể chạy trong bộ nhớ và hoàn thành integration test trong vài giây
Nhỏ, nhanh, đáng tin cậy, phân tán toàn cầu: hãy chọn cả 4
- Litestream có tính phân tán, có replication, và quan trọng nhất là dễ hiểu
- Nói thật, “hãy thử một lần đi” vì không có nhiều thứ phải học
- Đây là luận điểm của tôi:
- Nếu xây được replication ổn định và dễ dùng cho SQLite, thì ứng dụng full-stack chạy hoàn toàn trên SQLite sẽ trở nên hấp dẫn
- Thời xưa khi người ta viết các tutorial kiểu “làm blog bằng Rails”, lựa chọn này đã bị bỏ qua, nhưng SQLite ngày nay có thể chịu được tải ghi của phần lớn ứng dụng, và nhờ replica có thể load balance việc đọc trên rất nhiều instance
- Litestream cũng có giới hạn
- Vì được tạo ra cho ứng dụng single-node nên nó không hoạt động tốt với nền tảng serverless hay rolling deployment
- Vì phải khôi phục mọi thay đổi theo thứ tự tuần tự nên restore DB có thể mất vài phút
- Chúng tôi đang chuẩn bị replication thời gian thực, nhưng mô hình tiến trình tách biệt vẫn có hạn chế ở phần kiểm soát chi tiết về đảm bảo replication
- Chúng ta có thể làm tốt hơn
- Việc tôi làm trong năm qua là xác định phần cốt lõi của Litestream và tập trung vào tính đúng đắn
- Tôi hài lòng với vị trí hiện tại
- Nó bắt đầu như một công cụ backup dạng streaming đơn giản, nhưng đang dần tiến hóa thành một cơ sở dữ liệu ổn định và phân tán
- Công việc của tôi tại Fly.io là làm cho nó nhanh hơn và liền mạch hơn
- Dù có hay không có Fly.io, Litestream vẫn sẽ tiếp tục nhận thêm nhiều cải tiến
- Litestream đã có ngôi nhà mới tại Fly.io, nhưng vẫn sẽ là một dự án mã nguồn mở
- Kế hoạch của tôi trong vài năm tới là làm cho nó hữu ích hơn, bất kể ứng dụng chạy ở đâu, và xem mô hình SQLite có thể đi được xa đến mức nào
6 bình luận
Tôi lại muốn đọc kỹ bài này một lần nữa.
Tôi cũng từng có suy nghĩ tương tự, nhưng bài này được triển khai bài bản và nghiêm túc hơn nhiều. Đọc mà thấy thán phục. Tôi cũng muốn thử dùng Litestream.
Giá mà có thể truy vấn từ xa thì tốt hơn nữa... huhu
Đúng là khiến người ta nhớ đến Elixir. Đây là một công cụ cung cấp DB phân tán nhúng và orchestration ở cấp độ ngôn ngữ, nhưng liệu đó có phải là tương lai hay không thì tôi cũng không chắc.
Đọc rất thú vị!
Ban đầu tôi chỉ định đọc nhanh rồi tóm tắt, nhưng càng làm càng thấy thú vị nên thành ra hơi dài.
Litestream - công cụ sao chép luồng cho SQLite
Có lẽ sẽ hay hơn nếu đọc cùng với câu hỏi Có ai đã dùng SQLite làm DB primary chưa?.
Có vẻ cũng có liên hệ với Cloudflare công bố D1, cơ sở dữ liệu SQL cho Workers, vừa được công bố vài ngày trước.
Hãy tham khảo cả các bình luận trên HN: https://news.ycombinator.com/item?id=31318708