ソースを記入してくださいハ: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.取得方法:
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其实是一把瑞士军刀: