yii2 redis扩展
参考资料:http://www.yiichina.com/tutorial/904
1、redis扩展在2.6版本以前,set方法有改变,所以,先记录一下高版本的方式:
PHP Code复制内容到剪贴板
- // 增加配置文件,上面的fileCache 是文件缓存,随便用不用,删了也可以
- 'components' => [
- 'fileCache' => [
- 'class' => 'yii\caching\FileCache',
- 'dirMode' => 0777 // 防止console生成的目录导致web账户没写权限
- ],
- 'redis' => [
- 'class' => 'yii\redis\Cache',
- 'redis' => [
- 'hostname' => 'localhost',
- 'port' => 6379,
- 'database' => 0,
- ]
- ],
redis的方法就是,get,set,如果是数组,高版的redis是会对value进行序列化,然后读取的时候反序列化的
来试一下,第三个参数表示过期时间:
PHP Code复制内容到剪贴板
- <?php
- $expire = 60*60;
- $user_cart_rediskey = "key1";
- $transaction = \Yii::$app->db->beginTransaction();
- try{
- // 如果该用户购物车没有数据
- $arr1 = [
- $course_id => $carts[0]
- ];
- $r = \Yii::$app->redis->set($user_cart_rediskey,$arr1,$expire);
- if(!$r){
- throw new \Exception('下手太快,道理拥堵,请重试!');
- };
- $arr2 = [
- $plan_cart_rediskey => $carts[0]
- ];
- $r = \Yii::$app->redis->set($plan_cart_rediskey,$arr2,$expire);
- if(!$r){
- throw new \Exception('购物车更新失败,请重试!');
- };
- //以上执行都成功,则对数据库进行实际执行
- $transaction->commit();
- return $this->buildResponse('success', 200, 'success to add cart');
- }catch (\Exception $e){
- //如果抛出错误则进入catch,先callback,然后捕获错误,返回错误
- $transaction->rollBack();
- return $this->buildResponse('error', 400, $e->getMessage());
- }
- ?>
2、BUT 我遇到了一个redis版本是2.4的... 好吧,咱们来改一下,第三个参数过期时间是报错的,来看一下报错信息:
PHP Code复制内容到剪贴板
- Redis error: ERR wrong number of arguments for 'set' command\nRedis command was: SET 7615faba42795b1fb018980897442bcf a:2:{i:0;a:1:{i:82;a:4:{s:9:\"course_id\";s:2:\"82\";s:5:\"price\";s:4:\"1.00\";s:11:\"unique_code\";s:5:\"wujin\";s:6:\"status\";i:1;}}i:1;N;} PX 3600000"
好了,来改吧,首先是配置文件:
PHP Code复制内容到剪贴板
- 'redis' => [
- 'class' => 'yii\redis\Connection',
- 'hostname' => 'localhost',
- 'port' => 6379,
- 'database' => 0,
- ],
然后,GET和SET 照常用,不过得自己序列化,或者JSON了哈
可以在components新建一个Cache类:
PHP Code复制内容到剪贴板
- class Redis
- {
- /**
- * 获取指定值
- * @param $key
- * @return mixed
- */
- public static function get($key){
- $value = \Yii::$app->redis->get($key);
- $value_serl = @unserialize($value);
- if(is_object($value_serl)||is_array($value_serl)){
- return $value_serl;
- }
- return $value;
- }
- /**
- * @desc 获取锁键名
- */
- public static function getLockCacheKey($key)
- {
- return "lock_{$key}";
- }
- /**
- * @desc 获取锁
- * @param $key string | 要上锁的键名
- * @param null $timeout int | 上锁时间
- * @return bool
- */
- public static function getLock($key, $timeout = NULL)
- {
- $timeout = $timeout ? $timeout : 10;
- $lockCacheKey = self::getLockCacheKey($key);
- $expireAt = time() + $timeout;
- $isGet = (bool)\Yii::$app->redis->setnx($lockCacheKey, $expireAt);
- if ($isGet) {
- return true;
- }else if(!$isGet){
- usleep(1);
- $time = time();
- $oldExpire = \Yii::$app->redis->get($lockCacheKey);
- if ($oldExpire >= $time) {
- return true;
- }
- $newExpire = $time + $timeout;
- $expireAt = \Yii::$app->redis->getset($lockCacheKey, $newExpire);
- if ($oldExpire != $expireAt) {
- return false;
- }
- return true;
- }
- }
- /**
- * @desc 释放锁
- * @param $key string | 加锁的字段
- * @param $newExpire int | 加锁的截止时间
- * @return bool | 是否释放成功
- */
- public static function releaseLock($key)
- {
- $lockCacheKey = self::getLockCacheKey($key);
- return \Yii::$app->redis->del($lockCacheKey);
- }
- /**
- * 设置新的键值和过期时间
- * @param $key
- * @param $value
- * @param $expire 过期时间,可选
- * @return mixed
- */
- public static function set($key,$value,$expire = 0){
- if(is_object($value)||is_array($value)){
- $value = serialize($value);
- }
- if($expire){
- $r = \Yii::$app->redis->setex($key,$expire,$value);
- return $r;
- }else{
- return \Yii::$app->redis->set($key,$value);
- }
- }
- /**
- * 返回指定键的过期时间,会有-1的存在
- * @param $key
- * @return mixed
- */
- public static function getExpireTime($key){
- return \Yii::$app->redis->ttl($key);
- }
- /**
- * 设置指定键的过期时间
- * @param $key
- * @param $expire
- * @return mixed
- */
- public static function expire($key,$expire){
- return \Yii::$app->redis->expire($key,$expire);
- }
- /**
- * 查找所有相似值
- * @param $key 如 foo*
- * @return mixed
- */
- public static function getLikeValues($key){
- $r = \Yii::$app->redis->keys($key);
- return $r;
- }
- public static function lpush($key,$value){
- if(is_object($value)||is_array($value)){
- $value = serialize($value);
- }
- return \Yii::$app->redis->lpush($key,$value);
- }
- }
这里有一个大大的坑,如果有过期时间,时间是放在中间的,然后value是搁在后面的!
试了一下队列与原子,判断还是出错,
PHP Code复制内容到剪贴板
- $plan_cart = Redis::getLikeValues("course:".$plan_id.":*"); //获取该计划下购物车的json
- $cartNum = count($plan_cart);
- if($cartNum > ($courses['limited']-1)){
- exit('该课程已经被购完');
- };
- // 像这种的并发高了以后完全不能用...,来加个锁:
- /* 锁定 */
- $lock_key = "course:".$plan_id;
- $lock = Redis::getLock($lock_key);
- if(!$lock){
- exit('道路拥挤,请稍候再试~');
- }
- $plan_cart = Redis::getLikeValues("course:".$plan_id.":*"); //获取该计划下购物车的json
- $cartNum = count($plan_cart);
- if($cartNum > ($courses['limited']-1)){
- exit('该课程已经被购完');
- };
- Redis::releaseLock($lock_key); //解锁
- /* end */
- 不知道是不是我哪里逻辑写的不对,反正我加了两次锁,第一次是加入购物车的动作时,还有一次在判断完以后准备 写入用户购物redis数据的时候还加了一次锁