Những điều ước gì có ai đó đã nói cho tôi biết về Postgres
(challahscript.com)- 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ư
nulltrong 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
psqlvới pager,\x,.psqlrc,\pset null, tự động hoàn thành, lệnh backslash và\copylà đã 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_emailtrong bảngdocuments, 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
documentstham chiếu tới một hàng ở bảng khác nhưusersbằng khóa ngoạiuser_id
- Thay vào đó, có thể để mỗi hàng trong
- 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
- Khi lưu văn bản, hãy dùng kiểu
text - Khi lưu timestamp, hãy dùng
timestampz/time with time zone - Đặt tên bảng theo snake_case
- Khi lưu văn bản, hãy dùng kiểu
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
NULLtrong SQL gần với “không biết” hơn lànullhayniltrong ngôn ngữ lập trình thông thườngNULL = NULLkhông trả vềtruemà 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 saux IS NULL: trả vềtruenếuxlàNULLx IS NOT NULL: trả vềtruenếuxkhông phảiNULLx IS NOT DISTINCT FROM y: tương tựx = ynhưng xử lýNULLnhư một giá trị thông thườngx IS DISTINCT FROM y: tương tựx != y/x <> ynhưng xử lýNULLnhư một giá trị thông thường
- Mệnh đề
WHEREchỉ trả về hàng khi điều kiện làtrueSELECT * FROM users WHERE title != 'manager'sẽ không trả về các hàng cótitlelàNULL- Vì
NULL != 'manager'cho kết quả làNULL
COALESCEtrả về giá trị đầu tiên không phảiNULLtrong 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 expandedhoặc\x - Nếu muốn dùng mặc định, hãy thêm
\xvào~/.psqlrctrong 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
NULLhay không trong output - Bạn có thể chỉ định chuỗi hiển thị cho
NULLtrongpsql
\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
- Thiết lập mặc định không cho thấy rõ một giá trị có phải
-
Tận dụng tự động hoàn thành và lệnh backslash
psqlhỗ 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\copygiúp tránh phải dùng quyền nâng cao mà câu lệnhCOPYchuẩn hơn thường yêu cầu- Các cột output của
SELECTcó thể được đặt bí danh bằngAS
SELECT vendor, COUNT(*) AS number_of_backpacks FROM backpacks GROUP BY vendor ORDER BY number_of_backpacks DESC;- Trong
GROUP BYvàORDER BY, bạn có thể tham chiếu số thứ tự cột xuất hiện sauSELECT
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
- Có thể lưu kết quả truy vấn ra CSV bằng
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 = 3và đ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
EXPLAINtrướcSELECT ... 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ức và explain.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 = 2có thể nhanh hơn so với trường hợp đặt index riêng choavàb - 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 theoanhanh tương đương index chỉ cóa - Các truy vấn như
WHERE b = 5có 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
atrước rồi mới đếnb, nên phải đi qua mọi giá trịađể tìm giá trịb
- Vì index được sắp khóa theo
- 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 chob - Tùy nhu cầu, bạn cũng có thể chỉ dựa vào các index riêng cho
avàb
-
Dùng
text_pattern_opscho 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:SELECTROW SHARE:SELECT ... FOR UPDATEROW EXCLUSIVE:UPDATE,DELETE,INSERTSHARE UPDATE EXCLUSIVE:CREATE INDEX CONCURRENTLYSHARE:CREATE INDEX, khi không dùngCONCURRENTLYACCESS EXCLUSIVE: nhiều dạngALTER 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ờ
UPDATEtrong khiSELECT: đượcUPDATEtrong khiCREATE INDEX CONCURRENTLY: đượcSELECTtrong khiCREATE INDEX: đượcSELECTtrong khiALTER TABLE: thường phải chờALTER TABLEtrong khiSELECT: thường phải chờ
- Một số dạng
ALTER TABLEcó 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ức và hướng dẫn xung đột lock theo từng thao tác
-
ALTER TABLEchậm và hàng đợi lock- Nếu
ALTER TABLEchạy lâu, ngay cả cácSELECTđọc cùng bảng đó cũng có thể bị chặn - Nếu đó là bảng lõi như
usersmà 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 TABLEchậ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 TABLElà 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
SELECTchậm từ dashboard nội bộ cũ chạy sẵn từ trước,ALTER TABLEsẽ phải đợi
- Nếu đang có một
- 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
- Nếu
-
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
BEGINvà kết thúc bằngCOMMIT - 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
BEGINbạnUPDATEmột row cụ thể rồi bỏ đi, thìDELETEtrê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
- 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
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
backpacksmà trườngbrandtrong cột JSONBdatacó 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
JanSportkhô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
textcủa Postgres
select * from backpacks where data['brand'] = '"JanSport"'; select * from backpacks where data['brand'] = '"JanSport"'::jsonb; select * from backpacks where data->>'brand' = 'JanSport';NULLcủa SQL vànullcủa JSONB hoạt động khác nhau'null'::jsonb = 'null'::jsonblàtrue, cònNULL = NULLlàNULL
- 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
- Khi muốn tìm các hàng trong bảng
2 bình luận
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 đó.
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 CLINế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
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ứ
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
Đâ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
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
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 MySQLVí dụ, nếu có bảng người dùng trong đó
emailkhông được NULL,usernamecó 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ộtemailvớiusernamelà NULL. Vì NULL không bằng một NULL kháchttps://www.postgresql.org/docs/devel/sql-createtable.html#S...
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
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
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
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
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
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ả
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
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
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ỉ theobĐ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