引語:線上運作的真實環境總是變幻莫測,明明你在本地測試的時候各種情況都是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使用)。适當使用兩個功能,解決問題!
...
在真實開發中,其實遇到很多有意思的問題,但一時間也難以想起來,想起來再補充,你也可以下方回複你遇到的問題及解決方案,大家一起參考!
不要害怕今日的苦,你要相信明天,更苦!