前段時間給pdo設置了下emulate_prepare屬性,引起了此次的血案。在這記錄下事情的通過,沒準你們能避免一樣的錯誤。php
先說如下環境。php 5.2.5,mysql 5.0.81,服務器使用的GBK編碼。mysql
首先是看到一個報錯信息,說是sql語句的syntax error。這裏給一個能重現的例子。報錯的位置就是在紅框的位置。sql
從上面看,看不出語法錯誤。然而GBK編碼的「玕」字後一個字節是0x5c,跟「\」同樣。看到這,是否是就以爲這可能會有問題?可是,我記得咱們是使用了pdo的prepare這種方式的(上面sql語句中我使用實際的值代替了?佔位符),即便這個漢子特殊,也不會報語法錯誤纔對。因而我翻了翻代碼,發現,果真沒有顯示的設置PDO::ATTR_EMULATE_PREPARES屬性的值。而默認是設置爲true的(從字面意思就能理解,模擬prepare,不是真正意義上的prepare執行方式)。數據庫
使用tcpdump抓包,wireshark查看,肯定了一下,發現果真是上面這樣。編程
抓包的結果,服務器
0xab5c是漢字玕的GBK編碼。這裏看到的是,pdo模擬處理後,多加了一個'\',而在這以前,已經告訴mysql server,客戶端使用的是GBK編碼(set names gbk),mysql server按着GBK字符集處理sql語句,多出來的轉義符號,就形成了sql語句的語法錯誤。(後來查資料確認,這是pdo在處理多字符時候的一個bug,好像在php5.3.6,以後版本經過dsn裏面設置charset能夠解決,我沒去確認從哪一個版本開始起做用的,可是5.4是能夠起做用的。)tcp
既然pdo模擬prepare有這個問題,而真正意義的prepare既沒有這個問題,又在防止sql注入上更勝一籌,那我就直接使用mysql提供的prepare執行方式就行了。因此,設置PDO::ATTR_EMULATE_PREPARES爲false。測試發現,果真插入正常了。其餘一切數據庫操做貌似都正常。測試
在我解決完問題挺高興的時候,發現問題了。數據庫主從同步出問題了。後來定位問題發現,mysql 的binlog裏面sql語句裏面的數據全都是轉換到了ascii碼的表示,例如 insert test (`id`) values(1),在binlog裏面是insert test(`id`) values(0x31)。此時也才瞭解到mysql5.0不支持row同步方式,只支持語句的同步方式。binlog異常參見這篇文章http://backend.blog.163.com/blog/static/20229412620133274030845編碼
編程不容易,說多了都是淚。。努力,多學學吧。。spa