一、基于redis字符串string类型的简单缓存实现
$redis = new \Redis();
$redis->connect("127.0.0.1", 6379);
$redis->set("cache-key", json_encode([
"data-list" => "这是个缓存数据~",
"data-list-en" => "This a data of cache~",
]), JSON_UNESCAPED_UNICODE);
echo "字符串缓存成功~ \n\n";
//获取缓存数据
$data = $redis->get("cache-key");
echo "读取缓存数据为: \n";
print_r(json_decode($data,true));
二、基于redis列表list类型的简单队列实现
$redis = new \Redis();
$redis->connect("127.0.0.1", 6379);
// 进队列
$userId = mt_rand(000000, 999999);
$redis->rpush("QUEUE_NAME",json_encode(["user_id" => $userId]));
$userId = mt_rand(000000, 999999);
$redis->rpush("QUEUE_NAME",json_encode(["user_id" => $userId]));
$userId = mt_rand(000000, 999999);
$redis->rpush("QUEUE_NAME",json_encode(["user_id" => $userId]));
echo "数据进队列成功 \n";
// 查看队列
$res = $redis->lrange("QUEUE_NAME", 0, 1000);
echo "当前队列数据为: \n";
print_r($res);
echo "----------------------------- \n";
// 出队列
$redis->lpop("QUEUE_NAME");
echo "数据出队列成功 \n";
// 查看队列
$res = $redis->lrange("QUEUE_NAME", 0, 1000);
echo "当前队列数据为: \n";
print_r($res);
三、基于redis字符串setnx的悲观锁实现
解释:悲观锁(Pessimistic Lock), 顾名思义,就是很悲观。
每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁。
场景:如果项目中使用了缓存且对缓存设置了超时时间。
当并发量比较大的时候,如果没有锁机制,那么缓存过期的瞬间,
大量并发请求会穿透缓存直接查询数据库,造成雪崩效应。
$timeout = 5000;
$redis = new \Redis();
$redis->connect("127.0.0.1", 6379);
do {
$microtime = microtime(true) * 1000;
$microtimeout = $microtime+$timeout+1;
// 上锁
$isLock = $redis->setnx("lock.count", $microtimeout);
if (!$isLock) {
$getTime = $redis->get("lock.count");
if ($getTime > $microtime) {
// 睡眠 降低抢锁频率 缓解redis压力
usleep(5000);
// 未超时继续等待
continue;
}
// 超时,抢锁,可能有几毫秒级时间差可忽略
$previousTime = $redis->getset("lock.count", $microtimeout);
if ((int)$previousTime < $microtime) {
break;
}
}
} while (!$isLock);
$count = $redis->get("count")? : 0;
// file_put_contents("/var/log/count.log.1", ($count+1));
// 业务逻辑
echo "执行count加1操作~ \n\n";
$redis->set("count", $count+1);
// 删除锁
$redis->del("lock.count");
// 打印count值
echo $redis->get("count");
四、基于redis事务的乐观锁实现
解释:乐观锁(Optimistic Lock), 顾名思义,就是很乐观。
每次去拿数据的时候都认为别人不会修改,所以不会上锁。
watch命令会监视给定的key,当exec时候如果监视的key从调用watch后发生过变化,则整个事务会失败。
也可以调用watch多次监视多个key。这样就可以对指定的key加乐观锁了。
注意watch的key是对整个连接有效的,事务也一样。
如果连接断开,监视和事务都会被自动清除。
当然了exec,discard,unwatch命令都会清除连接中的所有监视。
$redis = new \Redis();
$redis->connect("127.0.0.1", 6379);
// 监视 count 值
$redis->watch("count");
// 开启事务
$redis->multi();
// 操作count
$time = time();
$redis->set("count", $time);
/**
* 模拟并发下其他进程进行set count操作 请执行下面操作
* redis-cli 执行 $redis->set("count", "is simulate"); 模拟其他终端
*/
sleep(10);
// 提交事务
$res = $redis->exec();
if ($res) {
// 成功...
echo "success:" . $time . "\n";
return;
}
// 失败...
echo "fail:" . $time . "\n";
五、基于redis的发布订阅实现
①、发布
//发布
$redis = new \Redis();
$redis->connect("127.0.0.1", 6379);
$redis->publish("msg", "来自msg频道的推送");
echo "msg频道消息推送成功~ \n";
$redis->close();
②、订阅
// ini_set("default_socket_timeout", -1);
$redis = new \Redis();
$redis->pconnect("127.0.0.1", 6379);
//订阅
echo "订阅msg这个频道,等待消息推送... \n";
$redis->subscribe(["msg"], "callfun");
function callfun($redis, $channel, $msg)
{
print_r([
"redis" => $redis,
"channel" => $channel,
"msg" => $msg
]);
}
六、redis解决高并发问题,如商品秒杀
redis真的是一个很好的技术,它可以很好的在一定程度上解决网站一瞬间的并发量,例如商品抢购秒杀等活动。。。
redis之所以能解决高并发的原因是它可以直接访问内存,而以往我们用的是数据库(硬盘),提高了访问效率,解决了数据库服务器压力。
为什么redis的地位越来越高,我们为何不选择memcache,这是因为memcache只能存储字符串,而redis存储类型很丰富(例如有字符串、LIST、SET等),memcache每个值最大只能存储1M,存储资源非常有限,十分消耗内存资源,而redis可以存储1G,最重要的是memcache它不如redis安全,当服务器发生故障或者意外关机等情况时,redsi会把内存中的数据备份到硬盘中,而memcache所存储的东西全部丢失;这也说明了memcache不适合做数据库来用,可以用来做缓存。
下面用redis解决瞬间秒杀活动来说明:
下面这个程序模拟了20人一瞬间涌入这个页面进行秒杀,能够秒杀成功的只有10人,我们把先进来的用户放入redis队列中,当队列中的用户达到10时,后来用户就转到秒杀结束页面。这里用随机数来表示不同的用户。
connect("127.0.0.1",6379);
$redis_name = "miaoshala";
$num = 10;
$len = $redis->llen($redis_name);
if($len < $num){
$uuid = mt_rand(100000,999999);
$redis->rpush($redis_name,$uuid);
echo "秒杀成功!";
}else{
echo "对不起,来晚了!";
}
用 jmeter 模拟并发请求