今天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)
<?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 );
}
}