소스를 표시하시기 바랍니다 하 : 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. 취득 법 :
- # 가져 오기 userId를 = 5003의 microblogging의 사용자 수
- (1) get u:5003
- (2) hget allUser u:5003
- (3) hget u:50 f:3
4. 内存占用量对比(100万用户 userId u:1~u:1000000)
- #方法一 Memory
- used_memory:118002640
- used_memory_human:112.54M
- used_memory_rss:127504384
- used_memory_peak:118002640
- used_memory_peak_human:112.54M
- used_memory_lua:36864
- mem_fragmentation_ratio:1.08
- mem_allocator:jemalloc-3.6.0
- ---------------------------------------------------
- #方法二 Memory
- used_memory:134002968
- used_memory_human:127.80M
- used_memory_rss:144261120
- used_memory_peak:134002968
- used_memory_peak_human:127.80M
- used_memory_lua:36864
- mem_fragmentation_ratio:1.08
- mem_allocator:jemalloc-3.6.0
- --------------------------------------------------------
- #方法三 Memory
- used_memory:19249088
- used_memory_human:18.36M
- used_memory_rss:26558464
- used_memory_peak:134002968
- used_memory_peak_human:127.80M
- used_memory_lua:36864
- mem_fragmentation_ratio:1.38
- mem_allocator:jemalloc-3.6.0
那么为什么第三种能少那么多内存呢?之前有人说用了共享对象的原因,现在我将key,field,value全部都变成了字符串,仍然还是节约很多内存。
之前我也怀疑过是hashkey,field的字节数少造成的,但是我们下面通过一个实验看就清楚是为什么了。当我将hash-max-ziplist-entries设置为2并且重启后,所有的hashkey都变为了hashtable编码。
同时我们看到了内存从18.36M变为了122.30M,变化还是很大的。
- 127.0.0.1:8000> object encoding u:8417
- "ziplist"
- 127.0.0.1:8000> config set hash-max-ziplist-entries 2
- OK
- 127.0.0.1:8000> debug reload
- OK
- (1.08s)
- 127.0.0.1:8000> config get hash-max-ziplist-entries
- 1) "hash-max-ziplist-entries"
- 2) "2"
- 127.0.0.1:8000> info memory
- # Memory
- used_memory:128241008
- used_memory_human:122.30M
- used_memory_rss:137662464
- used_memory_peak:134002968
- used_memory_peak_human:127.80M
- used_memory_lua:36864
- mem_fragmentation_ratio:1.07
- mem_allocator:jemalloc-3.6.0
- 127.0.0.1:8000> object encoding u:8417
- "hashtable"
内存使用量:
5. 导入数据代码(不考虑代码优雅性,单纯为了测试,勿喷)
注意:
为了排除共享对象的问题,这里所有key,field,value都用字符串类型。
- package com.carlosfu.redis;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.Random;
- import org.junit.Test;
- import redis.clients.jedis.Jedis;
- /**
- * 一次string-hash优化
- *
- * @author carlosfu
- * @Date 2015-11-8
- * @Time 下午7:27:45
- */
- public class TestRedisMemoryOptimize {
- private final static int TOTAL_USER_COUNT = 1000000;
- private final static String HOST = "127.0.0.1";
- private final static int PORT = 6379;
- /**
- * 纯字符串
- */
- @Test
- public void testString() {
- int mBatchSize = 2000;
- Jedis jedis = null;
- try {
- jedis = new Jedis(HOST, PORT);
- List<String> kvsList = new ArrayList<String>(mBatchSize);
- for (int i = 1; i <= TOTAL_USER_COUNT; i++) {
- String key = "u:" + i;
- kvsList.add(key);
- String value = "v:" + i;
- kvsList.add(value);
- if (i % mBatchSize == 0) {
- System.out.println(i);
- jedis.mset(kvsList.toArray(new String[kvsList.size()]));
- kvsList = new ArrayList<String>(mBatchSize);
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- if (jedis != null) {
- jedis.close();
- }
- }
- }
- /**
- * 纯hash
- */
- @Test
- public void testHash() {
- int mBatchSize = 2000;
- String hashKey = "allUser";
- Jedis jedis = null;
- try {
- jedis = new Jedis(HOST, PORT);
- Map<String, String> kvMap = new HashMap<String, String>();
- for (int i = 1; i <= TOTAL_USER_COUNT; i++) {
- String key = "u:" + i;
- String value = "v:" + i;
- kvMap.put(key, value);
- if (i % mBatchSize == 0) {
- System.out.println(i);
- jedis.hmset(hashKey, kvMap);
- kvMap = new HashMap<String, String>();
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- if (jedis != null) {
- jedis.close();
- }
- }
- }
- /**
- * segment hash
- */
- @Test
- public void testSegmentHash() {
- int segment = 100;
- Jedis jedis = null;
- try {
- jedis = new Jedis(HOST, PORT);
- Map<String, String> kvMap = new HashMap<String, String>();
- for (int i = 1; i <= TOTAL_USER_COUNT; i++) {
- String key = "f:" + String.valueOf(i % segment);
- String value = "v:" + i;
- kvMap.put(key, value);
- if (i % segment == 0) {
- System.out.println(i);
- int hash = (i - 1) / segment;
- jedis.hmset("u:" + String.valueOf(hash), kvMap);
- kvMap = new HashMap<String, String>();
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- if (jedis != null) {
- jedis.close();
- }
- }
- }
- }
三、结果对比
redis核心对象 数据类型 + 编码方式 + ptr 分段hash也不会造成drift
方案 | 优点 | 缺点 |
string | 直观、容易理解 |
|
hash | 直观、容易理解、整合整体 |
|
segment-hash | 内存占用量小,虽然理解不够直观,但是总体上是最优的。 |
理解不够直观。 |
四、结论:
在使用Redis时,要选择合理的数据结构解决实际问题,那样既可以提高效率又可以节省内存。所以此次优化方案三为最佳。
附图一张:redis其实是一把瑞士军刀:
![](http://dl2.iteye.com/upload/attachment/0112/9197/ef625555-2559-3e5d-af5a-580443e71b93.png)