PHP使用SW-X框架的Redis-UML组件,对用户信息进行缓存读写

Posted PHP初学机

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PHP使用SW-X框架的Redis-UML组件,对用户信息进行缓存读写相关的知识,希望对你有一定的参考价值。

前言

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

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


1、Redis-UML介绍

Redis-UML组件,类似于游戏项目中的人物建模,它是一种面向单个数据表创建缓存模型的概念。 UML有着类似于mysql-ORM的便捷操作语法支持,同时可自由开关数据更新时的缓存刷新标记,用于读取更新的缓存对象都有哪些,便于将缓存内容回写到数据库中。

2、UML的初衷

由于项目业务需要用到Reids缓存Mysql的更热新数据,进而减少数据库在​​update​​与​​select​​时对单个数据表的频繁操作(这样的高频操作往往会将整个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));

      以上是关于PHP使用SW-X框架的Redis-UML组件,对用户信息进行缓存读写的主要内容,如果未能解决你的问题,请参考以下文章

PHP 基于 SW-X 框架,搭建高性能API架构

PHP 基于 SW-X 框架,搭建高性能API架构

PHP 基于 SW-X 框架,搭建RPC微服务支持

PHP使用组件构建自己的PHP框架使用 Eloquent 模型进行数据库操作

PHP使用组件构建自己的PHP框架使用 Eloquent 模型进行数据库操作

PHP使用组件构建自己的PHP框架