前言
官网地址: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)); 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]; }}