网站首页php

Redis分步锁解决Mysql并发处理问题

发布时间:2023-01-13 15:14:07编辑:slayer.hover阅读(1261)

    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操作
           	... 
    });

    如此, 每个进程读到的数据都不一行,  即可解决同步读取写入的问题.


评论