公司的項目大量用到Redis,而咱們的Redis通常可能有不少組構成,多的有十幾臺,咱們平常業務常常會碰到要去服務器查數據的問題,所以一臺一臺的去找會顯得很慢。由於以前在學golang開發,我就順手用go語言寫了一版根據RedisKey去查詢所在服務器的小腳本。對go也不是很熟,在解決了一個一個的語言問題後發現我本機go語言獲得的crc32老是會跟php中獲得的結果不一致,php
func getIntvalKey(strKey string) uint32 { table := crc32.MakeTable(crc32.IEEE) ret := crc32.Checksum([]byte(strKey), table) if ret > 2147483647 { ret = 4294967295 - ret + 1 } return ret }
/** * 將字符串key,轉換爲對應的無符號整型 * @param string $strKey * @return int */ protected static function _getIntvalKey($strKey = '') { $intKey = crc32($strKey); //若是IntKey爲負值,轉換爲正數 if ($intKey < 0) { $intKey = 0 - $intKey; } return $intKey; }
咱們的一致性哈希是將host_post_i的值經過crc32函數計算結果做爲數組的key(crc32(10.*.*.*_8008_1)),value爲host_port(10.*.*.*_8008)。默認狀況下,咱們單個redis服務器會配置128的節點(也即crc32(10.*.*.*_8008_0)到crc32(10.*.*.*_8008_127)他們對應的值都是10.*.*.*_8008。因而就碰到了crc32函數在32位系統中go語言跟php不一致問題,最終用上面的2^32-val+1的方式解決了。golang
但最後使用過程當中,我發現結果貌似確實不一致,其實我之前用PHP移植了一個用於查詢redis的值的程序,當時個人感受是這個絕對100%可信的,直接跟線上業務一致的源碼應該不會有問題,直到上個周要去查線上電影購票評論的key時發現了問題:redis
這兩部電影這個key被定位到了231,219服務器,然而結果返回爲空,以前我一直以爲是確實沒有值,昨天上服務器看了下,本機PHP的命中居然是錯的。最開始個人懷疑仍是在了Crc32算法上,我就決定用PHP寫個單文件來看看究竟是哪裏問題,windows跟centos上究竟有啥區別。算法
function getIntvalKey($strKey = '') { $intKey = crc32($strKey); //echo $intKey.'|'; //若是IntKey爲負值,轉換爲正數 if ($intKey < 0) { $intKey =4294967295 + $intKey + 1; } //if ($intKey < 0) { // $intKey = 0 - $intKey; //} //echo $strKey.'|'.$intKey.PHP_EOL; return $intKey; }
一到處的打輸出,最終我仍是用上面的辦法將Windows的結果跟值獲得了一致。但此次我要去肯定這兩個key的命中到底對不對,不過結果依然讓我大跌眼鏡,即便哈希環獲得的結果一致,命中也不一致。windows
後來通過一點點的輸出centos
$val1="10.*.*.*_28002_121"; $abc=getIntvalKey($val1); echo $abc; $a[$abc]=$abc; //echo $val1; print_r($a); exit;
發現如上結果在32位跟在64位是不一樣的,咱們crc32獲得的結果多是二十多億,但將這個數字值做爲數組的key放進去後數組的key的值自動被轉成了負數,也就是說咱們計算好的hash環最終在保存到數組裏面的時候。被替換成了錯誤的值。可能你們都以爲這是由於操做系統32位,而PHP版本是32位的問題,當我把本機的PHP改爲64位的php 5.6的時候,狀況依然。證實這跟PHP是32仍是64無關.最後我答應PHP_MAX_INT,確實不論是64位的PHP仍是32位的PHP,獲得的都是4位並不是8位。數組
通過搜索,我獲得了以下結果,並未去嘗試L服務器
php 函數crc32()會按照php中的兩個常量參考計算 PHP_INT_SIZE,PHP_INT_MAX
輸出下32位中PHP_INT_SIZE:4,PHP_INT_MAX:2147483647
輸出下64位中PHP_INT_SIZE:8,PHP_INT_MAX:9223372036854775807
項目中使用crc32()函數來進行文件路徑計算,原系統爲32位,新系統64位,致使同一個文件的路徑計算髮生誤差,引發不少問題。全部須要讓新系統下的計算結果和原來老系統下保持一致。
個人問題是,我如何在個人64位系統中將PHP_INT_SIZE改成4,將PHP_INT_MAX改成2147483647
方法一:代碼中定義兩個常量的值,define (*PHP_INT_MAX*, 2147483647);define (*PHP_INT_SIZE*, 4);實測,無效;
方法二:在php.ini配置文件中添加PHP_INT_MAX = 2147483647 和 PHP_INT_SIZE = 4 兩行信息,實測,無效函數
++++++++++++++++++++++++post
固然在咱們的項目中咱們能夠把計算獲得的結果放到string中做爲數組key保存來規避這個bug,但這也只是一個規避方案。我還未去嘗試在go語言中我把結果保存到int64類型,而後hash環用[]int64切片來跑在我本機跑的結果如何,等有空了嘗試後再補上來。