1.Redis介紹
REmote DIctionary Server(Redis) 是一個由Salvatore Sanfilippo寫的 key-value 存儲系統,是跨平台的非關系型數據庫,Redis 是一個開源的使用 ANSI C 語言編寫、遵守 BSD 協議、支持網絡、可基於內存、分布式、可選持久性的鍵值對(Key-Value)存儲的非關系型數據庫,常被用於分布式緩存,作為進程外的緩存,它具有以下特點:
1.方便擴展
2.大數據量高性能
3.八大數據結構
4.分布式存儲
2.什么時候使用Redis
在分析什么場景使用Redis時,我們首先看下面的系統設計圖,我們用戶發起請求到達Nginx,然后由Nginx來將請求負載均衡到不同的服務實例,再由服務實例去訪問數據庫,從流程上看基本沒什么問題,但是由於集群中的每一個節點實例,都是一個獨立的系統,如果我們在系統中使用了本地緩存就會出現一些問題,具體是什么問題呢?

1.當用戶請求被負載均衡到不同的節點實例時,第一個請求被實例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文件可以拷貝到不同的redis服務,將數據
加載。
-
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文件。

