Mysql gap locking repetable read not working - mysql

I have table routes with 3 records with route_id is 1,2,3.
I began run transaction 1
start transaction;
select * from routes where route_id>0 LOCK IN SHARE MODE;
do sleep(5);
delete from routes where route_id=1;
select * from routes where route_id>0;
rollback ;
When trans 1 is executing, I had run tran 2
start transaction;
delete from routes where route_id=2;
rollback ;
And as the result, I saw 2 different result for 2 same select query (same transaction).
So why gap lock can not lock record with route_id > 0? Its violate repetable read.
Isolation Level is repetable read

Related

MySQL SELECT FOR UPDATE is locking whole table

MySQL Version 5.7.16
Process 1:
START TRANSACTION;
SELECT * from statistic_activity WHERE activity_id = 1 FOR UPDATE;
Process 2:
START TRANSACTION;
INSERT INTO `statistic_activity` (`activity_id`) values (2678597);
If Process 1 SELECT statement returns results, Process 2 is not blocked (as you will expect)
But If Process 1 returns empty set (no rows exists with activity_id = 1) then whole table is locked and all INSERTS are blocked until Process 1 transaction ends.
Is this expected behavior ?

Issue with inserting values that fetched from locked rows

I have two scripts bounded by transactions:
The first:
START TRANSACTION;
update product set price = 70;
SELECT SLEEP(20);
rollback;
The second:
START TRANSACTION;
insert into product_order(product_id, amount, price) select id, amount, price from product;
commit;
The second transaction has started execute when the first one is in 'sleep' state.
So, I expected that second one will have executed during sleeping of the first transaction.
Unexpectedly the second transaction is waiting until the first one goes out from sleep state.
I know that it is something connected to row locking. But I hadn't updated the rows that included into the first transaction.
My question: What is the reason of such behaviour and how I can get rid of it?
It look like the lock will be released after the end of the transaction (You can't read the data because if the transaction fails the database will have to rollback to the previous state)
Before your insert you should set the sessions transaction isolation level so it can read data that are not commited:
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
insert into product_order(product_id, amount, price) select id, amount, price from product;
COMMIT;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

Is it possible to create a Lost Update with MySQL Workbench

I want to create a Lost Update with MySQL Workbench. Therefore, I have 2 connections to my database and 2 transactions. I also changed the transaction isolation level to read uncommitted but transaction A uses the current data when the update statement starts. It never uses the data from the first select statement and with select ... for update the transaction b is blocked.
Transaction A (starts first):
Start transaction;
SELECT * FROM table;
Select sleep(10); -- <- Transaction B executes in this 10 seconds
UPDATE table SET Number = Number + 10 WHERE FirstName = "Name1";
COMMIT;
Transaction B:
Start transaction;
UPDATE table SET Number = Number - 5 WHERE FirstName = "Name1";
COMMIT;
Is it possible to create this failure with MySQL Workbench. What´s wrong with my code?
Thanks for your help
The update in A work with data after the sleep is executed. Select before does nothing in the transaction.

Row Level Locking in Mysql

I have 5 rows in a table (1 to 5). I want row 2 lock for some update and in the meanwhile if someone tries to update row 4, then he should able to update.
I am trying this with code below, but I feel its placing lock on table level rather than row level.
------ session 1
START TRANSACTION;
SELECT * FROM test WHERE t=1 FOR UPDATE;
UPDATE test SET NAME='irfandd' WHERE t=2;
COMMIT;
----- session 2 (which is being blocked)
START TRANSACTION;
UPDATE test SET NAME='irfandd' WHERE t=4;
COMMIT;
Instead of FOR UPDATE use LOCK IN SHARE MODE. FOR UPDATE prevents other transactions to read the row as well. LOCK IN SHARE MODE allows read, but prevents updating.
Reference: MySQL Manual
------ session 1
START TRANSACTION;
SELECT * FROM test WHERE t=1 LOCK IN SHARE MODE;
UPDATE test SET NAME='irfandd' WHERE t=2;
COMMIT;
----- session 2 (which is not being blocked anymore :) )
START TRANSACTION;
UPDATE test SET NAME='irfandd' WHERE t=4;
COMMIT;
Update:
Realizing that the table has no index on t, I have the following explanation:
First, transaction T1 locks the row 1 in SELECT * FROM test WHERE t=1 FOR UPDATE
Next, transaction T2 tries to execute UPDATE test SET NAME='irfandd' WHERE t=4. To find out which row(s) are affected, it needs to scan all rows, including row 1. But that is locked, so T2 must wait until T1 finishes.
If there is any kind of index, the WHERE t=4 can use the index to decide if row 1 contains t=4 or not, so no need to wait.
Option 1: add an index on test.t so your update can use it.
Option 2: use LOCK IN SHARE MODE, which is intended for putting a read lock only.
Unfortunately this option creates a deadlock. Interestingly, T2 transaction executes (updating row 4), and T1 fails (updating row 2). It seems that T1 read-locks row 4 also, and since T2 modifies it, T1 fails because of the transaction isolation level (REPEATABLE READ by default). The final solution would be playing with Transaction Isolation Levels, using READ UNCOMMITTED or READ COMMITTED transaction levels.
The simplest is Option 1, IMHO, but it's up to your possibilities.
I found below option is more appropriate i generate 40000 numbers from concurrent session on the same time. I didnt found any duplicate number. Without below command i generate 10000 numbers and found 5 duplicate numbers.
START TRANSACTION
SELECT * FROM test WHERE t=1 FOR UPDATE;
UPDATE test SET NAME='irfandd' WHERE t=2;
COMMIT;

NHibernate query deadlock in case multiple connection

I have next transaction:
Desc d = new Desc();
d.Descr = "new";
_sess.Transaction.Begin();
_sess.SaveOrUpdate(d);
var desc = _sess.CreateCriteria(typeof(Desc)).List<Desc>();
_sess.Transaction.Commit();
This transaction performs next query:
BEGIN TRANSACTION
INSERT
SELECT
COMMIT TRANSACTION
When I perform this code in two processes I have deadlock, because
1 Process
Perform INSERT and lock Key
2 Process
Perform INSERT and lock key
1 Process wants to perform SELECT and passes in TIMEOUT STATE
2 Process wants to perform SELECT and passes in TIMEOUT STATE
result: deadlock
BD: MS SQL Server 2008 R2
2 questions:
How do me set UPDATE LOCK on All tables what included in transaction
If I use this code:
Desc d = new Desc();
d.Descr = "new";
_sess.Transaction.Begin(IsolationLevel.Serializable);
_sess.SaveOrUpdate(d);
var desc = _sess.CreateCriteria(typeof(Desc)).List();
_sess.Transaction.Commit();
Nothing changes.
What does IsolationLevel.Serializable do ?
UPDATE:
I need to get following:
USE Test
BEGIN TRANSACTION
SELECT TOP 1 Id FROM [Desc] (UPDLOCK)
INSERT INTO [Desc] (Descr) VALUES ('33333')
SELECT * FROM [Desc]
COMMIT TRANSACTION
How do me perform with help NHibernate following:
SELECT TOP 1 Id FROM [Desc] (UPDLOCK)
?
I would change the transaction isolation level to snapshot. This avoids locks when reading data, allows much more concurrency and particularly no deadlocks in read-only transactions.
The reason for the deadlock is following: insert do not conflict with each other. They lock the newly inserted row. The query however is locked out, because it tries to read the newly inserted row from the other transaction. So you get two queries both waiting for the other transaction to complete, which is a deadlock. With isolation level snapshot, the query doesn't care about non committed row at all. Instead of waiting for locks to be released, it only "sees" rows that had been committed. This avoids deadlocks in queries.