事务不能解决并发,只能保证在一个事务内所有操作的一致性
常见的并发处理如下:
1.悲观锁
为什么叫悲观锁?
默认每次的执行都会发生并发
表必须是innodb类型,必须在事务中执行,加上for update 查询的表id=10数据是,这条数据就被锁定了,第一个人获得锁,后面的人只能等待第一个人完成事务提交后才能获得锁进行操作
$this->db->begin(); // 开启事务 try{ $info = $this->db->findOne("select * from table where id=1 FOR UPDATE "); sleep(5); $this->db->commit() // 只有提交后后面的请求才可以执行 } catch (\Exception $e) { $this->db->rollback() }
2.乐观锁
为什么叫乐观锁?
默认每次的执行都不会发生并发,只有到真正执行变更的时候检测并发
原理是利用mysql update 原子性,也就是不论多少次并发,mysql update 只会一条一条的更新
例:
$this->db->begin(); //开启事务 try{ $info = $this->db->findOne("select id,name,version from table where id=1"); sleep(5); // 其他逻辑 $version = $info['version'] + 1; // 主要防止并发在这里 $this->db->excute("update set name='123', version=$version where version=$info['version'] and id=1 "); $this->db->commit() } catch (\Exception $e) { $this->db->rollback() }
3.redis锁
// 单个用户重复提交 场景 $hasLock = $this->redis->set($key . '用户唯一标识', 1, 'nx', 'ex', $expire); if (!$hasLock) { // 没有获得锁 throw new Exception('排队中请稍后...'); } // 并发场景 $hasLock = $this->redis->set($key, 1, 'nx', 'ex', $expire); if (!$hasLock) { // 没有获得锁 throw new Exception(''排队中请稍后...'); } sleep(5); // 处理完业务 解锁 $this->redis->del($key);
4.redis队列
以上方法都不是真正意义上的并发,都是强制用户排队一个一个的来,而redis 队列可以实现真正的并发
根据具体业务的提前使用$this->redis->lpush(); 将需要并发的红包或者商品lpush入列,并发的时候lpop出列,因为lpop是原子性操作所以不会发生超卖或超领情况
5.其他
redis lua脚本、redis事务、中间件 等