4. ATOMICITY
ā¤ requires that each transaction
be "all or nothingā
ā¤ if one part of the transaction
fails, then the entire
transaction fails, and the
database state is left
unchanged
6. ISOLATION
ā¤ ensures that the concurrent
execution of transactions
results in a system state that
would be obtained if
transactions were executed
sequentially, i.e., one after the
other.Ā
7. DURABILITY
ā¤ ensures that once a
transaction has been
committed, it will remain so,
even in the event of power
loss,Ā crashes, or errors
9. TRANSACTION ISOLATION LEVELS:
ā¤ read uncommitted
ā¤ Transaction B will seeĀ allĀ the mutations that transaction A makes
ā¤ read committed (default in PostgreSQL)
ā¤ Transaction B will seeĀ all the mutations that transaction A commit
ā¤ repeatable read (default in MySQL)
ā¤ Transaction B will not seeĀ any mutations that transaction A commit
ā¤ serializable(the āparanoidā level)
ā¤ Even selects in transaction B will be locked by the mutations that transaction A makes
10. READ UNCOMMITTED
> start transaction;
> select * from test; (val = 8)
> update test set val = val + 1; (val = 9)
> rollback;
> set session transaction isolation level read
uncommitted;
> start transaction;
> select * from test; (val = 8)āØ
> select * from test; (val = 9, dirty read)
> select * from test; (val = 8)
TRANSACTION A: TRANSACTION B:
> select * from test;
+----+-----+
| id | val |
+----+-----+
| 1 | 8 |
+----+-----+
11. READ COMMITTED
> start transaction;
> select * from test; (val = 8)
> update test set val = val + 1; (val = 9)
> commit;
> set session transaction isolation level read
committed;
> start transaction;
> select * from test; (val = 8)āØ
> select * from test; (val = 8, no dirty read)
> select * from test; (val = 9, committed read)
TRANSACTION A: TRANSACTION B:
> select * from test;
+----+-----+
| id | val |
+----+-----+
| 1 | 8 |
+----+-----+
12. REPEATABLE READ - CASE 1
> start transaction;
> select * from test; (val = 8)
> update test set val = val + 1; (val = 9)
> commit;
> set session transaction isolation level
repeatable read;
> start transaction;
> select * from test; (val = 8)āØ
> select * from test; (val = 8)
> select * from test; (val = 8, repeatable read!)
> commit;
> select * from test; (val = 9)
TRANSACTION A: TRANSACTION B:
> select * from test;
+----+-----+
| id | val |
+----+-----+
| 1 | 8 |
+----+-----+
13. REPEATABLE READ - CASE 2
> start transaction;
> select * from test; (val = 8)
> update test set val = val + 1; (val = 9)
> commit;
> set session transaction isolation level
repeatable read;
> start transaction;
> select * from test; (val = 8)āØ
> select * from test; (val = 8, repeatable read!)
> update test set val = val + 1; (val = 10)
> commit;
TRANSACTION A: TRANSACTION B:
> select * from test;
+----+-----+
| id | val |
+----+-----+
| 1 | 8 |
+----+-----+
14. REPEATABLE READ - CASE 3
> start transaction;
> select * from test; (val = 8)
> into into test values (2, 9);
> commit;
> set session transaction isolation level
repeatable read;
> start transaction;
> select * from test; (val = 8)āØ
> select * from test; (val = 8, repeatable read!)
> update test set val = val + 1; (val = 10, 9)
> Query OK, 2 rows affected
TRANSACTION A: TRANSACTION B:
> select * from test;
+----+-----+
| id | val |
+----+-----+
| 1 | 8 |
+----+-----+
15. SERIALIZABLE - CASE 1
> start transaction;
> select * from test; (val = 8)
> update test set val = val + 1; (val = 9)
> commit;
> set session transaction isolation level
serializable;
> start transaction;
> select * from test; āØ
> LOCKED, NO OUTPUT
> (val = 9)
TRANSACTION A: TRANSACTION B:
> select * from test;
+----+-----+
| id | val |
+----+-----+
| 1 | 8 |
+----+-----+
16. SERIALIZABLE - CASE 2
> start transaction;
> select * from test; (val = 8)
> update test set val = val + 1; (val = 9)
> LOCKED, NO OUTPUT
> Query OK, 1 row updated!
> set session transaction isolation level
serializable;
> start transaction;
> select * from test; (val = 8)āØ
> commit;
TRANSACTION A: TRANSACTION B:
> select * from test;
+----+-----+
| id | val |
+----+-----+
| 1 | 8 |
+----+-----+
17. āSuccess isn't always about greatness.
It's about consistency. Consistent
hard work leads to success. Greatness
will come.
Dwayne Johnson
18. LOCK MODES: SHARED, EXCLUSIVE
ā¤ SHARED
ā¤ [row level] allow multiple users to read data, but do not allow any users to
change that data
ā¤ EXCLUSIVE
ā¤ [row/table level] allows only one user/connection to update a particular piece
of data (insert, update, and delete)
20. FOREIGN KEYS - SPECIAL CASE
ā¤ during insert shared locks will be added to parent rows from
all foreign keys
ā¤ during update shared locks will be added to parent rows from
only foreign keys being updated (mysql is smart enough)
21. SHARED LOCK
> start transaction;
> insert into role values (2, 1, āuserā);
> commit;
> start transaction;
> insert into role values (3, 1, āguestā);
> commit;
TRANSACTION A: TRANSACTION B:
Transaction A set exclusive lock to row ID 2 in table role and shared lock is set to row ID 1
in table user. Transaction B set exclusive lock to row ID 3 in table role and shared lock to
row ID 1 in table user. Summary: two shared locks were set on the same row with ID 1 in
table user.
> select * from user;
+----+--------+
| id | name |
+----+--------+
| 1 | test |
+----+--------+
> select * from role;+----+----------+-------+| id | user_id | name |+----+----------+-------+| 1 | 1 | admin |+----+----------+-------+
22. EXCLUSIVE LOCK
> start transaction;
> update user set name = ātest editā;
> commit;
> start transaction;
> update user set name = ātest 2 editā;āØ
> LOCKED, NO OUTPUT
> 1 rows updated;
> commit;
TRANSACTION A: TRANSACTION B:
Transaction A set exclusive lock on row ID 1 in table User. Transaction B try to set exclusive
lock on the same row in table User and need to wait till transaction A is committed and
exclusive lock will be released.
> select * from user;
+----+--------+
| id | name |
+----+--------+
| 1 | test |
+----+--------+
> select * from role;+----+----------+-------+| id | user_id | name |+----+----------+-------+| 1 | 1 | admin |+----+----------+-------+
24. DEAD LOCK
> start transaction;
> insert into role values (2, 1, āuserā);
> update user set name = ātest editā;
> LOCKED, NO OUTPUT
> start transaction;
> insert into role values (3, 1, āguestā);
> update user set name = ātest editā;āØ
> DEADLOCK! ROLLBACK!
TRANSACTION A: TRANSACTION B:
> select * from user;
+----+--------+
| id | name |
+----+--------+
| 1 | test |
+----+--------+
> select * from role;+----+----------+-------+| id | user_id | name |+----+----------+-------+| 1 | 1 | admin |+----+----------+-------+
Transaction A set exclusive lock on row ID 2 in table Role and shared lock on row ID 1 in
table User. Transaction B do the same (exclusive lock ID 3 in table Role, shared lock ID 1 in
table User). Transaction A try to set exclusive lock on row ID 1 in table User and need to
wait till Transaction B will release shared lock. Transaction B try to set also exclusive lock
on row ID 1 in table User - mysql engine detects DEAD Lock and rollback transaction B.
25. HOW TO AVOID DEAD LOCKS
ā¤ Use UPDATEs before INSERTs
ā¤ Use SELECT ā¦ FOR UPDATE (it will set exclusive lock on all
selected rows)
ā¤ This will impact on performance - all other transaction will
wait till exclusive lock will be released
ā¤ Change isolation level to serializable
ā¤ Consider removing foreign keys (soft deletable will keep your
database consistent)
ā¤ Attention! There are always some raw removes!! If not
business will ļ¬nd the use case for that!!
26. HOW TO ANALYZE DEAD LOCKS
ā¤ mysql> SHOW ENGINE INNODB STATUS;
ā¤ you must be logged to mysql as a root user
------------------------
LATEST DETECTED DEADLOCK
------------------------
2017-10-13 08:30:08 0x7f7b03396700
*** (1) TRANSACTION:
TRANSACTION 1809, ACTIVE 35 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 5 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 6, OS thread handle 140166312064768, query id 46 localhost test updating
update user set name = 'artur'
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 25 page no 3 n bits 72 index PRIMARY of table `test`.`user` trx id 1809 lock_mode X waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 1; hex 31; asc 1;;
1: len 6; hex 00000000070e; asc ;;
2: len 7; hex ae000001220110; asc " ;;
3: len 5; hex 726166616c; asc rafal;;
*** (2) TRANSACTION:
TRANSACTION 1814, ACTIVE 21 sec starting index read
mysql tables in use 1, locked 1
5 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 7, OS thread handle 140166311798528, query id 47 localhost test updating
update user set name = 'bogdan'
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 25 page no 3 n bits 72 index PRIMARY of table `test`.`user` trx id 1814 lock mode S locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 1; hex 31; asc 1;;
1: len 6; hex 00000000070e; asc ;;
2: len 7; hex ae000001220110; asc " ;;
3: len 5; hex 726166616c; asc rafal;;
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 25 page no 3 n bits 72 index PRIMARY of table `test`.`user` trx id 1814 lock_mode X waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
0: len 1; hex 31; asc 1;;
1: len 6; hex 00000000070e; asc ;;
2: len 7; hex ae000001220110; asc " ;;
3: len 5; hex 726166616c; asc rafal;;
27. HOW TO READ LOGS
ā¤ keep in mind that the order in logs is diļ¬erent than in mysql
[259] [2017-08-31 16:20:56] doctrine.DEBUG: "START TRANSACTION" []
[259] [2017-08-31 16:20:56] doctrine.DEBUG: INSERT INTO sales_order_activity (actor,
target, title, published, changeset, object, verb, target_id, object_iā¦
[263] [2017-08-31 16:20:56] doctrine.DEBUG: "START TRANSACTION" []
[259] [2017-08-31 16:20:56] doctrine.DEBUG: UPDATE sales_orders SET cached_total = ?,
updated_at = ? WHERE id = ? [875,"2017-08-31 16:20:56ā,ād72a1d81-f3a4ā¦
[263] [2017-08-31 16:20:56] doctrine.DEBUG: INSERT INTO sales_order_activity (actor,
target, title, published, changeset, object, verb, target_id, object_iā¦
[263] [2017-08-31 16:20:56] doctrine.DEBUG: UPDATE sales_orders SET cached_total = ?,
updated_at = ? WHERE id = ? [875,"2017-08-31 16:20:56ā,ād72a1d81-f3a4ā¦.
[263] [2017-08-31 16:20:56] request.CRITICAL: Uncaught PHP Exception
DoctrineDBALDBALException: "An exception occurred while executing 'UPDATE
sales_ordeā¦
28. ā¤ Thanks for watching!
ā¤ RafaÅ KsiÄ Å¼ek, IT Leader