PHP 操作redis常用方法代码

以下是本人使用redis的场景和对应示例代码:

1.redis连接和常用函数

$redis = new Redis();
//连接redis服务器
$redis->connect('127.0.0.1', 6379);
// echo "Connection to server sucessfully <br/>";

//1.设置key和value的值
$redis->set('test','123456');
//2.获取指定key的值
$redis->get('test');
//3.删除指定key的值
$redis->delete('test');
//4.如果不存在该键,则设置。如果存在,则不设置
$redis->setnx('test','456789');
//5.为指定的key设置过期时间
$redis->setex('test',60,'123456');
//6.判断指定的键是否存在
$redis->exists('test');
//7.数字递增
$redis->incr('test');
//8.数字递减
$redis->decr('test');
//9.取得所有指定键的值,如果一个或多个的键不存在,则数组中该键的值返回假
$redis->getMultiple(['test1','test2']);
//10.由列表头部添加字符串值
$redis->lpush('test','1111');
//11.由列表尾部添加字符串值
$redis->rpush('test','222');
//12.返回和移除列表中的第一个元素(从左往右)
$redis->lpop('test');
//13.返回和移除列表中的第一个元素(从右往左)
$redis->rpop('test');
//14.返回列表的长度
$redis->llen('test');
$redis->lsize('test');
//15.返回指定键存储在列表中的指定长度
$redis->lget('test',3);
//16。为列表中指定索引赋予新的值
$redis->lset('test',3,'555');
//17.为一个key添加一个值,如果这个值已经在这个key中,则返回false.集合
$redis->sadd('test','111');
$redis->sadd('test','112');
//18.移除key中的value值
$redis->sremove('test','112');
//19.将key1中的值移到key2中
$redis->smove('test1','test2','111');
var_dump($redis->sort('test2'));//排序并返回值
//20.检查集合中是否存在指定的值
$redis->scontains('test','111');
//21.返回集合中存储值得数量
$redis->ssize('test');
//22.移除集合中的指定 key 的一个或多个随机元素,移除后会返回移除的元素。
$redis->spop('test');
//23.返回指定键的交集,如果只指定一个键,那么这个命令生成这个集合的成员。如果不存在某个键,则返回FALSE。
$redis->sinter('test','test1');
//24.执行sinter命令并把交集存储到新建的变量当中
$redis->sinterstore('new','test1','test2');
//25.返回集合中的所有成员
$redis->smembers('news');
$redis->sgetmembers('new');
//26.返回指定键的并集
$redis->sunion('test','test2');
//27.执行sunion命令并把结果存储到新建的变量当中
$redis->sunionstore('new','test','test1');
//28.返回第一个集合中存在并且在其他所有集合中不存在的结果
$redis->sdiff('test','test1');
//29.执行sdiff命令并把结果存储到新建的变量中
$redis->sdiffstore('new','test','test1');
//30.根据参数COUNT的值移除列表中与参数VALUE相等的元素
$redis->lrem('test',0,'111');
//count > 0 : 从表头开始向表尾搜索,移除与 VALUE 相等的元素,数量为 COUNT 。 
//count < 0 : 从表尾开始向表头搜索,移除与 VALUE 相等的元素,数量为 COUNT 的绝对值。 
//count = 0 : 移除表中所有与 VALUE 相等的值
//31.为哈希表中的字段赋值
$redis->Hset('test','test-age','55');
//32.返回哈希表中指定字段的值
$redis->Hget('test','test-age');
$redis->Hgetall('test');
//33.返回列表中指定区间内的元素,区间以偏移量 START 和 END 指定。
$redis->lrange('test',0,-1);//全部的元素
View Code

2.比如实现一个简单的日志收集功能或发送大量短信、邮件的功能,实现方式是先将数据收集到队列中,然后有一个定时任务去消耗队列,处理该做的事情。

直接使用redis的rpush,lpop和lpush,rpop

//进队列
$redis->lpush(key, value);
//出队列
$redis->rpop(key);

3.比如我们要存储用户信息,ID、姓名、电话、年龄、身高 ,怎么存储?

key = userdata用户ID

hashKey = 姓名,value = xx

hashKey = 电话,value = xx

hashKey = 年龄,value = xx

hashKey = 身高,value = xx

查询时,取出key即可。

//新增
$redis->hSet(key, hashKey, value);
$redis->hSet(key, hashKey, value);
$redis->hSet(key, hashKey, value);
//编辑
$redis->hSet(key, hashKey, value);
//查询
$redis->hGetAll(key); //查询所有属性
$redis->hGet(key, hashKey); //查询某个属性

4.互关用户

//关注我的
if(!empty($_GET['uid'])&&!empty($_GET['fansid'])){
    $key="user:{$_GET['uid']}:fansid";
    $redis->sadd($key,$_GET['fansid']);
 
}else{
    echo "关注不成功";
}
//我关注的
if(!empty($_GET['uid'])&&!empty($_GET['followsid'])){
    $key="user:{$_GET['uid']}:follows";
    $redis->sadd($key,$_GET['followsid']);
 
}else{
    echo "关注不成功";
}
//互关,求2个的交集
$userlist1 = $redis->sinter("user:{$_GET['uid']}:fansid","user:{$_GET['uid']}:follows");

5.排行榜功能

class Rank
{
    private $redis = null;
    //构造方法,创建redis对象,并连接
    public  function __construct($ip,$port)
    {
        $this->redis = new redis();
        $this->redis->connect($ip,$port);
    }
    //像zsert类型集合添加元素,包括用户信息和排序用的分数
    function set( $key,  $score,  $userinfo)
    {
        //增加一个或多个元素,如果该元素存在,更新他的score
        if($this->redis->zadd($key,$score,json_encode($userinfo)))//zadd命令用于将一个或多个成员元素及其分数值加入到有序集当中。
        {
            print_r($userinfo);
            echo "数据添加成功";
        }
        else
        {
        echo "数据存在只更新score或者添加失败";
        }
    }
 
    //从zsert类型集合种获取全部排行好的用户信息和分数
    function get( $key,$withscores=true)
    {
        //返回key对应的有序集合中指定区间的所有元素。这些元素按照score从高到低顺序排列
        //0代表第一个元素,1第二个,-1代表最后一个,-2倒数第二个
        return $this->redis->zrevrange($key,0,-1,$withscores);//Zrevrange 命令返回有序集中,指定区间内的成员。
    }
}
 
$rank = new Rank('127.0.0.1',6379);
//设置A的score
$rank->set('Rank',100,array('img'=>'xx.jpg','username'=>'A','userid'=>1));
//设置B的score
$rank->set('Rank',250,array('img'=>'xx.jpg','username'=>'B','userid'=>3));
//设置C的score
$rank->set('Rank',50,array('img'=>'xx.jpg','username'=>'C','userid'=>2));
//设置C的score
$rank->set('Rank',600,array('img'=>'xx.jpg','username'=>'C','userid'=>2));
echo "<pre>";
print_r($rank->get('Rank'));

6.购物车

session_start();
class Cart
{
    private $redis=null;
    public function __construct()
    {
        $this->redis = new Redis();
        $this->redis->connect('127.0.0.1',6379);
    }
    //向购物车添加商品,修改已经有的商品数量
    public function addCart($gid,$cartNum=1)
    {
        //根据商品ID查询调用内部方法,模拟从数据库获取商品数据信息
        $goodDate = $this->goodsDta($gid);
        //组合key,seeion_id关联用户,gid关联商品
        $key = 'cart:'.session_id().':'.$gid;
        //将当前用户放入购物车的所有商品ID放到集合种,组合集合key
        $idskey = 'cart:ids:'.session_id();
        //通过key获取对应的商品数量,如果获取到说明商品存在
        $pnum = $this->redis->hget($key,'num');
        //购物车有对应的商品,只需要添加对应商品的数量
        $newNum= $pnum + $cartNum;
 
        //判断购物车中是否存在商品,如果不存在
        if(!$pnum)
        {
            //如果用户传入的数是负数,就证明用户在减少数量,传入的数和原购物车中的商品数量之和应该大于0
            if($newNum>0)
            {
                //向购物车的商品添加数量
                $goodsDta['num'] = $cartNum;
                //将商品数据存放到redis的hash,使用Hmset一次添加多个字段
                $this->redis->hmset($key,$goodsDta);
                //将商品id存放集合是为了更好的将用户购物车的商品遍历出来
                $this->redis->sadd($idskey,$gid);
            }
            else
            {
                //如果数量小于1,清除购物车商品
                if($newNum<1)
                {
                    $this->redis->del($key); //删除用户购物车的商品
                    $this->redis->srem($idskey,$gid);//在集合中去掉对应的商品id
                }
                else
                {
                    $this->redis->hset($key,'num',$newNum);//原来的数量加上用户新传入的数量
                }
            }
        }
    }
 
    //通过商品od删除购物车一条信息,如果没有传gid就清空购物车
    public function delCart($gid = false)
    {
        //获取当前用户放入购物车中所有商品id集合key
        $idskey = 'cart:ids:'.session_id();
        //如果参数$gid没有传入商品id,清空购物车
        if(!$gid)
        {
            //去集合拿到商品id
            foreach ($this->redis->sMembers($idskey) as $key => $id) 
            {
                 //组合一个key,使用session_id和用户关联,使用gid和商品关联
                $key = 'cart:'.session_id().':'.$id;
            }
            //删除按当前用户购买的商品id集合
            $this->redis->del($idskey);
        }
        else
        {
            //组合一个key,使用session_id和用户关联,使用gid和商品关联
            $key = 'cart:'.session_id().':'.$gid;
            $this->redis->del($key);
            $this->redis->srem($idskey,$gid);
        }
    }
    //显示用户购物车的所有商品
    public function showCartList()
    {
        $idskey = 'cart:ids'.session_id();
        $idsArr = $this->redis->sMembers($idskey);
 
        $list = null; //声明商品列表
        $total = 0;//商品总加个变量
 
        foreach ($idsArr as $key => $gid) 
        {
            //获取当前用户放入购物车中所有商品
            $good = $this->redis->hGetAll('cart:'.session_id().':'.$gid);
            $list[] = $good;
            //将所有商品的价格汇总
            $total +=$good['price'] * $good['num'];
        }
        $list['total'] = $total; // 将总金额一并放到商品列表
        return $list;
    }
    //临时模拟从mysql数据库中获取商品数据
    private function goodsDta($gid)
    {
        $goodsDta = [
            1 => ['id'=>1,'gname'=>'qwe','price'=>'21'],
            2 => ['id'=>2,'gname'=>'22','price'=>'333'],
            3 => ['id'=>3,'gname'=>'33','price'=>'444'],
            4 => ['id'=>4,'gname'=>'44','price'=>'555']
        ];
        return $goodsDta[$gid];
    }
}
    //简单测试向购物车增,删,改,查等商品的操作
    $cart = new Cart();
    $cart->addCart(1);
    // $cart->addCart(2,2); //id为2的商品放入购物车,数量为2
    // $cart->addCart(2,1); //修改id为2的商品,原数量加1
    // $cart->addCart(3,3); //id为3的商品放入购物车,数量为3
    // $cart->addCart(4,-4);// 商品为4的放入,数量-4
    // $cart->addCart(3,-1);//id为3的放入,数量-1
 
    echo "<pre>";
    print_r($cart->showCartList());//打印购物车列表的金额
 
    // $cart->delCart(2);
    // print_r($cart->showCartList());//打印购物车列表的金额    
 
    // $cart->delCart(); //清空购物车
View Code

7.简单队列

$strQueueName = 'Test_bihu_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 "---- 进队列成功 ---- <br /><br />";
//查看队列
$strCount = $redis->lrange($strQueueName, 0, -1);
echo "当前队列数据为: <br />";
print_r($strCount);
//出队列
$redis->lpop($strQueueName);
echo "<br /><br /> ---- 出队列成功 ---- <br /><br />";
//查看队列
$strCount = $redis->lrange($strQueueName, 0, -1);
echo "当前队列数据为: <br />";
print_r($strCount);

8.乐观锁防止商品超卖

$redis->watch('sales');//乐观锁 监视作用 set() 初始值0
$sales=$redis->get('sales');
$n=100
if($sales >= $n){
  exit('结束');
}
//开启事务
$redis->multi();
$redis->incr('sales');
//提交事务
$res=$redis->exec()
if($res){
    //成功
    include 'db.php';
    $sql="update products set store=store-1 where id=1";
    if($mod->exec($sql)){
      echo "完成";
    }
  }else{ 
      exit('失败');
  }

9.悲观锁

由于系统并发量较大,并且有频繁的写操作,所以选择悲观锁来控制每个任务只能同时被一个用户领取。主要思路如下:
1、从任务池中找出一部分可分配的任务;
2、根据一定顺序,选择一个任务,作为候选推送任务;
3、尝试对候选推送任务加锁;
4、如果加锁成功,则推送任务给用户,并修改对应的任务状态和用户状态;
5、如果加锁失败,则任务已被领取,重复2-5,直到推送成功。

//加锁
function lock($strMutex,$timeOut){
    $res=$redis->set($strMutex,1,'ex',$timeOut,'nx');
    if ($res==='OK'){
          return true;
     }
        return false;
}


// 定义锁标识
$key = 'Test_bihu_lock';
// 获取锁
$is_lock = lock($key, 10);
if ($is_lock) {
 echo 'get lock success<br>';
 echo 'do sth..<br>';
 sleep(5);
 echo 'success<br>';
 //解锁
 $redis->del($key);
} else { //获取锁失败
  echo 'request too frequently<br>';
}

10.分布式锁(常用)

为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

  1. 互斥性。在任意时刻,只有一个客户端能持有锁。
  2. 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
  3. 具有容错性。只要大部分的Redis节点正常运行,客户端就可以加锁和解锁。
  4. 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了
<?php
class RedLock
{
    private $retryDelay;
    private $retryCount;
    private $clockDriftFactor = 0.01;
    private $quorum;
    private $servers = array();
    private $instances = array();
    function __construct(array $servers, $retryDelay = 200, $retryCount = 3)
    {
        $this->servers = $servers;
        $this->retryDelay = $retryDelay;
        $this->retryCount = $retryCount;
        $this->quorum  = min(count($servers), (count($servers) / 2 + 1));
    }
    public function lock($resource, $ttl)
    {
        $this->initInstances();
        $token = uniqid();
        $retry = $this->retryCount;
        do {
            $n = 0;
            $startTime = microtime(true) * 1000;
            foreach ($this->instances as $instance) {
                if ($this->lockInstance($instance, $resource, $token, $ttl)) {
                    $n++;
                }
            }
            # Add 2 milliseconds to the drift to account for Redis expires
            # precision, which is 1 millisecond, plus 1 millisecond min drift
            # for small TTLs.
            $drift = ($ttl * $this->clockDriftFactor) + 2;
            $validityTime = $ttl - (microtime(true) * 1000 - $startTime) - $drift;
            if ($n >= $this->quorum && $validityTime > 0) {
                return [
                    'validity' => $validityTime,
                    'resource' => $resource,
                    'token'    => $token,
                ];
            } else {
                foreach ($this->instances as $instance) {
                    $this->unlockInstance($instance, $resource, $token);
                }
            }
            // Wait a random delay before to retry
            $delay = mt_rand(floor($this->retryDelay / 2), $this->retryDelay);
            usleep($delay * 1000);
            $retry--;
        } while ($retry > 0);
        return false;
    }
    public function unlock(array $lock)
    {
        $this->initInstances();
        $resource = $lock['resource'];
        $token    = $lock['token'];
        foreach ($this->instances as $instance) {
            $this->unlockInstance($instance, $resource, $token);
        }
    }
    private function initInstances()
    {
        if (empty($this->instances)) {
            foreach ($this->servers as $server) {
                list($host, $port, $timeout) = $server;
                $redis = new Redis();
                $redis->connect($host, $port, $timeout);
                $this->instances[] = $redis;
            }
        }
    }
    private function lockInstance($instance, $resource, $token, $ttl)
    {
        return $instance->set($resource, $token, ['NX', 'PX' => $ttl]);
    }
    private function unlockInstance($instance, $resource, $token)
    {
        $script = '
            if redis.call("GET", KEYS[1]) == ARGV[1] then
                return redis.call("DEL", KEYS[1])
            else
                return 0
            end
        ';
        return $instance->eval($script, [$resource, $token], 1);
    }
}  

使用示例:

<?php
require_once __DIR__ . '/../src/RedLock.php';
$servers = [
    ['127.0.0.1', 6379, 0.01],
    ['127.0.0.1', 6389, 0.01],
    ['127.0.0.1', 6399, 0.01],
];
$redLock = new RedLock($servers);
while (true) {
    $lock = $redLock->lock('test', 10000);
    if ($lock) {
        print_r($lock);
    } else {
        print "Lock not acquired
";
    }
}

  

原文地址:https://www.cnblogs.com/jackzhuo/p/12957115.html