网站首页php
Redis分步锁解决Mysql并发处理问题
发布时间:2023-01-13 15:14:07编辑:slayer.hover阅读(537)
1. 测试10个进程并发读取同一个数据库表, 需要保证每个进程读到的数据行都是唯一的.
当使用mysql锁来实现:
DB::transaction(function (){
$row = DB::Table('record')
->where('create_at', '<=', time()) //读取当前时间之前的数据进行处理
->lockForUpdate()
->first();
//更新$row操作
...
});当数据量开始增加到1W条的时候, 就可以明显感觉到执行异常的慢了.
抛却数据库锁, 可以使用redis同步锁来控制一下流程.
2. Redis分步锁, 代码如下所示:
function sync($key, callable $func, $expire = 10000)
{
if (!$cache_enable) {
$result = call_user_func($func);
}else {
$random = uniqid($key) . rand(0, 1000000);
while (!Cache::set($key, $random, ['nx', 'px' => $expire])) {
usleep(100000);
}
$result = call_user_func($func);
if (Cache::get($key) == $random) {
Cache::delete($key);
}
}
return $result;
}备注: Redis不可用则返回原流程.
Cache::set($key, $random, ['nx', 'px'=>$expire]);
$key不存在时写入, 并设置$expire毫秒后自动过期. 写入成功则返回TRUE;
未加锁成功, 则100毫秒后重试. 加锁成功后执行业务流程, 完成后释放当前锁.
3. 替换上面的加锁方法:
sync('lock001', function (){
$key = Cache::get('Key') ?: 0; //读取到已缓存的ID
$row = DB::Table('record')
->where('id', '>', $key) //以ID控制每次读到的数据都不一样
->where('create_at', '<=', time())
->first();
Cache::set('Key', $row->id); //将当前读取到的ID缓存起来
//更新$row操作
...
});如此, 每个进程读到的数据都不一行, 即可解决同步读取写入的问题.
评论