這篇主要講如何將數據保存回Mysql,但是裡面還會涉及到如何將錯誤信息以及提示信息保存到文件里,方便以後的運維,再有就是如何使用PHP寫進程BAT。 Redis數據刷回資料庫前的知識準備 首先針對上篇提到的關於redis刷回資料庫的安全性的設計模式,因為我們使用的是list來做數據索引,所以在我們將 ...
這篇主要講如何將數據保存回Mysql,但是裡面還會涉及到如何將錯誤信息以及提示信息保存到文件里,方便以後的運維,再有就是如何使用PHP寫進程BAT。
Redis數據刷回資料庫前的知識準備
首先針對上篇提到的關於redis刷回資料庫的安全性的設計模式,因為我們使用的是list來做數據索引,所以在我們將list數據提取出來的時候,一旦redis在這時候出現異常,就會導致剛提取出來的數據丟失!有些小伙伴就說,丟失就丟失唄,才一點數據。但是我們做程式,就應該以嚴謹為基礎,所以下麵就來說下Redis List這位大佬給我們提供了什麼幫助。
- Redis List -》RpopLpush()函數
- 使用方法:RPOPLPUSH source destination
- 說明:命令RPOPLPUSH在一個原子時間內,執行以下兩個動作:①命令RPOPLPUSH在一個原子時間內,執行以下兩個動作;②將source彈出的元素插入到列表destination,作為destination列表的的頭元素。
- 設計模式:
Redis的列表經常被用作隊列(queue),用於在不同程式之間有序地交換消息(message)。一個程式(稱之為生產者,producer)通過LPUSH命令將消息放入隊列中,而另一個程式(稱之為消費者,consumer)通過RPOP命令取出隊列中等待時間最長的消息。
不幸的是,在這個過程中,一個消費者可能在獲得一個消息之後崩潰,而未執行完成的消息也因此丟失。
使用RPOPLPUSH命令可以解決這個問題,因為它在返回一個消息之餘,還將該消息添加到另一個列表當中,另外的這個列表可以用作消息的備份表:假如一切正常,當消費者完成該消息的處理之後,可以用LREM命令將該消息從備份表刪除。
Redis數據刷回資料庫
方面文字太多?沒關係。下麵先來一段代碼!我們的主體部分:
index.php:
1 <?php 2 require_once(__DIR__."/Mysql.class.php"); 3 require_once(__DIR__."/Redis.class.php"); 4 require_once(__DIR__."/Output_Log.class.php"); 5 6 7 $rel = true; //無限迴圈的變數 8 $num = 0; //用來沒數據時的判斷依據 9 date_default_timezone_set("Asia/Shanghai"); 10 $now = date("Y-m-d H:i:s"); //當前時間 11 //file log 12 $txt = dirname(__DIR__)."/Script_Log/clickgood_log.txt"; 13 $output = new OutputLog(); 14 $test = $output->open($txt,"a+"); 15 16 while($rel) 17 { 18 $redis = new RedisCtrl(); 19 20 //開始幹活 21 if($num==0){ 22 //這裡就是將信息輸出到文件里記錄,下麵很多地方都是一樣的。 23 $text = "start ".$name."\n"; 24 echo $text; 25 $output->write($test,$text); 26 } 27 28 //獲取備份隊列的長度 29 $copylistlength = $redis->llen("comment:uploadcopylist"); 30 31 //我這裡展示的是第一數據回滾到mysql,小伙伴想批量回滾的,自己改裝下就可以用了。 32 //自己動手豐衣足食! 33 if($copylistlength>1) 34 { 35 //由於是單一數據回滾,所以我要判斷它是否超過我設定的值,小伙伴們最好也自己定一個閾值。 36 //report error 37 echo $now." ->false\n"; 38 $rel = false; 39 return; 40 } 41 else if($copylistlength==1) 42 { 43 //這裡判斷防止上次redis出現錯誤,導致數據沒有及時回到mysql 44 $data = $redis->rpop("comment:uploadcopylist"); 45 $rel = $redis->UpdateClickGoodDataToMysql($data); 46 $text = $rel."\n"; 47 echo $text; 48 $output->write($test,$text); 49 } 50 else 51 { 52 //獲取主要隊列的長度 53 $listlength = $redis->llen("comment:uploadlist"); 54 if ($listlength>0) { 55 //使用之前說到的設計模式 56 $data = $redis->rpoplpush("comment:uploadlist","comment:uploadcopylist"); 57 58 $rel = $redis->UpdateClickGoodDataToMysql($data); 59 $text = $rel."\n"; 60 echo $text; 61 $output->write($test,$text); 62 }else{ 63 // 隊列為空 64 // 列印關閉信息,這裡的寫法算是維持進程視窗不關閉,需要手動關閉 65 // 如果想讓它執行完自動關閉的, 66 // 把下麵改寫成$rel = false; 67 if($num<=3){ 68 $text = $now." -> please close .\n"; 69 echo $text; 70 $output->write($test,$text); 71 $num++; 72 } 73 else 74 { 75 $output->close($test); 76 } 77 } 78 } 79 80 }
Redis.class.php: redis操作類
1 <?php 2 class RedisCtrl 3 { 4 //init redis 5 static $redisIp = "127.0.0.1"; 6 static $redisPort =6379; 7 static $redisPass =""; 8 public $redis = null; 9 10 //Redis 11 public function __construct() 12 { 13 $this->redis = new Redis(); 14 $this->redis->connect(self::$redisIp,self::$redisPort); 15 $this->redis->auth(self::$redisPass); 16 } 17 18 public function llen($key) 19 { 20 $rel = $this->redis->llen($key); 21 return $rel; 22 } 23 24 public function rpop($key) 25 { 26 $rel = $this->redis->rpop($key); 27 return $rel; 28 } 29 30 public function rpoplpush($source,$destination) 31 { 32 $rel = $this->redis->rpoplpush($source,$destination); 33 return $rel; 34 } 35 36 public function UpdateClickGoodDataToMysql($data) 37 { 38 //get id and time from redis list 39 $result = json_decode($data,true); 40 $id = $result['id']; 41 $time = $result['time']; 42 $arr = array(); 43 44 //like 45 $like = $this->redis->zscore("comment:like",$id); 46 $like = $like?$like:0; 47 //hate 48 $hate = $this->redis->zscore("comment:hate",$id); 49 $hate = $hate?$hate:0; 50 51 $sql = "update comment_info set like_count=".$like.", hate_count=".$hate." where id=".$id; 52 $arr[] = $sql; 53 //update sql 54 $mysql = new MysqlCtrl(); 55 $mysql->saveMySQL($arr); 56 57 //更新完,將set集合里需要更新的id去掉 58 $this->redis->srem("comment:uploadset",$id); 59 //更新完畢,將備份隊列里的數據去掉 60 $this->redis->lrem("comment:uploadcopylist",$data); 61 62 return $sql."\n"; 63 } 64 }
Mysql.class.php mysql類
1 <?php 2 //封裝函數 3 class MysqlCtrl 4 { 5 //初始化參數 6 //資料庫參數配置 7 static $dbms = "mysql"; 8 static $host = Your host; 9 static $user = Your user; 10 static $pass = Your pass; 11 static $database = Your database; 12 //睡眠時間 13 static $sleep = 1; 14 15 public $dsn = null; 16 public $dbh = null; 17 18 public function __construct() 19 { 20 $this->dsn = self::$dbms.":host=".self::$host.";dbname=".self::$database; 21 //return $dsn; 22 try { 23 $this->dbh = new PDO($this->dsn, self::$user, self::$pass); 24 echo "Connected\n"; 25 } catch (Exception $e) { 26 echo $this->dsn; 27 die("Unable to connect: " . $e->getMessage()); 28 } 29 } 30 31 //保存數據到資料庫 32 //PDO 33 public function saveMySQL($arr) 34 { 35 36 try { 37 $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 38 39 $this->dbh->beginTransaction(); 40 41 $count = count($arr); 42 for ($i=0; $i < $count; $i++) { 43 $this->dbh->exec($arr[$i]); 44 } 45 46 $this->dbh->commit(); 47 48 } catch (Exception $e) { 49 $this->dbh->rollBack(); 50 echo "Failed: " . $e->getMessage()."\n"; 51 $json = json_encode($arr); 52 echo "False-SQL: ".$json."\n"; 53 exit(); 54 } 55 } 56 }
Output_Log.class.php 輸出信息到文件的類
1 <?php 2 class OutputLog 3 { 4 public function open($name,$r) 5 { 6 $text = fopen($name, $r); 7 return $text; 8 } 9 10 public function write($name,$title) 11 { 12 $rel = fwrite($name, $title); 13 return $rel; 14 } 15 16 public function close($name) 17 { 18 fclose($name); 19 } 20 }
clickgood_log.txt 這裡是保存輸出的信息,裡面是空白的。
hellO world
上面這些就是整套數據保存到mysql的操作,這是本人源碼copy過來的,所以細分程度比較高,但是可擴展性也很高。有什麼錯誤的地方希望小伙伴們能提出,謝謝。
最後就是我們的進程調用了,其實很簡單
創建一個txt文件,然後改名為clickgood.bat,記得把txt尾碼文件名改為bat
clickgood.bat:
1 D:\Software\wamp64\bin\php\php7.0.10\php.exe index.php
關於上面的bat註意兩點,一前面那個php.exe,按照你自己的路徑去找,我使用wampserver測試環境,基本就是上面的路徑。第二點,後面的index.php和clickgood.bat同一目錄下。
好了!終於可以收尾了!、
歡迎大家交流,上面只是本人自己的做法,希望大家能夠指出錯誤的地方!或者提供更好的做法。