Redis常見問題彙總
問題1:懂Redis事務麼?
Redis Cluster叢集架構,不同的key是有可能分配在不同的Redis節點上的,在這種情況下Redis的事務機制是不生效的。 Redis的事務功能較弱(不支援回滾),而且叢集版本(自研和官方)要求一次事務操作的key必須在一個slot上(可以使用 hashtag 功能解決)
問題2:Redis的多資料庫機制,瞭解多少?
Redis支援多個資料庫,並且每個資料庫的資料是隔離的不能共享,單機下的redis可以支援16個資料庫(db0 ~ db15)
在Redis Cluster叢集架構下只有一個資料庫空間,即db0。因此,我們沒有使用Redis的多資料庫功能!
問題3:Redis叢集機制中,你覺得有什麼不足的地方嗎?
Redis因為是要提升效能,所以直接採用的非同步複製,當在Master上寫入資料後直接返回,然後把資料快照廣播給
Slave,讓所有的Slaves去執行操作,資料通過非同步複製,不保證資料的強一致性
假設有一個key,對應的value是Hash型別的。如果Hash物件非常大,是不支援對映到不同節點的!只能對映到集
群中的一個節點上!還有就是做批量操作比較麻煩!
問題4:懂Redis的批量操作麼?
我們在生產上採用的是Redis Cluster叢集架構,不同的key會劃分到不同的slot中,因此直接使用mset或者mget等操作是行不通的。
問題5:那在Redis叢集模式下,如何進行批量操作的優化?
redis引入cluster模式後,如果存在的節點異常多的時候,IO的代價已經超過資料傳輸,在這種情況下再增加節點已經沒法再提高效率了。例如批量獲取操作mget,RedisCluster將資料按key雜湊到16384個slot上,每個redis node負責一部分的slot。mget需要執行的操作就是從redis node獲取所有的key-value值,然後進行merge然後返回。
①序列命令:由於n個key是比較均勻地分佈在Redis Cluster的各個節點上,因此無法使用mget命令一次性獲取,所以通常來講要獲取n個key的值,最簡單的方法就是逐次執行n個get命令,這種操作時間複雜度較高,它的操作時間=n次網路時間+n次命令時間,網路次數是n。很顯然這種方案不是最優的,但是實現起來比較簡單。
②序列IO:Redis Cluster使用CRC16演算法計算出雜湊值,再取對16383的餘數就可以算出slot值,同時Smart客戶端會儲存slot和節點的對應關係,有了這兩個資料就可以將屬於同一個節點的key進行歸檔,得到每個節點的key子列表,之後對每個節點執行mget或者Pipeline操作,它的操作時間=node次網路時間+n次命令時間,網路次數是node的個數,很明顯這種方案比第一種要好很多,但是如果節點數太多,還是有一定的效能問題。
Map<String, String> serialIOMget (List<String> keys) {
//結果集
Map<String, String> keyValueMap = new HashMap<>();
//屬於各個節點的key列表,JedisPool要提供基於ip和port的hashcode方法
Map<JedisPool, List<String>> nodeKeyListMap= new HashMap<>();
//遍歷所有的key
for (String key : keys) {
//使用CRC16本地計算每個key的slot
int slot = JedisClusterCRC16.getSlot(key);
//通過iediscluster本地slot->node對映獲取slot對應的node
JedisPool jedisPool = jedisCluster.getConnectionHandler().getJedisPoolFromSlot(slot);
//歸檔
if (nodeKeyListMap.containsKey(jedisPool)) {
nodeKeyListMap.get(jedisPool).add(key);
} else {
List<String> list = new ArrayList<String>();
list.add(key);
nodeKeyListMap.put(jedisPool, list);
}
}
//從每個節點上批量獲取,這裡使用mget也可以使用pipeline
for (Map.Entry<JedisPool, List<String>> entry : nodeKeyListMap.entrySet()) {
JedisPool jedisPool = entry.getKey();
List<String> nodeKeyList = entry.getValue();
//列表變為陣列
String[] nodeKeyArray = nodeKeyList.toArray(new String[nodeKeyList.size()]);
//批量獲取,可以使用mget或者Pipeline
List<String> nodeValueList = jedisPool.getResource().mget(nodeKeyArray);
//歸檔
for (int i = 0; i < nodeKeyList.size(); i++) {
keyValueMap.put(nodeKeyList.get(i), nodeValueList.get(i));
}
}
return keyValueMap;
}
③並行IO:此方案是將方案2中的最後一步改為多執行緒執行,網路次數雖然還是節點個數,但由於使用多執行緒網路時間變為O(1),這種方案會增加程式設計的複雜度。
④hash_tag實現:Redis Cluster的hash_tag功能,它可以將多個key強制分配到一個節點上,它的操作時間=1次網路時間+n次命令時間。hashtag的解決方案:可以使用twitter的 twemproxy
使用Redis Cluster時,pipelining、事務和LUA Script功能涉及的key必須在同一個資料分片上,否則將會返
回錯誤。如要在Redis Cluster中使用上述功能,就必須通過hash tags來確保一個pipeline或一個事務中操作
的所有key都位於同一個Slot中。
有一些客戶端(如Redisson)實現了叢集化的pipelining操作,可以自動將一個pipeline裡的命令按key所在的
分片進行分組,分別發到不同的分片上執行。但是Redis不支援跨分片的事務,事務和LUA Script還是必須遵循所
有key在一個分片上的規則要求。
問題6:你們有對Redis做讀寫分離麼?
不做讀寫分離。我們用的是Redis Cluster的架構,是屬於分片叢集的架構。而Redis本身在記憶體上操作,不會涉及IO吞吐,即使讀寫分離也不會提升太多效能,Redis在生產上的主要問題是考慮容量,單機最多10-20G,key太多降低Redis效能.因此採用分片叢集結構,已經能保證了我們的效能。其次,用上了讀寫分離後,還要考慮主從一致性,主從延遲等問題,徒增業務複雜度。
問題7:Redis 序列化方式有哪些?
當我們的資料儲存到Redis的時候,我們的鍵(key)和值(value)都是通過Spring提供的Serializer序列化到資料庫的。RedisTemplate預設使用的是JdkSerializationRedisSerializer,StringRedisTemplate預設使用的是StringRedisSerializer。
Spring Data JPA為我們提供了下面的Serializer:GenericToStringSerializer、Jackson2JsonRedisSerializer、JacksonJsonRedisSerializer、JdkSerializationRedisSerializer、OxmSerializer、StringRedisSerializer。
序列化方式對比:
JdkSerializationRedisSerializer: 使用JDK提供的序列化功能。 優點是反序列化時不需要提供型別資訊(class),但缺點是需要實現Serializable介面,還有序列化後的結果非常龐大,是JSON格式的5倍左右,這樣就會消耗redis伺服器的大量記憶體。
Jackson2JsonRedisSerializer: 使用Jackson庫將物件序列化為JSON字串。優點是速度快,序列化後的字串短小精悍,不需要實現Serializable介面。但缺點也非常致命,那就是此類的建構函式中有一個型別引數,必須提供要序列化物件的型別資訊(.class物件)。 通過檢視原始碼,發現其只在反序列化過程中用到了型別資訊。
問題8:Redis 如何做非同步佇列?
一般使用list結構作為佇列,rpush生產訊息,lpop消費訊息。當lpop沒有訊息的時候,要適當sleep一會再重試。
如果不用sleep呢?list還有個指令叫blpop,在沒有訊息的時候,它會阻塞住直到訊息到來。
能不能生產一次消費多次呢?
使用pub/sub主題訂閱者模式,可以實現1:N的訊息佇列。pub/sub有什麼缺點?在消費者下線的情況下,生產的訊息會丟失,得使用專業的訊息佇列如rabbitmq等。
redis如何實現延時佇列?使用sortedset,拿時間戳作為score,訊息內容作為key呼叫zadd來生產訊息,消費者用zrangebyscore指令獲取N秒之前的資料輪詢進行處理
相關文章
- Redis Manager 常見問題彙總Redis
- Java 常見問題彙總Java
- Bootstrap常見問題彙總boot
- 代理IP常見問題彙總
- SpringMvc常見問題彙總SpringMVC
- JuniperNetScreen常見問題彙總
- mybatis常見庫及問題彙總MyBatis
- Vue專案常見問題彙總Vue
- Java程式設計常見問題彙總Java程式設計
- Android Studio 常見問題彙總Android
- 安裝PHP常見問題彙總薦PHP
- Redis問題彙總Redis
- mount命令詳解及常見問題彙總
- Java程式設計常見問題彙總(一)Java程式設計
- MyBatis學習總結(24)——Mybatis常見問題彙總MyBatis
- Redis面試常見問題Redis面試
- redis快取常見問題場景總結Redis快取
- 新手雲伺服器租用常見問題彙總伺服器
- 雲端計算 常見問題案例彙總情況
- Oracle RAC+DG巡檢常見問題彙總(一)Oracle
- 物聯網平臺常見問題與答案彙總
- JavaScript常見面試題彙總(含答案)JavaScript面試題
- Java常見面試題及答案彙總Java面試題
- 常見問題總結
- Gtalent圈子大佬常談的Redis基礎面試問題彙總Redis面試
- Linux常見問題彙總,比較適合菜鳥哈Linux
- VNC常用操作及常見問題解決辦法彙總VNC
- Win7系統上常見IE 10問題彙總Win7
- redis 叢集常見問題 QARedis
- Redis常見問題(快取雪崩)Redis快取
- 【彙總】Python爬蟲常見面試題!Python爬蟲面試題
- mysql常見問題總結MySql
- Vue 常見問題總結Vue
- TCP常見問題總結TCP
- GeoServer 常見問題總結Server
- Xlua常見API彙總API
- 分享視訊直播常見問題與解決辦法彙總
- 蘋果Apple Watch系列智慧手錶的常見問題彙總蘋果APP