7 điểm bởi GN⁺ 2024-11-13 | 2 bình luận | Chia sẻ qua WhatsApp
  • Tài liệu chính thức của Postgres rất xuất sắc, nhưng bản PDF Postgres 17 dài tới 3.200 trang, nên người mới rất khó chỉ học từ tài liệu mà nắm hết thiết kế schema, cách SQL hoạt động và các bẫy vận hành trước khi làm việc thực tế
  • Nếu không có lý do đặc biệt, hãy chuẩn hóa dữ liệu; việc phi chuẩn hóa để tăng tốc đọc bằng cách lưu dữ liệu trùng lặp sẽ phải đánh đổi bằng nguy cơ không nhất quán và độ phức tạp khi ghi
  • Từ khóa SQL không phân biệt hoa thường, nhưng NULL gần với “không biết” hơn, nên nếu so sánh như null trong các ngôn ngữ lập trình thông thường thì có thể cho ra kết quả khác mong đợi
  • Chỉ cần dùng tốt psql với pager, \x, .psqlrc, \pset null, tự động hoàn thành, lệnh backslash và \copy là đã thuận tiện hơn rất nhiều trong việc đọc output, khám phá dữ liệu và xuất CSV
  • Index, lock, transaction và JSONB đều rất mạnh, nhưng nếu không hiểu query plan và các ràng buộc vận hành thì có thể dẫn tới suy giảm hiệu năng hoặc vấn đề về tính sẵn sàng

Bối cảnh nên biết trước khi lao vào tài liệu chính thức đồ sộ

  • Tài liệu chính thức của Postgres, tính theo phiên bản hiện tại là 17, khi in ra PDF khổ US letter dài 3.200 trang, còn nếu in khổ A4 thì là 3.024 trang
  • Có rất nhiều kiến thức thực chiến hữu ích nếu biết trước khi dùng Postgres; một phần trong số đó cũng áp dụng được cho các SQL DBMS khác, nhưng phạm vi áp dụng không phải lúc nào cũng chắc chắn

Mặc định hãy chuẩn hóa dữ liệu

  • Chuẩn hóa là quá trình loại bỏ dữ liệu trùng lặp hoặc không cần thiết trong schema cơ sở dữ liệu
  • Nếu lưu trực tiếp user_email trong bảng documents, thì khi người dùng đổi email bạn sẽ phải cập nhật mọi hàng tài liệu của người đó
    • Thay vào đó, có thể để mỗi hàng trong documents tham chiếu tới một hàng ở bảng khác như users bằng khóa ngoại user_id
  • Không cần phải học thuộc mọi chuẩn như “1st normal form”, nhưng quy trình chuẩn hóa nói chung thường dẫn đến schema dễ bảo trì hơn
  • Phi chuẩn hóa là cách giữ dữ liệu trùng lặp để đọc nhanh mà không phải tính toán lại một dữ liệu nào đó mỗi lần
    • Trong ứng dụng quản lý ca làm của nhân viên, thay vì mỗi lần đều tính tổng số giờ làm tích lũy trong năm bằng cách cộng toàn bộ shift duration, bạn có thể tính định kỳ hoặc tính lại khi thời gian làm việc thay đổi rồi lưu sẵn
    • Dữ liệu này có thể đặt trong chính Postgres hoặc trong một lớp cache như Redis
  • Phi chuẩn hóa gần như luôn đi kèm chi phí, tiêu biểu là khả năng dữ liệu không nhất quán và độ phức tạp khi ghi tăng lên

Lời khuyên “đừng làm thế” từ dự án Postgres

  • Wiki chính thức của Postgres có danh sách “Don’t do this”
  • Không sao nếu bạn chưa hiểu hết mọi mục; những mục bạn chưa hiểu thì cũng ít có khả năng bạn sẽ mắc đúng lỗi đó
  • Đặc biệt nên nhớ các lời khuyên sau

Những hành vi trong SQL dễ gây nhầm lẫn

  • Từ khóa SQL không cần viết hoa

    • Từ khóa SQL không phân biệt chữ hoa chữ thường
    • Các truy vấn sau có cùng ý nghĩa
    SELECT * FROM my_table WHERE x = 1 AND y > 2 LIMIT 10;
    select * from my_table where x = 1 and y > 2 limit 10;
    SELECT * from my_table WHERE x = 1 and y > 2 LIMIT 10;
    
    • Đặc điểm này không chỉ riêng Postgres
  • NULL khác với null/nil trong ngôn ngữ lập trình thông thường

    • NULL trong SQL gần với “không biết” hơn là null hay nil trong ngôn ngữ lập trình thông thường
    • NULL = NULL không trả về true mà trả về NULL
    • Trong hầu hết các phép so sánh mà một bên là NULL, kết quả cũng là NULL
    • Để so sánh với NULL, cần dùng các phép sau
      • x IS NULL: trả về true nếu xNULL
      • x IS NOT NULL: trả về true nếu x không phải NULL
      • x IS NOT DISTINCT FROM y: tương tự x = y nhưng xử lý NULL như một giá trị thông thường
      • x IS DISTINCT FROM y: tương tự x != y/x <> y nhưng xử lý NULL như một giá trị thông thường
    • Mệnh đề WHERE chỉ trả về hàng khi điều kiện là true
      • SELECT * FROM users WHERE title != 'manager' sẽ không trả về các hàng có titleNULL
      • NULL != 'manager' cho kết quả là NULL
    • COALESCE trả về giá trị đầu tiên không phải NULL trong nhiều đối số
    COALESCE(NULL, 5, 10) = 5
    COALESCE(2, NULL, 9) = 2
    COALESCE(NULL, NULL) IS NULL
    

Dùng psql hữu ích hơn

  • Cải thiện khả năng đọc output

    • Nếu khi truy vấn một bảng có nhiều cột hoặc giá trị dài mà output khó đọc, có thể pager đang bị tắt
    • Pager của terminal cho phép bạn cuộn trong viewport để xem văn bản lớn hoặc bảng psql
    • Với các bảng nhiều cột, bạn có thể bật expanded mode bằng \pset expanded hoặc \x
    • Nếu muốn dùng mặc định, hãy thêm \x vào ~/.psqlrc trong thư mục home
  • Hiển thị NULL rõ ràng hơn

    • Thiết lập mặc định không cho thấy rõ một giá trị có phải NULL hay không trong output
    • Bạn có thể chỉ định chuỗi hiển thị cho NULL trong psql
    \pset null '[NULL]'
    
    • Cũng có thể dùng chuỗi Unicode; nếu muốn dùng mặc định thì thêm cùng lệnh đó vào ~/.psqlrc
  • Tận dụng tự động hoàn thành và lệnh backslash

    • psql hỗ trợ tự động hoàn thành như một console tương tác
    • Gõ một phần từ khóa hoặc tên bảng rồi nhấn Tab để điền phần còn lại
    • Một số lệnh backslash hữu ích gồm
      • \?: danh sách mọi shortcut
      • \d: hiển thị danh sách relation, tức bảng và sequence, cùng chủ sở hữu
      • \d+: thêm kích thước và một phần metadata vào \d
      • \d table_name: hiển thị schema bảng, kiểu cột, khả năng nullable, giá trị mặc định, index và ràng buộc khóa ngoại
      • \e: chỉnh sửa truy vấn trong trình soạn thảo mặc định được đặt trong biến môi trường $EDITOR
      • \h SQL_KEYWORD: hiển thị cú pháp và link tài liệu của từ khóa SQL đó
  • Xuất CSV và alias trong SELECT

    • Có thể lưu kết quả truy vấn ra CSV bằng \copy
    \copy (select * from some_table) to 'my_file.csv' CSV
    
    • Để thêm tên cột vào dòng đầu tiên, thêm tùy chọn HEADER
    \copy (select * from some_table) to 'my_file.csv' CSV HEADER
    
    • \copy giúp tránh phải dùng quyền nâng cao mà câu lệnh COPY chuẩn hơn thường yêu cầu
    • Các cột output của SELECT có thể được đặt bí danh bằng AS
    SELECT vendor, COUNT(*) AS number_of_backpacks
    FROM backpacks
    GROUP BY vendor
    ORDER BY number_of_backpacks DESC;
    
    • Trong GROUP BYORDER BY, bạn có thể tham chiếu số thứ tự cột xuất hiện sau SELECT
    SELECT vendor, COUNT(*) AS number_of_backpacks
    FROM backpacks
    GROUP BY 1
    ORDER BY 2 DESC;
    
    • Cách viết rút gọn này hữu ích, nhưng tốt hơn là không đưa vào các truy vấn được triển khai production

Thêm index không có nghĩa là lúc nào cũng được dùng

  • Index và query plan

    • Index là một cấu trúc dữ liệu đóng vai trò như thư mục tắt để tìm các hàng trong bảng theo một trường cụ thể
    • Loại index phổ biến nhất là B-tree, hoạt động tốt với điều kiện bằng chính xác như WHERE a = 3 và điều kiện phạm vi như WHERE a > 5
    • Bạn không thể chỉ định trực tiếp cho Postgres phải dùng một index cụ thể
    • Dựa trên thống kê mà Postgres duy trì cho từng bảng, nó sẽ dự đoán xem dùng index có nhanh hơn so với sequential scan đọc toàn bộ bảng từ đầu đến cuối hay không
    • Nếu thêm EXPLAIN trước SELECT ... FROM ..., bạn có thể xem query plan về cách Postgres sẽ thực thi truy vấn
    • Khi đọc query plan, có thể tham khảo hướng dẫn EXPLAIN ANALYZE của thoughtbot, tài liệu của pganalyze, tài liệu chính thứcexplain.depesz.com
  • Bảng nhỏ và index nhiều cột

    • Với các bảng ít hàng như trong DB phát triển cục bộ, index có thể không giúp được nhiều
    • Nếu chỉ có khoảng 100 hàng, Postgres có thể kết luận rằng sequential scan nhanh hơn dùng index
    • Postgres hỗ trợ index nhiều cột
    CREATE INDEX CONCURRENTLY ON tbl (a, b);
    
    • Các điều kiện như WHERE a = 1 AND b = 2 có thể nhanh hơn so với trường hợp đặt index riêng cho ab
    • Vì nó có thể duyệt một B-tree duy nhất và kết hợp điều kiện tìm kiếm một cách hiệu quả
    • Index (a, b) cũng giúp các truy vấn chỉ lọc theo a nhanh tương đương index chỉ có a
    • Các truy vấn như WHERE b = 5 có thể nhanh hơn, nhưng có thể không phải tối ưu nhất
      • Vì index được sắp khóa theo a trước rồi mới đến b, nên phải đi qua mọi giá trị a để tìm giá trị b
    • Nếu cần truy vấn theo nhiều tổ hợp cột, thường người ta sẽ vừa có index (a, b) vừa có index riêng cho b
    • Tùy nhu cầu, bạn cũng có thể chỉ dựa vào các index riêng cho ab
  • Dùng text_pattern_ops cho prefix match

    • Bạn có thể lưu thư mục phân cấp theo kiểu materialized path và cần tìm mọi descendant bắt đầu bằng một prefix nhất định
    SELECT * FROM directories WHERE path LIKE '/1/2/3/%'
    
    • Dù có tạo index B-tree mặc định trên cột path, truy vấn này vẫn có thể không dùng đến
    CREATE INDEX CONCURRENTLY ON directories (path);
    
    • Để cho phép sắp xếp theo từng ký tự cần thiết cho prefix match hoặc pattern match, cần chỉ định operator class
    CREATE INDEX CONCURRENTLY ON directories (path text_pattern_ops);
    

Những vấn đề vận hành do lock và transaction tạo ra

  • Lock trong Postgres

    • Lock hoặc mutex là cơ chế đảm bảo tại một thời điểm chỉ một client được thực hiện công việc nguy hiểm nào đó
    • Trong cơ sở dữ liệu, việc cập nhật các đối tượng như row, table, view phải hoặc thành công toàn bộ hoặc thất bại toàn bộ; để ngăn việc chỉ một phần thành công do thao tác đồng thời, hệ thống sẽ lấy lock trên các đối tượng liên quan
    • Các mức table lock của Postgres có nhiều cấp độ, từ ít hạn chế hơn đến hạn chế hơn
      • ACCESS SHARE: SELECT
      • ROW SHARE: SELECT ... FOR UPDATE
      • ROW EXCLUSIVE: UPDATE, DELETE, INSERT
      • SHARE UPDATE EXCLUSIVE: CREATE INDEX CONCURRENTLY
      • SHARE: CREATE INDEX, khi không dùng CONCURRENTLY
      • ACCESS EXCLUSIVE: nhiều dạng ALTER TABLE, ALTER INDEX
    • Trên cùng một bảng, các thao tác sau có thể chạy được hoặc phải chờ
      • UPDATE trong khi SELECT: được
      • UPDATE trong khi CREATE INDEX CONCURRENTLY: được
      • SELECT trong khi CREATE INDEX: được
      • SELECT trong khi ALTER TABLE: thường phải chờ
      • ALTER TABLE trong khi SELECT: thường phải chờ
    • Một số dạng ALTER TABLE có thể chỉ cần lock yếu hơn; thông tin đầy đủ có thể xem ở tài liệu lock tường minh chính thứchướng dẫn xung đột lock theo từng thao tác
  • ALTER TABLE chậm và hàng đợi lock

    • Nếu ALTER TABLE chạy lâu, ngay cả các SELECT đọc cùng bảng đó cũng có thể bị chặn
    • Nếu đó là bảng lõi như users mà mọi request của ứng dụng web đều tham chiếu tới, các request có thể phải chờ rồi timeout và trả về 503
    • Những nguyên nhân phổ biến khiến ALTER TABLE chậm gồm
      • thêm cột với default không phải hằng số
      • thay đổi kiểu cột
      • thêm uniqueness constraint
    • Từ Postgres 11 trở đi, vấn đề mọi default đều làm chậm khi thêm cột đã được sửa; trường hợp default không phải hằng số vẫn có thể là vấn đề
    • Ngay cả khi bản thân ALTER TABLE là thao tác nhanh, nó cũng không thể chạy cho tới khi lấy được lock
      • Nếu đang có một SELECT chậm từ dashboard nội bộ cũ chạy sẵn từ trước, ALTER TABLE sẽ phải đợi
    • Vì lock của Postgres tạo thành hàng đợi, nên các truy vấn đến sau trên cùng bảng nằm phía sau ALTER TABLE đang chờ cũng có thể phải đợi
    • Kịch bản tương tự được phân tích thêm trong Migrations and exclusive locks
  • Transaction dài cũng nguy hiểm

    • Transaction là cách gom nhiều câu lệnh cơ sở dữ liệu thành một khối all-or-nothing, bắt đầu bằng BEGIN và kết thúc bằng COMMIT
    • Các thay đổi trong transaction không hiện ra với client khác, và chỉ được công bố ra cơ sở dữ liệu khi COMMIT
    • Điều này phù hợp với các tác vụ như chuyển tiền, nơi việc giảm số dư ở một tài khoản và tăng số dư ở tài khoản khác phải cùng thành công hoặc cùng bị hủy
    • Khi transaction lấy được lock, nó giữ lock đó cho tới COMMIT
    • Nếu sau BEGIN bạn UPDATE một row cụ thể rồi bỏ đi, thì DELETE trên row đó từ client khác sẽ bị dừng lại cho tới khi transaction được commit
    • Việc giữ transaction mở lâu hơn cần thiết có thể chặn truy vấn hoặc cập nhật của các client khác

JSONB là một công cụ sắc bén

  • Vấn đề hiệu năng và schema của JSONB

    • JSONB rất linh hoạt, nhưng nếu dùng sai thì nhược điểm cũng rất lớn
    • Postgres không theo dõi thống kê cho các cột JSONB, vì vậy truy vấn so sánh bằng trên một cột JSONB đơn lẻ có thể chậm hơn rất nhiều so với truy vấn trên một tập cột thông thường
    • Trong một trường hợp, có thể thấy ví dụ chậm hơn 2000 lần vì JSONB
    • Cột JSONB gần như có thể chứa bất cứ thứ gì, nên rất mạnh nhưng lại ít đảm bảo về cấu trúc
    • Với bảng thông thường, bạn có thể nhìn schema để dự đoán kết quả truy vấn; còn với JSONB thì không chắc key dùng camelCase hay snake_case, trạng thái là boolean hay enum
    • Tính chất kiểu tĩnh mà dữ liệu Postgres thông thường có được không áp dụng cho JSONB theo cùng cách đó
  • Sự khó xử khi so sánh kiểu trong JSONB

    • Khi muốn tìm các hàng trong bảng backpacks mà trường brand trong cột JSONB data có giá trị JanSport, truy vấn sau sẽ không hoạt động
    select * from backpacks where data['brand'] = 'JanSport';
    
    • Postgres kỳ vọng kiểu ở vế phải của phép so sánh phải khớp với kiểu ở vế trái, và vế phải phải là một tài liệu JSON hợp lệ
    • Một tài liệu JSON phải là object, array, string, number, boolean hoặc null, nên riêng JanSport không phải JSON hợp lệ
    • Truy vấn đúng là hoặc so sánh bằng chuỗi JSON, hoặc chuyển vế trái sang text của Postgres
    select * from backpacks where data['brand'] = '"JanSport"';
    
    select * from backpacks where data['brand'] = '"JanSport"'::jsonb;
    
    select * from backpacks where data->>'brand' = 'JanSport';
    
    • NULL của SQL và null của JSONB hoạt động khác nhau
      • 'null'::jsonb = 'null'::jsonbtrue, còn NULL = NULLNULL
    • JSONB có rất nhiều toán tử và hàm chuyên dụng, nên khó nhớ hết trong một lần
    • Postgres có cả JSON, lưu giá trị JSON dưới dạng text, và JSONB, chuyển nó sang định dạng nhị phân hiệu quả hơn
    • JSONB có những ưu điểm như có thể đánh index, còn định dạng JSON có thể xem là dành cho các trường hợp đặc biệt

2 bình luận

 
bbulbum 2024-11-19

Những điều không nên làm, chắc phải đọc thử một lần vào lúc nào đó.

 
GN⁺ 2024-11-13
Các ý kiến trên Hacker News
  • PostgreSQL nhìn chung phân biệt chữ hoa/chữ thường, nhưng việc viết hoa các từ khóa SQL thường là nỗ lực tăng tính dễ đọc bằng cách khớp mẫu trực quan
    Không nhất thiết phải làm vậy, nhưng nếu phải debug truy vấn của người khác, tôi sẽ đưa nó vào prettifier để có thể lướt nhanh qua định nghĩa mà không bị vướng vào các chi tiết hình thức cú pháp nhỏ nhặt
    Cũng như việc định dạng code trong các ngôn ngữ khác, cấu trúc trực quan như thụt lề nhất quán giúp giảm thời gian hiểu những phần hiển nhiên và cho phép tập trung vào chỗ quan trọng
    Tuy nhiên, tôi thật sự ghét việc thực sự trộn chữ hoa/chữ thường trong identifier như actuallyUsingCaseInIdentifiers, và không muốn thấy các cột cần dấu ngoặc kép chỉ để kiểm tra trong CLI

    • Identifier viết hoa trông như các khối có thể hoán đổi cho nhau, nên làm chậm tốc độ đọc hơn so với hình dạng từ mà chữ thường có
    • Khi làm việc tương tác với SQL, việc biết sự phân biệt này khá hữu ích
      Nếu chỉ gõ nhanh một truy vấn tạm thời không ai xem rồi bỏ đi thì không quan tâm chữ hoa/chữ thường, nhưng SQL được commit vào repository thì viết lệnh bằng ALL CAPS
    • Tôi hiểu rằng chữ hoa từng đóng vai trò như syntax highlighting trên màn hình đen trắng
      Giờ đã có màu thì không còn cần nữa, nhưng đó là ký ức cũ nên tôi không có tài liệu làm căn cứ
    • PostgreSQL gập identifier về chữ thường, trong khi chuẩn gập về chữ hoa, vì vậy về xử lý chữ hoa/chữ thường thì nó không tuân theo chuẩn
      Dù vậy, không nên trộn identifier có dấu nháy và không có dấu nháy, và việc truy vấn cấu trúc nội bộ nhìn chung cũng không được chuẩn hóa, nên điều này không có nhiều ý nghĩa
    • Tôi muốn biết có prettifier hoặc linter nào được khuyến nghị cho SQL không
  • Đây là lần đầu tôi thấy mục “don’t do this” trên wiki PostgreSQL, và nó khá hữu ích: https://wiki.postgresql.org/wiki/Don%27t_Do_This

    • Nếu những tính năng này là các cái bẫy dễ mắc đến vậy, tôi thắc mắc vì sao không đưa chúng vào diện dự kiến loại bỏ
      Ví dụ, với schema mới thì có vẻ hợp lý hơn nếu vô hiệu hóa các tính năng như kế thừa bảng, và muốn bật lại thì phải cố ý dùng một thiết lập phức tạp
    • Nó làm tôi nhớ đến SQL Anti-patterns, và tôi cho rằng đó là cuốn sách mà bất kỳ ai làm việc với cơ sở dữ liệu cũng nên đọc
    • Nó khiến tôi phải suy nghĩ lại về một số thói quen học được từ phía MySQL
  • Nhiều nội dung ở đây không chỉ áp dụng riêng cho PostgreSQL
    Chẳng hạn hành vi kỳ lạ của NULL, thứ tự cột trong index, và đặc biệt là tương tác giữa NULL và index/ràng buộc unique cũng không trực quan trong MySQL
    Ví dụ, nếu có bảng người dùng trong đó email không được NULL, username có thể NULL, và đặt ràng buộc unique trên (email, username), thì có thể chèn nhiều lần cùng một email với username là NULL. Vì NULL không bằng một NULL khác

    • Tham khảo thêm: từ PostgreSQL 15, có thể ảnh hưởng đến hành vi này trong constraint và unique index bằng NULLS [NOT] DISTINCT
      https://www.postgresql.org/docs/devel/sql-createtable.html#S...
    • Tôi nghĩ giá trị mặc định này thực tế là ổn
      Các use case cần hành vi ngược lại hiếm hơn nhiều
  • Nếu chỉ nói kiểu “hãy chuẩn hóa dữ liệu nếu không có lý do chính đáng” rồi bỏ qua thì không ổn
    Ngay cả trang mà tác giả liên kết cũng liệt kê tới 11 dạng chuẩn hóa, bao gồm cả dạng phi chuẩn; đa số mọi người còn không biết chúng là gì, và 7 trong số đó hầu như không có dịp dùng
    Không nên khiến người ta lang thang tìm kiếm các dạng chuẩn cao hơn

    • Dù vậy, tác giả nhìn chung đã thêm đoạn giải thích ý mình là gì, và tôi nghĩ hướng đó là đúng
      Trong dự án tôi mới chuyển sang gần đây, tôi cũng phải sửa vài vấn đề như vậy; hầu như không có lý do để trùng lặp dữ liệu
    • Nếu bài viết này nhắm đến người mới, thì khi không chắc chắn, câu trả lời gần như luôn là dạng chuẩn thứ ba
    • Quy tắc chung là chuẩn hóa tối đa, rồi phi chuẩn hóa cho đến khi đạt được hiệu năng cần thiết
  • Mẹo đầu tiên là hãy VACUUM hằng ngày
    Lúc mới bắt đầu tôi không biết điều này nên hoàn toàn không VACUUM cơ sở dữ liệu của reddit, và đến một ngày khi buộc phải chạy, reddit gần như sập suốt gần một ngày trong lúc chờ nó hoàn tất

    • Có vẻ khi đó chưa có autovacuum
      Với quy mô của reddit, thật ngạc nhiên là transaction ID đã không cạn trước
  • Tôi ước các nhà phát triển chú ý hơn đến chuẩn hóa và ngừng nhét mọi thứ vào các cột JSONB

    • Từ rất lâu trước khi cơ sở dữ liệu có thể lưu JSON có cấu trúc, các lập trình viên junior đã tranh luận trên giấy rất gay gắt về mức độ chuẩn hóa phù hợp
      Những lập trình viên có kinh nghiệm hơn biết rằng đáp án đúng là không trùng lặp bất cứ thứ gì ngoài khóa, và chỉ phi chuẩn hóa khi thật sự bất đắc dĩ
      Sau đó các cơ sở dữ liệu như Mongo xuất hiện, cung cấp một thứ “giống cơ sở dữ liệu” nơi chuẩn hóa khó hoặc vô nghĩa, cổ vũ những junior đó; kết quả là thiết kế cơ sở dữ liệu tệ hại và những đống rác không thể bảo trì đã có một thời gian ngắn nở rộ
      Giờ con lắc đã quay lại và mọi người tái khám phá lợi ích của cơ sở dữ liệu đã chuẩn hóa, nhưng cột JSON vẫn là lối thoát để các thực hành xấu có thể sinh sôi
    • Có hai lý do để dùng cột JSONB
      Thứ nhất là để lưu JSON. Khi webserver gọi API bên thứ ba, nếu lưu phản hồi API gốc vào cột JSONB rồi xử lý từ đó, bạn sẽ có một bản ghi có thể kiểm toán khi debug các vấn đề đến từ API đó
      Thứ hai là để lưu sum type. Việc SQL không hỗ trợ sum type có thể xem là một trong những thiếu sót lớn nhất khi mô hình hóa dữ liệu trong cơ sở dữ liệu SQL
      Có nhiều cách lách, và “cứ nhét vào cột JSONB rồi kiểm chứng trong ứng dụng” cũng là một trong số đó, nhưng chẳng cách lách nào đặc biệt hay cả
    • Ngay cả khi quan tâm đến chuẩn hóa, rốt cuộc thường vẫn xuất hiện một ngăn kéo JSONB linh tinh
      Miễn là bạn không viết các truy vấn tệ bên trong đó thay vì kéo các giá trị trong JSONB ra thành cột riêng, thì bản thân nó không hẳn là vấn đề lớn
    • Phần lớn các nhà phát triển dùng các công cụ kiểu này ngày nay thực chất đang tự tạo hệ quản trị cơ sở dữ liệu của riêng mình, và chỉ giao phần lưu bền vững cho một DBMS khác
      Vì nếu chỉ cần đáp ứng thành công yêu cầu lưu bền vững thì không có áp lực mạnh buộc phải suy nghĩ về thiết kế tốt
      Có nên xây thêm một DBMS trên một DBMS hay không thì còn đáng nghi, nhưng dù sao hiện trạng là vậy
    • Để cách này hoạt động đúng, cần có quy trình migration schema bao gồm cả khả năng hoàn tác thay đổi schema
      Nếu một cột mới làm hỏng hiệu năng hoặc gây vấn đề, bạn phải có thể quay lại
      Nếu có liên quan đến công cụ CLI, bạn còn phải xử lý cả việc có thể chấp nhận bao nhiêu downtime, liệu có thể cập nhật phiên bản đồng bộ trên toàn công ty hay không, hoặc có cần hỗ trợ cả schema cũ và mới trong một thời gian hay không
      Nếu cơ sở dữ liệu không phải là một phần trong sản phẩm chủ lực của nhóm, tất cả những thứ này có thể đều bị thiếu
  • Tôi viết bài này để giúp người mới bắt đầu: https://tomcam.github.io/postgres/

  • Bài viết thật sự rất hay, và tôi không biết tài liệu PostgreSQL dài tới 3200 trang
    Tôi đã dùng nó một thời gian và học khi cần; tôi cũng khá thích tài liệu chính thức, và cũng thích đọc các bài viết liên quan khi cần một chủ đề cụ thể
    Tôi nghĩ sẽ hữu ích cho độc giả nếu tác giả bổ sung tại https://challahscript.com/what_i_wish_someone_told_me_about_... rằng index trên các cột (b, a) hoạt động tốt khi truy vấn chỉ theo b
    Điều này đã được ngụ ý phần nào khi nói về truy vấn chỉ theo a, nhưng nói rõ hơn cũng không hại gì
    Phần JSON/JSONB thì tôi hầu như không dùng nên chưa xem nhiều

  • Nghĩ đến những câu SQL lố bịch từng thấy ngoài thực tế, tôi nghĩ nên bắt đầu bằng việc đọc bài báo của Codd và hiểu mô hình quan hệ là gì
    Nó chỉ dài 11 trang, và chỉ cần đọc nó thôi cũng sẽ làm giảm bớt đau khổ trên thế giới này

  • Gần như toàn bộ bài này cũng áp dụng cho các cơ sở dữ liệu MVCC khác như MySQL
    Chi tiết có thể khác nhau, nhưng MySQL cũng có những vấn đề thú vị tương tự, như khổ sở vì các transaction dài và giữ khóa metadata trong lúc ALTER