Blogger Information
Blog 12
fans 0
comment 0
visits 8775
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
PHP使用SW-X框架的Redis-UML组件,对用户信息进行缓存读写
广州PHP
Original
584 people have browsed it

前言

官网地址: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、测试结果

Statement of this Website
The copyright of this blog article belongs to the blogger. Please specify the address when reprinting! If there is any infringement or violation of the law, please contact admin@php.cn Report processing!
All comments Speak rationally on civilized internet, please comply with News Comment Service Agreement
0 comments
Author's latest blog post