redis 官網 https://redis.io
redis 下載 進入下載頁面 https://redis.io/download
https://github.com/MicrosoftArchive/redis/releases
單擊 Learn more 進入GitHub頁面
github地址: https://github.com/antirez/redis
然后:
這里只有64位的。下載msi。安裝后。就可以在服務看到redis服務了
RedisDesktopManager 管理工具下載 https://redisdesktop.com/download
https://github.com/uglide/RedisDesktopManager/releases/tag/0.8.8
https://github.com/uglide/RedisDesktopManager
net客戶端使用redis 一般都是使用 StackExchange.Redis 和 ServiceStack.Redis
SE是免費的。SS是收費的。這兩個dll都能通過NuGet安裝
既然SE是免費的。所以本文就使用SE.。
Redis簡介(收集網絡)
Remote Dictionary Server(Redis這個名稱是一個縮寫)是一個基於 key-value 鍵值對的、可以持久化的、完全開源免費的、遵守BSD協議的內存數據庫存儲系統,常用作緩存或者消息隊列。支持多種數據結構,包括 string (字符串)、list (鏈表)、set (集合)、zset (sorted set:有序集合)和 hash(哈希類型)。這些數據類型都支持 push/pop、add/remove 及取交集並集和差集及更豐富的操作,而且這些操作都是原子性的。
接下來,我們在說說Redis的優勢的,如果沒有優勢,鬼才會使用它呢。
1、讀寫性能極高 – Redis讀的速度是110000次/s,寫的速度是81000次/s,所以使用Redis緩存數據,存取數據幾乎是0感覺,當然是對於用戶來說的 。
2、支持豐富的數據類型 – Redis支持豐富的數據類型,如: String(字符串), Lists(鏈表), Hash(哈希),Set(無序集合) 及 ZSet(有序集合)等數據類型,所以我們放棄了Memched,因為它支持的數據類型太少了。
3、所有操作支持原子性 – Redis的所有操作都是原子性的,意思就是要么成功執行,要么失敗。單個操作是原子性的。多個操作也支持事務,即原子性,通過MULTI和EXEC指令包起來。
4、豐富的特性 – Redis支持 publish/subscribe(發布/訂閱),也支持事務、隊列、持久化,可以設置key過期時間等等特性。
准備工作
導入dll后,創建一個單列
/// <summary> /// 單列 /// </summary> public class RedisManager { private RedisManager() { } private static ConnectionMultiplexer instance;
private static readonly object locker = new object(); /// <summary> /// 單例模式獲取redis連接實例 /// </summary> public static ConnectionMultiplexer Instance { get {
lock (locker)
{
if (instance == null) { if (instance == null) instance = ConnectionMultiplexer.Connect("127.0.0.1"); //這里應該配置文件,不過這里演示就沒寫 }
} return instance; } } }
打開RedisDesktopManager 工具
RedisDesktopManager文檔:http://docs.redisdesktop.com/en/latest/quick-start/
String(字符串)
如果RedisKey 存在,則會替換
var db = RedisManager.Instance.GetDatabase(); var result = db.StringSet("string", "字符串"); //插入成功,返回true
db.StringGet("string") //獲取值
存json也可以
StringAppend 追加字符串
db.StringAppend("string", "追加");
List (列表)
特點:有序排列,值可以重復。我們可以通過pop,push操作來從頭部和尾部刪除或者添加元素。這使得list既可以做棧也可以做隊列
主要有:
ListRightPush:底部插入數據
ListLeftPush:頂部插入數據
ListLeftPop:出棧一條數據,出一條。則redis中就少一條
ListRightPop:同理
首先看看ListRightPush 方法,在list_right 中添加10條數據
for (int i = 0; i < 10; i++) { db.ListRightPush("list_right", i); }
ListLeftPush 方法:
for (int i = 0; i < 10; i++) { db.ListLeftPush("list_left", i); }
如果有個需求,要顯示3個最后來的用戶,可以用ListLeftPush 方法。
只要3個,則可以用ListTrim 方法
db.ListTrim("list_left", 0, 2); //只顯示前3個
看看pop方法。比如ListRightPop
db.ListRightPop("list_left");
從底部出棧一條數據
來看看獲取List數據,獲取不是pop操作
刪除一個鍵可以用 db.KeyDelete("list_right");
set(無序排列)
db.SetAdd("set_add", "你好"); db.SetAdd("set_add","小明");
set提供了取並集,交集,差集的方法
SetCombine方法
RedisValue[] ds = db.SetCombine(SetOperation.Union, "set_add", "set_add"); foreach (var item in ds.OrderBy(m => m).ToList()) { Console.Write((string)item + " "); }
SortedSet(有序排列)
添加順序不同,但最后會排序
db.SortedSetAdd("sort", "t1",2); db.SortedSetAdd("sort", "t2", 12); db.SortedSetAdd("sort", "t3", 5);
SortedSet方法提供了累加的方法SortedSetIncrement
如果key和value都存在則累加。否則則新增
db.SortedSetIncrement("sort", "t1", 8);//t1存在,則累加 db.SortedSetIncrement("sort", "t4", 5); //t4不存在,則新增
可以看到。如果Score相同。則根據value排序
獲取key的話。通過SortedSetRangeByRank方法,該方法可以排序。通過Order.Ascending,
還可以從指定位置獲取
獲取key后。通過SortedSetScore 獲取Score
RedisValue[] vs = db.SortedSetRangeByRank("sort", 1, 2, Order.Ascending); for (int i = 0; i < vs.Length; i++) { var value = db.SortedSetScore("sort", vs[i]); }
Hash(哈希表)
Hash是一個string類型的field和value的對應表,它更適合來存儲對象,相比於每個屬性進行一次緩存,利用hash來存儲整個對象會占用更小的內存。但是存儲速度並不會更快
db.HashSet("hash", "name","張三"); db.HashSet("hash", "age", "20"); db.HashSet("hash", "address", "中國");
取值也很容易
db.HashGet("hash", "name"); //取單個 db.HashGetAll("hash"); //取所有 db.HashGet("hash", new RedisValue[] { "name", "age" }); //根據指定的hashkey取值
上面也說了,哈希表一般會把一個對象序列化后緩存。這樣比每個屬性緩存一次效率高
string 存在二進制文件
上面講了。string的值是字符串的。可以是字符串類型,json類型。當然也可以是二進制類型
序列化二進制的方法是BinaryFormatter 命名空間在System.Runtime.Serialization.Formatters.Binary;
BinaryFormatter是不能序列化匿名類的。因為序列化的類必須要標記為Serializable
[Serializable] class user { public string name; public string age; }
當然。你也可以把json字符串序列化成二進制文件在緩存。根據你的需求吧。
現在我們把一個對象序列化成二進制文件后在緩存
user u = new user { name = "你好",age="18"}; var json = JsonConvert.SerializeObject(post); byte[] bytes; //序列化成二進制 using(var stream = new MemoryStream()) { new BinaryFormatter().Serialize(stream, u); bytes = stream.ToArray(); } db.StringSet("bf", bytes); //讀取二進制文件 byte[] bf = (byte[])db.StringGet("bf"); using(var stream = new MemoryStream(bf)) { user obj =(user) new BinaryFormatter().Deserialize(stream); }
Redis 事物
Redis因為有並發的問題,所以要考慮到事物。Redis通過CreateTransaction函數(multi)來創建一個事物,調用其Execute函數(exec)提交事物
var db = RedisManager.Instance.GetDatabase(); //設置值 db.StringSet("name", "張三"); db.StringSet("age", 90); string name = db.StringGet("name"); string age = db.StringGet("age"); Console.WriteLine("name:" + name); Console.WriteLine("age:" + age); //創建一個事物 ITransaction trans = db.CreateTransaction(); //鎖定RedisKey=name RedisValue=張三的緩存 trans.AddCondition(Condition.StringEqual("name", name)); Console.WriteLine("begin trans"); trans.StringSetAsync("name", "Tom"); bool isExec = trans.Execute(); //提交事物,name才會修改成功 name = Tom Console.WriteLine("事物執行結果:" + isExec); string _name = db.StringGet("name"); string _age = db.StringGet("age"); Console.WriteLine("name:" + _name); Console.WriteLine("age:" + _age); Console.WriteLine("end trans");
執行結果
這里通過trans.AddCondition(Condition.StringEqual("name", name)); 鎖定了name,
必須通過 trans.Execute();提交事物。才能成功
可以試試,在Execute 提交事物之前,先修改name的值,在執行 trans.Execute()的時候,則會失敗,圖中紅色標記部分
執行結果
因為在trans.Execute()的時候發現值已經被修改了。則回滾事物,導致事物執行失敗
StackExchange.Redis中對於連續多次的緩存等請求,我們會多次調用相關的函數來執行Redis命令。然而這種方式有個弊端就是每一次的請求都需要等待返回結果
如果在網絡狀況不好的情況下,可能會造成不好的用戶體驗。
對於這種問題可以用StackExchange.Redis提供的CreateBatch()解決
IBatch batch = db.CreateBatch(); Task t1 = batch.StringSetAsync("name", "tom"); Task t2 = batch.StringSetAsync("age", 20); batch.Execute(); Task.WaitAll(t1, t2);
batch會把所需要執行的命令打包成一條請求發到Redis,然后一起等待返回結果。這樣批量操作的速度就大大提升啦!
StackExchange.Redis中的發布和訂閱
發布端代碼
var db = RedisManager.Instance.GetDatabase(); db.PublishAsync("name", "張三"); db.PublishAsync("age", 18); db.PublishAsync("address", "長沙");
訂閱端代碼
var redis = RedisManager.Instance.GetSubscriber(); //channel是通道名稱,message是消息 redis.SubscribeAsync("name", (channel, message) => { Console.WriteLine("我叫:" + message); }); redis.SubscribeAsync("age", (channel, message) => { Console.WriteLine("我今年:" + message); }); redis.SubscribeAsync("address", (channel, message) => { Console.WriteLine("我來自:" + message); });
先運行訂閱端,然后在打開發布端。否則收不到信息
參考:
https://www.cnblogs.com/bluesummer/p/7677969.html
http://www.cnblogs.com/PatrickLiu/p/8260228.html
http://blog.csdn.net/WuLex/article/details/52637196
http://www.cnblogs.com/bluesummer/p/7874788.html