一、Redis环境
1、安装Redis软件
在宝塔的软件商店中搜索Redis,并进行安装

2、为php安装Redis
搜索之前安装的php版本,对您所需要的php进行redis的安装


在安装的部分可以查看redis的信息,便于后续部署

3、配置redis.php(没有创建)
在路径application/extra的路径中,如果没有就新建文件redis|.php文件

配置redis.php的文件

4、给端口6379放行
需要确保服务器的安全组是否存在6379(阿里云,腾讯云等的配置),并且在安全->系统防火墙中添加端口

5、测试是否连接成功
随便写个方法,添加链接是否成功

public function testRedis()
{
try {
$redis = new \think\cache\driver\Redis(config('redis')); // 配置数组名‘redis’需对应你的配置文件
$redis->set('test_key', 'Hello Redis from FastAdmin!');
$value = $redis->get('test_key');
return '连接成功!值:' . $value;
} catch (\Exception $e) {
return '连接失败:' . $e->getMessage();
}
}
apifox之类的软件都可以测试

二、SimpleLock基础搭建
1、文件创建
在文件路径/application/common中,创建service文件夹,并创建SimpleLock.php文件
SimpleLock类是一个生产可用的分布式锁实现
-
✅ 实现了正确的分布式锁逻辑
-
✅ 防止了死锁
-
✅ 保证了锁的安全性
-
✅ 提供了良好的用户体验(等待超时提示)
例如:
-
解决库存计算并发问题
-
数据重复插入问题
-
事务隔离问题(可以脏读、不可重复读、幻读)
-
业务逻辑一致性问题
2、代码文件
<?php
// app/common/service/SimpleLock.php
namespace app\common\service;
class SimpleLock
{
private static $redis = null;
/**
* 获取Redis实例(带连接测试)
*/
private static function getRedis()
{
if (self::$redis === null) {
self::$redis = new \Redis();
// 尝试连接Redis
try {
// 这里使用localhost,如果需要可以修改
$connected = self::$redis->connect('127.0.0.1', 6379, 2.0);
if (!$connected) {
throw new \Exception('无法连接到Redis服务器');
}
// 测试连接
$ping = self::$redis->ping();
if (!$ping) {
throw new \Exception('Redis连接测试失败');
}
} catch (\Exception $e) {
// 记录错误并抛出
error_log('Redis连接错误: ' . $e->getMessage());
throw new \Exception('缓存服务暂时不可用,请稍后重试');
}
}
return self::$redis;
}
/**
* 分布式锁核心方法
* @param string $lockName 锁名称
* @param callable $callback 业务逻辑回调函数
* @param int $waitSeconds 最大等待时间(秒)
* @return mixed
*/
public static function run($lockName, callable $callback, $waitSeconds = 3)
{
$redis = null;
try {
// 获取Redis连接
$redis = self::getRedis();
$lockKey = "lock:{$lockName}";
$lockValue = uniqid('', true); // 生成唯一值
$startTime = microtime(true);
$maxWait = $waitSeconds * 1000000; // 微秒
// 尝试获取锁
while (true) {
// 尝试设置锁(NX: 不存在才设置,EX: 过期时间30秒)
$setResult = $redis->set($lockKey, $lockValue, ['nx', 'ex' => 30]);
if ($setResult) {
// 成功获得锁
break;
}
// 检查是否超时
$elapsed = (microtime(true) - $startTime) * 1000000;
if ($elapsed > $maxWait) {
throw new \Exception('系统繁忙,请稍后重试');
}
// 等待一段时间再重试
usleep(100000); // 100毫秒
}
// 执行业务逻辑
try {
return $callback();
} finally {
// 确保释放锁
self::releaseLock($redis, $lockKey, $lockValue);
}
} catch (\Exception $e) {
// 如果有Redis连接且在获取锁阶段出错,尝试释放锁
if ($redis && isset($lockKey) && isset($lockValue)) {
try {
self::releaseLock($redis, $lockKey, $lockValue);
} catch (\Exception $releaseError) {
// 释放锁失败,记录但不影响主错误
error_log('释放锁失败: ' . $releaseError->getMessage());
}
}
// 重新抛出异常
throw $e;
}
}
/**
* 释放锁(原子操作)
*/
private static function releaseLock($redis, $lockKey, $lockValue)
{
try {
// 使用Lua脚本确保原子性
$luaScript = "
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end
";
$redis->eval($luaScript, [$lockKey, $lockValue], 1);
} catch (\Exception $e) {
// 记录错误但不影响主流程
error_log('释放锁异常: ' . $e->getMessage());
}
}
/**
* 更简单的调用方式
*/
public static function sync($lockName, callable $callback)
{
return self::run($lockName, $callback);
}
}
3、使用方法
这里主要想要解决SimpleLock强制让事务串行执行,从而避免了所有隔离问题
// 我的代码逻辑
SimpleLock::run('upshelf_lock', function() {
Db::startTrans();
// 事务A的所有操作
Db::commit();
});
效果是:
-
事务A执行时,事务B等待
-
事务A完成后,事务B才开始
4、完成测试
(1)创建测试方法
创建测试方法,/application/api/Demo/testRealConcurrent
public function testRealConcurrent()
{
// 模拟多个并发请求
$results = [];
$logFile = '/tmp/real_concurrent.log';

最低0.47元/天 解锁文章
173万+

被折叠的 条评论
为什么被折叠?



