1. 程式人生 > 資料庫 >面試衝刺——Redis 專題

面試衝刺——Redis 專題

Redis 專題

Redis 的持久化機制

RDB ----------> 記憶體中的資料集快照 ; 預設開啟的 ; -------> dump.rdb

配置 : save  60  10  --------> 60s內,有10個key發生變化,觸發RDB操作.

優點 : 
	1). 恢復速度快 ; -----> 
	2). 佔用磁碟空間小
	
缺點 : 		
	1). 容易丟失資料 ; ------> 丟失資料風險高 
	2). 如果記憶體中的資料量過大,會造成持久化時,造成大量的磁碟IO,及CPU,有可能會影響其他的執行緒; 
	3). 檔案不可讀

AOF -----------> redis操作的指令,操作日誌 ; 預設未開啟的 ; -----> append only file

開啟 : 
	appendonly yes
	
	#appendfsync always
	appendfsync everysec
	#appendfsync no
	

優點 : 
	1). 檔案是可讀的 ;
	2). 丟失資料的風險小 ;
	
缺點 : 
	1). 恢復速度慢 ;
	2). aof的日誌檔案可能會比較大 ;
	


日誌重寫 : 
	
	bgrewriteaof
	


redis 中的資料庫一共有16個,預設為第一個0 號資料庫 ;

切換資料庫 : select  0 ; 

Redis 記憶體淘汰策略

概述 :

每臺redis的伺服器的記憶體都是有限的,而且也不是所有的記憶體都用來儲存資訊。

而且redis的實現並沒有在記憶體這塊做太多的優化,所以實現者為了防止記憶體過於飽和,採取了一些措施來管控記憶體。

Redis的記憶體設定 :

maxmemory <bytes> 

記憶體淘汰(置換)策略 :

1). volatile-lru -> remove the key with an expire set using an LRU algorithm
	
	只從設定失效(expire set)的key中選擇最近最不經常使用的key進行刪除,用以儲存新資料
	
2). allkeys-lru -> remove any key according to the LRU algorithm
	
	優先刪除掉最近最不經常使用的key,用以儲存新資料
	
3). volatile-random -> remove a random key with an expire set
	
	只從設定失效(expire set)的key中,(隨機)選擇一些key進行刪除,用以儲存新資料
	
4). allkeys-random -> remove a random key,any key
	
	隨機從all-keys中(隨機)選擇一些key進行刪除,用以儲存新資料 
	
5). volatile-ttl -> remove the key with the nearest expire time (minor TTL)
	
	只從設定失效(expire set)的key中,選出存活時間(TTL)最短的key進行刪除,用以儲存新資料
	
6). noeviction -> don't expire at all,just return an error on write operations
	
	不進行淘汰,表示即使記憶體達到上限也不進行置換,所有能引起記憶體增加的命令都會返回error

配置 : 
	
	maxmemory-policy noeviction

樣本數量 :

maxmemory-samples 3

Redis 中的 LRU 不是嚴格意義上的LRU演算法實現,是一種近似的 LRU 實現,主要是為了節約記憶體佔用以及提升效能。Redis 有這樣一個配置 —— maxmemory-samples,Redis 的 LRU 是取出配置的數目的key,然後從中選擇一個最近最不經常使用的 key 進行置換,預設的 5,可以通過調整樣本數量來取得 LRU 置換演算法的速度或是精確性方面的優勢。

1559557517522
在這裡插入圖片描述
在這裡插入圖片描述
快取穿透

快取穿透,是指查詢一個數據庫一定不存在的資料。正常的使用快取流程大致是,資料查詢先進行快取查詢,如果key不存在或者key已經過期,再對資料庫進行查詢,並把查詢到的物件,放進快取。如果資料庫查詢物件為空,則不放進快取。
1559558795953

@Override
public List findByCategoryId(Long categoryId) { // -1 / -10
// 加入快取的程式碼:
List list = (List) redisTemplate.boundHashOps(“content”).get(categoryId);

if(list==null){
	System.out.println("查詢資料庫===================");
	TbContentExample example = new TbContentExample();
	Criteria criteria = example.createCriteria();
	// 有效廣告:
	criteria.andStatusEqualTo("1");
	
	criteria.andCategoryIdEqualTo(categoryId);
	// 排序
	example.setOrderByClause("sort_order");
	
	list = contentMapper.selectByExample(example);
	if(list !=null){
		redisTemplate.boundHashOps("content").put(categoryId,list);
    }
}else{
	System.out.println("從快取中獲取====================");
}

return list;

}
解決方案 :

1). 設定過期時間

@Override
public List findByCategoryId(Long categoryId) {
// 加入快取的程式碼:
List list = (List) redisTemplate.boundValueOps(“content_”+categoryId).get();

if(list == null){
	System.out.println("查詢資料庫===================");
	TbContentExample example = new TbContentExample();
	Criteria criteria = example.createCriteria();
	// 有效廣告:
	criteria.andStatusEqualTo("1");
	
	criteria.andCategoryIdEqualTo(categoryId);
	// 排序
	example.setOrderByClause("sort_order");
	
	list = contentMapper.selectByExample(example);
	if(list != null){
		redisTemplate.boundValueOps("content_"+categoryId).set(list); //-1 
    }else{  //-1,-2,-10 
        redisTemplate.boundValueOps("content_"+categoryId).set(null); //null
        redisTemplate.expire("content_"+categoryId,7200,TimeUnit.SECONDS);
    }
}else{
	System.out.println("從快取中獲取====================");
}

return list;

}
2). 只查詢快取,不查詢資料庫 ;

image-20200206150953540

快取擊穿

快取擊穿,是指一個key非常熱點,在不停的扛著大併發,大併發集中對這一個點進行訪問,當這個key在失效的瞬間,持續的大併發就穿破快取,直接請求資料庫,就像在一個屏障上鑿開了一個洞 。

解決方案 :

1). 對熱點資料,不設定過期時間 ;  

2). 互斥鎖

3). 只查詢redis快取,不查詢資料庫 ;

public class RedisDemo {

private static Lock lock = new ReentrantLock();

public static String getData(String key) throws InterruptedException {
    
    String result = getDataFromRedis(key); //從redis獲取資料
	
    if(result == null){ // 如果資料為null,需要從資料庫中獲取 lock.lock();  lock.unlock(); lock.tryLock()
        
        if(lock.tryLock()){ //嘗試獲取鎖
            
            result = getDataFromMysql(key); //從資料庫中查詢
            
            if(result != null){ //如果查詢到資料,就快取在redis中
                saveDataToRedis(key,result);
            }
            lock.unlock();//釋放鎖
        }else{
            TimeUnit.MILLISECONDS.sleep(100);
            result = getData(key);
        }
    }
    return result;
}

private static void saveDataToRedis(String key,String result) {
    System.out.println("儲存資料到redis中,key - value ");
}

private static String getDataFromMysql(String key) {
    System.out.println("從資料庫中獲取資料 ");
    return null;
}

public static String getDataFromRedis(String key){
    System.out.println("從redis中獲取資料 ");
    return null;
}

}
快取雪崩

快取雪崩,是指在某一個時間點,快取集中過期失效。

產生雪崩的原因之一,比如在寫本文的時候,馬上就要到雙十二零點,很快就會迎來一波搶購,這波商品時間比較集中的放入了快取,假設快取一個小時。那麼到了凌晨一點鐘的時候,這批商品的快取就都過期了。而對這批商品的訪問查詢,都落到了資料庫上,對於資料庫而言,就會產生週期性的壓力波峰。

解決方案

1). 如果設定快取的過期時間,需要根據業務劃分,不同型別的資料,可以設定不同的過期時間,不要設定為相同的過期時間,從而造成快取在同一個時間點過期 ;

2). 只查詢redis,不查詢資料庫 ;

Linux 版本的 redis 安裝

1). 上傳 redis-3.0.0.tar.gz

alt + p ------> put D:/redis-3.0.0.tar.gz 

2). 安裝C語言編譯環境

yum install gcc-c++   (需要聯網)

3). 解壓壓縮包

tar  -zxvf redis-3.0.0.tar.gz

4). 編譯原始碼

cd  redis-3.0.0

make

5). 安裝

make  install  PREFIX=/usr/local/redis

6). 拷貝解壓目錄下的redis配置檔案redis.conf到redis安裝目錄

cp redis.conf  /usr/local/redis

7). 啟動

cd  /usr/local/redis

bin/redis-server redis.conf

啟動之後,介面如下(不要關閉該視窗,預設為前臺執行) :

1563413526684