1、Redis 簡介
Redis 是一個支持資料結構更多的鍵值對資料庫,它的值不僅可以是字串等基本資料
型別,也可以是類物件,更可以是 Set、List、計數器等高級的資料結構,
Memcached 也可以保存類似于 Set、List 這樣的結構,但是如果說要向 List 中增加元素,
Memcached 則需要把 List 全部元素取出來,然后再把元素增加進去,然后再保存回去,不
僅效率低,而且有并發訪問問題,
Redis 內置的 Set、List 等可以直接支持增加、洗掉元素的操作,效率很高,操作是原子的,
Memcached 資料存在記憶體中,memcached 重啟后資料就消失;而 Redis 會把資料持久
化到硬碟中,Redis 重啟后資料還存在,
2、Redis 的安裝
redis for windows >=2.8 的版本支持直接安裝為 windows 服務
https://github.com/MicrosoftArchive/redis
如果下載 msi 自動裝完服務,如果下載 zip 需要按照下面的方法安裝為服務:
https://raw.githubusercontent.com/MSOpenTech/redis/3.0/Windows%20Service%20Documenta
tion.md
3、redis 的優點:
-
1) 支持 string、list、set、geo 等復雜的資料結構,
-
2) 高命中的資料運行時是在記憶體中,資料最侄訓是可以保存到磁盤中,這樣服務器重啟之后資料還在,
-
3) 服務器是單執行緒的,來自所有客戶端的所有命令都是串行執行的,因此不用擔心并發修改(串行操作當然還是有并發問題)的問題,編程模型簡單;
-
4) 支持訊息訂閱/通知機制,可以用作訊息佇列;
-
5) Key、Value 最大長度允許 512M;
4、redis 的缺點:
-
1) Redis 是單執行緒的,因此單個 Redis 實體只能使用一個 CPU 核,不能充分發揮服務器的性能,可以在一臺服務器上運行多個 Redis 實體,不同實體監聽不同埠,再互相組成集群,
-
2) 做快取性能不如 Memcached;
5、Memcached 的優點:
1) 多執行緒,可以充分利用 CPU 多核的性能;
2) 做快取性能最高;
6、Memcached 的缺點:
-
1) 只能保存鍵值對資料,鍵值對只能是字串,如果有物件資料只能自己序列化成 json字串;
-
2) 資料保存在記憶體中,重啟后會丟失;
-
3) Key 最大長度 255 個字符,Value 最長 1M,
7、總結
Memcached 只能當快取服務器用,也是最合適的;Redis 不僅可以做快取服務器(性能沒有 Memcached 好),還可以存盤業務資料,
8、redis 命令列管理客戶端:
1)直接啟動 redis 安裝目錄下的 redis-cli 即可,不用管惡心的自動提示, 執行 set name yzk,就是設定鍵值對 name=yzk 執行 get name 就是查找名字是 name 的值; keys *是查找所有的 key key *n*是查找所有名字中含有 n 的 key
2) 和 Redis 一樣,Redis 也是不同系統放到 Redis 中的資料都是不隔離的,因此設定 Key 的
時候也要選擇好 Key,
3) Redis 服務器默認建了 16 個資料庫,Redis 的想法是讓大家把不同系統的資料放到不同
的資料庫中,但是建議大家不要這樣用,因為 Redis 是單執行緒的,不同業務都放到同一個 Redis
實體的話效率就不高,建議放到不同的實體中,
因此盡量只用默認的 db0資料庫命令列下可以用 select0、select1 這樣的指令切換資料庫,最高為15,試試在不同資料 庫下新建、查詢資料,
4) 了解的常用的幾個命令就可以,所有對資料的操作都可以通過命令列進行,后面講 的.net 操作 Redis 的驅動其實就是對這些命令的封裝,
9、redis GUI 管理客戶端
GUI 客戶端非常多,個人推薦使用 RedisDesktopManager安裝后點擊【Connect to Redis Server】連接服務器,展開節點可以看到所有的 Key,雙擊 Key 可以查看 Key 的值,在根節點上點右鍵,選擇 【Console】,這樣就可以輸入命令,
10、.net 操作 Redis
用 StackExchange.Redis ,而不是 ServiceStack.Redis,因為 StackExchange.Redis 依賴組件 少,而且操作更接近原生的 redis 操作,ServiceStack 封裝的太厲害,而且有過收費的“前科”,
Install-Package StackExchange.Redis
using (ConnectionMultiplexer redis = ConnectionMultiplexer.Connect("localhost:6379"))
{
IDatabase db = redis.GetDatabase();//默認是訪問 db0 資料庫,可以通過方法引數指定數 字訪問不同的資料庫
db.StringSet("Name", "abc");
}
支持設定過期時間:db.StringSet("name", "rupeng.com", TimeSpan.FromSeconds(10)) 獲取資料:string s = db.StringGet("Name")如果查不到則回傳 null
Redis 里所有方法幾乎都支持異步,比如 StringGetAsync()、StringSetAsync(),盡量用異步方法,
注意看到訪問的引數、回傳值是 RedisKey、RedisValue 型別,進行了運算子多載,可以和 string、 byte[]之間進行隱式轉換,
11、Key 操作
Key 操作:因為 Redis 里所有資料型別都是用 KeyValue 保存,因此 Key 操作針對所有資料型別,
KeyDelete(RedisKey key):根據 Key 洗掉;KeyExists(RedisKey key)
判斷 Key 是否存在,盡量不要用,
因為會有并發問題;KeyExpire(RedisKey key, TimeSpan? expiry)、KeyExpire(RedisKey key, DateTime?
expiry)設定過期時間;
12、資料型別
Redis 支持的資料結構:string、list、set、sortedset、hash、geo(redis 3.2 以上版本),對應 的 Redis 客戶端里的方法都是 StringXXX、HashXXX、GeoXXX 等方法,
不同資料型別的操作方
法不能混用,比如不能用 ListXXX 寫入的值用 StringXXX 去讀取或者寫 入等操作,
13、String 型別
可以用 StringGet、StringSet 來讀寫鍵值對,是基礎操作StringAppend(RedisKey key, RedisValue value):向 Key 的 Value 中附加內容,不存在則新建; 可以用作計數器:db.StringIncrement("count", 2.5);
給 count 這個計數器增加一個值,如果不存在則從 0 開始加;db.StringDecrement("count",1)計數器減值;獲取還是用 StringGet()獲取字串型別的 值,比如可以用這個來計算新聞點擊量、點贊量,效率非常高,
private static string XinWen_Prefix = "WWW_XinWen_";
public async Task<ActionResult> Index(int id)
{
using (ConnectionMultiplexer redis = await ConnectionMultiplexer.ConnectAsync("localhost:6379")) {
IDatabase db = redis.GetDatabase();//默認是訪問 db0 資料庫,可以通過方法引數指定數字訪 問不同的資料庫
//以 ip 地址和文章 id 為 key
string hasClickKey = XinWen_Prefix + Request.UserHostAddress + "_" + id;
//如果之前這個 ip 給這個文章貢獻過點擊量,則不重復計算點擊量 if(await db.KeyExistsAsync(hasClickKey)==false)
{
await db.StringIncrementAsync(XinWen_Prefix + "XWClickCount" + id, 1); //記錄一下這個 ip 給這個文章貢獻過點擊量,有效期一天 db.StringSet(hasClickKey, "a", TimeSpan.FromDays(1));
}
RedisValue clickCount = await db.StringGetAsync(XinWen_Prefix + "XWClickCount" + id); XinWenModel model = new XinWenModel();
model.ClickCount = Convert.ToInt32(clickCount);
return View(model);
}
return View(); }
14、list 型別
Redis 中用 List 保存字串集合, 比如可以把聊天記錄保存到 List 中;商品的物流資訊記錄,也 可以當成雙向佇列或者雙向堆疊用,list 長度是無限,
ListLeftPush(RedisKey key, RedisValue value)從左側壓堆疊;RedisValue ListLeftPop(RedisKey key) 從左側彈出;
ListRightPush(RedisKey key, RedisValue value ) 從右側壓堆疊;RedisValue ListRightPop(RedisKey key) 從右側彈出;
RedisValue ListGetByIndex(RedisKey key, long index)獲取 Key 為 key 的 List 中第 index 個元素的值; long ListLength(RedisKey key) 獲取 Key 為 key 的 List 中元素個數;盡量不要用 ListGetByIndex、 ListLength 因為會有并發問題;,
如果是讀取而不 Pop,則使用 ListRange:RedisValue[] ListRange(RedisKey key, long start = 0, long stop = -1),不傳 start、end 表示獲取所有資料,指定之后則獲取某個范圍,
可以把 Redis 的 list 當成訊息佇列使用,比如向注冊用戶發送歡迎郵件的作業,可以在注冊的流 程中把要發送郵件的郵箱放到 list 中,另一個程式從 list 中 pop 獲取郵件來發送,
生產者、消費者模式,把生產程序和消費程序隔離,
15、set 型別
如大家所知,set 是一種元素不重復的集合,
SetAdd(RedisKey key, RedisValue value)向 set 中增加元素
bool SetContains(RedisKey key, RedisValue value) 判斷 set 中是否存在某個元素; long SetLength(RedisKey key) 獲得 set 中元素的個數;
SetRemove(RedisKey key, RedisValue value)從 set 中洗掉元素;
RedisValue[] SetMembers(RedisKey key)獲取集合中的元素;
如果使用 set 保存封禁用 id 等,就不用做重復性判斷了,
注意 set 不是按照插入順序遍歷的,而是按照自己的一個存盤方式來遍歷,因為沒有保存插入的 順序,
16、sortedset
如果對于資料遍歷順序有要求,可以使用 sortedset,他會按照打分來進行遍歷,
SortedSetAdd(RedisKey key, RedisValue member, double score) 在 key 這個 sortedset 中增加member,并且給這個 member 打分,如果 member 已經存在,則覆寫之前的打分; doubleSortedSetIncrement(RedisKeykey,RedisValuemember,doublevalue) 給key中member這一項增加 value 分;
double SortedSetDecrement(RedisKey key, RedisValue member, double value):給 key 中 member 這一項減 value 分;
SortedSetEntry[] SortedSetRangeByRankWithScores(RedisKey key, long start = 0, long stop = -1,Orderorder=Order.Ascending) 根據排序回傳sortedset中的元素以及元素的打分,start、stop用來分頁 查詢、order 用來指定排序規則,
測驗:
db.SortedSetIncrement("Hotwords", "test", 1);
db.SortedSetIncrement("Hotwords", "test", 1); db.SortedSetIncrement("Hotwords", "test", 1); db.SortedSetIncrement("Hotwords", "楊中科", 1); db.SortedSetIncrement("Hotwords", "侯寶林", 1); db.SortedSetIncrement("Hotwords", "侯寶林", 1);
SortedSetEntry[] items = db.SortedSetRangeByRankWithScores("Hotwords"); foreach(var item in items)
{
Console.WriteLine(item.Element+"="+item.Score);
}
RedisValue[] SortedSetRangeByRank(RedisKey key, long start = 0, long stop = -1, Order order =Order.Ascending) 根據打分排序回傳值,可以根據序號查詢其中一部分;
RedisValue[] SortedSetRangeByScore(RedisKey key, double start = double.NegativeInfinity, doublestop = double.PositiveInfinity, Exclude exclude = Exclude.None, Order order = Order.Ascending, long skip = 0, long take = -1)
根據打分排序回傳值,可以只回傳 start- stop 這個范圍的打分;
sortedset 應用場景:
1) 用戶每搜一次一個關鍵詞,就給這個關鍵詞加一分;展示熱搜的時候就把前 N 個獲取出來就行了;
2) 高積分用戶排行榜;
3) 熱門商品;
4) 給寶寶投票;
17、Hash
相當于 value 又是一個“鍵值對集合”或者值是另外一個 Dictionary, 沒想到有什么應用場景,
18、Geo 型別
Geo 是 Redis 3.2 版本后新增的資料型別,用來保存興趣點(POI,point of interest)的坐標資訊,
可以實作計算兩 POI 之間的距離、獲取一個點周邊指定距離的 POI, 下面添加興趣點資料,”1”、”2”是點的主鍵,點的名稱、地址、電話等存到其他表中,
db.GeoAdd("ShopsGeo", new GeoEntry(116.34039, 39.94218,"1"));
db.GeoAdd("ShopsGeo", new GeoEntry(116.340934, 39.942221, "2"));
db.GeoAdd("ShopsGeo", new GeoEntry(116.341082, 39.941025, "3"));
db.GeoAdd("ShopsGeo", new GeoEntry(116.340848, 39.937758, "4"));
db.GeoAdd("ShopsGeo", new GeoEntry(116.342982, 39.937325, "5"));
db.GeoAdd("ShopsGeo", new GeoEntry(116.340866, 39.936827, "6"));
GeoRemove(RedisKey key, RedisValue member)洗掉一個點
查詢兩個 POI 之間的舉例:double? dist = db.GeoDistance("ShopsGeo", "1", "5", GeoUnit.Meters);// 最后一個引數為距離單位根據點的主鍵獲取坐標:GeoPosition? pos = db.GeoPosition("ShopsGeo", "1")
獲取一個 POI 周邊的 POI:
GeoRadiusResult[] results = db.GeoRadius("ShopsGeo", "2", 200, GeoUnit.Meters);//獲取”2”這個周邊 200 米范圍內的 POI
foreach(GeoRadiusResult result in results)
{
Console.WriteLine("Id="+result.Member+",位置"+result.Position+",距離"+result.Distance); }
獲取一個坐標(這個坐標不一定是 POI)周邊的 POI:
GeoRadiusResult[] results = db.GeoRadius("ShopsGeo", 116.34092, 39.94223, 200, GeoUnit.Meters);// 獲 取(116.34092, 39.94223)這個周邊 200 米范圍內的 POI
foreach(GeoRadiusResult result in results)
{
Console.WriteLine("Id="+result.Member+",位置"+result.Position+",距離"+result.Distance);
}
Geo Hash 原理:http://www.cnblogs.com/LBSer/p/3310455.html
19、Redis 的批量操作
如果一次性操作很多,會很慢,那么可以使用批量操作,兩種方式: 1)幾乎所有的操作都支持陣列型別,這樣就可以一次性操作多條資料:比如
GeoAdd(RedisKey key, GeoEntry[] values)、SortedSetAdd(RedisKey key, SortedSetEntry[] values) 2) 如果一次性的操作不是簡單的同型別操作,那么就要使用批量模式:
IBatch batch = db.CreateBatch();
db.GeoAdd("ShopsGeo1", new GeoEntry(116.34039, 39.94218, "1"));
db.StringSet("abc", "123"); batch.Execute();
會把當前連接的 CreateBatch()、Execute()之間的操作一次性提交給服務器,
20、redis 分布式鎖
多執行緒中的 lock 等的作用范圍是當前的程式范圍內的,如果想跨多臺服務器的鎖(盡量避免這樣搞),就要使用分布式鎖,
RedisValue token = Environment.MachineName;
//實際專案秒殺此處可換成商品 ID
if (db.LockTake("mylock", token, TimeSpan.FromSeconds(10)))//第三個引數為鎖超時時間,鎖占 用最多 10 秒鐘,超過 10 秒鐘如果還沒有 LockRelease,則也自動釋放鎖,避免了死鎖
{
try {
} finally {
db.LockRelease("mylock", token); }
} else {
Console.WriteLine("獲得鎖失敗");
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/235319.html
標籤:其他
上一篇:Mysql—主從復制
下一篇:JDBC
