Consider a table with fields ID, X, Y, and Z.
CREATE TABLE TABLE_NAME (
ID int NOT NULL,
X varchar(255) NOT NULL,
Y varchar(255) NOT NULL,
Z varchar(255) NOT NULL,
PRIMARY KEY (ID)
);
create index idx on TABLE_NAME (X, Y);
Suppose I have the following transaction with repeatable read isolation level -
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
BEGIN TRANSACTION;
SELECT * FROM TABLE_NAME WHERE X="A" AND Y="B";
INSERT INTO TABLE (ID, X, Y, Z) VALUES (25, "A", "B", "C");
INSERT INTO TABLE (ID, X, Y, Z) VALUES (26, "A", "B", "D");
DELETE FROM TABLE_NAME WHERE X="A" AND Y="B" AND (Z != "C" OR Z != "D")
COMMIT TRANSACTION;
Now suppose I have a normal SELECT
statement being executed outside the transaction. I want to know what happens to the SELECT
statement if -
- It executes between the
SELECT
andINSERT
of the transaction. - It executes between the first
INSERT
statement and secondINSERT
statement of the transaction. - It executes between the
INSERT
statement 2 andDELETE
statement of the transaction. - It executes between
DELETE
statement and committing the transaction.
I did go through the docs, and these are my assumptions for the scenarios -
SELECT
only obtains a shared lock, so theSELECT
statement outside the transaction is allowed to execute and sees all related rows in the table.INSERT
obtains an exclusive lock the the newly inserted row. TheSELECT
statement outside the transaction is allowed to execute but it does not see the newly inserted row.- Same as #2, allowed but it does not see both newly inserted rows.
DELETE
obtains an exclusie lock, so theSELECT
statement outside the transaction is blocked, until the executing transaction is committed.
Is my assumption correct? Specially confused about #2 and #3 here.
Another way to visualize InnoDB actions:
When a transaction starts (or a statement starts with autocommit=ON), a snapshot of the entire database is taken. The transaction can see only that snapshot while it is running. When it finishes, all changes that were made are made permanent (COMMIT) or tossed (ROLLBACK).
Now for a little detail of how such a massive 'snapshot' could be taken efficiently. Each row has a number that indicates 'when' it was created. Meanwhile, when another transaction starts a similar number is grabbed, thereby defining its snapshot. Any rows older than that number are visible in
READ COMMITTED
. All rows, even newer ones, are visible toREAD_UNCOMMITTED
.Ah, but this implies that each row is potentially a set of versions of that row. For example, an
UPDATE
that has not yet been committed has at least 2 copies of the row in what is called a "history list".I guess that
REPEATABLE_READ
grabs the number at the time theSELECT
starts, which may be later than when the transaction started?At
COMMIT
orROLLBACK
time, the history list of modified rows gets cleaned up. This happens (I think) after the client has been told that the Commit/Rollback is finished.