mysql 同一秒入库问题_mysql锁(行锁,表锁)同一用户同一秒操作保持唯一性

今天mysql群里一群友询问,同一用户同一秒只可以有一行数据insert入库,否则是update。表面上看起来很简单,用个判断语句就行了,先查询表是否已经存在,但其实不然。

原文:

【冒泡】小宝他爸@上海

2014/4/3 14:31:10

用户再同一秒做了两个请求,都正常入库了

【冒泡】小宝他爸@上海

2014/4/3 14:31:42

但是我还在同一秒只能一个入库,另一个就拒绝

【冒泡】小宝他爸@上海

2014/4/3 14:32:05

但是我是想在同一秒只能一个入库,另一个就拒绝

if($db->getone("select id from log where user_id='1' and date_create='".date("Y-m-d 00:00:00")."'")){

$db->query("UPDATE log SET hits = hits+1 WHERE user_id='1' and date_create='".date("Y-m-d 00:00:00")."'");

}else{

$db->insert('log_user_count', array('hits' => 1, 'user_id'=>1, 'date_create'=>date("Y-m-d 00:00:00")));

}

表面上看代码是没有问题的,这个逻辑应该可以。但细看会发现,如果2个并发过来时,还没来得及insert操作,都在select层面上,所以结果都是false,2个同时去执行了insert

后来经过讨论,还是用mysql加锁来保持数据一致性,表锁?行锁,还没定。具体没测试,应该可行。

(用事务应该也行!)

//执行SQL语句 锁掉stat_num表

$sql = "LOCK TABLES stat_num WRITE"; //表的WRITE锁定,阻塞其他所有mysql查询进程

$DatabaseHandler->exeCute($sql);

//执行更新或写入操作

$sql = "UPDATE stat_num SET `correct_num`=`correct_num`+1 WHERE stat_date='{$cur_date}'";

$DatabaseHandler->exeCute($sql);

//当前请求的所有写操作做完后,执行解锁sql语句

$sql = "UNLOCK TABLES";

$DatabaseHandler->exeCute($sql);

上面方法可能不行!!!!!

这个东东,好像还是比较麻烦的。

一般最简单的办法是字段加唯一索引。否则的话就用复杂一点方法。

1、用生成文件方法

2、用memcache方法(转自http://ju.outofmemory.cn/entry/48924)

/**

* 使用Memcache实现给进程加锁的类

*

* Copyright (C) 2013 JeffJing

*

* 一些时候需要让系统的某些操作串行化,这个时候就要对这些操作来加上一把锁。 好比你去上厕所, 你要先推厕所门看能否进去,进去的话上锁,其他人就进不来了, 等你拉完粑粑之后把锁打开,这样的话就保证了厕所永远只有1个人, 上厕所的过程是串行化的, 同时只有1个人上。

* 网上的一些解决方案大多是使用文件的, 我就纳闷了,加锁的时候创建一个文件, 解锁的时候把文件删掉。

* 我就纳闷: Why not memcached ? 顺手写了这么一个 , 希望对大家"拉粑粑"的这种操作有所帮助

*

* 举个栗子:

* $key = '厕锁';

* if(MemLock::addLoack($key)) {

* //拉粑粑喽,pu~pu~~~~~

* MemLock::releaseLock($key);

* } else {

* //不好意思, 厕所有人啦!!

* }

*

* http://www.phpv5.com

*/

class MemLock {

private static $memcache = null;

/**

* 获取memcached连接

*

* @return Memcached

*/

public static function getConnection() {

if (! isset ( self::$memcache )) {

self::$memcache = new Memcache ();

self::$memcache->connect ( '127.0.0.1', 11211 );

}

return self::$memcache;

}

/**

* 加锁

*

* @param $key 锁关键字

*

* @return boolean true 成功获取到锁 false 获取锁失败

*/

public static function addLock($key) {

$memcache = self::getConnection ();

//不存在的话,先创建空值

$v = $memcache->get ( $key );

if ($v === false) {

$memcache->set ( $key, 0 );

}

$index = $memcache->increment ( $key, 1 );

if ($index == 1) {

return true;

}

return false;

}

/**

* 释放锁

*

* @param $key 锁关键字

*

* @return boolean true 释放成功 false 释放失败

*/

public static function releaseLock($key) {

$memcache = self::getConnection ();

return $memcache->delete ( $key );

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 MySQL 中,行锁表锁定级别是不同的。行锁定级别比表锁低,因此在同一事务内先加行锁再加表锁是不会冲突的。 当一个事务对行加时,该事务对其他事务的影响仅限于该行,其他事务可以继续访问其他行。而表锁则会定整张,其他事务将无法访问该中的任何行。因此,在同一事务内先加行锁再加表锁不会冲突,因为行锁定范围要小于表锁。 ### 回答2: 在MySQL中,行锁表锁是用来控制多个事务对同一数据资源进行访问和修改的方式。行锁是在数据行级别上进行的定,而表锁是在整个级别上进行的定。 当在同一事务内先加行锁再加表锁时,并不会发生冲突的原因是的粒度不同。行锁只会定特定的行,而表锁则会定整个。事务在执行时,会按照顺序先获取行锁,再获取表锁。 假设有两个事务T1和T2都要对同一进行操作,其中T1先获取到了行锁,然后进行相应的操作。在T1未释放行锁之前,T2无法获取到行锁,因此也无法进行操作,避免了冲突的发生。 而在T1释放了行锁之后,T1将获取到表锁,然后进行相应的操作。此时,T2再次进行操作时,会发现表锁已经被T1获取了,无法继续进行操作,也就避免了冲突的发生。 因此,通过先加行锁再加表锁的方式,可以保证同一事务内对同一数据资源的操作不会发生冲突。这是因为行锁表锁的不同粒度,行锁在事务执行期间保证了行级别的独占性,而表锁在事务结束时进行申请,保证操作的顺序性和互斥性。 ### 回答3: 在 MySQL 中,行锁表锁是用来保证事务并发执行时数据一致性的机制。 行锁是针对一个具体的数据行加,只有在需要修改或读取该行数据时才会加行锁的粒度更细,所以能够允许更多的并发访问。当一个事务要加行锁时,会先检查该行是否已经被其他事务加了行锁,如果已经加,则需要等待其他事务释放才能继续执行。 表锁是针对整张,当一个事务要加表锁时,会先检查该是否已经被其他事务加了,如果已经加,则需要等待其他事务释放才能继续执行。表锁的粒度比行锁大,所以在加时需要定更多的资源,而且会导致并发性能下降。 当一个事务内先加行锁再加表锁时,是因为行锁表锁的加顺序是互相兼容的。事务在加行锁时只会定具体的数据行,不会涉及到整张定。而在加表锁时,只需要定整张,不会涉及具体的数据行。由于行锁表锁的粒度是不同的,所以在加时不会产生冲突。 因此,当一个事务先加行锁再加表锁时,由于行锁表锁的粒度不同,所以不会发生冲突。这样可以保证事务在并发执行时能够正确地读取和修改数据,并且能够保持数据的一致性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值