MySQL社区

 找回密码
 注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

搜索
查看: 17369|回复: 20
打印 上一主题 下一主题

[事务及锁] 通过一个实例说明高并发条件下,合理使用innodb的锁机制解决问题

[复制链接]
跳转到指定楼层
1#
发表于 2009-10-28 21:01:45 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
本帖最后由 yinshi 于 2009-11-1 12:11 编辑

场景:
有一个存储过程在单进程下运行良好。
涉及表:
create table 't_tmpsellticket'
(
id int auto_increment,
pool int default 0,
ticketid int default 0,
primary key (id)
) engine=innodb default charset=utf8;
create table 't_sellticket'
(
id int auto_increment,
pool int default 0,
ticketid int default 0,
used int default 0,
primary key (id),
key ‘sellticket’(ticketid)
) engine=innodb default charset=utf8;

评分

1

查看全部评分

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
收藏收藏1 分享淘帖 顶1 踩
20#
发表于 2016-9-5 10:06:58 | 只看该作者
有些看不明白怎么产生的死锁,两个过程都要锁定簇索引,也就是说innodb是一行行进行加锁的吗,这样才导致两边事务各自占了一部分行锁,无法完全锁定吗
19#
 楼主| 发表于 2009-11-1 13:32:19 | 只看该作者
最终版本:
createt definner='root'@'loclahost' procedure 'sp_getpresellticket'(in startno int,in endno int)
begin

insert into t_tmpsellticket(pool,ticketid,maxid) select pool,tocketid,
cast(concat(cast(pool as BINARY),cast(ticketid as BINARY)) as signed)
from t_sellticket
where id>=startno and id<=endno;

select ticketid
from t_tmpsellticket
where maxid=startno;
end $$
delimiter;
18#
 楼主| 发表于 2009-11-1 13:26:11 | 只看该作者
未来瓶颈来自存储和网络带宽
17#
 楼主| 发表于 2009-11-1 13:25:30 | 只看该作者
这是因为sellticket非簇索引、和簇索引互相等待导致的死锁情况,于是老老实实实现第二点,问题解决。
在若干个节点同时运行情况下,售票效率可以通过增加主机实现了提高售票能力。
16#
 楼主| 发表于 2009-11-1 13:17:39 | 只看该作者
为验证这个解决方案,只实现第1点。同样出现
update t_sellticket,t_tmpsellticket
set t_sellticket.used=1
where t_sellticket.pool=t_tmpsellticket.pool and t_sellticket.ticketid=t_tmpsellticket.ticketid;
死锁的情况。
15#
 楼主| 发表于 2009-11-1 13:01:52 | 只看该作者
本帖最后由 yinshi 于 2009-11-1 13:15 编辑

2 如何避免并发处理条件下的死锁,保障进程接收范围后就可以并行调用选票存储过程处理。
答案:
lock_mode x越少越好。 尽量使用lock_mode S。不需要使用limit限定查询范围,直接按售票范围查询已售出彩票,这样可以充分利用簇索引。
然后不再使用t_tmpsellticket中的used表示已售出。写入t_tmpsellticket中的记录就表示已售出。这样可以规避update对表的锁定.
通过把pool和ticketid进行编码,将两个连接字段压缩为一个连接字段。这样可以大大提高查询效率。
14#
 楼主| 发表于 2009-11-1 12:59:19 | 只看该作者
所以这里有两个问题要解决。
1 如何在不使用锁定读的情况下避免重复售票。
答案:这里最后把逻辑放在应用层处理,通过集群缓存treecache构造一个全局售票管理器,管理售票数量。然后每个并发应用进程排队向全局售票管理器申请购票数量,由全局售票管理器为每个进程返回相应的不重复的销售范围。进程接收范围后就可以并行调用选票存储过程处理了。
13#
 楼主| 发表于 2009-11-1 12:23:40 | 只看该作者
这个问题要解决,必须适应高并发应用的特点,怎么处理才能让多个业务能并行处理。显然 lock_mode x越少越好。 尽量使用lock_mode S。大家共享相同的资源,各取所需,互不影响。方为上策。
12#
 楼主| 发表于 2009-11-1 12:20:42 | 只看该作者
几乎同时
UPDATE T_sellticket,t_tmpsellticket
set t_sellticket.used=1
where t_sellticket.pool=t_tmpsellticket.pool and t_sellticket.ticketid
t_tmpsellticket相关的簇索引(cluster index)记录也将被锁定。在多进程分别调用存储过程的情况下,两边互相等待簇索引(cluster index)释放造成死锁。
11#
 楼主| 发表于 2009-11-1 12:15:28 | 只看该作者
当对第1个语句实施锁定读时,t_tmpsellticket被锁定。相关的簇索引(cluster index)记录也被锁定

SELECT IFNULL(max(id),0) as high
into p
from t_tmpsellticket for update;
10#
 楼主| 发表于 2009-11-1 12:10:44 | 只看该作者
本帖最后由 yinshi 于 2009-11-1 12:12 编辑

查询MySQL官网文档,发现这跟MySQL的索引机制有关。MySQL的InnoDB引擎是行级锁,我原来的理解是直接对记录进行锁定,实际上并不是这样的。

要点如下:


不是对记录进行锁定,而是对索引进行锁定;


在UPDATE、DELETE操作时,MySQL不仅锁定WHERE条件扫描过的所有索引记录,而且会锁定相邻的键值,即所谓的next-key locking;

如语句UPDATE T_sellticket,t_tmpsellticket
set t_sellticket.used=1
where t_sellticket.pool=t_tmpsellticket.pool and t_sellticket.ticketid=t_tmpsellticket.ticketid会锁定所有pool和ticketid的所有记录,在该语句完成之前,你就不能对锁定的记录进行操作;


当非簇索引(non-cluster index)记录被锁定时,相关的簇索引(cluster index)记录也需要被锁定才能完成相应的操作。


再分析一下发生问题的两条SQL语句,就不难找到问题所在了:
9#
 楼主| 发表于 2009-11-1 11:49:16 | 只看该作者
本帖最后由 yinshi 于 2009-11-1 12:18 编辑

**(2) TRANSACTION:
transaction 015928900 ,ACTIVE 0 SEC
PROCESS MO 8125,OS THREAD ID 1439734672
FETCHING ROWS THREAD DECLARE INSIDE INNODB 366
MYSQL TABLES IN USE 2,LOCKED 2
166 LOCKSTRUCTS,HEAP SIZE
44352 ,UNDO LOG ENTRIES 200
MYSQL THREAD ID 21492 ,QUERY ID 11171124
10.10.10.101 ROOT SEND DATA
UPDATE T_sellticket,t_tmpsellticket
set t_sellticket.used=1
where t_sellticket.pool=t_tmpsellticket.pool and t_sellticket.ticketid=t_tmpsellticket.ticketid
**(2) HOLD THE LCOK(s)
record locks space id 0 page no 49157 n bits
448 index 'primary' of table 'bingo_jms/t_tmpsellticket'
trx id 015928900 lock_mode x lock rec but not gap
8#
 楼主| 发表于 2009-10-31 23:32:20 | 只看该作者
**(1) waiting for this lock to be
granted
record lock space id 0 page no 49157
bits 448 index 'primary' of
'bingo_jms'/t_tmpsellticket ' trx id 015928902 lock_mode x waiting
record lock heap heap no 381 physical record下面是锁定的记录,略)
您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|申请友链|小黑屋|Archiver|手机版|MySQL社区 ( 京ICP备07012489号   
联系人:周生; 联系电话:13911732319

GMT+8, 2024-5-6 05:09 , Processed in 0.076669 second(s), 24 queries , Gzip On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表