Default Concurrency Control Implementation in MySQL - mysql

What is the default implementation of concurrency control in MySQL? Is it optimistic locking (multi version concurrency control), or pessimistic locking (2 phase locking)? More specifically, how does InnoDb do it?
Internally, how does mysql (with innodb) decide on the start of a transaction whether to lock the row, or rollback after a conflict?

InnoDB uses optimistic locking.
There is no locking at the start of a transaction. How would it know which rows to lock until you execute a specific query? It doesn't even know which table(s) that you will eventually need to lock rows in.
There is no need for a rollback after a lock conflict. If you do a query in one transaction that has to wait because another session holds the lock, then your query waits up to a certain number of seconds (per the config option innodb_lock_wait_timeout, default 50 seconds).
If the other session commits before the timeout, then your session stops waiting, acquires the locks it needs, and proceeds with the query.
If your wait times out before the other session commits, your query returns an error. This still does NOT rollback your transaction; previous changes you made during your transaction are still able to be committed. You can even try the query that timed out again.
Exception: in cases of deadlock, InnoDB chooses one of the transactions involved in the deadlock, and forcibly does a rollback on one of them. It tries to choose the transaction that has modified fewer rows. If the transactions are tied, then the choice is arbitrary.

Related

MySQL InnoDB volunteer transaction to be deadlock victim

Is there a mechanism in MySQL (5.6 or later) to have a transaction (or statement) to volunteer to be a victim in the case it is involved in a deadlock?
With InnoDB deadlock detection on, when a deadlock is identified, InnoDB determines which transaction to kill (to be the victim) in order to allow the other transaction(s) to proceed. There's an algorithm used to determine which transaction is the victim.
My question is whether there is any syntax that we can use in a statement that will influence the algorithm, that basically tells InnoDB "if this statement/transaction is involved in a deadlock, then pick me as the victim."
"It is a far, far better thing I do..." — TRX #8675309
The current algorithm for which transaction is killed in case of a deadlock is that the transaction that has changed fewer rows is killed. In case of a tie, the choice is made arbitrarily by InnoDB internal code; we do not know the reason for the choice.
So the only way one transaction can "volunteer" is to change fewer rows than the other transaction.

Physical lock on DB rows obtained from a JPA PESSIMISTIC lock

According to the JPA 2.1 specification...
The lock modes PESSIMISTIC_READ, PESSIMISTIC_WRITE, and
PESSIMISTIC_FORCE_INCREMENT are used to immediately obtain long-term
database locks.
I assume a pessimistic lock will always trigger a SELECT ... FOR UPDATE SQL on the database, no matter what lock-mode is used. Now three questions on that:
Is the assumption correct or are there exceptions from this rule, if correct?
Given a SELECT ... FOR UPDATE locked the rows. Locked rows cannot be updated by any other transaction except the transaction which locked it?
The lock can be released by performing a commit or rollback on the transaction. What happens with the lock if the application (and the transaction which locked the rows) suddenly terminates without doing a commit or a rollback on the transaction?
For the question 1 and 2, your assumptions are correct:
Yes - pessimistic lock generally uses SELECT ... FOR UPDATE, as most databases and JPA implementations only support this type of lock. In this case there is no difference between READ and WRITE block, and JPA specification allows it as long as both behave as WRITE locks.
Yes - locked rows cannot be modified by any other transaction. In case of WRITE lock (and most time also for READ lock - se answer for 1), locked rows cannot be read also until the lock is released. Note that other unlocked rows in the same table are free to be read and modified.
To answer also question 3:
Yes - locks are release in case of commit or rollback. However, rollback also happens automatically when an error happens, or connection is dropped, or transaction takes too long. So, when the application dies, rollback is triggered immediately. If not, it is rolled back after some timeout (usually 5 minutes).

Repeating transactions after a deadlock in InnoDB

I'm a little confused on how to handle deadlocks in innodb. Here's acommon scenario i've found online:
while (some deadlock condition)
{
try {
begin transaction
query 1
query 2 // let's assume this one failed with a deadlock
...
query N
commit
}
catch {
make sure it's a deadlock AND
rollback
}
}
Question 1: So assuming query 2 fails, shouldn't i simply reapeat that query instead of rolling back the entire transaction and only roll back after X attempts?
Question 2: Could a simple select without any additional isolation instructions (i.e. SELECT * FROM table WHERE smth=smth) ever get into a deadlock?
Answer 1:
A transaction deadlock causes InnoDB to roll back the entire transaction. Retry the whole transaction when this happens.
Answer 2:
No if the transaction isolation level is lower than SERIALIZABLE (or if run outside of a transaction):
SELECT ... FROM is a consistent read, reading a snapshot of the database and setting no locks unless the transaction isolation level is set to SERIALIZABLE.
Yes if SERIALIZABLE:
For SERIALIZABLE level, the search sets shared next-key locks on the index records it encounters.
Remember that
Normally, you must write your applications so that they are always prepared to re-issue a transaction if it gets rolled back because of a deadlock.
A SELECT may also just time-out, waiting for a lock from another transaction (e.g. a table-lock).

Mysql InnoDB - Locking scenario

I am a developer and have only fair knowledge about databases. I need to understand the transaction level locking mechanism in InnoDB.
I read that InnoDB uses row level locking? As far as I understand, it locks down a particular row within a transaction. What will happen to a select statement when a table update is going on ?
For Example, assume there is transaction and a select statement both triggered from two different processes and assume Transaction1 starts before the select statement is issued.
Transaction1 : Start
Update table_x set x = y where 1=1
Transaction1 : End
Select Query
Select x from table_x
What will happen to the select statement. Will it return values "during" Transaction1 takes place or "after" it completes? And if it can begin only after Transaction1 ends, where is Row level locking in this picture?
Am I making sense or my fundamental understanding itself is wrong? Please advise.
It depends on the Isolation level.
SERIALIZABLE
REPEATABLE READS
READ COMMITTED
READ UNCOMMITTED
Good explained on wikipedia
And the mySQL docu
It does not depend only on the locking involved, but on the isolation level, which uses locking to provide the transaction isolation as defined by ACID standards. InnoDB uses not only locking, but also multiversioning of the rows to speed up transactions.
In serializable isolation level it would use read-lock with the update, so the select will have to wait for first transaction to be completed. On lower isolation levels however the lock will be write, and selects won't be blocked. In repeatable read and read committed it will scan the rollback log to get the previous value of the record, if it is updated, and in read uncommitted in will return the current value.
The difference between table-level locking and row-level locking is when you have 2 transactions that run update query. In table-level locking, the 2nd will have to wait the first one, as the whole table is locked. In row-level locking, only the rows that match the where clause* (as well as some gaps between them, but this is another topic) will be locked, which means that different transactions can update different parts of the table without need to wait for each other.
*assuming that there is index covering the where clause
The select will not wait for the transaction to complete, instead it will return the current value of the rows (aka, before the transaction started).
If you want the select to wait for the transaction to finish you can use "LOCK IN SHARE MODE":
Select x from table_x LOCK IN SHARE MODE;
This will cause the select to wait for any row(s) that are currently lock by a transaction holding an exclusive (update/delete) lock on them.
A read performed with LOCK IN SHARE MODE reads the latest available
data and sets a shared mode lock on the rows read. A shared mode lock
prevents others from updating or deleting the row read. Also, if the
latest data belongs to a yet uncommitted transaction of another
session, we wait until that transaction ends.
http://dev.mysql.com/doc/refman/5.0/en/innodb-lock-modes.html
SELECT started from outside of a transaction will see the table as it was before transaction started. It will see updated values only after transsaction is commited.

When does InnoDB time out instead of reporting deadlock?

I have a "Lock wait timeout exceeded" error from MySQL that I can't reproduce or diagnose. I'm sure it's deadlock (as opposed to a transaction grabbing a lock then twiddling its thumbs), because my logs show that another process started at the same time, also hung, then continued when the first timed out. But normally, InnoDB detects deadlocks without timing out. So I am trying to understand why this deadlock was not detected.
Both transactions are using isolation level serializable. (I have a fair understanding of InnoDB locking in this isolation level.) There is one non-InnoDB (MyISAM) table used in the transaction, which I insert into and update. However, I don't understand how it could be involved in the deadlock, because I believe MyISAM just takes a table lock during the inserts and updates (then immediately releases it since MyISAM is not transactional), so no other lock is taken while this table lock is held.
So I'm convinced that the deadlock involves only InnoDB tables, which brings me back to the question of why it was not detected. The MySQL documentation (http://dev.mysql.com/doc/refman/5.1/en/innodb-deadlock-detection.html) implies that deadlock detection pretty much always works. The problem cases I found while searching involve things like explicit "lock table", "alter table", and "insert delayed". I'm not doing any of these things, just inserts, updates, and selects (some of my selects are "for update").
I tried to reproduce by creating one MyISAM table and a couple InnoDB tables and doing various sequences of insert and update into MyISAM, and "select for update"s in InnoDB. But every time I produced a deadlock, InnoDB reported it immediately. I could not reproduce a timeout.
Any other tips for diagnosing this? I am using mysql 5.1.49.
One tip is that you can use SHOW INNODB STATUS to, you guessed it, show the status of the InnoDB engine.
The information it returns (a big hunk of text) includes info on current table locks, and the last detected deadlock (under the heading "LATEST DETECTED DEADLOCK"), so this trick isn't that useful well after the fact, but it can help you track down a hung query while it's happening.
mysqladmin debug can also print useful lock-debugging information.
A third trick is to create a magically-named table called innodb_lock_monitor as described at http://dev.mysql.com/doc/refman/5.1/en/innodb-monitors.html which gives more detailed lock debugging.
HTH!
UPDATE:
It may not be detecting a deadlock becuase it isn't actually a deadlock, but more likely that one process is waiting for a row lock on a row that is locked by another process. From the manual for the innodb_lock_wait_timeout variable:
The timeout in seconds an InnoDB
transaction may wait for a row lock
before giving up. The default value is
50 seconds. A transaction that tries
to access a row that is locked by
another InnoDB transaction will hang
for at most this many seconds before
issuing the following error:
ERROR 1205 (HY000): Lock wait timeout
exceeded; try restarting transaction
When a lock wait timeout occurs, the
current statement is not executed. The
current transaction is not rolled
back. (Until MySQL 5.0.13 InnoDB
rolled back the entire transaction if
a lock wait timeout happened.
A deadlock occurs, for example, when two processes each need to lock rows that are locked by the other process, and no amount of waiting will resolve the conflict.
I managed to reproduce and diagnose the problem. It is a deadlock involving MyISAM and InnoDB. It appears to be an interaction between transactional InnoDB row locking and non-transactional MyISAM table locking. I've filed a bug: http://bugs.mysql.com/bug.php?id=57118. At any rate, I believe the answer to my original question is, InnoDB should always detect deadlocks, unless there is a bug in MySQL. ;-)