RedisToMysql.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: wangfanchang
  5. * Date: 18/4/24
  6. * Time: 上午11:06
  7. */
  8. namespace app\admin\command;
  9. use app\common\library\Redis;
  10. use app\common\model\User;
  11. use app\main\constants\CacheConstants;
  12. use think\console\Command;
  13. use think\console\Input;
  14. use think\console\input\Option;
  15. use think\console\Output;
  16. use think\Config;
  17. use app\common\model\Book;
  18. use think\Db;
  19. use think\Log;
  20. use think\Request;
  21. class RedisToMysql extends Command
  22. {
  23. protected function configure()
  24. {
  25. $this
  26. ->setName('redisToMysql')
  27. ->addOption('type', 't', Option::VALUE_OPTIONAL, '要处理的缓存类型,all:默认全部处理,bn:书籍阅读数,ubc:用户行为标签, quv:自定义二维码UV统计', 'all')
  28. ->setDescription('将redis缓存中的数据存入mysql数据库');
  29. }
  30. protected function execute(Input $input, Output $output)
  31. {
  32. Request::instance()->module('admin'); //cli模式下无法获取到当前的项目模块,手动指定一下
  33. $type = $input->getOption('type');
  34. switch ($type) {
  35. case 'all':
  36. $this->customQrCodeUvCollect($input, $output);
  37. $output->writeln("Type: all 全部处理");
  38. $this->bn($input, $output);
  39. // //0408 去掉用户喜好字段维护
  40. //$this->ubc($input, $output);
  41. break;
  42. case 'bn':
  43. $output->writeln("Type: bn 书籍阅读数");
  44. $this->bn($input, $output);
  45. break;
  46. case 'ubc':
  47. // //0408 去掉用户喜好字段维护
  48. // $output->writeln("Type: ubc 用户行为标签");
  49. // $this->ubc($input, $output);
  50. $output->writeln("Type: ubc 用户行为标签停止维护");
  51. break;
  52. case 'quv':
  53. $output->writeln("Type: quv 自定义二维码UV");
  54. $this->customQrCodeUvCollect($input, $output);
  55. break;
  56. default:
  57. $output->writeln("Type: {$type} 无法识别的类型");
  58. }
  59. $output->writeln("处理完毕!");
  60. }
  61. /**
  62. * 处理自定义二维码 UV 统计
  63. * @param Input $input
  64. * @param Output $output
  65. */
  66. private function customQrCodeUvCollect(Input $input, Output $output)
  67. {
  68. try {
  69. $output->writeln("处理自定义二维码UV统计---开始");
  70. Log::info("处理自定义二维码UV统计---开始");
  71. $redis = Redis::instance();
  72. Db::table('custom_qrcode')->chunk(100, function ($result) use ($redis) {
  73. foreach ($result as $val) {
  74. if ($uv = $redis->pfCount("QR_UV:{$val['admin_id']}:{$val['index']}")) {
  75. $is_update = model('CustomQrcode')->where('id', $val['id'])->update(['uv' => $uv]);
  76. if ($is_update !== false) {
  77. Log::info("处理自定义二维码UV统计: id:{$val['id']} set uv:{$uv} success");
  78. } else {
  79. Log::error("处理自定义二维码UV统计: id:{$val['id']} set uv:{$uv} fail");
  80. }
  81. }
  82. }
  83. sleep(5);
  84. });
  85. $output->info("处理自定义二维码UV统计---完成");
  86. Log::info("处理自定义二维码UV统计---完成");
  87. } catch (\Exception $e) {
  88. $output->writeln('处理自定义二维码UV统计---失败' . $e->getMessage());
  89. Log::error('处理自定义二维码UV统计---失败' . $e->getMessage());
  90. }
  91. }
  92. /**
  93. * 链接redis
  94. */
  95. private function connect($idx)
  96. {
  97. $redis = null;
  98. $config = Config::get('redis');
  99. $list = explode(';', $config['list']); //共n个redis
  100. foreach ($list as $item) {
  101. $con = explode('=', $item); // 0 编号 1 配置
  102. if (count($con) >= 2) {
  103. if ($con[0] == $idx) {
  104. $c = explode(':', $con[1]); //配置 0IP 1端口 2密码 3库编号
  105. if (count($c) >= 2) {
  106. $redis = new \Redis();
  107. if ($config['pconnect']) {
  108. $redis->pconnect($c[0], $c[1], $config['timeout']);
  109. } else {
  110. $redis->connect($c[0], $c[1], $config['timeout']);
  111. }
  112. if (count($c) >= 3 && $c[2]) {
  113. $redis->auth($c[2]);
  114. }
  115. if (count($c) >= 4 && is_numeric($c[3])) {
  116. $redis->select($c[3]);
  117. }
  118. }
  119. return $redis;
  120. }
  121. }
  122. }
  123. return $redis;
  124. }
  125. /**
  126. * 根据key获取redis编号
  127. * @param $key
  128. */
  129. private function getRedis($key)
  130. {
  131. $default = Config::get('redis.default', 0); //默认redis编号
  132. $rules = Config::get('redis.rules');
  133. $list = explode(';', $rules);
  134. foreach ($list as $item) {
  135. $con = explode('=', $item); // 0 编号 1 配置
  136. if (count($con) >= 2) {
  137. $c = explode(',', $con[1]); //规则
  138. foreach ($c as $it) {
  139. if ($it) {
  140. if (strpos($key, $it) === 0) { //查找到,且必须为第一个0
  141. $default = $con[0];
  142. break 2;
  143. }
  144. }
  145. }
  146. }
  147. }
  148. return $this->connect($default);
  149. }
  150. /**
  151. * 处理书籍阅读数
  152. */
  153. private function bn(Input $input, Output $output)
  154. {
  155. $output->writeln("处理书籍阅读数---开始");
  156. Log::info("处理书籍阅读数---开始");
  157. $key = 'B-N';
  158. $redis = $this->getRedis($key);
  159. if ($redis) {
  160. $hash = $redis->hGetAll($key);
  161. $model = new Book();
  162. $i = 0;
  163. foreach ($hash as $book => $num) {
  164. $data = [
  165. 'read_num' => ['exp', "`read_num`+{$num}"]
  166. ];
  167. $model->update($data, ['id' => $book]);
  168. $redis->hDel($key, $book);
  169. $redis->del('B:' . $book);
  170. $redis->del(CacheConstants::BOOK_USER_READ_COUNT . $book);
  171. $i++;
  172. if ($i % 1000 === 0) { //每处理1000个hash name 延时1秒
  173. $output->writeln("处理书籍阅读数---处理1000个,延时1秒");
  174. Log::info("处理书籍阅读数---处理1000个,延时1秒");
  175. sleep(1);
  176. }
  177. }
  178. } else {
  179. $output->writeln("处理书籍阅读数---实例化redis错误!");
  180. Log::error("处理书籍阅读数---实例化redis错误!");
  181. }
  182. $output->writeln("处理书籍阅读数---完毕");
  183. Log::info("处理书籍阅读数---完毕");
  184. }
  185. /**
  186. * 用户行为标签
  187. */
  188. private function ubc(Input $input, Output $output)
  189. {
  190. $output->writeln("处理用户行为标签---开始");
  191. Log::info("处理用户行为标签---开始");
  192. Redis::instance();
  193. $key = 'U-BC:';
  194. $modKeyRules = Config::get('redis.modkeyrules');
  195. $aModKeyRules = explode(',', $modKeyRules);
  196. if (in_array($key, $aModKeyRules)) {
  197. //获取所有取模模式的redis服务器信息
  198. $strModKeyList = Config::get('redis.modkeylist');
  199. $aModKeyItem = explode(';', $strModKeyList);
  200. $aModKeyServerNum = [];
  201. array_walk($aModKeyItem, function ($v) use (&$aModKeyServerNum) {
  202. $aItem = explode('=', $v);
  203. $aModKeyServerNum[] = $aItem[0];
  204. });
  205. array_walk($aModKeyServerNum, function ($v) use ($output) {
  206. $redis = Redis::getRedisConnect($v);
  207. if (empty($redis)) {
  208. $output->writeln("处理用户行为标签---实例化redis错误!redisIndex:$v");
  209. Log::info("处理用户行为标签---实例化redis错误!redisIndex:$v");
  210. } else {
  211. $this->processUserTag($redis, $output);
  212. }
  213. });
  214. } else {
  215. $redisIndex = Redis::getRedisIndex($key);
  216. $redis = Redis::getRedisConnect($redisIndex);
  217. if (empty($redis)) {
  218. $output->writeln("处理用户行为标签---实例化redis错误!redisKey:$key");
  219. Log::info("处理用户行为标签---实例化redis错误!redisKey:$key");
  220. } else {
  221. $this->processUserTag($redis, $output);
  222. }
  223. }
  224. $output->writeln("处理用户行为标签---完毕");
  225. Log::info("处理用户行为标签---完毕");
  226. }
  227. /**
  228. * @param \Redis $redis
  229. * @param Output $output
  230. */
  231. private function processUserTag($redis, Output $output)
  232. {
  233. $pattern = 'U-BC:*';
  234. $cursor = null;
  235. $i = 0;
  236. while ($cursor !== 0) {
  237. $keys = $redis->scan($cursor, $pattern, 500);
  238. if (is_array($keys) && count($keys)) {
  239. foreach ($keys as $value) {
  240. $ids = $redis->get($value);
  241. $id = preg_replace('/\D/s', '', $value);
  242. if ($ids && $id && is_numeric($id)) {
  243. $model = new User();
  244. $model->setConnect($id)->update(['book_category_ids' => $ids], ['id' => $id]);
  245. unset($model);
  246. }
  247. $redis->del($value); //删除key
  248. $i++;
  249. if ($i % 1000 === 0) { //每处理1000个key 延时1秒
  250. $output->writeln("处理用户行为标签---处理1000个,延时1秒");
  251. Log::info("处理用户行为标签---处理1000个,延时1秒");
  252. sleep(1);
  253. }
  254. }
  255. }
  256. }
  257. }
  258. }