setName('redisToMysql') ->addOption('type', 't', Option::VALUE_OPTIONAL, '要处理的缓存类型,all:默认全部处理,bn:书籍阅读数,ubc:用户行为标签, quv:自定义二维码UV统计', 'all') ->setDescription('将redis缓存中的数据存入mysql数据库'); } protected function execute(Input $input, Output $output) { Request::instance()->module('admin'); //cli模式下无法获取到当前的项目模块,手动指定一下 $type = $input->getOption('type'); switch ($type) { case 'all': $this->customQrCodeUvCollect($input, $output); $output->writeln("Type: all 全部处理"); $this->bn($input, $output); // //0408 去掉用户喜好字段维护 //$this->ubc($input, $output); break; case 'bn': $output->writeln("Type: bn 书籍阅读数"); $this->bn($input, $output); break; case 'ubc': // //0408 去掉用户喜好字段维护 // $output->writeln("Type: ubc 用户行为标签"); // $this->ubc($input, $output); $output->writeln("Type: ubc 用户行为标签停止维护"); break; case 'quv': $output->writeln("Type: quv 自定义二维码UV"); $this->customQrCodeUvCollect($input, $output); break; default: $output->writeln("Type: {$type} 无法识别的类型"); } $output->writeln("处理完毕!"); } /** * 处理自定义二维码 UV 统计 * @param Input $input * @param Output $output */ private function customQrCodeUvCollect(Input $input, Output $output) { try { $output->writeln("处理自定义二维码UV统计---开始"); Log::info("处理自定义二维码UV统计---开始"); $redis = Redis::instance(); Db::table('custom_qrcode')->chunk(100, function ($result) use ($redis) { foreach ($result as $val) { if ($uv = $redis->pfCount("QR_UV:{$val['admin_id']}:{$val['index']}")) { $is_update = model('CustomQrcode')->where('id', $val['id'])->update(['uv' => $uv]); if ($is_update !== false) { Log::info("处理自定义二维码UV统计: id:{$val['id']} set uv:{$uv} success"); } else { Log::error("处理自定义二维码UV统计: id:{$val['id']} set uv:{$uv} fail"); } } } sleep(5); }); $output->info("处理自定义二维码UV统计---完成"); Log::info("处理自定义二维码UV统计---完成"); } catch (\Exception $e) { $output->writeln('处理自定义二维码UV统计---失败' . $e->getMessage()); Log::error('处理自定义二维码UV统计---失败' . $e->getMessage()); } } /** * 链接redis */ private function connect($idx) { $redis = null; $config = Config::get('redis'); $list = explode(';', $config['list']); //共n个redis foreach ($list as $item) { $con = explode('=', $item); // 0 编号 1 配置 if (count($con) >= 2) { if ($con[0] == $idx) { $c = explode(':', $con[1]); //配置 0IP 1端口 2密码 3库编号 if (count($c) >= 2) { $redis = new \Redis(); if ($config['pconnect']) { $redis->pconnect($c[0], $c[1], $config['timeout']); } else { $redis->connect($c[0], $c[1], $config['timeout']); } if (count($c) >= 3 && $c[2]) { $redis->auth($c[2]); } if (count($c) >= 4 && is_numeric($c[3])) { $redis->select($c[3]); } } return $redis; } } } return $redis; } /** * 根据key获取redis编号 * @param $key */ private function getRedis($key) { $default = Config::get('redis.default', 0); //默认redis编号 $rules = Config::get('redis.rules'); $list = explode(';', $rules); foreach ($list as $item) { $con = explode('=', $item); // 0 编号 1 配置 if (count($con) >= 2) { $c = explode(',', $con[1]); //规则 foreach ($c as $it) { if ($it) { if (strpos($key, $it) === 0) { //查找到,且必须为第一个0 $default = $con[0]; break 2; } } } } } return $this->connect($default); } /** * 处理书籍阅读数 */ private function bn(Input $input, Output $output) { $output->writeln("处理书籍阅读数---开始"); Log::info("处理书籍阅读数---开始"); $key = 'B-N'; $redis = $this->getRedis($key); if ($redis) { $hash = $redis->hGetAll($key); $model = new Book(); $i = 0; foreach ($hash as $book => $num) { $data = [ 'read_num' => ['exp', "`read_num`+{$num}"] ]; $model->update($data, ['id' => $book]); $redis->hDel($key, $book); $redis->del('B:' . $book); $redis->del(CacheConstants::BOOK_USER_READ_COUNT . $book); $i++; if ($i % 1000 === 0) { //每处理1000个hash name 延时1秒 $output->writeln("处理书籍阅读数---处理1000个,延时1秒"); Log::info("处理书籍阅读数---处理1000个,延时1秒"); sleep(1); } } } else { $output->writeln("处理书籍阅读数---实例化redis错误!"); Log::error("处理书籍阅读数---实例化redis错误!"); } $output->writeln("处理书籍阅读数---完毕"); Log::info("处理书籍阅读数---完毕"); } /** * 用户行为标签 */ private function ubc(Input $input, Output $output) { $output->writeln("处理用户行为标签---开始"); Log::info("处理用户行为标签---开始"); Redis::instance(); $key = 'U-BC:'; $modKeyRules = Config::get('redis.modkeyrules'); $aModKeyRules = explode(',', $modKeyRules); if (in_array($key, $aModKeyRules)) { //获取所有取模模式的redis服务器信息 $strModKeyList = Config::get('redis.modkeylist'); $aModKeyItem = explode(';', $strModKeyList); $aModKeyServerNum = []; array_walk($aModKeyItem, function ($v) use (&$aModKeyServerNum) { $aItem = explode('=', $v); $aModKeyServerNum[] = $aItem[0]; }); array_walk($aModKeyServerNum, function ($v) use ($output) { $redis = Redis::getRedisConnect($v); if (empty($redis)) { $output->writeln("处理用户行为标签---实例化redis错误!redisIndex:$v"); Log::info("处理用户行为标签---实例化redis错误!redisIndex:$v"); } else { $this->processUserTag($redis, $output); } }); } else { $redisIndex = Redis::getRedisIndex($key); $redis = Redis::getRedisConnect($redisIndex); if (empty($redis)) { $output->writeln("处理用户行为标签---实例化redis错误!redisKey:$key"); Log::info("处理用户行为标签---实例化redis错误!redisKey:$key"); } else { $this->processUserTag($redis, $output); } } $output->writeln("处理用户行为标签---完毕"); Log::info("处理用户行为标签---完毕"); } /** * @param \Redis $redis * @param Output $output */ private function processUserTag($redis, Output $output) { $pattern = 'U-BC:*'; $cursor = null; $i = 0; while ($cursor !== 0) { $keys = $redis->scan($cursor, $pattern, 500); if (is_array($keys) && count($keys)) { foreach ($keys as $value) { $ids = $redis->get($value); $id = preg_replace('/\D/s', '', $value); if ($ids && $id && is_numeric($id)) { $model = new User(); $model->setConnect($id)->update(['book_category_ids' => $ids], ['id' => $id]); unset($model); } $redis->del($value); //删除key $i++; if ($i % 1000 === 0) { //每处理1000个key 延时1秒 $output->writeln("处理用户行为标签---处理1000个,延时1秒"); Log::info("处理用户行为标签---处理1000个,延时1秒"); sleep(1); } } } } } }