Php implementation of the memcache distributed [consistent hash algorithm] has recently been reading some articles on the distributed architecture. Therefore, we have used php to implement consistent hash, previously, we used the original hash modulo for distributed processing. when a memcache instance is added or deleted during the production process, all data will be invalid. Consistent hash is used to solve this problem, minimize the number of invalid data items. For more information, see google!
Php implementation efficiency is lacking. if it is necessary to be efficient, it is better to write extension.
Tests show that each of the five memcache instances generates 100 virtual nodes, and the set and get1000 times are added, which is five times slower than a single memcache instance set. Therefore, the efficiency is average and needs to be optimized!
Implementation process:
Memcache configures ip + Port + virtual node serial number for hash and uses crc32 to form a closed loop.
Perform crc32 on the key to be operated
Find the nearest virtual node in the virtual node loop using the binary method.
Extract the real memcache ip address and port from the virtual node for single-instance connection
* @ Copyright Copyright©2003-2012 phpd.cn * @ license */class memcacheHashMap {private $ _ node = array (); private $ _ nodeData = array (); private $ _ keyNode = 0; private $ _ memcache = null; // The number of virtual nodes generated by each physical server [Note: The larger the number of nodes, the better the uniformity of the cache distribution. during the set get operation, it also consumes more resources. 10 physical servers use 200 more reasonable] private $ _ virtualNodeNum = 200; private function _ construct () {/* put the configuration file */$ config = array ('2017. 0.0.1: 100000', '123. 0.0.1: 100000', '123. 0.0.1: 100000', '123. 0.0. ', '2017. 0.0.1: 100'); if (! $ Config) throw new Exception ('cache config Null'); foreach ($ config as $ key => $ value) {for ($ I = 0; $ I <$ this-> _ virtualNodeNum; $ I ++) {$ this-> _ node [sprintf ("% u", crc32 ($ value. '_'. $ I)] = $ value. '_'. $ I ;}} ksort ($ this-> _ node);} private function _ clone () {}/ *** Singleton, ensure that only one instance */static public function getInstance () {static $ memcacheObj = null; if (! Is_object ($ memcacheObj) {$ memcacheObj = new self ();} return $ memcacheObj ;} /*** perform consistent hash based on the key and connect to a physical memcache server * @ param string $ key */private function _ connectMemcache ($ key) {$ this-> _ nodeData = array_keys ($ this-> _ node); $ this-> _ keyNode = sprintf ("% u", crc32 ($ key )); $ nodeKey = $ this-> _ findServerNode (); // if the loop is exceeded, use the binary method to find the nearest node, and then use the start and end of the loop for judgment, obtain the nearest node if ($ this-> _ keyNode> end ($ this-> _ nodeData) {$ This-> _ keyNode-= end ($ this-> _ nodeData); $ nodeKey2 = $ this-> _ findServerNode (); if (abs ($ nodeKey2-$ this-> _ keyNode) <abs ($ nodeKey-$ this-> _ keyNode) $ nodeKey = $ nodeKey2 ;} var_dump ($ this-> _ node [$ nodeKey]); list ($ config, $ num) = explode ('_', $ this-> _ node [$ nodeKey]); if (! $ Config) throw new Exception ('cache config error'); if (! Isset ($ this-> _ memcache [$ config]) {$ this-> _ memcache [$ config] = new Memcache; list ($ host, $ port) = explode (':', $ config); $ this-> _ memcache [$ config]-> connect ($ host, $ port );} return $ this-> _ memcache [$ config];} /*** search for the nearest node from the virtual memcache node using the bipartite method * @ param unknown_type $ m * @ param unknown_type $ B */private function _ findServerNode ($ m = 0, $ B = 0) {$ total = count ($ this-> _ nodeData); if ($ total! = 0 & $ B = 0) $ B = $ total-1; if ($ m <$ B) {$ avg = intval ($ m + $ B) /2); if ($ this-> _ nodeData [$ avg] ==$ this-> _ keyNode) return $ this-> _ nodeData [$ avg]; elseif ($ this-> _ keyNode <$ this-> _ nodeData [$ avg] & ($ AVG-1> = 0) return $ this-> _ findServerNode ($ m, $ AVG-1); else return $ this-> _ findServerNode ($ avg + 1, $ B );} if (abs ($ this-> _ nodeData [$ B]-$ this-> _ keyNode) <abs ($ this-> _ nodeData [$ m]-$ this-> _ keyNode) return $ this-> _ nodeData [$ B]; else return $ this-> _ nodeData [$ m];} public function set ($ key, $ value, $ expire = 0) {return $ this-> _ connectMemcache ($ key)-> set ($ key, json_encode ($ value), 0, $ expire);} public function add ($ key, $ value, $ expire = 0) {return $ this-> _ connectMemcache ($ key)-> add ($ key, json_encode ($ value), 0, $ expire );} public function get ($ key) {return json_decode ($ this-> _ connectMemcache ($ key)-> get ($ key), true);} public function delete ($ key) {return $ this-> _ connectMemcache ($ key)-> delete ($ key );}}
$ RunData ['In in _ time'] = microtime (true); // Test 10 thousand sets plus getfor ($ I = 0; $ I <10000; $ I ++) {$ key = md5 (mt_rand (); $ B = memcacheHashMap: getInstance ()-> set ($ key, time (), 10 );} var_dump (number_format (microtime (true)-$ runData ['In in _ time'], 6); $ runData ['In in _ time'] = microtime (true ); $ m = new Memcache; $ m-> connect ('2017. 0.0.1 ', 11211); for ($ I = 0; $ I <10000; $ I ++) {$ key = md5 (mt_rand ()); $ B = $ m-> set ($ key, time (), 0, 10);} var_dump (number_format (microtime (true) -$ runData ['In in _ time'], 6 ));
// Test results: the consistent hash distribution efficiency is about five times faster than that of a single native server.