引語:線上運行的真實環境總是變幻莫測,明明你在本地測試的時候各種情況都是OK得不要不要的,也許你還在為自己某個地方炫酷的效果以及神奇的設計感到激動不已!但是,到線上以后,他就會偶爾跟抽風一樣的跟你say Hello,World!然后會有人跟你說,這里出問題了,那里出問題了!反應往往是這樣,“不可能!”,“媽蛋,怎么可能?”,“我就納悶了,怎么可能出現這種問題呢?”。哈哈,這也許就是大多數攻城獅朋友們最經常發出的感嘆吧!
那么,今天我們就來聊聊怎樣發現你的錯誤以及解決一些錯誤的快速定位方法,而不是等到用戶來你這里反饋,因為那時候可能已經錯太久了!我們要做的,應該是將錯誤發現在第一時間,解決在萌芽之中,作出事后總結以避免以后再犯類似錯誤!因為,錯不可怕,可怕的是一直犯同樣的錯誤,那樣的話,你和新手有何差別?
說明幾點:
1. 本文主要解決的問題是SQL相關的錯誤;
2. 本文以PHP微視角出發;(媽蛋,誰叫我是從事PHP開發呢?)
3. 本文解決的問題為,1 如何第一時間發現問題;2 如何解決問題;
4. 歡迎質疑、補充;
正題一、如何發現問題?
測試什么的那就不用說了,誰TM敢不過測試就直接上線?如果真是那樣,我只能說,你牛逼!測試是一道很重要的防線保障,一般來說,經過測試后的功能,上線之后,Bug不會太多,或者說不會太明顯!好吧,我就假設測試一個問題都沒有發現,那么, 我們就上線吧!其實,上線之后,我們心里是沒有底的,尤其是在某些還沒有一套完善的部署系統的公司或項目中,上線前和上線后總會出各種稀奇古怪。到底開發和用戶是兩個層面的人,鬼知道會發生什么呢?我們懸着的一顆心,竟然要完全依賴於用戶的操作,用戶的反饋?噢,不,那樣,太被動了!
主動發現問題。 一、上線之后,你也不可能再進行測試了,你現在就是一個普通用戶,那么,你自己去操作就是必須的,一個大概走下來,基本功能可以確定了,Ok,接下來,真正交給用戶! 二、如果沒有后手,就真的完全交給用戶,那你還是太Low了,因為,必要的監控措施是一定要有的!這里指的監控是程序級別的,也就是所謂的報錯。怎樣記錄報錯信息?怎樣知道報錯了?都說了嘛,我是從事PHP開發的。PHP中有一個記錄錯誤日志的功能,error_reporting,把這個給打開,指定錯誤日志位置,級別,就可以記錄PHP的錯誤了,但是一定要關閉錯誤的頁面顯示,否則,用戶看到此類錯誤,你就完蛋了!既然本文說的是SQL錯誤,那不應該是這里,沒錯。但是我要說的是,當SQL報錯的時候,PHP也已經發出報錯了,通常是一個警告級別的錯誤,而且這種錯誤往往是關聯性的發生,如下面的語句依賴於上面的查詢,而上面已經報錯,那么后續也會跟着報錯。PHP知道錯誤了,但是只是大概,那SQL具體哪里錯了?只有他自己知道,這種記錄就交給他吧!大概原理就是,在查詢出錯的地方,記錄錯誤日志,錯誤日志主要記錄信息有:錯誤信息,文件位置(追根溯源一個個文件查上去直到入口),下面是一段示例記錄錯誤信息的PHP代碼供參考:
<?php // 記錄sql錯誤日志 private function logError($msg = "") { if (isset($this->_logfile)) { if (!$msg) { if (!mysql_errno()) { return; } $msg = "mysql_errno: " . mysql_errno() . "\nmysql_error: " . mysql_error(); } file_put_contents($this->_logfile, '[' . date('Y-m-d H:i:s') . "] $msg\n", FILE_APPEND); $trace = debug_backtrace(); foreach ($trace as $call) { if(empty($call['file']) && empty($call['line'])) { continue; } file_put_contents($this->_logfile, "{$call['file']} on line {$call['line']}\n", FILE_APPEND); } file_put_contents($this->_logfile, "\n", FILE_APPEND); } }
這樣,你就有了一個可供參考的東西了。但是,還有一個關鍵的問題,就是你怎么知道報錯了?那就是報警提示了,你可以使用微信提供的測試號通知自己、你可以使用公司的短信平台給自己發短信、你可以發郵件給自己,然后設置郵件短信提醒!收到信息,趕緊解決去吧,更改的時候,你只需去查看這個文件指示的地方,你就大概知道是什么錯了,都知道什么錯了,我想,要想解決問題,應該只是個時間問題了,而且是個短時間問題!
正題二、如何解決問題?
其實前面我已經說了,既然已經找到問題根源了,要解決問題只是個短時間問題,但是,我還是將給出一些解決方案作參考,畢竟,大家都這么忙,哪有時間破解你那爛代碼!如下是一經常會出現的錯誤:
1. 報錯:[2002] 由於目標計算機積極拒絕,無法連接。解析:數據庫連接信息錯誤,你可能把測試環境連接部署到線上去了,這問題大發了,趕緊恢復吧? 附注:如果沒有報錯是因為你線上機器可以連接測試庫,那問題也大發了;
2. 報錯:[1146] Table 'bbbq' doesn't exist。解析:表不存在,趕緊查查,是不是本次新加的表沒有被添加到線上吧,或者表名寫錯了? 附注:一般過了測試的不太可能是表名寫錯了;
3. 報錯:[1054] Unknown column 'column_x' in 'field list'。解析:指定的列不存在,要么庫中沒有,要么寫錯了,趕緊查! 附注: 同上;
4. 報錯:[1366] Incorrect string value: '\xA9\x96' for column 'x_name' at row 1。解析:字符集問題,如一個gbk的字被字段設置為gb2312接收則會出現此問題,趕緊改回來吧!附注:請盡量使用utf-8編碼,代碼與入庫都方便;
5. 報錯:[1364] Field 'pid' doesn't have a default value。解析:指定字段沒有默認值,請確認應該獲取到的值是否未獲取從而為null,為某些不必要字段指定數據庫默認值。 附注: 如有索引關系,請一定設置not null 選項,否則索引將可能失效;
6. 報錯:[1366] Incorrect integer value: '' for column 'townId' at row 1。解析:給定的值不符合字段類型要求,在入庫之前先確認該字段需要什么類型,可做相應強制轉換,再入庫。 附注:某些版本的mysql可以自行強制轉換類型處理此問題,但是結果可能已經超出你的預期;
7. LOAD DATA INFILE ,從文件直接導入數據到數據庫
7.1. 報錯:[2] File 'D:/wamp/www/a/area.csv' not found (Errcode: 2)。解析:找不到指定的csv文件,確認目錄位置是否給定正確,如果正確,確認該文件是否為動態生成而目前尚未生成。 附注:小問題;
7.2. 報錯:[13] Can't get stat of '/opt/app/mysql5/var/D:/wamp/www/a/area.csv' (Errcode: 2)或者提示沒有權限操作數據庫。解析:數據庫與web不在同一台服務器,需要指定關鍵字LOCAL,從而將web與數據庫分開。 附注:LOCAL參數是在必要時候使用,因為不指定LOCAL的操作將看起來更安全;
7.3. 報錯:[1366] Incorrect integer value: '北京' for column 'provinceId' at row 1。解析:這里使用不指定字段的方式插入數據庫,因此,如果csv文件的值順序與數據庫字段順序不對應的話,將會有很多類似的報錯,而這則是致命的,因為全部都錯了,即使偶有個別正確入庫的,那也是錯的你也不會想要的結果,請以正確的順序寫入csv文件或者指定字段; 附注:這里的1366 與前面的1366 意義是不一樣的;
7.3. 示例LOAD: LOAD DATA LOCAL INFILE 'D:/wamp/www/a/area.csv' REPLACE INTO TABLE `address` CHARACTER SET utf8 FIELDS TERMINATED BY ',' (`provinceId`, `provinceName`, `cityId`, `cityName`, `areaId`, `areaName`, `townId`, `townName`); 附注:要求每個字段都是有值的,即幾個字段就要幾個',';
8. [1064] You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'LIMIT 0, 1000' at line 1。解析:好吧,這是個最通用的錯誤解釋,就是說你的語法寫錯了。比如 where a_id= order by id desc;這里你原本是想獲取一個ID然后去查詢,但是后面的ID得到空值,所以整個語法就錯了。其實,如果是直接接收參數去查詢,這本身可能報錯,也是一個注入點,請一定要處理傳入的值,一般要求'='號后給''號,整型參數用intval()格式化等等安全意識!
9. 講解:REPLACE與INSERT 功能其實是差不多的(唯一鍵是必須的),但是REPLACE會在數據庫中存在此記錄時先刪除再插入,如有自增ID,將會被迅速變大,從而不必手動執行刪除操作,而INSERT重復數據時則將報錯(可配合UPDATE使用)。適當使用兩個功能,解決問題!
...
在真實開發中,其實遇到很多有意思的問題,但一時間也難以想起來,想起來再補充,你也可以下方回復你遇到的問題及解決方案,大家一起參考!