什么是事务
事务定义了一个服务操作序列,由服务器保证这些操作序列在多个客户并发访问和服务器出现故障情况下的原子性
事务的属性
A --redo&undo
C --undo
I --lock
D --redo
事务编程
锁的概念
lock与lath的区别
--对象:事务/线程
--保护:数据库对象/内存结构
--持续时间:长/短
--模式:表锁行锁/互斥
--死锁:有/无
InnoDB锁分析
--IS,IX,S,X
--record lock,gap lock,next-key lock
MDL锁分析
死锁原理与分析
SELECT @@GLOBAL.tx_isolation, @@tx_isolation;
SET GLOBAL tx_isolation='REPEATABLE-READ';
SET SESSION tx_isolation='SERIALIZABLE';
level:
REPEATABLE READ
| READ COMMITTED
| READ UNCOMMITTED
| SERIALIZABLE
mysql> set tx_isolation='read-committed';
Query OK, 0 rows affected (0.00 sec)
mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
1 row in set (0.00 sec)
set tx_isolation='repeatable-read';
SET GLOBAL tx_isolation='READ-COMMITTED';
SET GLOBAL tx_isolation='REPEATABLE-READ';
SELECT
r.trx_id waiting_trx_id,
r.trx_mysql_thread_id waiting_thread,
r.trx_query waiting_query,
b.trx_id blocking_trx_id,
b.trx_mysql_thread_id blocking_thread,
b.trx_query blocking_query
FROM information_schema.innodb_lock_waits w
INNER JOIN information_schema.innodb_trx b
ON b.trx_id= w.blocking_trx_id
INNER JOIN information_schema.innodb_trx r
ON r.trx_id= w.requesting_trx_id;
select * from information_schema.innodb_trx\G
performance_schema.events_statements_current
lock_wait_timeout=n
--
create table iso_t1 (id int auto_increment primary key,name1 varchar(10));
insert iso_t1 values (null,'aaa'),(null,'bbbb');
RC级别,READ-COMMITTED,
设置set autocommit=1;
session 1: select * from iso_t1;
session 2: drop table iso_t1;--成功
设置set autocommit=0;
session 1: select * from iso_t1;
session 2: drop table iso_t1;--失败,session加了metadata lock(mdl)锁模式为share_read
,session2 想加x锁,2个互斥
Waiting for table metadata lock | drop table iso_t1
RR级别
SET GLOBAL tx_isolation='REPEATABLE-READ';
设置set autocommit=1;
session 1: select * from iso_t1;
session 2: drop table iso_t1;--成功
设置set autocommit=0;
session 1: select * from iso_t1;
session 2: drop table iso_t1;--失败,Waiting for table metadata lock
1.MySQL参数autocommit生产环境设1还是0?为什么?
设置autocommit=0,不自动提交,可以rollback操作,但是每一个select后需要进行commit,
不管事务级别是RR/RC,不然会加mdl锁.
所以pro上面设置为autocommit=1.
2.MySQL参数tx_isolation生产环境上大多数是设什么值,为什么?
生产环境tx_isolation,事务隔离级别设置为已提交读'READ-COMMITTED'--RC
RC:并发高,开销低,不会产生gap锁,但是不支持binlog的statement模式,
语句级快照,每条语句,新建readview
RR:支持gap锁,事务级快照读,一个事务对应一个readview
加锁冲突严重,不支持semi-consistend read,不支持early unlock
3.与MySQL锁相关的有哪些因素?
mysql innodb锁相关
1 事务隔离级别RR/RC
2 主键/唯一索引/普通索引/没有索引
3 autocommit=1/0
--innodb的行锁是逐步获得的
--语句1,2加锁获取的记录是多行
Repeatable Read隔离级别下,id列上有一个非唯一索引,对应SQL:delete from t1 where id = 10;
首先,通过id索引定位到第一条满足查询条件的记录,加记录上的X锁,加GAP上的GAP锁,然后加主键
聚簇索引上的记录X锁,然后返回;然后读取下一条,重复进行。直至进行到第一条不满足条件的记录
[11,f],此时,不需要加记录X锁,但是仍旧需要加GAP锁,最后返回结束
--
GEN_CLUST_INDEX --innodb表无主键,默认rowid
由于表定义没有显示的索引,而InnoDB又是索引组织表,会自动创建一个索引,这里面叫index GEN_CLUST_INDEX
lock_mode X:next key(x+gap)
lock_mode X locks rec but not gap:记录锁
lock_mode x plus gap
数据锁模式
– 数据锁:仅仅锁住数据
– LOCK_IS, LOCK_S, LOCK_IX, LOCK_X
– 意向锁: LOCK_IS, LOCK_IX
? 表级锁;加记录锁前必须先加意向锁;
? 功能: 杜绝行锁与表锁相互等待
InnoDB锁-锁模式(cont.)
? 非数据锁模式
– 不锁数据,标识数据前GAP的加锁情况;非数据锁与数据锁之间不冲突
– LOCK_ORDINARY
? next key锁,同时锁住记录(数据),并且锁住记录前面的Gap
– LOCK_GAP
? Gap锁,不锁记录,仅仅记录前面的Gap
– LOCK_NOT_GAP
? 非Gap锁,锁数据,不锁Gap
– LOCK_INSERT_INTENSION
? Insert操作,若Insert的位置的下一项上已经有Gap锁,
则获取insert_intension锁,等待Gap锁释放
– LOCK_WAIT
? 当前锁不能立即获取,需要等待
--innodb锁分析
mysql> create table t2(a int,b int,key(b))engine=innodb;
Query OK, 0 rows affected (0.10 sec)
mysql> insert into t2 values(1,10),(2,10),(2,20),(3,30);
Query OK, 4 rows affected (0.04 sec)
Records: 4 Duplicates: 0 Warnings: 0
--session1
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t2 where b=20 for update;
+------+------+
| a | b |
+------+------+
| 2 | 20 |
+------+------+
1 row in set (0.02 sec)
--session2
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t2 where b=10 order by a for update;
+------+------+
| a | b |
+------+------+
| 1 | 10 |
| 2 | 10 |
+------+------+
2 rows in set (0.00 sec)
session1
mysql> update t2 set b=10 where a=10;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
session2
mysql> select * from t2 where b=10 order by a for update;
+------+------+
| a | b |
+------+------+
| 1 | 10 |
| 2 | 10 |
+------+------+
2 rows in set (0.00 sec)
--session1
1 session 1对b索引的(20)加锁;lock_rec(32)+lock_x(5)+lock_ordinary(0)
2 session 1对聚簇索引的(2,20加锁);lock_rec_not_gap(1024)+lock_rec+lock_x
3 session 1对b索引的(30)加锁;lock_gap(512)+lock_x
4 session 1对b索引上,创建两个lock实例
--session2
1 b索引上的(1,10),(2,10),加锁,lock_rec(32)+lock_x(5)+lock_ordinary(10)
2 b索引上的(2,20),加锁lock_gap(512)+lock_x
3 聚簇索引上的(1,10),(2,10)加锁,lcok_rec_not_gap(1024)+lock_rec+lock_x
4 session2 在b索引上,同样需要创建2个lock实例
5 session2 不需要等待session1的提交,(2,20)上的锁,并不冲突
InnoDB多版本-更新
测试各种更新场景下,聚簇索引记录/二级索引记录的变化
create table test (id int primary key, comment char(50)) engine=innodb;
create index test_idx on test(comment);
insert into test values (1, 'aaa');
insert into test values (2, 'bbb');
InnoDB多版本-更新(cont.)
更新主键
update test set id = 9 where id = 1;
-旧记录标识为删除
– 插入一条新纪录
– 新旧记录前项均进入回滚段
更新非主键
update test set comment = 'ccc' where id = 9;
InnoDB更新总结
– 更新主键,聚簇索引/二级索引均无法进行in place update,均会产生
两个版本
– 更新非主键,聚簇索引可以in place update;二级索引产生两个版本
– 聚簇索引记录undo,二级索引不记录undo
– 更新聚簇索引,记录旧版本会进入Rollback Segment Undo Page
– 更新二级索引,同时需要判断是否修改索引页面的MAX_TRX_ID
– 属于同一事务的undo记录,在undo page中保存逆向指针
RR vs RC
– Read Committed
? 优势
– 高并发,低锁开销: semi-consistent read
– no gap lock; early unlock
? 劣势
– 不支持statement binlog
– 语句级快照读:每条语句,新建ReadView
– Repeatable Read
? 优势
– 支持gap lock; statement binlog
– 事务级快照读:一个事务,对应一个ReadView
? 劣势
– 并发冲突高,加锁冲突更为剧烈
– 不支持semi-consistent read;不支持early unlock