Redis在工作中的应用

Redis作为常用缓存,在工作中经常要使用到,对于一些常用又不变的数据,使用Redis可以缓解查询数据库的压力。相比Memcache只有键值对,Redis支持string,hash,list,set,zset五种基本数据类型。

一 安装扩展

php7环境,下载redis.so文件,新建30-redis.ini,输入extension=redis.so
在这里插入图片描述

打开虚拟机
ini文件放在/etc/php.d

在这里插入图片描述

so文件放在/usr/lib64/php/modules/

在这里插入图片描述

二 构建基类

对安装的Redis扩展进一步封装

<?php
namespace lib;

use RedisCluster;

/**
 * Redis Cluster Lib
 * 对phpRedis进行进一步封装,对于其原生函数可直接调用
 * 但需要去除第一位的key,并通过keyConfig或者setKeyInfo进行设key
 * 支持链式调用
 */
class RedisClusterLib {
    /**
     * redis集群机器配置文件路径及配置
     * @var string
     */
    protected $INI_PATH = '';
    protected $INI_FILE = null;
    protected $INI_CONFIG = null;

    /**
     * redis各KEY配置文件路径及配置
     * @var string
     */
    protected $KEY_PATH = '';
    protected $KEY_FILE = null;
    protected $KEY_CONFIG = null;
    protected $MODULE = NULL;

    /**
     * key名各项分隔符,默认_
     * @var string
     */
    protected $KEY_DELIMITER = '_';

    /**
     * redisCluster实例
     * @var RedisCluster
     */
    protected $CLUSTER;

    /**
     * redisCluster名称,通过ini读取
     * @var null
     */
    private $CLUSTER_NAME = null;

    /**
     * RedisCluster constructor
     * @param string $clusterName
     * @param string $module
     * @param int $mode
     * 1 (default) 读主机,如果主挂了,可以读从机 2 随机主从 3 随机从机 4 只读主机
     */
    public function __construct($clusterName = 'default', $module = '', $mode = 1, $configName = '') {
        $this->INI_PATH = CONFIG_PATH . "/redis/{$clusterName}/redisCluster.ini.php";
        $this->KEY_PATH = CONFIG_PATH . "/redis/{$clusterName}/redis.key.php";

        $this->INI_FILE = include($this->INI_PATH);
        $this->KEY_FILE = include($this->KEY_PATH);

        $this->connect($clusterName, $module, $mode, $configName);
    }

    /**
     * 主动连接
     * @param $clusterName
     * @param string $module
     * @param int $mode
     * @return $this
     */
    public function connect($clusterName, $module = '', $mode = 1, $configName = '') {
        $this->CLUSTER_NAME = trim($clusterName);
        if (!empty($configName)) {
            $this->CLUSTER_NAME = $configName;
        }
        $this->MODULE = empty($module) ? $this->CLUSTER_NAME : trim($module);
        $this->INI_CONFIG = $this->INI_FILE[$this->CLUSTER_NAME];

        try {
            if (!empty($this->INI_CONFIG['nodes'])) {
                $this->CLUSTER = new RedisCluster(null, $this->INI_CONFIG['nodes'], 1, null, false, $this->INI_CONFIG['auth'] ? $this->INI_CONFIG['auth'] : null);
                $this->setMode($mode);
                // 连接成功后设置key字典
                $this->KEY_CONFIG = $this->KEY_FILE[$this->MODULE];
            } else {
                throw new \RedisClusterException('invalid redis cluster config', '010204');
            }
        } catch (\Throwable $e) {
            if (strtolower(RUN_ENVIRONMENT) == 'dev') echo $e->getCode() . ':' . $e->getMessage() . "\n";
            // cluster连接失败后记录日志
            $this->Log([
                'return' => $e->getMessage(),
                'msg' => 'connect redis cluster failed'
            ]);
            $this->setError($e->getCode());
        }

        return $this;
    }

    /**
     * 异常记录日志
     * @param $info
     * @return bool
     */
    private function Log($info) {
        Log::error([
            'type' => 'redis_cluster',
            'call_file' => __FILE__,
            'call_function' => __FUNCTION__,
            'param' => [
                'time' => date("Y-m-d H:i:s")
            ],
            'return' => empty($info['return']) ? '' : $info['return'],
            'msg' => empty($info['msg']) ? '' : $info['msg']
        ]);
        return false;
    }

    /**
     * 设置集群访问模式
     * @param $mode
     * @return $this
     */
    public function setMode($mode) {
        switch ($mode) {
            case 2:
                $this->CLUSTER->setOption(RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_DISTRIBUTE);
                break;
            case 3:
                $this->CLUSTER->setOption(RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_DISTRIBUTE_SLAVES);
                break;
            case 4:
                $this->CLUSTER->setOption(RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_NONE);
                break;
            case 1:
            default:
                $this->CLUSTER->setOption(RedisCluster::OPT_SLAVE_FAILOVER, RedisCluster::FAILOVER_ERROR);
        }
        return $this;
    }

    /**
     * 析构
     */
    public function __destruct() {
        // 断开连接
        if (is_object($this->CLUSTER)) {
            $this->CLUSTER->close();
        }
    }

    /**
     * 用于暂时存贮key相关信息
     * @var
     */
    private $keyInfo;

    /**
     * 如果需要可以读key配置生产key名,设置完后可直接链式操作
     * @param $keyConfigName
     * @param $postfixValue
     * @return $this
     */
    public function keyConfig($keyConfigName, $postfixValue = []) {
        try {
            if (!empty($this->KEY_CONFIG) && isset($this->KEY_CONFIG[$keyConfigName])) {
                $this->keyInfo = $this->KEY_CONFIG[$keyConfigName];
                // 设置后缀
                if (!empty($postfixValue)) {
                    $tmpConfigPostfix = '';
                    $tmpArrayPostfix = '';
                    // 按顺序读取配置里的postfix
                    foreach ($this->keyInfo['postfix'] as $defaultKey) {
                        if (key_exists($defaultKey, $postfixValue)) {
                            if (is_array($postfixValue[$defaultKey])) {//支持mget
                                $tmpArrayPostfix = $defaultKey;
                                continue;
                            }
                            $tmpConfigPostfix .= $this->KEY_DELIMITER . $defaultKey . $this->KEY_DELIMITER . $postfixValue[$defaultKey];
                        } else {
                            throw new \RedisClusterException('invalid postfix value', '010205');
                        }
                    }
                }
                // 设置key,有后缀的追加在后面,没后缀的直接用key
                $this->keyInfo['key'] .= empty($tmpConfigPostfix) ? '' : $tmpConfigPostfix;
                if (!empty($tmpArrayPostfix)) {
                    $tmpKeyInfo = [];
                    foreach ($postfixValue[$tmpArrayPostfix] as $sKey) {
                        $tmpKeyInfo[] = $this->keyInfo['key'] . $this->KEY_DELIMITER . $tmpArrayPostfix . $this->KEY_DELIMITER . $sKey;
                    }
                    $this->keyInfo['key'] = $tmpKeyInfo;
                }
            } else {
                throw new \RedisClusterException('invalid key config or key name', '010203');
            }
        } catch (\RedisClusterException $e) {
            if (strtolower(RUN_ENVIRONMENT) == 'dev') echo $e->getCode() . ':' . $e->getMessage() . "\n";
            // key配置不正确
            $this->Log([
                'return' => $e,
                'msg' => 'set key config failed'
            ]);
            $this->setError($e->getCode());
        }
        return $this;
    }

    /**
     * 设置临时key配置,设置完后可直接链式操作
     * @param $keyName
     * @param null $ttl
     * @return $this
     */
    public function setKeyInfo($keyName, $ttl = null) {
        $this->keyInfo = [
            'key' => $keyName,
            'ttl' => $ttl,
        ];
        return $this;
    }

    /**
     * 魔术方法
     * 实际调用redisCluster的各方法
     * @param $function
     * @param $args
     * @return $this
     */
    public function __call($function, $args) {
        // 链式操作时,如果有错直接返回error
        if (!empty($this->error)) return $this;
        try {
            if (is_object($this->CLUSTER)) {
                // key未设置直接报错
                if (empty($this->keyInfo['key'])) throw new \RedisClusterException('invalid key', '010203');

                // 第一个参数插入key
                array_unshift($args, $this->keyInfo['key']);
                $function = ltrim($function, '_');
                $result = call_user_func_array([$this->CLUSTER, $function], $args);
                $this->setResult($result);
            } else {
                throw new \RedisClusterException('invalid redis cluster', '010001');
            }
        } catch (\RedisClusterException $e) {
            if (strtolower(RUN_ENVIRONMENT) == 'dev') echo $e->getCode() . ':' . $e->getMessage() . "\n";
            // cluster操作失败后记录日志
            $this->Log([
                'return' => $e,
                'msg' => 'call redis cluster function failed'
            ]);
            $this->setError($e->getCode());
        }
        return $this;
    }

    /**
     * 设置字符串型值
     * @param string $value 值
     * @param null $timeout 过期时间,支持formatTTL转换
     * 默认null 读取配置内ttl
     *     -1   永不过期
     * @return $this
     */
    public function set($value, $timeout = null) {
        // 默认取配置
        if (!isset($timeout) && !empty($this->keyInfo)) $timeout = $this->keyInfo['ttl'];
        // 解析格式
        $timeout = $this->formatTTL($timeout);
        $params = (!isset($timeout) || $timeout <= 0) ? [] : ['ex' => $timeout];

        return $this->_set($value, $params);
    }

    /**
     * 如果key不存在就设置一个值,能确保原子性
     * @param $value
     * @param $timeout
     */
    public function setIfNotExist($value, $timeout = null) {
        // 默认取配置
        if (!isset($timeout) && !empty($this->keyInfo)) $timeout = $this->keyInfo['ttl'];
        // 解析格式
        $timeout = $this->formatTTL($timeout);
        $params = ['nx'];
        if ($timeout > 0) $params['ex'] = $timeout;

        return $this->_set($value, $params);
    }

    /**
     * 设置过期时间,可用于刷新
     * @param $timeout -过期时间, 支持formatTTL转换
     * 默认null 读取配置内ttl -1   永不过期
     * @return $this
     */
    public function expire($timeout = null) {
        // 默认取配置
        if (!isset($timeout) && !empty($this->keyInfo)) $timeout = $this->keyInfo['ttl'];
        // 取完配置仍为null,给-1
        if (!isset($timeout)) $timeout = -1;
        // 解析格式
        $timeout = $this->formatTTL($timeout);
        // 如果为-1,调用persist,去除过期时间,正常数字调用expire
        return $timeout === -1 ? $this->_persist() : $this->_expire($timeout);

    }

    /**
     * @brief 在redis中查找键值是否存在
     * @note  在redis中查找键值是否存在
     */
    public function exist() {
        $bRet = false;
        $aResult = $this->exists()->end();

        if ($aResult['status'] == 1 && ($aResult['result'] == 1 || $aResult['result'] === true)) {
            $bRet = true;
        }

        return $bRet;
    }

    /**
     * @brief   查看某一元素是否存在集合中
     * @param string $value 元素值
     */
    public function sIsMemberU($value) {
        $bRet = false;
        $aResult = $this->_sismember($value)->end();

        if ($aResult['status'] == 1 && ($aResult['result'] == 1 || $aResult['result'] === true)) {
            $bRet = true;
        }

        return $bRet;
    }

    /**
     * TTL时间转换函数
     * @param $type
     *  -1 永不过期
     *  纯数字 过期秒数
     *  today 此刻至23:59:59
     *  24h   24小时整
     * @return false|float|int|null
     */
    private function formatTTL($type) {
        $ttl = null;
        switch ($type) {
            case null:
                break;
            case -1:
                $ttl = -1;
                break;
            case is_numeric($type):
                $ttl = intval($type);
                break;
            case 'today':
                $ttl = strtotime(date("Y-m-d", strtotime("+1 day"))) - time();
                break;
            case '24h':
                $ttl = 3600 * 24;
                break;
            case '1h':
                $ttl = 3600;
                break;
        }
        return $ttl;
    }

    /**
     * 结束函数
     * 如果有错返回error,没有错返回result
     * @return array
     */
    public function end() {
        return empty($this->error) ? $this->result : $this->error;
    }

    /**
     * 返回结果
     * @var null
     */
    public $result = null;

    /**
     * 设置返回结果
     * @param $result
     * @return array|null
     */
    private function setResult($result) {
        return $this->result = ['status' => 1, 'result' => $result];
    }

    /**
     * 增加错误属性,用于返回错误信息
     * @var null
     */
    public $error = null;

    /**
     * 发生错误时,设置错误CODE
     * @param $errorCode
     * @return array
     */
    private function setError($errorCode) {
        if (empty($errorCode)) $errorCode = '999999';
        $this->result = null;
        $errorCode = str_pad($errorCode, 6, 0, STR_PAD_LEFT);

        return $this->error = ['status' => 0, 'error_code' => $errorCode];
    }

    public function getRedisClusterInstance() {
        return $this->CLUSTER;
    }
}

三 服务器节点的配置

开发环境两台服务器,六个节点。

 return [
     'base' => [
         'auth' => '123456',
         'nodes' => [
             '10.100.6.114:7000',
             '10.100.6.114:7001',
             '10.100.6.114:7002',
             '10.100.6.115:7000',
             '10.100.6.115:7001',
             '10.100.6.115:7002',
         ]
     ]
 ];

四 模块的分类

return [
    'company' => [ //模块名
        'basecompany' => [
            'key' => 'BASECOMPANY_V1', //前缀
            'postfix' => [
                'coid' //字段
            ],
            'ttl' => 86400, //有效期
        ],
    ],
];

五 使用方式

初始化

$this->redis = new RedisClusterLib('base', 'company');

设置数据

$this->redis->keyConfig('basecompany', ['coid' => '123456'])->set($data)->end();

获取数据

$this->redis->keyConfig('basecompany', ['coid' => '123456'])->get()->end();

清除数据

$this->redis->keyConfig('basecompany', ['coid' => '123456'])->del()->end();

比如限制用户每天登录输错10次,可以定义一个key,每次让其自增。

//首次
$term = strtotime(date("Y-m-d") . " 23:59:59") - time(); //当天剩余时间
$term = $term <= 0 ? 1 : $term; //最小ttl
$this->redis->keyConfig('login', ['hruid' => '123456'])->incr()->expire($term)->end();
//其他次
$this->redis->keyConfig('login', ['hruid' => '123456'])->incr()->end();

以上就是Redis的使用总结。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值