[재 인쇄] 미국 기업은 일부 구덩이 -4.redis 레디 스 메모리 사용 최적화를 밟았

레디 스에서 미국의 임무는 어떤 구덩이 -4.redis 메모리 사용 최적화를 밟았

카테고리 블로그 :
 

소스를 표시하시기 바랍니다 하 : http://carlosfu.iteye.com/blog/2254154

더 레디 스 개발, 운영 및 유지 보수뿐만 아니라 아키텍처의 새로운 발전, 마이크로 채널 대중 숫자에 오신 것을 환영합니다주의 :

 


    

 I. 배경 : 적절한 사용 시나리오를 선택
   메모리 소비, 높은 비용 가격 : 레디 스는 레디 스 인상의 결과로 종종 오해 무차별 적으로 사용된다 :
   레디 스 동시 시스템의 매우 적은 양으로 사용되는 MySQL 용 "패션"또는 "오해"레디 스 취 MySQL은 모든 원본 데이터 위해서 1.
     ---- (시스템이 복잡 미제라블이지만, 레디 스와 문제가있는 경우 더 강력한 기능에서 이야기하는 하나의 MySQL을, 그리고 MySQL의 성능은 이미 충분히 있기 때문에 레디 스는 고도의 동시성 시스템에 더 적합합니다.)
   2. KV는 레디 스가 캐시 생각
     ----- (레디 스 여러 데이터 구조를 지원하고, 기타 다양한 기능의 많은이 있습니다)
   3. 등 MySQL은, HBase를, 레디 스로서 콘트라스트의 모든 종류를 수행하고자
    ----- (각 데이터베이스는 HBase와 같은 자신의 사용 시나리오를 가지고 그것을 개인화 된 데이터 시스템 우리는 1T, 단순히 부적절 레디 스에이 시간,하지만 레디 스에 뜨거운 데이터가)
    어쨌든, 적절한 장면에서, 적절한 데이터베이스 제품을 선택합니다.
  그것은 두 따옴표와 함께 제공 :
에반 위버, 트위터, 2009 년 3 월 작성
모든 웹 2.0 메모리에서 실행!
팀 회색 썼다
IS, 플래시 디스크는, RAM 소재지 IS 왕. 죽은 테이프 테이프 디스크 인
(테이프가 죽었다는, 디스크가 새 테이프는, 플래시 메모리가 새 디스크이고, 랜덤 액세스 메모리 지역은 왕이다)
  
둘째, 최적화의 해시에 캐릭터
1. 시나리오 :
    사용자 ID : userId를,
    의 microblogging 사용자 수 : weiboCount    
userId를 (사용자 ID) weiboCount (微博数)
1 2,000

(10)

288

.... ...
1000000 1,000
 
2. 방법 :
값 레디 스 문자열 데이터 구조를 이용하여 (1), 키의 userId를, weiboCount
(2) 해시 레디 스 구조를 사용하여, 하나의 hashkey 키 = "allUserWeiboCount"필드 = userId를, = fieldValue의 weiboCount
(3) 해시 레디 스 구조를 사용하여, 복수 hashkey 키 = 사용자 ID / 100 % userId를 필드 = 100, = fieldValue의 weiboCount
각 hashKey 100 저장된 해시 KV 필드 = userId를 100 %, 즉 : 처음 두 비교적 쉽게 이해하기 위해, 제 3 실시 예를 설명 할
userId를 hashKey
1 0 1
0

0

... .... ...
99 0 99
(100) 1 0
(101) 1 1
.... ... ...
9999 99 99
100000 1,000 0

 

참고 :

모든 리얼 타임 테스트 키, 필드, 값, 공유 객체의 문제를 제거하기 위해 사용되는 문자열 유형입니다.

 

3. 취득 법 :

 

자바 코드   收藏代码
  1. # 가져 오기 userId를 = 5003의 microblogging의 사용자 수  
  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