Caso de escenario de uso de bloqueo concurrente incr en redis

PHP: casos de uso de bloqueos concurrentes Incr en Redis


La simultaneidad se encuentra a menudo durante el desarrollo. Actualmente me encuentro con un escenario de uso y necesito extraer la declaración con regularidad. Sin embargo, puede haber un problema con el marco original. Cuando la extracción original se inició a las 10 en punto, causó varias veces en 10 en punto al mismo tiempo. La solicitud no resultó en una interceptación exitosa en el programa, y ​​se insertaron múltiples partes de los mismos datos en los datos al mismo tiempo. De hecho, en el proyecto actual, se ha realizado un método para determinar si existe un método. Si la declaración actual existe, se actualizará directamente, pero debido a la existencia de concurrencia, bajo las circunstancias, múltiples piezas de la misma los datos al mismo tiempo se han juzgado si hay No, la operación de inserción se realizó al mismo tiempo, lo que resultó en una gran cantidad de datos duplicados. Es decir, incluso si hace un juicio sobre si existe, es en realidad no es confiable bajo concurrencia. En este momento, necesitamos bloquear el programa Operation, el bloqueo al principio se realiza con get y set, simplemente escriba una demostración de la siguiente manera:

<?php
	/**
	 * 此处是redis操作类中的方法
	 */
	public function lock($key, $ttl)
	{
    
    
		return $this->redis->set($key, true, $ttl);
	}
	public function unLock($key)
	{
    
    
		return $this->redis->delete($key);
	}

	public function existLock($key)
	{
    
    
		return $this->get($key) ? true : false;
	}
//
	/**
	 * 此处是调用锁
	 */
	$key = 'lock';
	// 如果锁存在则直接返回,否则上锁,继续下边流程
	if ($redis->existLock($key)) {
    
    
		return ;
	}
	
	// 进行上锁
	$redis->lock($key, $ttl);

	// 中间获取对账单数据,然后进行插入操作
	$this->insertOrUpdate($where, $data);	
	...
	...
	// 一个大致的更新数据库的方法
	public function insertOrUpdate($where, $data)
	{
    
    
		// 此时操作因为是并发请求,比如说是5条相同的数据进行了请求,此时五次并发请求都没有查找到相同数据,然后五条同时进行了插入操作,从而导致数据库有五条相同的数据
		$res = $model->getOne($where);
		if (!$res) {
    
    
			//执行插入
			return $model->insert($data);
		}
		// 执行更新
		return $model->update($res['id'], $data);
	}
	

Lo anterior es la distribución aproximada del código del problema. Obviamente, el problema está en la cerradura. Obtener y configurar no son razonables. Cuando son concurrentes, no se pueden interceptar todas las solicitudes. El motivo es el mismo que el de la inserción y actualización de la base de datos. En este momento, debe cambiar el método de bloqueo, usar la atomicidad de redis incr lock, la atomicidad se explica de la siguiente manera

原子性(atomicity):一个事务是一个不可分割的最小工作单位,事务中包括的诸操作要么都做,要么都不做。

Redis所有单个命令的执行都是原子性的,这与它的单线程机制有关;

Redis命令的原子性使得我们不用考虑并发问题,可以方便的利用原子性自增操作

实现简单计数器功能;

En pocas palabras, cuando los programas A, B y C llaman a redis-> incr ('incrKey') al mismo tiempo, el valor de incrKey debe ser de 1 a 3. Cuando se llama al programa A, incrKey es 1 y Se llama al programa B Cuando incrKey es 3, incrKey es 2 cuando se llama al programa C, o en otro orden. Podemos interceptar la concurrencia en función de esta función. Solo cuando incrKey es igual a 1, podemos realizar la siguiente operación lógica. Todos los demás valores se descartan, lo que puede evitar que varios programas soliciten al mismo tiempo, lo que resulta en operaciones repetidas. Implementación de código específico De la siguiente manera, cambiamos la clase de operación redis y el método de llamada comercial específico

<?php
	// redis操作类
	
	// 这次我们在上锁的时候直接进行锁判断,直接返回true或者false
	public function lock($key, $ttl)
	{
    
    
		$lock = $this->redis->incr($key);
        // 设置过期时间,incr会把key的过期时间设为长期有效,需要加上有效期
        $this->redis->expire($key, $expire);
		return $this->existLock($key);
	}

	public function existLock($key)
	{
    
    
		return $this->get($key) == 1 ? true : false;
	}

	// 然后业务逻辑
	if (!$redis->lock($key)) {
    
    
		echo '已上锁';
	}
	// 下边正常的逻辑

Después de completar este código, realice una prueba simultánea y luego descubra que todavía hay un problema, donde ocurrió el problema específico, apareció en la operación de bloqueo de redis,

	public function lock($key, $ttl)
	{
    
    
		$lock = $this->redis->incr($key);
        // 设置过期时间,incr会把key的过期时间设为长期有效,需要加上有效期
        $this->redis->expire($key, $expire);
        // 此时调用existLock的时候又发生了并发,上锁的时候是1,但是返回的时候,因为同时并发请求的原因,$key已经不是1了,导致程序依然有问题,没办法执行
		return $this->existLock($key);
	}

La razón es la anterior:
luego se encuentra la razón y saldrá la solución real

<?php
	// redis操作类
	
	// 这次我们在上锁的时候直接进行锁判断,直接返回true或者false
	public function lock($key, $ttl)
	{
    
    
		$lock = $this->redis->incr($key);
        // 设置过期时间,incr会把key的过期时间设为长期有效,需要加上有效期
        $this->redis->expire($key, $expire);
        // 保证程序完整性,在设置完后,立马进行判断返回
		if($lock == 1) {
    
    
			return true;
		} else {
    
    
			return false;
		}
	}

	//  要什么自行车,要什么检查,直接干掉
	//public function existLock($key)
	//{
    
    
	//	return $this->get($key) == 1 ? true : false;
	//}

	// 然后业务逻辑
	if (!$redis->lock($key)) {
    
    
		echo '已上锁';
	}
	// 下边正常的逻辑

	// 当然不要忘记解锁
	$redis->unLock($key);

En este momento, el problema de la inserción de datos repetida causada por la concurrencia ha sido resuelto.
EL FINAL

Supongo que te gusta

Origin blog.csdn.net/qq_15915293/article/details/100145820
Recomendado
Clasificación