Tổng hợp 161 câu hỏi phỏng vấn Database Essentials kèm đáp án chi tiết, song ngữ Việt–Anh — sắp xếp từ cơ bản đến nâng cao. Ôn luyện miễn phí, tự động lưu tiến độ.
Mẹo hiểu SELECT: SQL không chạy theo thứ tự bạn viết, mà theo thứ tự thực thi: FROM → JOIN → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT Hệ quả quan trọng: alias đặt trong SELECT (vd…
Ba lệnh cốt lõi để thay đổi dữ liệu. Vài kỹ thuật hữu ích: - Bulk INSERT (một round-trip thay vì N, nhanh hơn nhiều với lượng lớn): - RETURNING (PostgreSQL): lấy giá trị vừa sinh ra ngay, khỏi query lại — INSERT INTO users…
ER Diagram là bản vẽ schema trước khi code — như bản vẽ nhà trước khi xây, giúp tránh phải đập đi sửa lại tốn kém. Ba thành phần chính: - Entity (thực thể = bảng): vẽ hình chữ nhật. - Attribute (thuộc tính =…
CASCADE quy định "khi xoá/sửa dòng cha thì làm gì với dòng con". Các lựa chọn ON DELETE: - CASCADE: xoá cha → tự xoá luôn con (xoá user → mất hết order, comment, session). Tiện nhưng nguy hiểm nếu đặt nhầm. - SET NULL:…
Hai cách sinh khoá chính: - Auto-increment (SERIAL/BIGSERIAL): số tăng dần, nhỏ gọn (4/8 byte), index B-tree gọn, dễ debug. Nhược: lộ thông tin (đối thủ đoán được số bản ghi qua id), và không an toàn cho hệ phân tán vì cần bộ đếm…
Không phải "cái nào tốt hơn" mà là "hợp với dữ liệu nào". Chọn PostgreSQL khi: dữ liệu có quan hệ rõ, cần transaction ACID, schema ổn định, query phức tạp nhiều JOIN. PostgreSQL còn có kiểu JSONB (+ GIN index) cho phép lưu/truy vấn…
ORM là lớp trung gian ánh xạ bảng DB thành object trong code, để bạn thao tác bằng ngôn ngữ lập trình thay vì viết SQL tay. Hai phong cách: Active Record (model tự chứa logic DB — Rails, Sequelize) và Data Mapper (tách object…
Prisma là ORM thế hệ mới cho Node.js/TypeScript, theo hướng schema-first: bạn mô tả model trong schema.prisma, Prisma sinh ra mọi thứ từ đó. Điểm mạnh: - Client tự sinh, type-safe: mỗi lần chạy prisma generate tạo ra TypeScript types khớp chính xác với schema…
schema.prisma là source of truth duy nhất của Prisma, gồm ba phần: - datasource: kết nối DB. - generator: cấu hình Prisma Client. - models: định nghĩa bảng và quan hệ. Vài cú pháp hay dùng: - Quan hệ: @relation(fields: [authorId], references: [id]) cho khoá…
Sequelize là ORM "đời trước" của Node.js, lâu đời và hỗ trợ nhiều DB (MySQL, PostgreSQL, SQLite, MSSQL, MariaDB). Vài tính năng đáng biết: - Migration: sequelize-cli sinh file có up/down — mạnh nhưng dài dòng. - Hooks (sự kiện vòng đời): beforeCreate, afterCreate, beforeUpdate...…
Mongoose là ODM (Object Document Mapper) cho MongoDB — thêm schema, validation và middleware lên trên MongoDB vốn không ràng buộc cấu trúc. Vài khái niệm cốt lõi: - Middleware (hooks): chạy quanh thao tác — pre/post save, pre/post find... Dùng cho audit log, hash…
Dùng Redis làm cache khi: dữ liệu đọc nhiều hơn ghi, tính toán tốn kém (query DB phức tạp, gọi API), cần giảm tải DB, và chấp nhận dữ liệu cũ trong chốc lát. Vài chiến lược cache: - Cache-aside (phổ biến nhất): xem cache…
Drizzle là ORM TypeScript nhẹ, query builder viết gần giống SQL nhưng vẫn type-safe — hợp người đã quen SQL: Schema khai báo bằng TypeScript, tự suy ra type: pgTable('users', { id: serial('id').primaryKey(), name: text('name').notNull() }). Migration qua drizzle-kit generate / push. Khác biệt lớn…
Mô hình quan hệ tổ chức dữ liệu thành các bảng (relation): - Column (attribute): một trường có kiểu dữ liệu xác định (int, text, timestamp...). - Row (tuple): một bản ghi — một thực thể cụ thể. - Table: tập các row cùng cấu…
- Primary key (PK): định danh duy nhất mỗi hàng; không NULL, mỗi bảng chỉ một PK. Thường có index tự động. - Unique key: đảm bảo giá trị không trùng, nhưng cho phép NULL và một bảng có nhiều unique constraint. - Foreign key…
Chuẩn hoá là việc tổ chức bảng để giảm dư thừa và tránh bất thường khi thêm/sửa/xoá (update anomaly). - 1NF: mỗi ô chứa một giá trị atomic, không có nhóm lặp hay mảng trong cột; mỗi hàng là duy nhất. - 2NF: đạt 1NF…
Denormalization là việc cố ý thêm dữ liệu dư thừa (nhân bản cột, lưu giá trị đã tính sẵn) để giảm số JOIN và tăng tốc đọc. Đánh đổi: - Lợi: đọc nhanh hơn (ít JOIN/aggregate lúc query), hợp báo cáo/analytics đọc nhiều. - Hại:…
ACID là bốn đảm bảo cho transaction — nhóm thao tác chạy như một đơn vị: - Atomicity: toàn bộ thành công hoặc rollback hết, không có nửa vời. - Consistency (nhất quán): transaction đưa DB từ trạng thái hợp lệ này sang trạng thái…
Một transaction bọc nhiều câu lệnh thành một đơn vị atomic: - BEGIN (hoặc START TRANSACTION): mở transaction. - COMMIT: xác nhận, làm mọi thay đổi bền vững và hiển thị cho transaction khác. - ROLLBACK: huỷ toàn bộ thay đổi từ khi BEGIN, DB…
Index là cấu trúc dữ liệu có thứ tự giữ giá trị cột đã sắp xếp + con trỏ tới hàng thật. Loại phổ biến nhất là B-tree (cây cân bằng): - Root → branch → leaf: cây cân bằng nên mọi leaf ở cùng…
Nên index khi cột: - Xuất hiện trong WHERE, JOIN, ORDER BY, GROUP BY của truy vấn chạy thường xuyên. - Có độ chọn lọc (selectivity) cao — nhiều giá trị phân biệt (email, userid) nên index lọc bỏ được nhiều hàng. - Là foreign…
EXPLAIN cho xem kế hoạch thực thi mà planner chọn cho một truy vấn; EXPLAIN ANALYZE còn chạy thật và báo thời gian + số hàng thực tế (cẩn thận với UPDATE/DELETE trên production — nó thực sự ghi). cost chỉ là ước lượng tương…
N+1 xảy ra khi lấy một danh sách rồi lặp gọi thêm một truy vấn riêng cho từng phần tử: 1 query lấy N bản ghi cha + N query lấy dữ liệu con → N+1 lần vào DB. Thường gặp với ORM khi truy…
JOIN ghép hàng của hai bảng theo điều kiện (thường qua khoá): - INNER JOIN: chỉ giữ hàng khớp cả hai bên. Hàng không có cặp bị loại. - LEFT (OUTER) JOIN: giữ mọi hàng bảng trái, bên phải không khớp thì điền NULL. -…
- View: một truy vấn lưu sẵn dưới dạng tên gọi — không lưu dữ liệu. Mỗi lần query view, DB chạy lại câu SQL bên dưới trên dữ liệu hiện tại → luôn mới, không tốn storage, nhưng nặng nếu truy vấn phức tạp…
SQL (quan hệ): dữ liệu ở bảng, schema cố định, quan hệ qua PK/FK, ngôn ngữ SQL chuẩn hoá, đảm bảo ACID mạnh, thường scale dọc (máy mạnh hơn). Hợp dữ liệu có cấu trúc rõ và ràng buộc chặt (tài chính, đơn hàng). NoSQL:…
Bốn họ NoSQL chính, khác nhau ở mô hình dữ liệu: - Document: lưu tài liệu tự mô tả (JSON/BSON), schema linh hoạt; hợp catalog, profile, nội dung. Vd MongoDB, Couchbase. - Key-value: ánh xạ khoá → giá trị, đọc/ghi cực nhanh theo khoá; hợp…
Chọn theo mô hình truy vấn + yêu cầu nhất quán + cách scale, không theo trào lưu. Chọn SQL khi: - Dữ liệu có cấu trúc rõ, quan hệ nhiều, cần JOIN linh hoạt. - Cần giao dịch ACID và nhất quán mạnh (thanh…
Mở một kết nối DB tốn kém: bắt tay TCP, xác thực, cấp phát tài nguyên server. Nếu mỗi request lại mở rồi đóng kết nối thì độ trễ cao và DB dễ cạn kết nối. Connection pool giữ sẵn một tập kết nối mở,…
Replication là nhân bản dữ liệu ra nhiều node. Mô hình phổ biến là leader–follower (primary–replica): - Leader nhận mọi ghi (write), ghi vào WAL rồi stream thay đổi tới các follower. - Follower áp lại thay đổi, phục vụ đọc (read). Giải quyết: -…
Read replica là bản sao chỉ-đọc của DB chính, dùng để chia tải đọc khỏi node ghi. Ứng dụng gửi INSERT/UPDATE/DELETE tới primary, còn các SELECT chịu được dữ liệu hơi cũ thì định tuyến sang replica → primary rảnh hơn, hệ chịu tải đọc…
Postgres có hai kiểu lưu JSON: - json: lưu nguyên văn text, giữ nguyên khoảng trắng/thứ tự khoá/khoá trùng; mỗi lần truy vấn phải parse lại → ghi nhanh, đọc/thao tác chậm. - jsonb: lưu dạng nhị phân đã parse, bỏ khoảng trắng, không giữ…
MongoDB là document database: - Document: đơn vị dữ liệu dạng JSON (cặp field–value), có thể lồng nhau (object, array). Mỗi document có id làm khoá chính (mặc định là ObjectId). - Collection: nhóm các document — tương đương "bảng" nhưng không bắt schema cố…
Aggregation pipeline xử lý document qua một chuỗi stage, mỗi stage biến đổi luồng document rồi truyền sang stage kế — như pipeline, giống GROUP BY/JOIN/biến đổi trong SQL nhưng theo từng bước. Các stage hay dùng: - $match: lọc (như WHERE) — đặt sớm…
Cũng như DB quan hệ, MongoDB dùng B-tree index để tránh quét toàn collection (COLLSCAN). Mọi collection có sẵn index trên id. Các loại chính: - Single field: index một field. - Compound: nhiều field theo thứ tự — áp dụng leftmost prefix như SQL;…
Replica set là nhóm các node MongoDB giữ cùng dữ liệu để đảm bảo khả dụng cao: - Primary: node duy nhất nhận ghi; ghi được ghi vào oplog (operation log). - Secondary: sao chép oplog của primary và áp lại → giữ bản sao,…
MongoDB có hai cách mô hình quan hệ giữa dữ liệu: Embedding (nhúng): đặt dữ liệu con bên trong document cha (mảng/object lồng). - Ưu: đọc tất cả trong một lần truy vấn, không cần join; ghi atomic ở cấp một document. - Hợp: quan…
Cache giữ dữ liệu hay dùng ở tầng nhanh (RAM, vd Redis) → giảm tải DB, giảm độ trễ đọc, chịu tải cao hơn. Hợp dữ liệu đọc nhiều-ghi ít, tốn công tính, hoặc chấp nhận hơi cũ. Chiến lược đọc/ghi: - Cache-aside (lazy loading):…
Hai loại workload dữ liệu, tối ưu cho mục đích khác nhau: OLTP (Online Transaction Processing) — hệ giao dịch chạy ứng dụng: - Nhiều giao dịch nhỏ, nhanh: đọc/ghi vài hàng (đặt đơn, cập nhật hồ sơ). - Cần ACID, độ trễ thấp, đồng…
OFFSET/LIMIT (LIMIT 20 OFFSET 100000) buộc DB quét và bỏ đi toàn bộ hàng trước offset rồi mới trả 20 hàng → càng lật sâu càng chậm (chi phí tăng tuyến tính theo offset). Thêm rủi ro: nếu dữ liệu chèn/xoá giữa các lần lật,…
- Natural key: khoá lấy từ dữ liệu nghiệp vụ thật có sẵn ý nghĩa (email, số CMND, mã sản phẩm, countrycode). - Surrogate key: khoá nhân tạo, không mang nghĩa nghiệp vụ, sinh riêng để định danh — SERIAL/IDENTITY (số tăng), UUID, hoặc ObjectId…
- DELETE: xoá hàng theo điều kiện (DELETE FROM t WHERE ...), có thể xoá một phần. Là DML, ghi log từng hàng, chạy trong transaction (rollback được), kích hoạt trigger, chịu ràng buộc FK. Xoá nhiều hàng thì chậm và để lại dead tuple…
Hàm tổng hợp (aggregate) gộp nhiều hàng thành một giá trị: COUNT, SUM, AVG, MIN, MAX. GROUP BY chia hàng thành các nhóm theo cột, rồi hàm tổng hợp tính trên từng nhóm. Vd doanh thu theo từng khách: Quy tắc: mọi cột trong SELECT…
CTE (Common Table Expression) là truy vấn phụ đặt tên khai báo bằng WITH, dùng lại như một bảng tạm trong câu lệnh chính: So với subquery lồng trong FROM/WHERE: - CTE giúp truy vấn phức tạp dễ đọc, chia thành các bước có tên,…
- LIKE / ILIKE: khớp mẫu chuỗi đơn giản. col LIKE '%from%' với % ở đầu không dùng được B-tree index → quét toàn bảng, chậm trên dữ liệu lớn. Không hiểu ngôn ngữ: không phân biệt gốc từ, thứ hạng, dấu câu. - Full-text…
SQL được chia thành các nhóm lệnh theo mục đích: - DDL (Data Definition Language) — định nghĩa cấu trúc: CREATE, ALTER, DROP, TRUNCATE. Tác động lên schema (bảng, index, ràng buộc). - DML (Data Manipulation Language) — thao tác dữ liệu bên trong bảng:…
NULL biểu thị giá trị chưa biết / không tồn tại, khác hẳn số 0 hay chuỗi rỗng ''. Vì "chưa biết", SQL dùng logic ba giá trị: mọi so sánh cho ra TRUE, FALSE, hoặc UNKNOWN. Hệ quả thực tế: - NULL = NULL…
Các phép hợp tập hợp gộp kết quả của hai SELECT (phải cùng số cột và kiểu tương thích) theo chiều dọc: - UNION: gộp rồi loại bỏ hàng trùng → phải sắp xếp/băm để khử trùng nên tốn thêm chi phí. - UNION ALL:…
Ba kiểu chuỗi thường gặp: - CHAR(n) — độ dài cố định, luôn chiếm n ký tự (đệm khoảng trắng nếu ngắn hơn). Phần đệm là một phần của giá trị và theo nó ra tới ứng dụng — riêng MySQL cắt khoảng trắng cuối…
Ba loại quan hệ (cardinality) giữa hai thực thể và cách hiện thực bằng khoá: - Một–một (1:1): mỗi hàng bên A ứng với tối đa một hàng bên B (user ↔ hồ sơ chi tiết). Hiện thực: đặt FK có ràng buộc UNIQUE ở…
Self join là ghép một bảng với chính nó — dùng hai bí danh (alias) khác nhau cho cùng bảng như thể là hai bảng riêng. Dùng khi các hàng trong cùng một bảng có quan hệ với nhau. Ví dụ kinh điển — quan…
Subquery là truy vấn lồng bên trong truy vấn khác (ở SELECT, FROM, hoặc WHERE). - Subquery độc lập (non-correlated): chạy được một mình, không tham chiếu truy vấn ngoài. DB thường chạy một lần, lấy kết quả rồi dùng cho truy vấn ngoài. -…
Window function tính toán trên một tập hàng liên quan tới hàng hiện tại nhưng không gộp các hàng lại — mỗi hàng gốc vẫn được giữ, kèm thêm giá trị tính được. Nhận biết qua mệnh đề OVER (...). Khác GROUP BY: - GROUP…
Redis là kho dữ liệu key–value in-memory (lưu chủ yếu trong RAM) → đọc/ghi cực nhanh (thường dưới 1ms). Điểm mạnh là nó không chỉ lưu chuỗi mà là "data structure server" với nhiều kiểu dữ liệu có sẵn: - String: giá trị đơn (cache…
BASE là triết lý nhất quán của nhiều hệ NoSQL/phân tán, đặt cạnh ACID của DB quan hệ như một sự đánh đổi. BASE viết tắt: - Basically Available: hệ luôn phản hồi cho request (ưu tiên khả dụng), dù có thể là dữ liệu…
- Stored procedure: một khối code (SQL + logic thủ tục như PL/pgSQL) lưu và chạy bên trong DB, gọi theo tên (CALL proc(...)). Gói nhiều câu lệnh thành một đơn vị chạy sát dữ liệu. - Trigger: code tự động chạy khi có sự…
SELECT lấy mọi cột của bảng. Tiện lúc khám phá dữ liệu, nhưng trong code chạy thật gây nhiều vấn đề: - Truyền dữ liệu thừa: kéo cả cột không cần (blob, text lớn) → tốn băng thông, RAM, và I/O. - Chặn index-only scan:…
UPSERT = "insert nếu chưa có, update nếu đã có" trong một câu lệnh atomic. Tránh mẫu sai thường gặp "kiểm tra trước rồi ghi" (SELECT → nếu không có thì INSERT), vốn dính race condition: hai request cùng thấy "chưa có" rồi cùng INSERT…
- Hard delete: DELETE thật, xoá hẳn hàng khỏi bảng. Dữ liệu biến mất. - Soft delete: không xoá mà đánh dấu đã xoá — thêm cột deletedat TIMESTAMP (tốt hơn isdeleted BOOLEAN vì ghi lại cả thời điểm xoá); mọi truy vấn "còn sống"…
Số dấu phẩy động (float, double, real) lưu theo chuẩn nhị phân IEEE 754 → nhiều giá trị thập phân không biểu diễn chính xác được (kinh điển 0.1 + 0.2 !== 0.3). Với tiền tệ, sai số nhỏ tích luỹ qua nhiều phép cộng/nhân…
Cả hai là RDBMS mã nguồn mở phổ biến nhất, nhưng khác về kiến trúc và trọng tâm: - Kiến trúc kết nối: MySQL dùng thread cho mỗi kết nối (nhẹ RAM); PostgreSQL dùng process riêng cho mỗi kết nối (tốn RAM hơn khi kết…
Hai bài "vào tay" xuất hiện trong hầu hết vòng SQL: Lương cao thứ 2 (phải xử lý trùng lương và trường hợp không tồn tại): Dùng DENSERANK (không phải ROWNUMBER/RANK) vì lương trùng phải tính cùng một hạng, không nhảy số. Biến thể LIMIT…
SQL injection là lỗ hổng khi input của user được nối chuỗi thẳng vào câu SQL, khiến dữ liệu bị thực thi như code: Hậu quả: lộ dữ liệu (bypass đăng nhập, đọc bảng khác qua UNION SELECT), sửa/xoá dữ liệu, có khi chiếm cả…
Bài thiết kế kinh điển — lõi là cặp bảng orders + orderitems (quan hệ user–product là nhiều–nhiều qua đơn hàng): Các quyết định phải giải thích được: - unitprice snapshot: giá lưu ngay trong orderitems, KHÔNG join sang products.price — giá sản phẩm đổi…
- IN so giá trị với một danh sách hoặc kết quả subquery: x IN (SELECT ...). Hợp khi danh sách nhỏ, giá trị cụ thể. - EXISTS kiểm tra "có tồn tại ít nhất một dòng khớp không" và dừng ngay khi tìm thấy…
Cả ba là window function đánh số theo ORDER BY trong mỗi partition. Khác nhau khi gặp giá trị bằng nhau (ties): - ROWNUMBER(): đánh số liên tục duy nhất 1, 2, 3, 4... kể cả khi bằng nhau (thứ tự giữa các dòng bằng…
Các khái niệm khoá đều xoay quanh việc định danh duy nhất một dòng: - Super key: bất kỳ tập cột nào xác định duy nhất một dòng — có thể thừa (kèm thêm cột không cần thiết). - Candidate key: super key tối giản…
- DBMS (Database Management System): phần mềm quản lý dữ liệu nói chung — lưu, truy vấn, quản trị. Bao hàm mọi mô hình: file phẳng, phân cấp, mạng, quan hệ, tài liệu... - RDBMS (Relational DBMS): một loại DBMS theo mô hình quan hệ…
Các quy tắc ánh xạ cơ bản: - Thực thể (entity) → một bảng; thuộc tính → cột; khoá của thực thể → primary key. - Thuộc tính đa trị (multi-valued) → tách ra một bảng riêng nối bằng foreign key. - Quan hệ 1–1:…
Câu SELECT được viết theo thứ tự SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY, nhưng được hiểu theo logic gần như ngược lại: 1. FROM + JOIN — dựng tập dòng nguồn. 2. WHERE — lọc từng dòng thô.…
Vì WHERE được đánh giá trước SELECT, còn ORDER BY được đánh giá sau. Khi WHERE chạy, alias chưa được tạo ra. Ba cách sửa cho WHERE: Lặp lại biểu thức không làm chậm truy vấn: optimizer chỉ tính một lần. GROUP BY nằm giữa…
Khác nhau ở chỗ NULL có được đếm hay không: - COUNT() — đếm số dòng, kể cả dòng toàn NULL. - COUNT(col) — đếm số dòng có col IS NOT NULL. - COUNT(DISTINCT col) — đếm số giá trị khác nhau, bỏ NULL. Mọi…
COALESCE(a, b, c, ...) trả về đối số không NULL đầu tiên — dùng để đặt giá trị mặc định. NULLIF(a, b) trả về NULL nếu a = b, ngược lại trả về a — dùng để biến một giá trị "xấu" thành NULL. Cặp…
Vì NOT IN được khai triển thành chuỗi AND các phép khác (<), mà so sánh với NULL cho ra UNKNOWN chứ không phải TRUE/FALSE. Chỉ cần một NULL trong danh sách là toàn bộ điều kiện không bao giờ TRUE → không dòng nào…
Vì điều kiện lọc bảng bên phải bị đặt nhầm vào WHERE thay vì ON. LEFT JOIN sinh ra các dòng có cột bên phải toàn NULL; sau đó WHERE chạy và loại chính những dòng NULL đó → còn lại đúng bằng INNER JOIN.…
Vì JOIN quan hệ một–nhiều làm nhân bản dòng ở bảng cha. Mỗi lần bảng cha xuất hiện thêm một dòng thì giá trị của nó bị cộng thêm một lần. Ba cách chữa: Cách 3 chỉ cứu được COUNT, không cứu được SUM —…
Vì ORDER BY không xác định (non-deterministic) khi nhiều dòng có cùng giá trị sắp xếp. SQL không đảm bảo giữ nguyên thứ tự giữa các dòng bằng nhau, nên hai lần chạy có thể xếp chúng khác nhau — dòng ở cuối trang 1…
LAG(col, n) lấy giá trị của dòng trước trong cùng partition, LEAD(col, n) lấy dòng sau. Nhờ đó so sánh kỳ này với kỳ trước mà không cần self join. Ba điểm hay bị hỏi thêm: - Dòng đầu tiên không có dòng trước →…
Chúng cho phép tính nhiều mức tổng hợp trong một lần quét bảng, thay vì viết nhiều GROUP BY rồi UNION ALL. - GROUPING SETS ((a,b), (a), ()) — liệt kê tường minh các tổ hợp cần gom. - ROLLUP(a, b) — tổng theo cấp…
Index là một cấu trúc dữ liệu song song với bảng. Đọc nhanh vì tra cứu chỉ đi qua vài tầng B-tree thay vì quét toàn bộ bảng, nhưng mỗi thao tác ghi phải cập nhật cả bảng lẫn từng index. Chi phí ghi cụ…
Vì index lưu giá trị gốc của cột, không lưu kết quả của hàm. Khi cột bị bọc trong một hàm, database phải tính hàm đó cho từng dòng rồi mới so sánh — nghĩa là phải đọc hết bảng. Điều kiện như vậy gọi…
Thường là không, vì cột đó có cardinality thấp — số giá trị phân biệt quá ít nên selectivity kém. Nếu 90% dòng có isactive = true, đi qua index rồi nhảy về bảng đọc 90% số dòng còn chậm hơn quét tuần tự, do…
Vì B-tree sắp xếp theo thứ tự từ trái sang phải của giá trị. Với LIKE 'abc%' database biết mọi kết quả nằm liền nhau trong khoảng ['abc', 'abd') nên nhảy thẳng tới đó — đây là prefix search. Với '%abc%' thì phần đầu chuỗi…
Khi hai vế của phép so sánh khác kiểu, database phải ép một vế về kiểu kia. Nếu vế bị ép là cột, index trên cột đó trở nên vô dụng — hoàn toàn giống trường hợp bọc cột trong hàm, chỉ khác là lần…
Vấn đề nằm ở chỗ index có khớp được cả điều kiện lọc lẫn thứ tự sắp xếp hay không. Nếu chỉ khớp phần lọc, database phải gom hết dòng khớp rồi sort toàn bộ mới lấy được 20 dòng đầu — LIMIT không giúp…
Tuỳ database, và đây là chỗ hay nhầm khi chuyển giữa các hệ. PostgreSQL: B-tree có lưu NULL, nên WHERE col IS NULL dùng được index bình thường. Điều đó cũng có nghĩa index bao phủ mọi dòng của bảng, kể cả dòng NULL. Oracle:…
Transaction gom nhiều câu lệnh thành một đơn vị: hoặc tất cả có hiệu lực, hoặc không câu nào. Thiếu nó, luồng chuyển tiền có thể trừ tiền tài khoản A xong thì tiến trình dừng trước khi cộng cho B — số tiền đó…
Bốn mức chuẩn SQL, xếp từ lỏng đến chặt, mỗi mức chặn thêm một loại anomaly: Level Dirty read Non-repeatable read Phantom ------------ READ UNCOMMITTED có thể có thể có thể READ COMMITTED không có thể có thể REPEATABLE READ không không tuỳ engine SERIALIZABLE…
Lost update xảy ra với luồng đọc — sửa — ghi: hai transaction cùng đọc giá trị cũ, cùng tính toán rồi cùng ghi lại, bản ghi sau đè lên bản ghi trước nên một lần cập nhật biến mất. Ba cách chống: 1. Cập…
Một câu UPDATE/DELETE chỉ lấy row-level lock trên đúng những hàng nó thay đổi, giữ tới khi transaction kết thúc. Transaction khác vẫn SELECT được các hàng đó (đọc bản cũ qua MVCC), chỉ khi muốn sửa cùng hàng mới phải chờ. Row lock -…
Optimistic locking không giữ khoá nào cả. Bạn đọc dữ liệu kèm số version, khi ghi thì đưa version cũ vào WHERE; nếu ai đó đã sửa trước, câu lệnh cập nhật 0 hàng và bạn biết là có xung đột. Tầng ứng dụng bắt…
Đoạn code dưới đây trông gọn nhưng sai ở hai điểm: Vấn đề 1 — giữ khoá theo thời gian mạng. Khoá hàng orders chỉ nhả khi COMMIT. Cổng thanh toán chậm 5 giây thì hàng đó bị giữ 5 giây, kết nối trong pool…
SAVEPOINT đánh dấu một điểm giữa transaction để có thể quay lui một phần thay vì huỷ toàn bộ. Điểm cần nắm: - Database quan hệ không có transaction lồng nhau thật sự. Chỉ có một transaction duy nhất; BEGIN lần thứ hai không tạo…
Ba bẫy hay gặp, đều dẫn tới việc code trông như có transaction nhưng thực tế mỗi câu lệnh tự commit riêng. 1. Autocommit — không mở transaction thì mỗi câu là một transaction. Gọi hai lệnh ghi liên tiếp ngoài khối transaction() thì lệnh…
Vì đơn hàng là bản ghi lịch sử, còn bảng products là dữ liệu hiện hành, còn thay đổi. Nếu chỉ giữ productid rồi join sang products để lấy giá, thì hôm sau shop đổi giá hoặc đổi tên sản phẩm, hoá đơn cũ sẽ…
Tách sản phẩm (thông tin marketing) khỏi biến thể (thứ thực sự bán được). Áo thun là product; "áo thun đen size M" là variant, và variant mới là đơn vị có SKU, giá, tồn kho. Điểm mấu chốt: giỏ hàng và chi tiết đơn…
Tồn kho là quan hệ kho × biến thể, không phải một cột stock trên bảng sản phẩm. Tách onhand và reserved. Số bán được là onhand - reserved. Khi khách đặt hàng thì tăng reserved; khi xuất kho thực tế mới giảm onhand và…
Tách quy tắc khuyến mãi khỏi mã khỏi lần sử dụng — ba khái niệm khác nhau, gộp lại sẽ bế tắc khi cần một chiến dịch phát nhiều mã. Chặn dùng quá số lần không thể dựa vào SELECT count() rồi INSERT — hai…
Một số tiền cần ba thông tin, không phải một: giá trị, đơn vị tiền tệ, và (nếu có quy đổi) tỷ giá tại thời điểm giao dịch. Giá trị: dùng numeric/decimal hoặc số nguyên đơn vị nhỏ nhất (cent, đồng). Không dùng float/double vì…
Bảng trung gian không còn là "bảng nối kỹ thuật" nữa mà trở thành một thực thể có tên riêng. Đặt tên theo nghiệp vụ (enrollments, memberships, subscriptions) rồi cho nó cột dữ liệu như bảng bình thường. Khoá chính composite hay surrogate id? -…
bigint tự tăng nhanh và gọn hơn; UUID đổi lấy tính tạo được ở phía client và không lộ số lượng bản ghi. Chi phí của UUID ngẫu nhiên (v4): - Kích thước: 16 byte so với 8 byte. Khoá chính nằm trong mọi index…
Đây là lỗi thường gặp của soft delete: hàng đã "xoá" vẫn nằm trong bảng nên vẫn chiếm chỗ trong unique index. Cách 1 — partial unique index (gọn nhất trên PostgreSQL): chỉ áp ràng buộc cho hàng còn sống. Hàng đã xoá không nằm…
Tách làm hai việc: địa chỉ hiện tại của khách và địa chỉ dùng cho một đơn cụ thể. Đơn hàng snapshot địa chỉ vì nó mô tả nơi hàng đã thực sự được giao. Nếu chỉ join sang addresses, khách sửa địa chỉ là…
Có — đây là trường hợp denormalize hợp lý (counter cache), nhưng phải trả lời được câu hỏi kèm theo: ai chịu trách nhiệm giữ số đó đúng. Khi nào denormalize là đúng: tỷ lệ đọc/ghi rất lệch (đọc liên tục, comment thêm thưa), phép…
Bốn lựa chọn, khác nhau ở chỗ ai giữ danh sách giá trị hợp lệ. Số nguyên (0, 1, 2...) — tốn ít chỗ nhất nhưng đọc dữ liệu thô không hiểu gì, và ý nghĩa của số nằm trong code ứng dụng. Ai đó…
TTL là thời gian sống của một entry trước khi bị xoá tự động. Không có con số đúng cho mọi trường hợp — chọn theo mức cũ mà nghiệp vụ chịu được: - Dữ liệu gần như tĩnh (danh mục, cấu hình): vài giờ…
Key phải tự mô tả và chứa đủ mọi tham số ảnh hưởng tới kết quả. Quy ước phổ biến: <app:<entity:<id:<biến-thể. Nguyên tắc: - Thiếu tham số là bug: nếu response đổi theo locale, currency hay user role mà key không có, hai người dùng…
Không cache khi cái giá của một lần đọc sai lớn hơn cái lợi về tốc độ: - Quyền và trạng thái tài khoản: role, permission, trạng thái khoá/ban. Admin thu quyền của một người, cache còn 10 phút → người đó vẫn thao tác…
Hit ratio thấp nghĩa là phần lớn request vẫn xuống DB mà hệ thống vẫn phải trả chi phí duy trì cache. Thứ tự kiểm tra: 1. Key có bị phân mảnh quá mức không. Key chứa tham số không cần thiết (timestamp, request id,…
Mặc định nên xoá (invalidate), rồi để lượt đọc kế tiếp nạp lại từ DB. Vì sao xoá an toàn hơn ghi đè: khi hai request cùng cập nhật một bản ghi, thứ tự ghi vào cache có thể ngược với thứ tự commit vào…
Đây là cold cache: toàn bộ traffic đang dựa vào cache đột ngột dồn xuống DB. Xử lý theo ba lớp: 1. Không để cache biến mất cùng deploy. Cache phải nằm ở tiến trình riêng (Redis) chứ không phải in-process trong app; restart app…
Ba cách, đi từ đơn giản tới bền vững: 1. Ghi kép (dual write) trong code. Sau khi commit DB thì gọi ES index luôn. Đơn giản nhưng không atomic: DB commit xong mà ES lỗi thì hai bên lệch vĩnh viễn, không ai biết.…
Postgres full-text search (tsvector/tsquery + index GIN) là đủ cho phần lớn ứng dụng vừa: tìm trong tiêu đề/mô tả, vài trăm nghìn tới vài triệu bản ghi, kết hợp WHERE với cột quan hệ khác. Lợi thế lớn nhất: một source of truth, không…
Đây là vi phạm read-your-writes: replica bất đồng bộ luôn chậm hơn primary một khoảng (replication lag), và request đọc chạm replica trước khi WAL được áp xong. Các cách xử lý, chọn theo mức nghiêm ngặt cần thiết: 1. Định tuyến theo phiên (đơn…
Đi từ gần người dùng ra xa, mỗi tầng chặn bớt một phần lưu lượng cho tầng sau: 1. Trình duyệt — Cache-Control: max-age, cache HTTP local. Rẻ nhất: không có request nào rời máy client. 2. CDN / edge — tài nguyên tĩnh và…
CDN lưu response theo cache key. Mặc định key là method + host + đường dẫn + query string; Vary trong response bổ sung thêm header vào key. Hệ quả thực tế: - Query string thừa (?utmsource=...) tạo ra key khác nhau cho cùng một…