1.Redis介紹 REmote DIctionary Server(Redis) 是一個由Salvatore Sanfilippo寫的 key-value 存儲系統,是跨平臺的非關係型資料庫,Redis 是一個開源的使用 ANSI C 語言編寫、遵守 BSD 協議、支持網路、可基於記憶體、分散式、可 ...
1.Redis介紹
REmote DIctionary Server(Redis) 是一個由Salvatore Sanfilippo寫的 key-value 存儲系統,是跨平臺的非關係型資料庫,Redis 是一個開源的使用 ANSI C 語言編寫、遵守 BSD 協議、支持網路、可基於記憶體、分散式、可選持久性的鍵值對(Key-Value)存儲的非關係型資料庫,常被用於分散式緩存,作為進程外的緩存,它具有以下特點:
1.方便擴展
2.大數據量高性能
3.八大數據結構
4.分散式存儲
2.什麼時候使用Redis
1.在分析什麼場景使用Redis時,我們首先看下麵的系統設計圖,我們用戶發起請求到達Nginx,然後由Nginx來負載均衡到不同的服務實例,服務實例去訪問資料庫,正常來看沒什麼問題,但是如果集群的單個實例是一個獨立的系統,如果我們在單個系統中使用了緩存就會出現問題,具體是什麼問題呢?
2.當用戶請求被負載均衡到不同的伺服器時,第一個請求被服務實例1
接收,它的執行流程是查詢資料庫返回然後緩存,第二個用戶請求同樣的內容,但是被負載均衡分配到服務實例2
,對於服務實例2來說這是一個全新的請求,那麼又會訪問資料庫,對於我們來說這是很浪費的,當然在正常請求比較少造成的影響不大,但是如果請求併發較高,緩存的命中幾率被放大,導致請求直接訪問資料庫造成阻塞,影響系統可用性。
同理我們可以這樣理解,我們為了伺服器更好的承載,加入了更多的服務實例,那麼組成集群的單個實例越多,與緩存命中率是成反比的,當然我們可以在負載均衡時使用一些措施,使用iphash,來將某一客戶端的訪問與固定單個實例綁定,但是比較局限,不夠靈活,那麼應該怎麼去更好的解決這個問題呢?
此時我們嘗試在上述的架構圖中加入Redis,利用Redis的讀寫高性能的特點,來做統一的緩存,用於降低資料庫的壓力以及保證系統高可用,因為從讀寫速度而言CPU > 記憶體 > 磁碟
3.Redis通信原理
1.單線程和多線程對比
1.單線程原子性操,一個線程做一個任務,不需要鎖也不需要線程的上下文的切換。
2.多線程要實現原子性,涉及到各種鎖,上下文的切換性能的消耗。
3.單線程多進程 ,可以根據我們的服務,開啟多個實例。
2.IO多路復用
多路復用的概念就是多個IO復用一個線程處理,簡單的意思是在一個操作里同時監聽多個輸入輸出源,它和我們的非同步區別就是,前者是輪訓等待任務執行完成拿到結果,而後者則是在任務執行的過程中,自己去執行其他的任務,在不同的操作系統對多路復用有不同的實現,例如Windows內核中實現根據Select
而LInux內核中的實現是Epoll
接下來我們簡單介紹下他們2種的區別。
Select
用戶請求到達內核,內核將請求封裝成一個句柄描述,放入本地消息隊列,然後迴圈隊列中的所有句柄,判斷是否處理完成,如果完成待所有的迴圈走完後,就將請求轉發到用戶進程,但是會隨著連接數增大,性能下降,處理數據太大的話,性能很差。
Epoll
用戶請求到達內核,內核將請求封裝成一個句柄描述和時間回調,放入本地消息隊列,待處理完畢就會觸發事件,不用去迴圈隊列中的所有消息,內核再將請求轉發到用戶進程,隨著連接數增大,性能基本沒影響,適合併發強度大的場景.
4.基本數據結構
在實際過程中,我們利用Stackexchange.Redis
來進行操作.Net與Redis的互操作,當然也可以使用Servicestack.Redis
他也同樣可以實現交互。
1.web中安裝和配置環境
1.創建Net6
WebApi
項目,並且NuGet下載StackExchange.Redis
最新版安裝到項目中。
2.擴展Redis客戶端初始化連接,並且在Startup中添加到
IOC
容器
//擴展連接客戶端
public static class RedisServiceCollectionExtensions
{
public static IServiceCollection AddRedisCache(this IServiceCollection services, string connectionString)
{
ConfigurationOptions configuration = ConfigurationOptions.Parse(connectionString);
ConnectionMultiplexer connectionMultiplexer = ConnectionMultiplexer.Connect(connectionString);
services.AddSingleton(connectionMultiplexer);
return services;
}
}
//註入容器
services.AddRedisCache("127.0.0.1:6379,password=xxx,connectTimeout=2000");
3.在Api中創建RedisController
控制器,並註入ConnectionMultiplexer
public class RedisController : ControllerBase
{
private readonly ConnectionMultiplexer _connectionMultiplexer;
private readonly IDatabase db;
public RedisController(ConnectionMultiplexer connectionMultiplexer)
{
_connectionMultiplexer = connectionMultiplexer;
db = _connectionMultiplexer.GetDatabase(0);
}
}
2.string類型
1.
string
類型數據時Redis中最基礎的類型,其他類型在此基礎上構建.
2.
string
類型不僅僅會開闢占有的空間,還會預留一部分空間,最大能存儲 512MB,可以是簡單的Key,value,也可以是複雜的xml/json的字元串、二進位圖像或者音頻的字元串、以及可以是數字的字元串。
TestData testData = new TestData();
//首先序列化
string json = JsonConvert.SerializeObject(entity);
//設置30秒後失效
db.StringSet("TestData", demojson,TimeSpan.FromSeconds(30));
3.Set 集合
1.redis集合(set)類型和list列表類型類似,都可以用來存儲多個字元串元素的集合
2.和list不同的是set集合當中
不允許重覆
的元素。而且set集合當中元素是沒有順序
的,不存在元素下標,intzset 最大存儲2的64次方,或者內容超過512個位元組就使用HashTable
3.Redis中的
Set
數據結構它由數組和 HashTable組成,每一個數組值都經過hash計算
,支持集合內的增刪改查,並且支持多個集合間的交集、並集、差集操作
RedisValue[] values = db.SetMembers("testdatas");
List<TestData> data = new List<TestData>();
if (values.Length == 0)
{
// 2、從資料庫中查詢
data = testData.Add();
// 3、存儲到redis中
List<RedisValue> redisValues = new List<RedisValue>();
foreach (var item in data)
{
string json = JsonConvert.SerializeObject(item);//序列化
redisValues.Add(json);
}
db.SetAdd("testdatas", redisValues.ToArray());
return data;
}
// 4、序列化,反序列化
foreach (var redisValue in values)
{
TestData t = JsonConvert.DeserializeObject<TestData>(redisValue);//反序列化
data.Add(t);
}
4.hash
1.Redis hash數據結構 是一個鍵值對(key-value)集合,它是一個 string 類型的 field 和 value 的映射表。
2.hash數據結構相當於在value中又套了一層key-value型數據。
//根據HashKey獲取欄位為AccessTime 的值
string time =db.HashGet("HashKey", "AccessTime");
if (string.IsNullOrEmpty(time))
{
test = data.FirstOrDefault(s => s.Id == 1);
//設置HashKey為AccessTime欄位的值
db.HashSet("HashKey", "AccessTime", test.AccessTime);
}
// 次數加1
db.HashIncrement("HashKey", "AccessTime");
5.ZSet (有序集合)
1.它的實現是由Zskiplist+HashTable
2.redis有序集合也是集合類型的一部分,它保留了集合中元素不能重覆的特性,但是不同的是,有序集合給每個元素多設置了一個分數,利用該分數作為排序的依據。
6.List
1.list類型是用來存儲多個有序的字元串的,列表當中的每一個字元看做一個元素.
2.一個列表當中可以存儲有一個或者多個元素,redis的list支持存儲2^32次方-1個元素。
3.redis可以從列表的兩端進行插入(pubsh)和彈出(pop)元素,支持讀取指定範圍的元素集,
或者讀取指定下標的元素等操作。redis列表是一種比較靈活的鏈表數據結構,它可以充當隊列或者棧的角色
4.reids的鏈表結構,可以輕鬆實現阻塞隊列,可以使用左進右出的命令組成來完成隊列的設計。比如:數據的生產者可以通過Lpush命令從左邊插入數據,多個數據消費者,可以使用BRpop命令阻塞的“搶”列表尾部的數據。
7.事務操作
1.Redis中
不支持事務回滾
2.在sqlserver中如果開啟事務,修改數據,如果新的會話去同時操作會等待釋放鎖,而Redis中,在開啟事務前首先監聽了Key的版本號,如果在事務期間,新的會話可以修改目標Key,但是會影響當前事務提交不成功。
ITransaction transaction = db.CreateTransaction();
//執行操作
transaction.HashSetAsync("HashKey", "AccessTime", test.AccessTime);
bool commit = transaction.Execute();
if (commit)
{
Console.WriteLine("提交成功");
}
else
{
Console.WriteLine("提交失敗");
}
5.Redis持久化
1.RDB 快照
RDB
是Redis用來進行持久化的一種方式,是把當前記憶體中的數據集快照
寫入磁碟,恢復時是將快照文件直接讀到記憶體
里,持久化的RDB文件可以拷貝到不通過的服務,將數據
載入。
-
1.
bgsave
:父進程啟動一個子進程,由子進程將記憶體保存在硬碟文件,期間不會影響其他的指令操作. -
2.
save
:將記憶體數據鏡像保存為RDB文件,由於redis是單線程模型期間會阻塞redis服務進程
,redis服務不再處理任何指令,直到RDB文件創建完成. -
無論是
bgsave
還是save
,其過程如下:
1.生成臨時rdb文件,並寫入數據.
2.完成數據寫入,用臨時文代替代正式rdb文件.
3.刪除原來的db文件
save 900 1 #15分鐘內有一條數據被修改則保存
save 300 10 #300秒有10條修改則保存
save 60 10000 #60秒內有10000條數據修改則保存
# 是否壓縮rdb文件
rdbcompression yes
# rdb文件的名稱
dbfilename redis-6379.rdb
# rdb文件保存目錄
dir ~/redis
自動觸發備份原理
1.Redis有一個周期性操作函數,預設每隔100ms執行一次,它的其中一項工作就是檢查自動觸發Bgsave命令的條件是否成立.
2.計數器記錄了在上一次成功的持久化後,redis進行了多少次寫操作,其值在每次寫操作之後都加1,在成功完成持久化後清零.
RDB的優點
- 與AOF方式相比,通過rdb文件恢複數據比較快。
- rdb文件非常緊湊,適合於數據備份。
- 通過RDB進行數據備,由於使用子進程生成,所以對Redis伺服器性能影響較小。
2.AOF 文件追加
Redis
服務每次結束一個事件迴圈之前,都會調用flushAppendOnly
函數,其中調用write
函數將aof_buf
寫入文件,aof文件可以被修改。
1.開啟AOF配置
# 開啟aof機制
appendonly yes
# aof文件名
appendfilename "appendonly.aof"
# 寫入策略,always表示每個寫操作都保存到aof文件中,也可以是everysec或no
appendfsync always
# 預設不重寫aof文件
no-appendfsync-on-rewrite no
# 保存目錄
dir ~/redis
1.在配置文件開啟將
appendonly
設為yes
2.設置文件路徑
dir
,可以使用預設在當前目錄下
3.設置追加方式一共有三種,一般選用第二種方式
- 1.appendfsync always 只要有讀寫,
性能最低
但是安全性最高
- 2.appendfsync everysec 1s鐘的周期
- 3.appendfsync no 等業務不繁忙的時候,這種操作最不可靠
性能最高
但是安全性最低
。
2.AOF文件解讀
1.打開追加的持久化文件
- 第一個指令
*2
代表有2個參數,$6
第一個參數6個位元組為select
,$1
代表第二個參數0個位元組 - 第二個指令
*3
代表有3個參數,$3
第一個參數3個位元組為set
,$4
第2個參數4個位元組為name
,$3
第3個參數3個位元組為lll
3.Redis持久化載入
上面我們介紹了Redis支持2種持久化
的方式,那我們需要判斷在載入時
選擇哪種方式,因為不可能同時選擇2種,其實在Redis載入時會判斷是否開啟了AOF
,如果開啟了AOF
就會載入AOF
文件,如果沒有開啟,那麼就會選擇載入RDB
文件。