[復刻版]米国企業は、いくつかのピット-4.redis Redisのメモリ使用量の最適化を踏ん

Redisの米国ミッションは、いくつかのピット-4.redisメモリ使用量の最適化を踏ん

ブログカテゴリー:
 

ソースを記入してくださいハ:http://carlosfu.iteye.com/blog/2254154

もっとRedisの開発、運用・保守だけでなく、アーキテクチャの新たな展開、マイクロチャンネル公衆番号への歓迎の注意:

 


    

 I.背景:適切な使用シナリオを選択
   メモリ消費、高コストの価格:RedisのはRedisの印象で、その結果、しばしば誤解して無差別に使用されます。
   1. MySQLや「誤解」の「ファッション」するためにRedisのは、並行システムの非常に低い量でRedisの内の場所MySQLでのすべての元のデータを使用しました。
     ----(Redisのは、システムが複雑なミスであれば、非常にコンカレントシステムのために、より適しているが、Redisのとのトラブル、より強力な機能から話すように、単一のMySQLのために、とMySQLの性能はすでに十分です。)
   2. KVはRedisのはキャッシュだと思います
     -----(Redisのは、複数のデータ構造をサポートしており、他の多くの機能が豊富なを持っています)
   3.等のMysql、HBaseの、Redisのようなコントラストのすべての種類を、やりたいです
    -----(各データベースには、HBaseのこと、私たちは1T、Redisの上のこの時間は、単に不適切ますが、Redisの上のいくつかのホットデータを持ってパーソナライズされたデータシステムとして、独自の使用シナリオを、持っています)
    とにかく、適切な場面で、適切なデータベース製品を選択します。
  これは、2つの引用符が付属しています:
エヴァン・ウィーバー、ツイッター、2009年3月に書きました
すべてはウェブ2.0にメモリから実行されます!
ティム・グレイは書きました
デッドテープ、テープ、ディスクISは、フラッシュディスク、RAM地域IS王です。、IS
(テープが死んで、ディスクが新しいテープで、フラッシュメモリが新しいディスクで、ランダム・アクセス・メモリの局所性が王です)
  
第二に、最適化のハッシュに文字列
1.シナリオ:
    ユーザーID:userIdを、
    マイクロブログのユーザー数:weiboCount    
userId(ユーザID) weiboCount(微博数)
1 2000
2

10

3

288

.... ...
1000000 1000年
 
2.方法:
値としてRedisの文字列データ構造(1)を用いて、キーのにuserId、weiboCount
(2)ハッシュRedisの構造を使用して、ハッシュキーのみ、キー= "allUserWeiboCount"、フィールドUSERID =、= fieldValueのweiboCount
(3)ハッシュRedisの構造を使用して、ハッシュキー複数のキー=ユーザーID / 100、フィールドUSERID = 100%、fieldValueの= weiboCount
各ハッシュキー100格納されたハッシュ-KV、フィールドUSERID = 100%、すなわち:最初の二つは、比較的容易に理解するために、第三の実施形態を説明します
ユーザーID ハッシュキー フィールド
1 0 1
2 0

2

3 0

3

... .... ...
99 0 99
100 1 0
101 1 1
.... ... ...
9999 99 99
100000 1000年 0

 

注意:

すべてのリアルタイムテストキー、フィールド、値では、共有オブジェクトの問題を解消するために使用される文字列型です。

 

3.取得方法:

 

Javaコード   收藏代码
  1. #取得のuserId = 5003マイクロブログの利用者数  
  2. (1) get u:5003  
  3. (2) hget allUser u:5003  
  4. (3) hget u:50 f:3  

 

 

4. 内存占用量对比(100万用户 userId u:1~u:1000000) 

  

Java代码   收藏代码
  1. #方法一 Memory  
  2. used_memory:118002640  
  3. used_memory_human:112.54M  
  4. used_memory_rss:127504384  
  5. used_memory_peak:118002640  
  6. used_memory_peak_human:112.54M  
  7. used_memory_lua:36864  
  8. mem_fragmentation_ratio:1.08  
  9. mem_allocator:jemalloc-3.6.0  
  10. ---------------------------------------------------  
  11. #方法二 Memory  
  12. used_memory:134002968  
  13. used_memory_human:127.80M  
  14. used_memory_rss:144261120  
  15. used_memory_peak:134002968  
  16. used_memory_peak_human:127.80M  
  17. used_memory_lua:36864  
  18. mem_fragmentation_ratio:1.08  
  19. mem_allocator:jemalloc-3.6.0  
  20. --------------------------------------------------------  
  21. #方法三 Memory  
  22. used_memory:19249088  
  23. used_memory_human:18.36M  
  24. used_memory_rss:26558464  
  25. used_memory_peak:134002968  
  26. used_memory_peak_human:127.80M  
  27. used_memory_lua:36864  
  28. mem_fragmentation_ratio:1.38  
  29. mem_allocator:jemalloc-3.6.0  

  

 那么为什么第三种能少那么多内存呢?之前有人说用了共享对象的原因,现在我将key,field,value全部都变成了字符串,仍然还是节约很多内存。

 之前我也怀疑过是hashkey,field的字节数少造成的,但是我们下面通过一个实验看就清楚是为什么了。当我将hash-max-ziplist-entries设置为2并且重启后,所有的hashkey都变为了hashtable编码。

 同时我们看到了内存从18.36M变为了122.30M,变化还是很大的。

 

Java代码   收藏代码
  1. 127.0.0.1:8000> object encoding u:8417  
  2. "ziplist"  
  3. 127.0.0.1:8000> config set hash-max-ziplist-entries 2  
  4. OK  
  5. 127.0.0.1:8000> debug reload  
  6. OK  
  7. (1.08s)  
  8. 127.0.0.1:8000> config get hash-max-ziplist-entries  
  9. 1) "hash-max-ziplist-entries"  
  10. 2) "2"  
  11. 127.0.0.1:8000> info memory  
  12. # Memory  
  13. used_memory:128241008  
  14. used_memory_human:122.30M  
  15. used_memory_rss:137662464  
  16. used_memory_peak:134002968  
  17. used_memory_peak_human:127.80M  
  18. used_memory_lua:36864  
  19. mem_fragmentation_ratio:1.07  
  20. mem_allocator:jemalloc-3.6.0  
  21. 127.0.0.1:8000> object encoding u:8417  
  22. "hashtable"  
 

 

 

 

 

  内存使用量:

  

  

5. 导入数据代码(不考虑代码优雅性,单纯为了测试,勿喷)
    注意:
为了排除共享对象的问题,这里所有key,field,value都用字符串类型。
 
Java代码   收藏代码
  1. package com.carlosfu.redis;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.HashMap;  
  5. import java.util.List;  
  6. import java.util.Map;  
  7. import java.util.Random;  
  8.   
  9. import org.junit.Test;  
  10.   
  11. import redis.clients.jedis.Jedis;  
  12.   
  13. /** 
  14.  * 一次string-hash优化 
  15.  *  
  16.  * @author carlosfu 
  17.  * @Date 2015-11-8 
  18.  * @Time 下午7:27:45 
  19.  */  
  20. public class TestRedisMemoryOptimize {  
  21.   
  22.     private final static int TOTAL_USER_COUNT = 1000000;  
  23.   
  24.     private final static String HOST = "127.0.0.1";  
  25.   
  26.     private final static int PORT = 6379;  
  27.   
  28.     /** 
  29.      * 纯字符串 
  30.      */  
  31.     @Test  
  32.     public void testString() {  
  33.         int mBatchSize = 2000;  
  34.         Jedis jedis = null;  
  35.         try {  
  36.             jedis = new Jedis(HOST, PORT);  
  37.             List<String> kvsList = new ArrayList<String>(mBatchSize);  
  38.             for (int i = 1; i <= TOTAL_USER_COUNT; i++) {  
  39.                 String key = "u:" + i;  
  40.                 kvsList.add(key);  
  41.                 String value = "v:" + i;  
  42.                 kvsList.add(value);  
  43.                 if (i % mBatchSize == 0) {  
  44.                     System.out.println(i);  
  45.                     jedis.mset(kvsList.toArray(new String[kvsList.size()]));  
  46.                     kvsList = new ArrayList<String>(mBatchSize);  
  47.                 }  
  48.             }  
  49.         } catch (Exception e) {  
  50.             e.printStackTrace();  
  51.         } finally {  
  52.             if (jedis != null) {  
  53.                 jedis.close();  
  54.             }  
  55.         }  
  56.     }  
  57.   
  58.     /** 
  59.      * 纯hash 
  60.      */  
  61.     @Test  
  62.     public void testHash() {  
  63.         int mBatchSize = 2000;  
  64.         String hashKey = "allUser";  
  65.         Jedis jedis = null;  
  66.         try {  
  67.             jedis = new Jedis(HOST, PORT);  
  68.             Map<String, String> kvMap = new HashMap<String, String>();  
  69.             for (int i = 1; i <= TOTAL_USER_COUNT; i++) {  
  70.                 String key = "u:" + i;  
  71.                 String value = "v:" + i;  
  72.                 kvMap.put(key, value);  
  73.                 if (i % mBatchSize == 0) {  
  74.                     System.out.println(i);  
  75.                     jedis.hmset(hashKey, kvMap);  
  76.                     kvMap = new HashMap<String, String>();  
  77.                 }  
  78.             }  
  79.         } catch (Exception e) {  
  80.             e.printStackTrace();  
  81.         } finally {  
  82.             if (jedis != null) {  
  83.                 jedis.close();  
  84.             }  
  85.         }  
  86.     }  
  87.   
  88.     /** 
  89.      * segment hash 
  90.      */  
  91.     @Test  
  92.     public void testSegmentHash() {  
  93.         int segment = 100;  
  94.         Jedis jedis = null;  
  95.         try {  
  96.             jedis = new Jedis(HOST, PORT);  
  97.             Map<String, String> kvMap = new HashMap<String, String>();  
  98.             for (int i = 1; i <= TOTAL_USER_COUNT; i++) {  
  99.                 String key = "f:" + String.valueOf(i % segment);  
  100.                 String value = "v:" + i;  
  101.                 kvMap.put(key, value);  
  102.                 if (i % segment == 0) {  
  103.                     System.out.println(i);  
  104.                     int hash = (i - 1) / segment;  
  105.                     jedis.hmset("u:" + String.valueOf(hash), kvMap);  
  106.                     kvMap = new HashMap<String, String>();  
  107.                 }  
  108.             }  
  109.         } catch (Exception e) {  
  110.             e.printStackTrace();  
  111.         } finally {  
  112.             if (jedis != null) {  
  113.                 jedis.close();  
  114.             }  
  115.         }  
  116.     }  
  117.   
  118. }  
 
三、结果对比
 redis核心对象 数据类型 + 编码方式 + ptr  分段hash也不会造成drift
方案 优点 缺点
string

直观、容易理解

  1. 内存占用较大
  2. key值分散、不变于计算整体
hash

直观、容易理解、整合整体

  1. 内存占用大
  2. 一个key占用过大内存,如果是redis-cluster会出 现data drift

 

segment-hash

内存占用量小,虽然理解不够直观,但是总体上是最优的。

理解不够直观。

 
四、结论:
   在使用Redis时,要选择合理的数据结构解决实际问题,那样既可以提高效率又可以节省内存。所以此次优化方案三为最佳。
 
附图一张:redis其实是一把瑞士军刀:

おすすめ

転載: www.cnblogs.com/jinanxiaolaohu/p/12009723.html