今天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方法

<?php
/**
 * 使用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 );
	}
}