Reputation: 39
I need to take row level lock for update and at the same time allow other select queries to get their intended row which is not locked.
What I observed was if I take a lock on row1, no other select queries are allowed which are searching for other rows.
I have below table schema -
CREATE TABLE lock_test(
id int NOT NULL DEFAULT unique_rowid(),
col_with_unique_index text NOT NULL,
other_col text NOT NULL,
CONSTRAINT "primary" PRIMARY KEY (id ASC),
UNIQUE INDEX unique_idx (col_with_unique_index ASC),
FAMILY "primary"(id, col_with_unique_index, other_col));
Inserted below 2 rows -
insert into lock_test(col_with_unique_index, other_col) values('val1', 'some_val');
insert into lock_test(col_with_unique_index, other_col) values('val2', 'some_val');
Opened 2 terminals -
1st terminal -
begin;
select * from lock_test where col_with_unique_index = 'val1' for update;
2nd terminal -
select * from lock_test where col_with_unique_index = 'val2';
Expected 2nd terminal to show the result for val2 but it did not(went into waiting), instead after I executed commit in 1st terminal 2nd terminal showed the result.
I tried changing my where clause from col_with_unique_index to id which is the primary key here, and this time 2nd terminal did not wait and displayed the expected result.
I'm unable to understand the behaviour here. Can I only take row level locks if I have primary key in my where clause?
Upvotes: 0
Views: 1825
Reputation: 106
I redo the test yoonghm did, found there is another interesting thing.
While terminal1 is in TRANSACTION status, I issue the command in terminal2
SELECT * FROM my_table where id=2;
It works to return the result directly.
But if I issue the command in terminal2
SELECT * FROM my_table where number = 200;
It was hold until the transaction got commit.
Rethink about serialization isolation level, I believe it is expected behavior, the reason is below:
update the my_table set number=200 where id=1
, it should change the select result, so this kind of query will be blocked as well.TL,DR: in SERIALIZATION isolation level, the row lock will be mostly like the table lock, the only probability to use it as row lock is all select update and delete on the table is based on one unique key
Upvotes: 0
Reputation: 4645
I have tested row-level locking with cockroachDB v22.1.
SELECT version();
version
----------------------------------------------------------------------------------------
CockroachDB CCL v22.1.12 (x86_64-pc-linux-gnu, built 2022/12/12 19:53:40, go1.17.11)
(1 row)
CREATE TABLE my_table (
id INT PRIMARY KEY,
number INT
);
INSERT INTO my_table(id, number)
VALUES
(1, 100),
(2, 200),
(3, 300);
BEGIN TRANSACTION;
SELECT * FROM my_table WHERE id = 1 FOR UPDATE;
-- stop here
SELECT * FROM my_table; -- It will stop here
COMMIT TRANSACTION;
The output will continue.
id | number
-----+---------
1 | 100
2 | 200
3 | 300
(3 rows)
Upvotes: 1
Reputation: 191
I tried changing my where clause from col_with_unique_index to id which is the primary key here, and this time 2nd terminal did not wait and displayed the expected result.
I'm unable to understand the behaviour here. Can I only take row level locks if I have primary key in my where clause?
I suspect what happened here is that the number of rows in the table was very small and the optimizer determined that it would be cheaper to scan the primary index than to look at the unique index. We have tuned these hueristics a bit lately to account for this contention. You can force a scan against the secondary index using: select * from lock_test@unique_idx where col_with_unique_index = 'val2';
Or you can add more data and run stats (should run automatically).
Upvotes: 1