transaction 1 |
transaction 2 |
start transaction |
start transaction |
update user_product set click_count = 1 where id = 1; |
|
|
update user_product set click_count = 1 where id = 1; |
|
阻塞 |
commit |
|
更新成功 |
result1和result2读出来的结果一样
transaction 1 |
transaction 2 |
start transaction |
start transaction |
update user_product set click_count = 1 where id = 1; |
|
insert操作 |
|
|
result1 = select * from user_product; |
commit |
|
|
result2 = select * from user_product; |
下表中如果transaction2不加for update,则可以直接读取成功
transaction 1 |
transaction 2 |
start transaction |
start transaction |
select * from user_product where id = 1 for update; |
|
|
select * from user_product where id = 1 for update; |
|
阻塞 |
commit |
product表
id |
sale_money |
other_colunm |
1 |
500 |
0 |
user_product表
id |
user_id |
product_id |
click_max |
click_count |
other_column |
1 |
1 |
1 |
500 |
499 |
0 |
user_product_click_record(id, user_product_id, user_id)
丢失更新场景(说明:对于上表,多个事务同时读取,并将click_count+1)
transaction 1 |
transaction 2 |
start transaction |
start transaction |
record = select * from user_product where user_id = 1 and product_id = 1; |
record = select * from user_product where user_id = 1 and product_id = 1; |
if(record.get('click_count') < record.get('click_max')) //添加发红包记录 else //已达上限 |
if(record.get('click_count') < record.get('click_max')) //添加发红包记录 else //已达上限 |
update user_product set click_count = record.get(”click_count“)+1 where id = record.get("id") |
|
update user_product set click_count = record.get(”click_count“)+1 where id = record.get("id") |
|
commit |
|
commit |
A和B同时进行了点击操作,导致实际点击数超过了click_max,也就是A的更新丢失了
乐观锁 在user_product表增加version字段
transaction 1 |
transaction 2 |
start transaction |
start transaction |
record = select * from user_product where user_id = 1 and product_id = 1; |
record = select * from user_product where user_id = 1 and product_id = 1; |
if(record.get('click_count') < record.get('click_max')) //发红包记录 else //已达上限 |
if(record.get('click_count') < record.get('click_max')) //发红包 else //已达上限 |
update user_product set click_count = record.get(”click_count“)+1, version = record.get('version')+1 where id = record.get("id") and version = record.get('version') |
|
update user_product set click_count = record.get(”click_count“)+1, version = record.get('version')+1 where id = record.get("id") and version = record.get('version') |
|
commit |
阻塞 |
0 rows affected |
|
commit |
悲观锁
transaction 1 |
transaction 2 |
start transaction |
start transaction |
record = select * from user_product where user_id = 1 and product_id = 1 for update; |
|
if(record.get('click_count') < record.get('click_max')) //发红包 else //已达上限 |
record = select * from user_product where user_id = 1 and product_id = 1 for update;//读到的是transaction1已经提交的数据了 |
update user_product set click_count = record.get(”click_count“)+1 where id = record.get("id") |
阻塞 |
commit(释放锁) |
阻塞 |
if(record.get('click_count') < record.get('click_max')) //发红包 else //已达上限 |
|
update user_product set click_count = record.get(”click_count“)+1 where id = record.get("id") |
|
commit |
select * from information_schema.innodb_trx\G; 事务信息
select * from information_schema.innodb_locks\G; 锁信息
select * from information_schema.innodb_lock_waits\G; 阻塞等待情况
next-key lock = 记录+范围,取决于是否唯一索引
record-lock(锁降级):
transaction 1: select * from user_product where other_column_unique = 8 for update;
transaction 2: select * from user_product where other_column_unique = 8 for update;
next-key lock
transaction 1: select * from user_product where other_column_normal = 8 for update;//数据库中other_column_normal 值为1,2,5,8,13,16
transaction 2: insert into user_product (other_column_unique, other_column_normal ) values (55, 9)//插入不进去,因为(5,13)被锁住了
AB-BA mysql wait-for-graph自动检测
transaction 1 |
transaction 2 |
start transaction |
start transaction |
insert |
insert |
select * from user_product where other_column_unique = 8 for update; |
|
select * from user_product where other_column_unique = 3 for update; |
|
select * from user_product where other_column_unique = 3 for update; |
|
阻塞 |
|
select * from user_product where other_column_unique = 8 for update; |
|
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction(回滚) |
|
获得锁 |
insert//不属于当前事务了 |
show global variables like '%timeout%';
set innodb_lock_wait_timeout=50;
transaction 1 |
transaction 2 |
start transaction |
start transaction |
select * from user_product where other_column_unique = 8 for update; |
|
insert |
select * from user_product where other_column_unique = 3 for update; |
select * from user_product where other_column_unique = 3 for update; |
|
阻塞 |
|
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction |
|
insert//还在当前事务里 |
|
commit |
共享锁 lock in share mode,很少用到
CREATE TABLE `user_product` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_id` int(10) unsigned NOT NULL DEFAULT '0',
`product_id` int(10) unsigned NOT NULL DEFAULT '0',
`click_count` int(10) unsigned NOT NULL DEFAULT '0',
`click_max` int(10) unsigned NOT NULL DEFAULT '0',
`other_column_unique` int(11) NOT NULL DEFAULT '0',
`other_column_normal` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_index` (`other_column_unique`),
KEY `normal_index` (`other_column_normal`)
) ENGINE=InnoDB AUTO_INCREMENT=28 DEFAULT CHARSET=latin1;
INSERT INTO `user_product` VALUES
(1,1,1, 40,500,1, 1),
(2,1,2, 40,500,3, 3),
(3,1,9, 40,500,5, 5),
(4,1,4, 40,500,6, 6),
(5,1,13, 40,500,8, 8),
(6,1,10, 40,500,11,11),
(7,1,11, 40,500,13,13),
(10,1,12,40,500,19,19);