Redis 常见 7 种使用场景 (PHP 实战)

2018-05-09  本文已影响34人  liamu

Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
为了方便查看redis我这个装了一个redis客户端Redis desktop Manager


场景一:字符串缓存

$redis = new \Redis();
$redis->connect('127.0.0.1', 6379,3);//短链接,本地host,端口为6379,超过1秒放弃链接
$flag = $redis->auth('*****');//登录验证密码,返回【true | false】
if (!$flag) {
    echo 'redis连接失败';die();
}
$redis->select(15);//选择redis库,0~15 共16个库
// ==== 案例一:简单字符串缓存实战 ===
$strCacheKey  = 'Test_amu';

// SET 应用
/*
$arrCacheData = [
    'name' => 'job',
    'sex'  => '男',
    'age'  => '30'
];
$redis->set($strCacheKey,json_encode($arrCacheData));
$redis->expire($strCacheKey,30); # 设置30秒后过期
$json_data = $redis->get($strCacheKey);
$data = json_decode($json_data);
*/

// HSET 应用
$arrWebSite = [
    'google' => [
        'google.com',
        'google.com.hk'
    ],
];
$redis->hSet($strCacheKey,'google',json_encode($arrWebSite['google']));
$json_data = $redis->hGet($strCacheKey,'google');
$data = json_decode($json_data);

print_r($data);
set&hSet.png

场景二:简单队列实战

$redis = new \Redis();
$redis->connect('127.0.0.1', 6379,3);//短链接,本地host,端口为6379,超过1秒放弃链接
$flag = $redis->auth('****');//登录验证密码,返回【true | false】
if (!$flag) {
    echo 'redis连接失败';die();
}
$redis->select(15);//选择redis库,0~15 共16个库
// ==== 案例二:简单队列存实战 ===
$strQueueName = 'Test_amu_queue';

// 进队列
$redis->rpush($strQueueName,json_encode(['uid' => 1,'name' => 'Job']));
$redis->rpush($strQueueName,json_encode(['uid' => 2,'name' => 'Tom']));
$redis->rpush($strQueueName,json_encode(['uid' => 3,'name' => 'John']));

echo '------- 进队列成功 ------'.PHP_EOL;

// 查看队列
$strCount = $redis->lrange($strQueueName,0,-1);
echo '当前队列数据为:'.PHP_EOL;
print_r($strCount);

// 出队列
$redis->lpop($strQueueName);
echo '----- 出队列成功 -----'.PHP_EOL;

// 查看队列
$strCount = $redis->lrange($strQueueName,0,-1);
echo '当前队列数据为:'.PHP_EOL;
print_r($strCount);

运行结果

[root@localhost ~]# curl 127.0.0.1:6666/test2.php
------- 进队列成功 ------
当前队列数据为:
Array
(
    [0] => {"uid":1,"name":"Job"}
    [1] => {"uid":2,"name":"Tom"}
    [2] => {"uid":3,"name":"John"}
)
----- 出队列成功 -----
当前队列数据为:
Array
(
    [0] => {"uid":2,"name":"Tom"}
    [1] => {"uid":3,"name":"John"}
)

queue

实战三:发布与订阅

ini_set('default_socket_timeout',-1);
$redis = new \Redis();
$redis->connect('127.0.0.1', 6379);
$flag = $redis->auth('****');//登录验证密码,返回【true | false】
if (!$flag) {
    echo 'redis连接失败';die();
}
$redis->select(15);//选择redis库,0~15 共16个库
// ==== 案例三:简单发布订阅实战 ===
$strChannel = 'Test_amu_channel';

// 发布
$redis->publish($strChannel,"来自{$strChannel}频道的推送");
echo '消息推送成功'.PHP_EOL;
$redis->close();

运行结果:

[root@localhost ~]# curl 127.0.0.1:6666/test2.php
消息推送成功

redis订阅结果

Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "Test_amu_channel"
3) (integer) 1



1) "message"
2) "Test_amu_channel"
3) "\xe6\x9d\xa5\xe8\x87\xaaTest_amu_channel\xe9\xa2\x91\xe9\x81\x93\xe7\x9a\x84\xe6\x8e\xa8\xe9\x80\x81"

ini_set('default_socket_timeout',-1);
$redis = new \Redis();
$redis->connect('127.0.0.1', 6379);
$flag = $redis->auth('****');//登录验证密码,返回【true | false】
if (!$flag) {
    echo 'redis连接失败';die();
}
$redis->select(15);//选择redis库,0~15 共16个库
// ==== 案例三:简单发布订阅实战 ===
$strChannel = 'Test_amu_channel';

// 订阅
echo '--- 订阅中,等待消息推送 ---'.PHP_EOL;
$redis->subscribe([$strChannel],'callBackFun');
function callBackFun($redis,$channel,$msg){
    print_r([
        'redis'   => $redis,
        'channel' => $channel,
        'msg'     => $msg
    ]);
}

运行结果:

[root@localhost userauth]# php test2.php 
--- 订阅中,等待消息推送 ---
Array
(
    [redis] => Redis Object
        (
        )

    [channel] => Test_amu_channel
    [msg] => 来自Test_amu_channel频道的推送
)

实战四:计数器实战

$redis = new \Redis();
$redis->connect('127.0.0.1', 6379);
$flag = $redis->auth('****');//登录验证密码,返回【true | false】
if (!$flag) {
    echo 'redis连接失败';die();
}
$redis->select(15);//选择redis库,0~15 共16个库
// ==== 案例四:简单计数器实战 ===
$strKey = 'Test_amu_comments';

// 设置初始值
$redis->set($strKey,0);

$redis->INCR($strKey); // +1
$redis->INCR($strKey);
$redis->INCR($strKey);

$strNowCount = $redis->get($strKey);
echo '当前数量'.$strNowCount.PHP_EOL;

运行结果:

[root@localhost userauth]# curl 127.0.0.1:6666/test2.php
当前数量3
counts.png

实战五:计数器实战

$redis = new \Redis();
$redis->connect('127.0.0.1', 6379);
$flag = $redis->auth('****');//登录验证密码,返回【true | false】
if (!$flag) {
    echo 'redis连接失败';die();
}
$redis->select(15);//选择redis库,0~15 共16个库
// ==== 案例五:简单排行榜实战实战 ===
$strKey = 'Test_amu_score';

// 存储数据
$redis->zadd($strKey,50,json_encode(['name'=>'Tom']));
$redis->zadd($strKey,70,json_encode(['name'=>'John']));
$redis->zadd($strKey,90,json_encode(['name'=>'Jerry']));
$redis->zadd($strKey,30,json_encode(['name'=>'Job']));
$redis->zadd($strKey,100,json_encode(['name'=>'LiMing']));

$dataOne = $redis->ZREVRANGE($strKey,0,-1,true);
echo '从大到小排序'.PHP_EOL;
print_r($dataOne);

$dataOne = $redis->ZRANGE($strKey, 0, -1, true);
echo '从小到大排序'.PHP_EOL;
print_r($dataOne);

运行结果:

[root@localhost userauth]# curl 127.0.0.1:6666/test2.php
从大到小排序
Array
(
    [{"name":"LiMing"}] => 100
    [{"name":"Jerry"}] => 90
    [{"name":"John"}] => 70
    [{"name":"Tom"}] => 50
    [{"name":"Job"}] => 30
)
从小到大排序
Array
(
    [{"name":"Job"}] => 30
    [{"name":"Tom"}] => 50
    [{"name":"John"}] => 70
    [{"name":"Jerry"}] => 90
    [{"name":"LiMing"}] => 100
)

实战六:简单字符串悲观锁实战

解释:悲观锁(Pessimistic Lock), 顾名思义,就是很悲观。
每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁。
场景:如果项目中使用了缓存且对缓存设置了超时时间。
当并发量比较大的时候,如果没有锁机制,那么缓存过期的瞬间,
大量并发请求会穿透缓存直接查询数据库,造成雪崩效应。

$redis = new \Redis();
$redis->connect('127.0.0.1', 6379);
$flag = $redis->auth('****');//登录验证密码,返回【true | false】
if (!$flag) {
    echo 'redis连接失败';die();
}
$redis->select(15);//选择redis库,0~15 共16个库
// ==== 案例六:简单字符串悲观锁实战 ===
$Key = 'Test_amu_lock';

// 获取锁
$is_lock = lock($Key,10,$redis);
if($is_lock) {
    echo 'get lock success'.PHP_EOL;
    // 开始业务代码
    unlock($Key,$redis);
} else {
    echo '访问过于频繁'.PHP_EOL;
}

/**
 * 释放锁
 * @param  String  $key 锁标识
 * @return Boolean
 */
function unlock($key = '',$redis){
    return $redis->del($key);
}
/**
 * 获取锁
 * @param  String  $key    锁标识
 * @param  Int     $expire 锁过期时间
 * @return Boolean
 */
function lock($key = '', $expire = 5,$redis) {
    $is_lock = $redis->setnx($key, time()+$expire);
    //不能获取锁
    if(!$is_lock){
        //判断锁是否过期
        $lock_time = $redis->get($key);
        //锁已过期,删除锁,重新获取
        if (time() > $lock_time) {
            unlock($key);
            $is_lock = $redis->setnx($key, time() + $expire);
        }
    }

    return $is_lock? true : false;
}

运行结果:

[root@localhost userauth]# curl 127.0.0.1:6666/test2.php
get lock success
[root@localhost userauth]# curl 127.0.0.1:6666/test2.php
访问过于频繁

实战七:乐观锁实战

乐观锁(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);
$flag = $redis->auth('****');//登录验证密码,返回【true | false】
if (!$flag) {
    echo 'redis连接失败';die();
}
$redis->select(15);//选择redis库,0~15 共16个库
// ==== 案例七:简单事务的乐观锁实战 ===
$strKey = 'Test_amu_age';

$redis->set($strKey,10);
$age = $redis->get($strKey);

echo '事务前:年龄'.$age.PHP_EOL;
$redis->watch($strKey);
$redis->set($strKey,20);
// 开启事务
$redis->multi();

    // 在这个时候新开了一个新会话执行
    $redis->set($strKey,80);
    echo '设置了80'.PHP_EOL;
    $redis->set($strKey,90);
    echo '设置了90'.PHP_EOL;
    $redis->set($strKey,91);
    echo '设置了91'.PHP_EOL;
    
$rob_result = $redis->exec();
print_r($rob_result);

unset($age);
$age = $redis->get($strKey);
echo '事务后:年龄'.$age.PHP_EOL;
//当exec时候如果监视的key从调用watch后发生过变化,则整个事务会失败

运行结果:

[root@localhost userauth]# curl 127.0.0.1:6666/test2.php
事务前:年龄10
设置了80
设置了90
设置了91
事务后:年龄20

扩展:使用watch完成秒杀抢购功能

使用redis中两个key完成秒杀抢购功能,increate_num用于存储抢购数量和userList用户存储抢购列表。

它的优点如下:

$redis = new redis();  
$redis->connect('127.0.0.1', 6379);
$flag = $redis->auth('*****');
if (!$flag) {
    echo 'redis连接失败';die();
}
$redis->select(15);
$increate_num = $redis->get("increate_num");  
$rob_total = 4;   //抢购数量  
if($increate_num<$rob_total){  
    $redis->watch("increate_num");  
    $redis->multi();  
 
    //业务代码 

    $redis->hSet("userList","userid".mt_rand(1, 9999),time());  
    $redis->set("increate_num",$increate_num+1);  
    $rob_result = $redis->exec();  
    if($rob_result){  
        $userList = $redis->hGetAll("userList");  
        echo "抢购成功!<br/>";  
        echo "剩余数量:".($rob_total-$increate_num-1)."<br/>";  
        echo "用户列表:<pre>";  
        var_dump($userList);  
    }else{  
        echo "手气不好,请重新抢购!";exit;  
    }  
}  else {
    echo "活动已结束";exit;  
}

欢迎关注个人微信公众号:

不惑小年轻.jpg
上一篇下一篇

猜你喜欢

热点阅读