关于php框架:PHP使用SWX框架的RedisUML组件对用户信息进行缓存读写

3次阅读

共计 4769 个字符,预计需要花费 12 分钟才能阅读完成。

前言

官网地址:SW- X 框架 - 专一高性能便捷开发而生的 PHP-SwooleX 框架

心愿各大佬举起小手,给小弟一个 star:https://github.com/swoolex/swoolex

1、Redis-UML 介绍

Redis-UML 组件,相似于游戏我的项目中的人物建模,它是一种面向单个数据表创立缓存模型的概念。

UML 有着相似于 Mysql-ORM 的便捷操作语法反对,同时可自在开关数据更新时的缓存刷新标记,用于读取更新的缓存对象都有哪些,便于将缓存内容回写到数据库中。

2、UML 的初衷

因为我的项目业务须要用到 Reids 缓存 Mysql 的更热新数据,进而缩小数据库在 updateselect时对单个数据表的频繁操作(这样的高频操作往往会将整个 Mysql 服务器的性能拖垮)。

在以前,咱们通常会这样实现:

$Db = new \x\Db();
$Redis = new \x\Redis();
// 先查询数据库
$user = $Db->name('user')->where('id', 1)->find();
// 再更新到 redis
$res = $Redis->hmset($key.$user['id'], $user);

// 须要取得该数据缓存的时候,就须要这样拿
$user = $Redis->hgetall($key.$user['id']);

该过程看似没有问题,但却只实现了对 update 场景的优化,实际上并没有解决 select 的场景,因为往往在业务过程中,select 占据了最频繁的地位。

因为一旦将 update 由缓存代替后,那 Mysql 查出的数据将不肯定是最新的,这时候只能依赖缓存查问,而传统的实现形式又只能实现单条缓存读取,就变成了上面的读取形式:

$Db = new \x\Db();
$Redis = new \x\Redis();
// 先查数据库
$list = $Db->name('user')->field('id')->where('region_code = 430000')>where('status = 1 OR status = 3')->select();
// 再循环从 Redis 中取得
foreach ($list as $k=>$v) {
    // 指定须要返回的字段名
    $list[$k] = $Redis->hmget($key.$v['id'], ['id', 'name', 'phone']);
}

而 UML 组件的呈现,就是为了解决这样的业务场景。

$User = new \box\uml\User();
// 查问条件
$where = [];
$where[] = ['region_code', '=', 430000];
$where[] = ['status', '=', [1, 3]];
// 间接从查问中取得
$list = $User->where($where)->field('id, name, phone')->select();
// 你还能够写成
$list = $User->field('id, name, phone')
        ->where('status', 430000)
        ->where('region_code', [1, 3])
        ->limit(1000)
        ->order('id DESC, phone ASC')
        ->select();

3、UML 的优缺点

A、长处

1、UML 反对相似 Mysql-ORM 的查问语法,所以它能够很轻易的接替 Mysql 的大部分日常读写工作。2、UML 是由多种 Redis 数据结构组合而成的查问组件,其中查问条件能够依据模型进行配置,相似于查问索引的概念,能够很好的管制缓存所占用的内存开销。3、UML 反对开启缓存回写的数据标记,配合定时器组件很容易就能实现 Redis 对 Mysql 的缓存回写。

B、毛病

1、UML 不具备事务性,与 Redis 的 hset 一样,当不开启 Redis 事务时,无奈保证数据的并发批改。2、UML 的 like 含糊匹配时把记录集从缓存中检索进去后,再进行遍历匹配的,所以这块会存在肯定的内存开销,应用该查问反对时须要对前置的数据量进行肯定的限度,否则很可能会引起大量的内存开销。

4、条件语法的优先级

UML 的查问逻辑跟 Mysql-ORM 的不同,语法存在执行优先级的关系。

UML 的查问逻辑,共有 4 种,别离为:id()geo()where()like()

执行优先级为:

当应用 id()时,示意后续三种条件均不再应用,只操作主键数据。当应用 geo()时,示意先从 Redis-Geo 中查问数据,再从返回的数据中通过条件 where()、like()再次过滤。当同时应用 where()和 like(),再示意依据 where()查问出数据,再在 PHP 内存中进行 like()匹配过滤。

留神:UML 中不容许取得整个记录表的信息,因为那样须要 keys(‘*’)的操作,组件是不容许的。

5、创立测试模型

因为 UML 组件是基于 Redis 连接池实现的,所以须要先到 /config/redis.php 配置文件中,批改 Redis 对应的连贯参数。

该案例,是对标一个 司机 - 用户信息表,名称为:Driver,存储地位:\app\uml\Driver.php

namespace app\uml;
use x\redis\UML;

class Driver extends UML
{
    /**
     * 应用的 Redis 连接池标识
    */
    protected $driver = 'default';
    /**
     * 应用哪个 Redis 表存储
    */
    protected $database = 12;
    /**
     * 是否开启回写记录
    */
    protected $timer = true;
    /**
     * 主键字段
    */
    protected $primary = 'id';
    /**
     * 建模必传对象
    */
    protected $field_rule = [
        'id', // 主键值
        'driver_sn', // 编号
        'status', // 状态  1. 在线闲暇 2. 在线工作中  3. 离线
        'region_id', // 地区 ID
        'real_name', // 实在姓名
        'lng', // 经度
        'lat', // 纬度
        'add_time', // 增加工夫
    ];
    /**
     * 一般查问规定
    */
    protected $query_rule = ['id' => ['equal'], // 等于查问
        'status' => ['equal'], // 等于查问
        'region_id' => ['equal', 'range'], // 等于查问 OR 范畴查问
        'driver_sn' => ['equal'], // 等于查问
        'real_name' => ['equal'], // 等于查问
        'add_time' => ['range'], // 日期查问
    ];
    /**
     * geo 配置规定
    */
    protected $geo_rule = [
        'longitude' => 'lng', // 经度
        'latitude' => 'lat', // 纬度
    ];
}

6、通过 HTTP 服务,创立测试数据

HTTP 服务的,/app/http/Index.php控制器,写入以下代码:

namespace app\http;
use x\controller\Http;
use app\uml\Driver;

class Index extends Http
{
    /**
     * @RequestMapping(route="/test1")
    */
    public function index() {
        // 生成随机数据
        $all = [];
        $time = time();
        // 10W 测试
        for ($i=0; $i<=100000; $i++) {$all[] = ['id' => ($i+1),
                'status' => rand(1, 3),
                'driver_sn' => substr(md5($i), 0, 2).$i,
                'region_id' => rand(100, 200),
                'real_name' => \x\built\Str::randChinese(),
                'lng' => \x\common\Money::randomFloat(113.100000, 116.999999, 6),
                'lat' => \x\common\Money::randomFloat(23.100000, 25.999999, 6),
                'add_time' => $time+$i,
            ];
        }
        $Driver = new Driver();
        $num = $Driver->insertAll($all, 5000);
        return $this->fetch($num);
    }
}

重启服务,浏览器拜访http://IP: 端口 /test1,运行脚本。

倡议:下面的脚本我是用 3 核 +8G 的机器一起跑完的【大略须要个 30 秒,不要惊恐】,如果配置更差的同学记得本人批改测试量,或者分批运行,不要示弱。

7、UML 语法测试

HTTP 服务的,/app/http/Index.php控制器,改为以下代码:

namespace app\http;
use x\controller\Http;
use app\uml\Driver;

class Index extends Http
{
    /**
     * @RequestMapping(route="/test2")
    */
    public function index() {
        $html = '';

        // 统计内存
        $StartMemory = memory_get_usage();
        // 统计耗时
        $StartTime = microtime(true);

        $Driver = new Driver();
        // 查问 ID 等于 3 的数据
        $info = $Driver->id(3)->find();
        $html .= '场景一:'.dd($info);
        // 将其批改为工作中
        $res = $Driver->id(3)->update(['status' => 2]);
        $html .= '场景二:'.dd($res);
        // 查问在线,并且地区在 150 的司机
        $list = $Driver->where('status', [1, 2])->where('region_id', 150)->select(); 
        $html .= '场景三:'.dd(count($list));
        // 查问在线,并且地区在 150,同时要是姓林的司机
        $list = $Driver->where('status', [1, 2])->where('region_id', 150)->like('real_name', '林', '%s')->select(); 
        $html .= '场景四:'.dd(count($list));
        // 查问在线,并且在 geo 半径 5 公里内,地区在 100 - 120 之间的司机
        $list = $Driver->geo(113.402618, 23.149329, 5)->where('status', [1, 2])->where('region_id', 'range', [100, 120])->select();
        $html .= '场景五:'.dd(count($list));

        $StopTime = microtime(true);
        $TimeSpent=$StopTime-$StartTime;
        $html .= dd('上述总耗时:'.number_format($TimeSpent*1000, 4).'毫秒');
        $StopMemory = memory_get_usage();
        $Memory = $StopMemory-$StartMemory;
        $html .= dd('上述总耗内存:'.$this->formatSize($Memory));

        return $this->fetch($html);
    }

    // 计算内存单位
    function formatSize($bytes) {$units = ['B', 'KB', 'MB', 'GB', 'TB'];
            for ($i = 0; $bytes >= 1024 && $i < 4; $i++) $bytes /= 1024;
        return round($bytes, 2) . $units[$i];
    }
}

8、测试后果

正文完
 0