Thursday, September 22, 2005

MySQL to an Oracle DBA, Part IV


Today is where the rubber meets the road in the Oracle/MySQL comparison; locking and transactions. At the end, I'll give you a little Oracle fun.

As I said yesterday, the different storage engines may or may not support transactions. MyISAM tables do not support transactions while InnoDB tables do. First, I create two tables; one as InnoDB, one as MyISAM:

system@localhost:world> create table city_myisam
engine = myisam as select * from city;
Query OK, 4063 rows affected (0.78 sec)
Records: 4063 Duplicates: 0 Warnings: 0

system@localhost:world> create table city_innodb
engine = innodb as select * from city;
Query OK, 4063 rows affected (0.53 sec)
Records: 4063 Duplicates: 0 Warnings: 0

Next, I set autocommit off.

system@localhost:world> set autocommit = 0;
Query OK, 0 rows affected (0.06 sec)


Now I'll delete some rows from the tables in a transaction and see what happens:

system@localhost:world> begin;
Query OK, 0 rows affected (0.00 sec)

system@localhost:world> delete from city_myisam
where country = 'FRA';
Query OK, 40 rows affected (0.11 sec)

system@localhost:world> delete from city_innodb
where country = 'FRA';
Query OK, 40 rows affected (0.09 sec)

system@localhost:world> rollback;
Query OK, 0 rows affected, 1 warning (0.05 sec)

system@localhost:world> show warnings;
+---------+------+---------------------------------------------------------------+
| Level | Code | Message |
+---------+------+---------------------------------------------------------------+
| Warning | 1196 | Some non-transactional changed tables couldn't be rolled back |
+---------+------+---------------------------------------------------------------+
1 row in set (0.00 sec)

system@localhost:world> select count(*) from city_innodb
where country = 'FRA';
+----------+
| count(*) |
+----------+
| 40 |
+----------+
1 row in set (0.08 sec)

system@localhost:world> select count(*) from city_myisam
where country = 'FRA';
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (0.27 sec)

Aha, MySQL gives me a warning, but does it anyway. Note to self: no money transactions in MyISAM tables.

The next interesting thing was locking. The MyISAM storage engine only uses table level locks. When you update a row in a MyISAM table, you have to get an exclusive table level lock before you can update. These table level locks severly limit MyISAM scalability in a write intensive envrionment. The InnoDB engine, on the other hand, has row level locks, like Oracle.

The biggest Oracle misstatement today: Oracle will escalate to a table level lock when you delete more than 25% of the rows. Nope. In fact, I have a table with 5000 rows where the pk ranges from 1 to 5000:

SQL> l
1 select count(*),
2 min(x),
3 max(x)
4* from xyz
SQL> /

COUNT(*) MIN(X) MAX(X)
---------- ---------- ----------
5000 1 5000


In one session, I delete the first 4999 rows:

SQL> delete from xyz where x < 5000;

4999 rows deleted.


No problem. Now, from a different session I try to delete the row where x=4000.


SQL> delete from xyz where x=4000;


And, as expected, we are waiting on a lock from the first session:


SQL> l
1 select waiting_Session wt, holding_session ho, lock_type lt,
2 mode_held, mode_requested, lock_id1, lock_id2
3* from dba_waiters
SQL> /

WT HO LT MODE_HELD MODE_REQUE LOCK_ID1 LOCK_ID2
---- ---- ------------ ---------- ---------- -------- --------
24 25 Transaction Exclusive Exclusive 262148 163


But, if we try to delete the row that is not locked, it should go through with no issues.


SQL> delete from xyz where x=5000;

1 row deleted.

No problem, with no locks:

SQL> select waiting_Session wt, holding_session ho, lock_type lt,
2 mode_held, mode_requested, lock_id1, lock_id2
3 from dba_waiters
4 /

no rows selected

No comments: