Redisメモリ使用量の削減

11351 ワード

テキストリンク:http://blog.51cto.com/12879490/1923140
    
1.redisメモリ使用量を低減する利点
1.スナップショットの作成とロードにかかる時間を短縮
2、AOFファイルの読み込みとAOFファイルの書き換えの効率を高める
3、サーバーからの同期に要する時間を短縮する
4、追加のハードウェアを追加することなくredisにより多くのデータを保存できる
2、短い構造
Redisは、リスト、コレクション、ハッシュ、秩序化されたコレクションの構成オプションのセットを提供し、redisがより短い構造をより節約的に格納できるようにします.
2.1、ziplist圧縮リスト(リスト、ハッシュ、続編あり)
通常のストレージ方式
リスト、ハッシュ、整列セットの長さが短い場合、または体積が小さい場合、redisはziplistというコンパクトなストレージ方式を採用してこれらの構造を格納します.
ziplistは、リスト、ハッシュ、シーケンスの3つの異なるタイプのオブジェクトの非構造化表現であり、データがシーケンス化されて格納され、これらのシーケンス化されたデータは読み出されるたびに復号化され、書き込むたびに符号化される.
    双方向リストと圧縮リストの違い:
圧縮リストが他のデータ構造よりもメモリを節約できることを理解するために,リスト構造を例に深く検討した.
一般的な双方向リスト
通常の双方向リストでは、各値にノードが表示されます.各ノードには、チェーンテーブルの前のノードと後のノードを指すポインタと、ノードに含まれる文字列値を指すポインタがあります.
各ノードに含まれる文字列値は、3つの部分に分けて格納されます.文字列の長さ、文字列値の残りの使用可能なバイト数、空白文字で終わる文字列自体が含まれます.
例:
ノードに'abc'文字列が格納されている場合、32ビットのプラットフォームで推定値を保存するには21バイトの追加オーバーヘッドが必要です(3つのポインタ+2つのint+空の文字、すなわち、3*4+2*4+1=21)
例から、3バイト文字列を格納するには、少なくとも21バイトの追加オーバーヘッドが必要であることがわかる.
  ziplist
圧縮リストは、各ノードに2つの長さと文字列を含むノードからなるシーケンスです.最初の長さは、前のノードの長さを記録します(圧縮リストを後ろから前へ遍歴するために使用されます).2番目の長さは、本現在の点を記録する長さです.格納された文字列.
例:
格納文字列'abc'は、両方の長さを1バイトで格納できるため、追加のオーバーヘッドは2バイト(2つの長さ、すなわち1+1=2)である.
結論:
圧縮リストは、追加のポインタとメタデータの格納を回避することで、追加のオーバーヘッドを低減します.
構成:
1 #list2 list-max-ziplist-entries 512  #             3 list-max-ziplist-value 64    #               4 #hash                  #        ,     ziplist      5 hash-max-ziplist-entries 5126 hash-max-ziplist-value 647 #zset8 zset-max-ziplist-entries 1289 zset-max-ziplist-value 64

テストリスト:
1、test.phpファイルの作成
1 #test.php2 connect('192.168.95.11','6379');5 for ($i=0; $i<512  ; $i++) 
6 { 
7     $redis->lpush('test-list',$i.'-test-list');  # test-list  512   8 }9 ?>

 
このときtest-listには512個のデータが含まれており、プロファイルの制限を超えていない
2、test-listにもう一つのデータを押し込む
このときtest-listは513個のデータを含み、プロファイルに制限されている512個より大きく、インデックスはziplistストレージ方式を放棄し、元のlinkedlistストレージ方式を採用する
ハッシュは秩序化された集合と同じです.
2.2、intset整数集合(集合)
前提条件は,集合に含まれるすべてのmemberを10進数整数に解析できる.
シーケンス配列でコレクションを格納することで、メモリ消費を低減するだけでなく、コレクション操作の実行速度を向上させることができます.
構成:
1 set-max-intset-entries  512   #     member  ,      intset  

 
テスト:
test.phpファイルの作成
1 #test.php2 connect('192.168.95.11','6379');5 for ($i=0; $i<512  ; $i++) 
6 { 
7     $redis->sadd('test-set',$i);   #   test-set  512 member8 }9 ?>

2.3、性能問題
リスト、ハッシュ、秩序化された集合、集合にかかわらず、制限された条件を超えると、より典型的な下位構造タイプに変換されます.コンパクト構造の体積が大きくなるにつれて、これらの構造を操作する速度はますます遅くなるからである.
テスト:
#リストを使用して代表的なテストを行います
テストの考え方:
1、デフォルトの構成でtest-listに50000本のデータを押し込み、所要時間を表示する.次にrpoplpushを使用してtest-listデータをすべて新しいリストlist-newにプッシュし、所要時間を表示します.
2、構成を変更し、list-max-ziplist-entries 100000、上記の同様の操作を実行する
3、時間を比較して結論を出す
デフォルトの構成でテスト:
1、データの挿入、時間の表示
 1 #test1.php 2 connect('192.168.95.11','6379'); 6 $start=time(); 7 for ($i=0; $i<50000  ; $i++) 
 8 { 
 9     $redis->lpush('test-list',$i.'-aaaassssssddddddkkk');10 }11 $end=time();12 echo "     :".($end-$start).'s';13 ?>

   
結果は4秒かかりました
2、相応のコマンドを実行し、時間がかかることを確認する
 1 #test2.php 2 connect('192.168.95.11','6379'); 6 $start=time(); 7 $num=0; 8 while($redis->rpoplpush('test-list','test-new')) 9 {10     $num+=1;11 }12 echo '     :'.$num."
";13 $end=time();14 echo " :".($end-$start).'s';15 ?>

プロファイルを変更してテストする
1、プロファイルを変更する
  list-max-ziplist-entries 100000  #この値を大きく変更すると、パフォーマンスへの影響がより顕著になります.
  list-max-ziplist-value 64    #この値は変更できません
2、データの挿入
test 1.phpの実行
結果:12 sかかる
  
3、相応のコマンドを実行し、時間がかかることを確認する
test 2.phpの実行
結果:実行回数:50000、消費時間12 s
結論:
本機で50000個のデータをテストすると8 s差があり、高並列では長圧縮リストと大整数セットは最適化されず、かえって性能が低下する.
 
3、シート構造
スライスの本質は,単純なルールに基づいてデータをより小さな部分に分割し,データが属する部分に基づいてデータをどの位置に送信するかを決定することである.多くのデータベースでは、このテクノロジーを使用してストレージスペースを拡張し、自分が処理できる負荷量を向上させます.
前述したように,redisに対するスライス構造の重要な意義を発見することは難しくない.したがって、ziplistおよびintsetに関するプロファイルの適切な調整が必要です.
3.1、スライス式ハッシュ
 
  #ShardHash.class.php
 View Code
ハッシュスライスは、主にベースキーおよびハッシュに含まれるキーからスライスキーIDを算出し、ベースキーと完全なスライスキーに結合する.hsetとhget、およびhashコマンドの大部分を実行する場合、key(field)をshardKeyメソッドで処理し、スライスキーを得てから次の操作を行う必要があります.
3.2、スライス式集合
チップセットを構築してメモリを節約し、パフォーマンスを向上させるにはどうすればいいですか?主な考え方は,集合内に格納されているデータをできるだけ本来の機能を変えずに十進法に解析できるデータに変換することである.前述したように、セット内のすべてのメンバーが10進数データに解析できる場合、intsetストレージ方式が採用され、メモリを節約するだけでなく、応答のパフォーマンスも向上します.
例:
大規模なWebサイトに毎日の一意のユーザー・アクセス量を格納する必要がある場合.これにより、ユーザの一意の識別子を10進数に変換し、スライスsetに格納することができる.
#ShardSet.class.php
 View Code
 
 
4、情報パッケージをメモリーバイトに変換する
前述したスライス技術と組み合わせて、stringスライス構造を用いて、多数の連続したIDユーザに情報を格納する.
固定長文字列を使用して、IDごとにnバイトを割り当て、対応する情報を格納します.
次に、ストレージ・ユーザーの国、省の例について説明します.
あるユーザーが中国、広東省の2つの情報を格納し、utf 8文字セットを採用する必要がある場合、少なくとも5*3=15バイトを消費する必要があります.サイトのユーザー数が多ければ、このようなやり方は多くのリソースを占有します.次に,各ユーザが2バイトだけで記憶情報を完了する方法を採用する.
具体的な考え方と手順:
1、まず私達は国家、および各国家の省の情報のために相応の“情報の表”を創立します
2、「情報テーブル」を作成すると、国ごとに省ごとにインデックス番号があることを意味します.
3、ここで皆さんも考えていると思いますが、2つのインデックスを使用してユーザーが格納している情報ですが、この2つのインデックスに対応する処理が必要であることに注意してください.
4、インデックスをASCIIコードとしてASCII(0~255)に対応して指定された文字に変換する
5、前述のスライス技術を用いて、スライスstring構造を固定し、ユーザーの記憶位置を探し出す(redisの1つのstringは512 Mを超えてはならない)
6、情報の書き込み及び取り出しを実現する(getrange、setrange)
実装コード:
#PackBytes.class.php
  1 redis=new Redis(); 16         $this->redis->connect($host,$port); 17     } 
 18  19     /** 20     * @desc              21     * @param $countries string |      ,      22     * @param $provinces   array  |      ,       23     * @param $cache 1/0    |       ,  0    24     * 25     * @return array |       26     */ 27     public function dealData($countries,$provinces,$cache=0) 28     { 29         if($cache) 30         { 31             $result=$this->redis->get('cache_data'); 32             if($result) 33                 return unserialize($result); 34         } 35         $arr=explode(' ',$countries); 36         $areaArr[]=$arr; 37         $areaArr[]=$provinces; 38         $cache_data=serialize($areaArr); 39         $this->redis->set('cache_data',$cache_data); 40         return $areaArr; 41     } 42  43     /** 44     * @desc                  45     * 
 46     * @param $countries,$provinces,$cache|   dealData   47     * @param $country  string             |     --   48     * @param $province   string           |     --   49     * 50     * @return string |           51     */ 52     public function getCode($countries,$provinces,$country,$province,$cache=0) 53     { 54         $dataArr=$this->dealData($countries,$provinces,$cache=0); 55  56         $result=array_search($country, $dataArr[0]);  #         data1 57         if($result===false)         #       58             return chr(0).chr(0);   #          59         $code=chr($result); 60         $result=array_search($province, $dataArr[1][$country]);  #         data2 61         if($result===false) 62             return $code.chr(0); 63         return $code.chr($result);      #    ASCII(0~255)        64     } 65  66     /** 67     * @desc                   68     * 
 69     * @param $userID int |    ID 70     * 71     * @return array |                  ID、           (   ) 72     */ 73     public function savePosition($userID) 74     { 75         $shardSize=pow(2, 3);      #        76         $position=$userID*2;        #user    77         $arr['shardID']=floor($position/$shardSize);   #  ID 78         $arr['offset']=$position%$shardSize;      #    79         return $arr; 80     } 81  82     /** 83     * @desc |     ,       redis string      84     * 85     * @param $userID int           |   ID 86     * @param $countries string     |      ,      87     * @param $provinces   array  |      ,       88     * @param $country  string             |     --   89     * @param $province   string           |     --   90     * @param $cache 1/0            |       ,  0    91     * 92     * @return         /  false 93     */ 94     public function saveCode($userID,$countries,$provinces,$country,$province,$cache=0) 95     { 96         $code=$this->getCode($countries,$provinces,$country,$province,$cache=0); 97         $arr=$this->savePosition($userID);  #         98         return $this->redis->setrange('save_code_'.$arr['shardID'],$arr['offset'],$code); 99     }100 101     /**102     * @desc               103     *104     * @param $userID int |   ID105     *106     * @return array |               107     */108     public function getMessage($userID)109     {110         $position=$this->savePosition($userID);111         $code=$this->redis->getrange('save_code_'.$position['shardID'],$position['offset'],$position['offset']+1);112         $arr=str_split($code);113         $areaArr=$this->dealData('', '',$cache=1);  #      114         $message['country']=$areaArr[0][ord($arr[0])];115         $message['province']=$areaArr[1][$message['country']][ord($arr[1])];116         return $message;117     }118 119 }120 121 header("content-type: text/html;charset=utf8;");122 $countries="                         ";123 $provinces=array(124         ' '=>array(' '),125         '  '=>array(' ','  ','  ','  ','  ','  ','  ','  '),126         '  '=>array(' ','    ','   ','    ','   ','    '),127     );128 $obj=new PackBytes('192.168.95.11');129 /*130 #    ,      redis 131 $b=$obj->dealData($countries,$provinces);132 echo "
";133 print_r($b);134 echo "
";die;  
135 */136 /*137 # ユーザ 138 $country=' ';139 $province=' ';140 $result=$obj->saveCode(0,$countries,$provinces,$country,$province);141 echo "
";142 print_r($result);143 echo "
";144 */145 /*146 #ユーザの の を り す147 $a=$obj->getMessage(15);148 echo "
";149 print_r($a);150 echo "
";die;151 */152 153 ?>
 
テスト:
1、dealData の 、すなわち「 テーブルテーブル」
2、saveCode()
userID


0


13


15


 

3、getMessage()
 
:https://blog.51cto.com/12879490/1923140