首先我们先了解下mysql的锁表机制。一种是共享锁,一种是独占锁。共享锁就是读锁,这种锁主要就是防止在读的时候又有新数据的插入,而并不排斥其他的线程读取。而独占锁就是在写的时候加的锁,既防止读,又防止写。好了,现在我们分析下为什么会出现重复记录。
假设一种情况:假设有两个表:a,b。a为订单表,b为帐务记录表。
if order1.state == not_confirm then
update a set state=confirmed where orderid = order1.orderid
insert into b
endif
上面的意思是,如果订单未被处理,则更新订单状态为被处理,并在b中插入一条记录。
好了,现在订单表非常大,而且查询频繁,在用户执行这个操作时,正好有个执行速度很慢的查询将表a锁住了(这时候不管是共享锁还是独占锁,这个操作都不能进行了,只能等待)。然后用户觉得慢,就使劲刷新了几次,而这刷新的几次请求使得web服务器又重新执行了几次该脚本,但是那个慢速查询依然没有完成,所以,无论多少个请求,都会卡在第二行update a表这个地方,因为第一个还未更新,因此,后续取到的order1.state依然是not_confirm。一旦那个慢速查询执行完毕,则写队列里的update执行是相当快的。接着每个请求便会执行insert into b这个sql,而这是无法避免的。
一点解释。
重复请求的数据,如果没有使用时间,则数据应该是完全一样的,如果使用了时间,则写的时间有可能有些差别。如果是使用客户端(相对于mysql服务器)生成的时间,则需要看用户刷新的频率。如果是使用mysql的now()时间函数,则差别应该在毫秒一下,普通的时间字段很难看出其差别,这就造成一种假象,就是同一秒内插入大量相同的数据。
当然这只是一种想法,并未实际操作过。这是我结合事务操作原理以及对隔离级别的测试,进行的一种猜想。这个猜想有待验证。