前言

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

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

1、Redis-UML介绍

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

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

2、UML的初衷

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

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

  1. $Db = new \x\Db();
  2. $Redis = new \x\Redis();
  3. // 先查询数据库
  4. $user = $Db->name('user')->where('id', 1)->find();
  5. // 再更新到redis
  6. $res = $Redis->hmset($key.$user['id'], $user);
  7. // 需要获得该数据缓存的时候,就需要这样拿
  8. $user = $Redis->hgetall($key.$user['id']);

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

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

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

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

  1. $User = new \box\uml\User();
  2. // 查询条件
  3. $where = [];
  4. $where[] = ['region_code', '=', 430000];
  5. $where[] = ['status', '=', [1, 3]];
  6. // 直接从查询中获得
  7. $list = $User->where($where)->field('id, name, phone')->select();
  8. // 你还可以写成
  9. $list = $User->field('id, name, phone')
  10. ->where('status', 430000)
  11. ->where('region_code', [1, 3])
  12. ->limit(1000)
  13. ->order('id DESC, phone ASC')
  14. ->select();

3、UML的优缺点

A、优点

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

B、缺点

  1. 1UML不具备事务性,与Redishset一样,当不开启Redis事务时,无法保证数据的并发修改。
  2. 2UMLlike模糊匹配时把记录集从缓存中检索出来后,再进行遍历匹配的,所以这块会存在一定的内存开销,使用该查询支持时需要对前置的数据量进行一定的限制,否则很可能会引起大量的内存开销。

4、条件语法的优先级

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

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

执行优先级为:

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

注意:UML中不允许获得整个记录表的信息,因为那样需要keys(‘*’)的操作,组件是不允许的。

5、创建测试模型

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

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

  1. namespace app\uml;
  2. use x\redis\UML;
  3. class Driver extends UML
  4. {
  5. /**
  6. * 使用的Redis连接池标识
  7. */
  8. protected $driver = 'default';
  9. /**
  10. * 使用哪个Redis表存储
  11. */
  12. protected $database = 12;
  13. /**
  14. * 是否开启回写记录
  15. */
  16. protected $timer = true;
  17. /**
  18. * 主键字段
  19. */
  20. protected $primary = 'id';
  21. /**
  22. * 建模必传对象
  23. */
  24. protected $field_rule = [
  25. 'id', // 主键值
  26. 'driver_sn', // 编号
  27. 'status', // 状态 1.在线空闲 2.在线工作中 3.离线
  28. 'region_id', // 地区ID
  29. 'real_name', // 真实姓名
  30. 'lng', // 经度
  31. 'lat', // 纬度
  32. 'add_time', // 添加时间
  33. ];
  34. /**
  35. * 普通查询规则
  36. */
  37. protected $query_rule = [
  38. 'id' => ['equal'], // 等于查询
  39. 'status' => ['equal'], // 等于查询
  40. 'region_id' => ['equal', 'range'], // 等于查询 OR 范围查询
  41. 'driver_sn' => ['equal'], // 等于查询
  42. 'real_name' => ['equal'], // 等于查询
  43. 'add_time' => ['range'], // 日期查询
  44. ];
  45. /**
  46. * geo配置规则
  47. */
  48. protected $geo_rule = [
  49. 'longitude' => 'lng', // 经度
  50. 'latitude' => 'lat', // 纬度
  51. ];
  52. }

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

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

  1. namespace app\http;
  2. use x\controller\Http;
  3. use app\uml\Driver;
  4. class Index extends Http
  5. {
  6. /**
  7. * @RequestMapping(route="/test1")
  8. */
  9. public function index() {
  10. // 生成随机数据
  11. $all = [];
  12. $time = time();
  13. // 10W测试
  14. for ($i=0; $i<=100000; $i++) {
  15. $all[] = [
  16. 'id' => ($i+1),
  17. 'status' => rand(1, 3),
  18. 'driver_sn' => substr(md5($i), 0, 2).$i,
  19. 'region_id' => rand(100, 200),
  20. 'real_name' => \x\built\Str::randChinese(),
  21. 'lng' => \x\common\Money::randomFloat(113.100000, 116.999999, 6),
  22. 'lat' => \x\common\Money::randomFloat(23.100000, 25.999999, 6),
  23. 'add_time' => $time+$i,
  24. ];
  25. }
  26. $Driver = new Driver();
  27. $num = $Driver->insertAll($all, 5000);
  28. return $this->fetch($num);
  29. }
  30. }

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

建议:上面的脚本我是用3核+8G的机器一起跑完的【大概需要个30秒,不要惊慌】,如果配置更差的同学记得自己修改测试量,或者分批运行,不要逞强。

7、UML语法测试

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

  1. namespace app\http;
  2. use x\controller\Http;
  3. use app\uml\Driver;
  4. class Index extends Http
  5. {
  6. /**
  7. * @RequestMapping(route="/test2")
  8. */
  9. public function index() {
  10. $html = '';
  11. // 统计内存
  12. $StartMemory = memory_get_usage();
  13. // 统计耗时
  14. $StartTime = microtime(true);
  15. $Driver = new Driver();
  16. // 查询ID等于3的数据
  17. $info = $Driver->id(3)->find();
  18. $html .= '场景一:'.dd($info);
  19. // 将其修改为工作中
  20. $res = $Driver->id(3)->update([
  21. 'status' => 2
  22. ]);
  23. $html .= '场景二:'.dd($res);
  24. // 查询在线,并且地区在150的司 机
  25. $list = $Driver->where('status', [1, 2])->where('region_id', 150)->select();
  26. $html .= '场景三:'.dd(count($list));
  27. // 查询在线,并且地区在150,同时要是姓林的司 机
  28. $list = $Driver->where('status', [1, 2])->where('region_id', 150)->like('real_name', '林', '%s')->select();
  29. $html .= '场景四:'.dd(count($list));
  30. // 查询在线,并且在geo半径5公里内,地区在100 - 120 之间的司 机
  31. $list = $Driver->geo(113.402618, 23.149329, 5)->where('status', [1, 2])->where('region_id', 'range', [100, 120])->select();
  32. $html .= '场景五:'.dd(count($list));
  33. $StopTime = microtime(true);
  34. $TimeSpent=$StopTime-$StartTime;
  35. $html .= dd('上述总耗时:'.number_format($TimeSpent*1000, 4).'毫秒');
  36. $StopMemory = memory_get_usage();
  37. $Memory = $StopMemory-$StartMemory;
  38. $html .= dd('上述总耗内存:'.$this->formatSize($Memory));
  39. return $this->fetch($html);
  40. }
  41. // 计算内存单位
  42. function formatSize($bytes) {
  43. $units = [' B', ' KB', ' MB', ' GB', ' TB'];
  44. for ($i = 0; $bytes >= 1024 && $i < 4; $i++) $bytes /= 1024;
  45. return round($bytes, 2) . $units[$i];
  46. }
  47. }

8、测试结果

更多相关文章

  1. Android(安卓)查询远程服务器的工具QueryUtils
  2. android 4.4 webview加载部分https网页白屏
  3. Android(安卓)使用finalBitmap实现缓存读取
  4. android使用util工具包
  5. 【从头学android】在两个Activity之间实现界面切换
  6. android 将bitmap缓存到本地
  7. google地图demo
  8. Android下的SQLite数据库的相关操作及AndroidTestCase测试
  9. Android(安卓)Retrofit 笔记之一使用拦截器设置缓存

随机推荐

  1. C++函数与指针
  2. 关于ASP.NET如何利用AjaxPro完成前端跟后
  3. 介绍MVC、MVP和MVVM的区别与用法
  4. 学习asp.net的学习顺序与学习内容分享
  5. asp.net如何利用ashx生成图形验证码的实
  6. ASP.NET中有关Config文件的读写功能讲解
  7. 几种RIA技术介绍
  8. C#中的正则表达式如何验证中文字符的实例
  9. Asp.Net用控件实现本地图片的显示
  10. 理解ASP.NET中多层架构