Redis 介紹學習

一、Redis 簡介html

Redis 是一個支持數據結構更多的鍵值對數據庫。它的值不只能夠是字符串等基本數據git

類型,也能夠是類對象,更能夠是 Set、List、計數器等高級的數據結構。
Memcached 也能夠保存相似於 Set、List 這樣的結構,可是若是說要向 List 中增長元素, Memcached 則須要把 List 所有元素取出來,而後再把元素增長進去,而後再保存回去,不 僅效率低,並且有併發訪問問題。github

Redis 內置的 Set、List 等能夠直接支持增長、刪除元素的操做,效率很高,操做是原子的。redis

Memcached 數據存在內存中,memcached 重啓後數據就消失;而 Redis 會把數據持久 數據庫

化到硬盤中,Redis 重啓後數據還存在。
二、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 編程

三、redis 的優勢: json

  1. 1)  支持 string、list、set、geo 等複雜的數據結構。 windows

  2. 2)  高命中的數據運行時是在內存中,數據最終仍是能夠保存到磁盤中,這樣服務器重啓以後數據還在。數組

  3. 3)  服務器是單線程的,來自全部客戶端的全部命令都是串行執行的,所以不用擔憂併發修改(串行操做固然仍是有併發問題)的問題,編程模型簡單;緩存

  4. 4)  支持消息訂閱/通知機制,能夠用做消息隊列;

  5. 5)  Key、Value 最大長度容許 512M;

四、redis 的缺點:

  1. 1)  Redis 是單線程的,所以單個 Redis 實例只能使用一個 CPU 核,不能充分發揮服務器的性能。能夠在一臺服務器上運行多個 Redis 實例,不一樣實例監聽不一樣端口,再互相組成集羣。

  2. 2)  作緩存性能不如 Memcached;

五、Memcached 的優勢:
       1) 多線程,能夠充分利用 CPU 多核的性能;

       2) 作緩存性能最高;

六、Memcached 的缺點:

  1. 1)  只能保存鍵值對數據,鍵值對只能是字符串,若是有對象數據只能本身序列化成 json字符串;

  2. 2)  數據保存在內存中,重啓後會丟失;

  3. 3)  Key 最大長度 255 個字符,Value 最長 1M。

七、總結
      Memcached 只能當緩存服務器用,也是最合適的;Redis 不只能夠作緩存服務器(性能沒有 Memcached 好),還能夠存儲業務數據。

八、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 的驅動其實就是對這些命令的封裝。

九、redis GUI 管理客戶端

     GUI 客戶端很是多,我的推薦使用 RedisDesktopManager安裝後點擊【Connect to Redis Server】鏈接服務器。展開節點能夠看到全部的 Key,雙擊 Key 能夠查看 Key 的值。在根節點上點右鍵,選擇 【Console】,這樣就能夠輸入命令。

十、.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[]之間進行隱式轉換。

十一、Key 操做
       Key 操做:由於 Redis 裏全部數據類型都是用 KeyValue 保存,所以 Key 操做針對全部數據類型, KeyDelete(RedisKey key):根據 Key 刪除;KeyExists(RedisKey key)

      判斷 Key 是否存在,儘可能不要用, 由於會有併發問題;KeyExpire(RedisKey key, TimeSpan?  expiry)、KeyExpire(RedisKey key, DateTime? expiry)設置過時時間;
十二、數據類型

       Redis 支持的數據結構:string、list、set、sortedset、hash、geo(redis 3.2 以上版本)。對應 的 Redis 客戶端裏的方法都是 StringXXX、HashXXX、GeoXXX 等方法。

      不一樣數據類型的操做方 法不能混用,好比不能用 ListXXX 寫入的值用 StringXXX 去讀取或者寫 入等操做。
1三、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(); }

1四、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 獲取郵件來發送。

      生產者、消費者模式。把生產過程和消費過程隔離。

1五、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 不是按照插入順序遍歷的,而是按照本身的一個存儲方式來遍歷,由於沒有保存插入的 順序。

1六、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) 給寶寶投票;

1七、Hash

      至關於 value 又是一個「鍵值對集合」或者值是另一個 Dictionary。 沒想到有什麼應用場景。

1八、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

1九、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("得到鎖失敗"); 
}
相關文章
相關標籤/搜索