15.7.2.4 & NBSP; Khóa đọc
Nếu bạn truy vấn dữ liệu và sau đó chèn hoặc cập nhật dữ liệu liên quan trong cùng một giao dịch, câu lệnh SELECT
thông thường không bảo vệ đủ. Các giao dịch khác có thể cập nhật hoặc xóa cùng một hàng bạn vừa truy vấn. InnoDB
hỗ trợ hai loại bài đọc khóa cung cấp thêm sự an toàn:
________số 8
Đặt khóa chế độ chia sẻ trên bất kỳ hàng nào được đọc. Các phiên khác có thể đọc các hàng, nhưng không thể sửa đổi chúng cho đến khi giao dịch của bạn cam kết. Nếu bất kỳ hàng nào trong số các hàng này được thay đổi bởi một giao dịch khác chưa cam kết, truy vấn của bạn sẽ đợi cho đến khi giao dịch đó kết thúc và sau đó sử dụng các giá trị mới nhất.
Ghi chú
SELECT ... FOR SHARE
là một sự thay thế cho
0, nhưngSELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
1 vẫn có sẵn để tương thích ngược. Các tuyên bố là tương đương. Tuy nhiên,SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
2 hỗ trợ các tùy chọnSELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
3,SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
4 vàSELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
5. Xem khóa Đọc đồng thời với Nowait và bỏ qua khóa.SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
Trước MySQL 8.0.22,
SELECT ... FOR SHARE
yêu cầu đặc quyềnSELECT
và ít nhất một trong các đặc quyền
8,SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
9 hoặcSELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
0. Từ MySQL 8.0.22, chỉ cần đặc quyềnSELECT * FROM parent WHERE NAME = 'Jones' FOR SHARE;
SELECT
.Từ MySQL 8.0.22, các câu lệnh
SELECT ... FOR SHARE
không có được khóa đọc trên bảng tài trợ MySQL. Để biết thêm thông tin, xem đồng thời bảng cấp.
3SELECT * FROM parent WHERE NAME = 'Jones' FOR SHARE;
Đối với chỉ mục ghi lại các cuộc gặp gỡ tìm kiếm, khóa các hàng và bất kỳ mục chỉ mục liên quan nào, giống như khi bạn đã phát hành câu lệnh
0 cho các hàng đó. Các giao dịch khác bị chặn cập nhật các hàng đó, không thực hiệnSELECT * FROM parent WHERE NAME = 'Jones' FOR SHARE;
SELECT ... FOR SHARE
hoặc đọc dữ liệu trong một số mức cách ly một số giao dịch. Các lần đọc nhất quán bỏ qua bất kỳ khóa được đặt trên các bản ghi tồn tại trong chế độ xem đọc. .
6 yêu cầu đặc quyềnSELECT * FROM parent WHERE NAME = 'Jones' FOR SHARE;
SELECT
và ít nhất một trong các đặc quyền
8,SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
9 hoặcSELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
0.SELECT * FROM parent WHERE NAME = 'Jones' FOR SHARE;
Các mệnh đề này chủ yếu hữu ích khi xử lý dữ liệu có cấu trúc cây hoặc có cấu trúc đồ thị, trong một bảng hoặc phân chia trên nhiều bảng. Bạn đi qua các cạnh hoặc nhánh cây từ nơi này sang nơi khác, trong khi có quyền quay lại và thay đổi bất kỳ giá trị nào của con trỏ này.“pointer” values.
Tất cả các khóa được đặt bởi các truy vấn
SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
2 và SELECT counter_field FROM child_codes FOR UPDATE;
UPDATE child_codes SET counter_field = counter_field + 1;
2 được phát hành khi giao dịch được thực hiện hoặc quay lại.Ghi chú
SELECT ... FOR SHARE
là một sự thay thế cho
SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
0, nhưng SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
1 vẫn có sẵn để tương thích ngược. Các tuyên bố là tương đương. Tuy nhiên, SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
2 hỗ trợ các tùy chọn SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
3, SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
4 và SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
5. Xem khóa Đọc đồng thời với Nowait và bỏ qua khóa. Trước MySQL 8.0.22, SELECT ... FOR
SHARE
yêu cầu đặc quyền SELECT
và ít nhất một trong các đặc quyền
SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
8, SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
9 hoặc SELECT * FROM parent WHERE NAME = 'Jones' FOR SHARE;
0. Từ MySQL 8.0.22, chỉ cần đặc quyền SELECT
.SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2] FOR UPDATE;
Từ MySQL 8.0.22, các câu lệnh SELECT ... FOR SHARE
không có được khóa đọc trên bảng tài trợ MySQL. Để biết thêm thông tin, xem đồng thời bảng cấp.
SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
SELECT * FROM parent WHERE NAME = 'Jones' FOR SHARE;
3Đối với chỉ mục ghi lại các cuộc gặp gỡ tìm kiếm, khóa các hàng và bất kỳ mục chỉ mục liên quan nào, giống như khi bạn đã phát hành câu lệnh
SELECT * FROM parent WHERE NAME = 'Jones' FOR SHARE;
0 cho các hàng đó. Các giao dịch khác bị chặn cập nhật các hàng đó, không thực hiện SELECT ... FOR SHARE
hoặc đọc dữ liệu trong một số mức cách ly một số giao dịch. Các lần đọc nhất quán bỏ qua bất kỳ khóa được đặt trên các bản ghi tồn tại trong chế độ xem đọc. .
SELECT * FROM parent WHERE NAME = 'Jones' FOR SHARE;
6 yêu cầu đặc quyền SELECT
và ít nhất một trong các đặc quyền SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
8, SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
9 hoặc SELECT * FROM parent WHERE NAME = 'Jones' FOR SHARE;
0.Các mệnh đề này chủ yếu hữu ích khi xử lý dữ liệu có cấu trúc cây hoặc có cấu trúc đồ thị, trong một bảng hoặc phân chia trên nhiều bảng. Bạn đi qua các cạnh hoặc nhánh cây từ nơi này sang nơi khác, trong khi có quyền quay lại và thay đổi bất kỳ giá trị nào của con trỏ này.
SELECT * FROM parent WHERE NAME = 'Jones' FOR SHARE;
Tất cả các khóa được đặt bởi các truy vấn
SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
2 và SELECT counter_field FROM child_codes FOR UPDATE;
UPDATE child_codes SET counter_field = counter_field + 1;
2 được phát hành khi giao dịch được thực hiện hoặc quay lại.Khóa đọc chỉ có thể khi AutoCommit bị vô hiệu hóa [bằng cách bắt đầu giao dịch với
SELECT counter_field FROM child_codes FOR UPDATE;
UPDATE child_codes SET counter_field = counter_field + 1;
3 hoặc bằng cách đặt SELECT counter_field FROM child_codes FOR UPDATE;
UPDATE child_codes SET counter_field = counter_field + 1;
4 thành 0.Một mệnh đề đọc khóa trong một câu lệnh bên ngoài không khóa các hàng của bảng trong một trình điều khiển con lồng nhau trừ khi một điều khoản đọc khóa cũng được chỉ định trong trình điều khiển con. Ví dụ: câu lệnh sau không khóa các hàng trong bảng
SELECT counter_field FROM child_codes FOR UPDATE;
UPDATE child_codes SET counter_field = counter_field + 1;
5.Để khóa các hàng trong bảng
SELECT counter_field FROM child_codes FOR UPDATE;
UPDATE child_codes SET counter_field = counter_field + 1;
5, thêm một mệnh đề đọc khóa vào trình điều khiển con:SELECT counter_field FROM child_codes FOR UPDATE;
UPDATE child_codes SET counter_field = counter_field + 1;
Một
# Session 1:
mysql> CREATE TABLE t [i INT, PRIMARY KEY [i]] ENGINE = InnoDB;
mysql> INSERT INTO t [i] VALUES[1],[2],[3];
mysql> START TRANSACTION;
mysql> SELECT * FROM t WHERE i = 2 FOR UPDATE;
+---+
| i |
+---+
| 2 |
+---+
# Session 2:
mysql> START TRANSACTION;
mysql> SELECT * FROM t WHERE i = 2 FOR UPDATE NOWAIT;
ERROR 3572 [HY000]: Do not wait for lock.
# Session 3:
mysql> START TRANSACTION;
mysql> SELECT * FROM t FOR UPDATE SKIP LOCKED;
+---+
| i |
+---+
| 1 |
| 3 |
+---+
4 đọc dữ liệu mới nhất có sẵn, đặt khóa độc quyền trên mỗi hàng mà nó đọc. Do đó, nó đặt cùng một khóa mà SQL SELECT * FROM parent WHERE NAME = 'Jones' FOR SHARE;
0 đã tìm kiếm sẽ đặt trên các hàng.Mô tả trước đó chỉ là một ví dụ về cách thức hoạt động của
# Session 1:
mysql> CREATE TABLE t [i INT, PRIMARY KEY [i]] ENGINE = InnoDB;
mysql> INSERT INTO t [i] VALUES[1],[2],[3];
mysql> START TRANSACTION;
mysql> SELECT * FROM t WHERE i = 2 FOR UPDATE;
+---+
| i |
+---+
| 2 |
+---+
# Session 2:
mysql> START TRANSACTION;
mysql> SELECT * FROM t WHERE i = 2 FOR UPDATE NOWAIT;
ERROR 3572 [HY000]: Do not wait for lock.
# Session 3:
mysql> START TRANSACTION;
mysql> SELECT * FROM t FOR UPDATE SKIP LOCKED;
+---+
| i |
+---+
| 1 |
| 3 |
+---+
4. Trong MySQL, nhiệm vụ cụ thể là tạo một mã định danh duy nhất thực sự có thể được thực hiện chỉ bằng một quyền truy cập duy nhất vào bảng:UPDATE child_codes SET counter_field = LAST_INSERT_ID[counter_field + 1];
SELECT LAST_INSERT_ID[];
Câu lệnh SELECT
chỉ đơn thuần lấy thông tin định danh [cụ thể cho kết nối hiện tại]. Nó không truy cập bất kỳ bảng.
Nếu một hàng bị khóa bởi một giao dịch, giao dịch
# Session 1:
mysql> CREATE TABLE t [i INT, PRIMARY KEY [i]] ENGINE = InnoDB;
mysql> INSERT INTO t [i] VALUES[1],[2],[3];
mysql> START TRANSACTION;
mysql> SELECT * FROM t WHERE i = 2 FOR UPDATE;
+---+
| i |
+---+
| 2 |
+---+
# Session 2:
mysql> START TRANSACTION;
mysql> SELECT * FROM t WHERE i = 2 FOR UPDATE NOWAIT;
ERROR 3572 [HY000]: Do not wait for lock.
# Session 3:
mysql> START TRANSACTION;
mysql> SELECT * FROM t FOR UPDATE SKIP LOCKED;
+---+
| i |
+---+
| 1 |
| 3 |
+---+
4 hoặc SELECT ... FOR SHARE
yêu cầu cùng một hàng bị khóa phải đợi cho đến khi giao dịch chặn phát hành khóa hàng. Hành vi này ngăn các giao dịch cập nhật hoặc xóa các hàng được truy vấn để cập nhật bởi các giao dịch khác. Tuy nhiên, việc chờ khóa hàng được phát hành là không cần thiết nếu bạn muốn truy vấn quay lại ngay lập tức khi hàng được yêu cầu bị khóa hoặc nếu loại trừ các hàng bị khóa khỏi bộ kết quả được chấp nhận.Để tránh chờ các giao dịch khác phát hành khóa hàng, các tùy chọn
SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
4 và SELECT
1 có thể được sử dụng với các câu lệnh đọc # Session 1:
mysql> CREATE TABLE t [i INT, PRIMARY KEY [i]] ENGINE = InnoDB;
mysql> INSERT INTO t [i] VALUES[1],[2],[3];
mysql> START TRANSACTION;
mysql> SELECT * FROM t WHERE i = 2 FOR UPDATE;
+---+
| i |
+---+
| 2 |
+---+
# Session 2:
mysql> START TRANSACTION;
mysql> SELECT * FROM t WHERE i = 2 FOR UPDATE NOWAIT;
ERROR 3572 [HY000]: Do not wait for lock.
# Session 3:
mysql> START TRANSACTION;
mysql> SELECT * FROM t FOR UPDATE SKIP LOCKED;
+---+
| i |
+---+
| 1 |
| 3 |
+---+
4 hoặc SELECT ... FOR SHARE
.
4SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
Một lần đọc khóa sử dụng
4 không bao giờ chờ để có được một khóa hàng. Truy vấn thực thi ngay lập tức, không bị lỗi nếu hàng được yêu cầu bị khóa.SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
SELECT
1Một lần đọc khóa sử dụng
SELECT
1 không bao giờ chờ để có được một khóa hàng. Truy vấn thực thi ngay lập tức, loại bỏ các hàng bị khóa khỏi tập kết quả.Ghi chú
Các truy vấn bỏ qua các hàng bị khóa trả về một cái nhìn không nhất quán của dữ liệu. Do đó,
SELECT
1 không phù hợp với công việc giao dịch nói chung. Tuy nhiên, nó có thể được sử dụng để tránh sự tranh chấp khóa khi nhiều phiên truy cập vào cùng một bảng giống như hàng đợi.
SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
4 và SELECT
1 chỉ áp dụng cho các khóa cấp hàng.Các tuyên bố sử dụng
SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
4 hoặc InnoDB
2 không an toàn cho sao chép dựa trên câu lệnh.Ví dụ sau đây cho thấy
SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
4 và SELECT
1. Phần 1 bắt đầu một giao dịch khóa một hàng trên một bản ghi duy nhất. Phần 2 thử một khóa đọc trên cùng một bản ghi bằng tùy chọn SELECT * FROM t1 WHERE c1 = [SELECT c1 FROM t2 FOR UPDATE] FOR UPDATE;
4. Bởi vì hàng được yêu cầu bị khóa bởi Phần 1, việc đọc khóa trả về ngay lập tức với lỗi. Trong Phần 3, khóa đọc với InnoDB
2 trả về các hàng được yêu cầu ngoại trừ hàng bị khóa trong Phần 1.# Session 1:
mysql> CREATE TABLE t [i INT, PRIMARY KEY [i]] ENGINE = InnoDB;
mysql> INSERT INTO t [i] VALUES[1],[2],[3];
mysql> START TRANSACTION;
mysql> SELECT * FROM t WHERE i = 2 FOR UPDATE;
+---+
| i |
+---+
| 2 |
+---+
# Session 2:
mysql> START TRANSACTION;
mysql> SELECT * FROM t WHERE i = 2 FOR UPDATE NOWAIT;
ERROR 3572 [HY000]: Do not wait for lock.
# Session 3:
mysql> START TRANSACTION;
mysql> SELECT * FROM t FOR UPDATE SKIP LOCKED;
+---+
| i |
+---+
| 1 |
| 3 |
+---+