目錄
- 什么是Redis
- 為什么用Redis
- 如何使用Redis(5種數據類型)
- 底層數據結構
什么是Redis
Redis是一個開源的高性能鍵值對數據庫。它通過提供多種鍵值數據類型來適應不同場景下的存儲需求,並借助許多高層級的接口使其可以勝任如緩存、隊列系統等不同的角色
為什么用Redis(特性)
- 存儲結構
支持字符串、散列、列表、集合、有序集合等類型。與MySQL相比,對於小數據集的復雜關系操作更直觀,性能更好
- 內存存儲與持久化
Redis數據庫中的所有數據都存儲在內存中。由於內存的讀寫速度遠快於硬盤,因此Redis在性能上對比其他基於硬盤存儲的數據庫有非常明顯的優勢
Redis提供了對持久化的支持,即將可以內存中的數據異步寫入到硬盤中,同時不影響繼續提供服務
- 功能豐富
緩存:為每個鍵設置生存時間(Time To Live,TTL),生存時間到期后鍵會自動被刪除
隊列:Redis的列表類型鍵可以用來實現隊列,並且支持阻塞式讀取,可以很容易地實現一個高性能的優先級隊列
構建聊天室等系統:支持“發布/訂閱”的消息模式
- 簡單穩定
如何使用Redis
字符串

1 public void testString(){ 2 Jedis jedis = RedisUtil.connect(); 3 jedis.set("testKey", "testValue"); 4 System.out.println("testKey: " + jedis.get("testKey")); 5 Long delResult = jedis.del("testKey"); 6 System.out.println("del testKey, result = " + delResult); 7 System.out.println("del后,testKey: " + jedis.get("testKey")); 8 }
- 問題:字符串直接用char數組就好了,但是redis做了一些優化
目的:提高空間的利用率
方法:tryObjectEncoding包含的主要邏輯,依次執行以下策略
1. 判斷長度小於20,且可以將它轉換為long類型的值:int string2l(const char *s, size_t slen, long *lval)
2. 把value放在和與對象相同的內存塊中:robj *createEmbeddedStringObject(const char *ptr, size_t len)
注:o對象(即返回結果)獲得如下全部的內存空間,value是設置的值,與o對象分配在同一塊中
3. 無法轉換,把多分配的空間remove
列表

1 public void testList(){ 2 String testKey = "testKey"; 3 jedis.lpush(testKey, "testValue", "testValue2"); 4 System.out.println("testKey: " + jedis.lrange(testKey, 0, -1)); 5 jedis.lpush(testKey, "testValue", "testValue2", "testValue3"); 6 System.out.println("testKey set new: " + jedis.lrange(testKey, 0, -1)); 7 jedis.lrem(testKey, 2, "testValue"); 8 System.out.println("testKey lrem 2個數據后: " + jedis.lrange(testKey, 0, -1)); 9 Long delResult = jedis.del(testKey); 10 System.out.println("del testKey, result = " + delResult); 11 System.out.println("del后,testKey: " + jedis.get(testKey)); 12 System.out.println("del后,testKey: " + jedis.lrange(testKey, 0, -1)); 13 }
集合

1 public void testSet(){ 2 String testKey = "testKey"; 3 jedis.sadd(testKey, "testValue", "testValue2"); 4 System.out.println("testKey: " + jedis.smembers(testKey)); 5 jedis.sadd(testKey, "testValue", "testValue2", "testValue3"); 6 System.out.println("testKey set new: " + jedis.smembers(testKey)); 7 jedis.srem(testKey, "testValue"); 8 System.out.println("testKey srem后: " + jedis.smembers(testKey)); 9 }
有序集合

1 public void testSortedSet(){ 2 String testKey = "testKey"; 3 jedis.zadd(testKey, 5, "testValue5"); 4 jedis.zadd(testKey, 2, "testValue2"); 5 System.out.println("testKey: " + jedis.zrange(testKey, 0, -1)); 6 jedis.zadd(testKey, 2, "testValue2-2"); 7 jedis.zadd(testKey, 9, "testValue9"); 8 System.out.println("testKey zadd: " + jedis.zrange(testKey, 0, -1)); 9 jedis.zrem(testKey, "testValue2"); 10 System.out.println("testKey zrem后: " + jedis.zrange(testKey, 0, -1)); 11 }
哈希

1 public void testHash(){ 2 String testKey = "testKey"; 3 jedis.hset(testKey, "field", "100"); 4 System.out.println("testKey: " + jedis.hget(testKey, "field")); 5 jedis.hincrBy(testKey, "field", 100); 6 System.out.println("testKey 的value hincrBy: " + jedis.hvals(testKey)); 7 8 jedis.hset(testKey, "field", "testValue"); 9 jedis.hset(testKey, "field2", "testValue2"); 10 jedis.hset(testKey, "field3", "testValue3"); 11 System.out.println("testKey new hset, keys= " + jedis.hkeys(testKey)); 12 System.out.println("testKey new hset, values= " + jedis.hvals(testKey)); 13 jedis.hdel(testKey, "field3"); 14 System.out.println("testKey hdel, keys= " + jedis.hkeys(testKey)); 15 System.out.println("testKey hdel, values= " + jedis.hvals(testKey)); 16 }
底層數據結構

1 /*3個基礎數據結構 */ 2 typedef struct dict { 3 dictType *type; 4 void *privdata; 5 dictht ht[2];// 有兩個table 6 long rehashidx; 7 unsigned long iterators; 8 } dict; 9 10 /* hash table結構 */ 11 typedef struct dictht { 12 dictEntry **table; 13 unsigned long size; 14 unsigned long sizemask; 15 unsigned long used; 16 } dictht; 17 18 /* 節點結構*/ 19 typedef struct dictEntry { 20 void *key; 21 union { 22 void *val; 23 uint64_t u64; 24 int64_t s64; 25 double d; 26 } v; 27 struct dictEntry *next; 28 } dictEntry; 29 30 31 /* 32 * 根據key查找entry 33 * key 通過hash獲取index,沖突則通過掛鏈方式繼續查找 34 */ 35 dictEntry *dictFind(dict *d, const void *key) 36 { 37 dictEntry *he; 38 uint64_t h, idx, table; 39 40 if (d->ht[0].used + d->ht[1].used == 0) return NULL; /* dict is empty */ 41 if (dictIsRehashing(d)) _dictRehashStep(d); 42 h = dictHashKey(d, key); 43 for (table = 0; table <= 1; table++) { 44 idx = h & d->ht[table].sizemask; 45 he = d->ht[table].table[idx]; 46 while (he) {//遍歷該位置下所有key 47 if (key == he->key || dictCompareKeys(d, key, he->key)) 48 return he; 49 he = he->next; 50 } 51 if (!dictIsRehashing(d)) return NULL; 52 } 53 return NULL; 54 } 55 56 /* 創建dict,初始化時dictht大小為0 */ 57 dict *dictCreate(dictType *type, 58 void *privDataPtr) 59 { 60 dict *d = zmalloc(sizeof(*d)); 61 62 _dictInit(d, type, privDataPtr); 63 return d; 64 } 65 66 /* 擴展或創建dictht */ 67 int dictExpand(dict *d, unsigned long size) 68 { 69 /* the size is invalid if it is smaller than the number of 70 * elements already inside the hash table */ 71 if (dictIsRehashing(d) || d->ht[0].used > size) 72 return DICT_ERR; 73 74 dictht n; /* the new hash table */ 75 /* 獲取要分配的dictht的大小 */ 76 unsigned long realsize = _dictNextPower(size); 77 78 /* Rehashing to the same table size is not useful. */ 79 if (realsize == d->ht[0].size) return DICT_ERR; 80 81 /* Allocate the new hash table and initialize all pointers to NULL */ 82 n.size = realsize; 83 n.sizemask = realsize - 1; 84 n.table = zcalloc(realsize*sizeof(dictEntry*)); 85 n.used = 0; 86 87 /* Is this the first initialization? If so it's not really a rehashing 88 * we just set the first hash table so that it can accept keys. */ 89 if (d->ht[0].table == NULL) { 90 d->ht[0] = n; 91 return DICT_OK; 92 } 93 94 /* Prepare a second hash table for incremental rehashing */ 95 d->ht[1] = n; 96 d->rehashidx = 0; 97 return DICT_OK; 98 } 99 100 /* 101 * hash table的容量為2的冪 102 * 初始化大小為4 103 */ 104 static unsigned long _dictNextPower(unsigned long size) 105 { 106 unsigned long i = DICT_HT_INITIAL_SIZE; 107 108 if (size >= LONG_MAX) return LONG_MAX + 1LU; 109 while (1) { 110 if (i >= size) 111 return i; 112 i *= 2; 113 } 114 } 115 116 /* 添加一個kv */ 117 dictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing) 118 { 119 long index; 120 dictEntry *entry; 121 dictht *ht; 122 123 if (dictIsRehashing(d)) _dictRehashStep(d); 124 125 /* Get the index of the new element, or -1 if 126 * the element already exists. */ 127 if ((index = _dictKeyIndex(d, key, dictHashKey(d, key), existing)) == -1) 128 return NULL; 129 130 /* 最新的kv放在鏈表的第一個,該kv最可能被訪問到*/ 131 ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0]; 132 entry = zmalloc(sizeof(*entry)); 133 entry->next = ht->table[index]; 134 ht->table[index] = entry; 135 ht->used++; 136 137 /* Set the hash entry fields. */ 138 dictSetKey(d, entry, key); 139 return entry; 140 }