Redis安裝
Redis是一個(gè)用的比較廣泛的Key/Value的內(nèi)存數(shù)據(jù)庫(kù)。目前新浪微博进萄、Github已烤、StackOverflow 等大型應(yīng)用中都用其作為緩存,和Memcached類(lèi)似苛坚,但是支持?jǐn)?shù)據(jù)的持久化比被,解決了斷電后數(shù)據(jù)完全丟失的情況色难。而且它支持更多的類(lèi)型,除了string外等缀,還支持lists(鏈表)枷莉、sets(集合)和zsets(有序集合)幾種數(shù)據(jù)類(lèi)型。
下載地址:
http://redis.io/download
Redis安裝
Redis的安裝非常的簡(jiǎn)單尺迂,而且Redis并不依賴(lài)其他環(huán)境和標(biāo)準(zhǔn)庫(kù)笤妙,很容易上手,這可能也是它流行的一個(gè)原因噪裕。這里為了測(cè)試方便蹲盘,用的都是windows 環(huán)境下測(cè)試。
- redis.windows.conf 是redis的配置文件膳音。
- redis-server.exe 服務(wù)器端召衔。
- redis-cli.exe 命令行客戶(hù)端。
- redis-benchmark.exe Redis性能測(cè)試工具祭陷,測(cè)試Redis在你的系統(tǒng)及你的配置下的讀寫(xiě)性能苍凛。
啟動(dòng)服務(wù)
在命令行輸入如下命令 :redis-server redis.windows.conf。
同時(shí)也可以該命令保存為文件 startup.bat兵志,下次就可以直接啟動(dòng)了醇蝴。
如果提示redis-server 不是內(nèi)部命令。將該目錄加到環(huán)境變量里面即可毒姨。
redis相關(guān)配置
1. port 端口號(hào)哑蔫,例如6379
port 6379
2. bind 實(shí)例綁定的訪(fǎng)問(wèn)地址127.0.0.1
bind 127.0.0.1
3. requirepass 訪(fǎng)問(wèn)的密碼
requirepass 456
4. maxheap 記得把這個(gè)配置節(jié)點(diǎn)打開(kāi),否者redis 服務(wù)無(wú)法啟動(dòng)弧呐。例如maxheap 1024000000
maxheap 1024000000
5. timeout:請(qǐng)求超時(shí)時(shí)間
timeout 300
6. logfile:log文件位置
logfile stdout
7. databases:開(kāi)啟數(shù)據(jù)庫(kù)的數(shù)量
databases 16
8. dbfilename:數(shù)據(jù)快照文件名(只是文件名闸迷,不包括目錄)
dbfilename dump.rdb
連接測(cè)試
在命令行輸入如下命令:
redis-cli -h 127.0.0.1 -p 6379 -a 456
參數(shù)分別為host、port,如果設(shè)置了密碼俘枫,則必須要加上-a 456,456為登錄密碼腥沽。否則會(huì)提示沒(méi)有權(quán)限登錄系統(tǒng)。
如下圖所示鸠蚪。
讀寫(xiě)分離配置
redis的讀寫(xiě)分離需要修改配置文件今阳,把解壓的文件復(fù)制了一份。兩份文件是一樣的茅信,分別命名為MasterRedis-2.8.19(主redis服務(wù)),SlaveRedis-2.8.19(從redis服務(wù))盾舌。redis默認(rèn)綁定的是6379端口,
我們保持主服務(wù)配置不變,修改從服務(wù)配置蘸鲸。
- 修改從服務(wù)綁定端口(修改時(shí)可以直接搜索port關(guān)鍵字)
port 6380
- 修改從服務(wù)對(duì)應(yīng)的主服務(wù)地址(修改時(shí)可以直接搜索slaveof關(guān)鍵字)
slaveof 127.0.0.1 6379
- 配置文件修改完成以后,分別啟動(dòng)主服務(wù)和從服務(wù)
配置好我們看下redis的日志 看是否同步成功
5014:S 25 Jan 10:53:53.667 * Connecting to MASTER 221.224.85.186:6379
5014:S 25 Jan 10:53:53.667 * MASTER <-> SLAVE sync started
5014:S 25 Jan 10:53:53.700 * Non blocking connect for SYNC fired the event.
5014:S 25 Jan 10:53:53.734 * Master replied to PING, replication can continue...
5014:S 25 Jan 10:53:53.832 * Partial resynchronization not possible (no cached master)
5014:S 25 Jan 10:53:53.867 * Full resync from master: 4d6221e370675f397c396c9222b1b60bfcea1efb:1
5014:S 25 Jan 10:53:53.985 * MASTER <-> SLAVE sync: receiving 844 bytes from master
5014:S 25 Jan 10:53:53.985 * MASTER <-> SLAVE sync: Flushing old data
5014:S 25 Jan 10:53:53.985 * MASTER <-> SLAVE sync: Loading DB in memory
5014:S 25 Jan 10:53:53.985 * MASTER <-> SLAVE sync: Finished with success
五種數(shù)據(jù)類(lèi)型使用
服務(wù)搭建好以后可以使用.Net版本Redis操作類(lèi)庫(kù)ServiceStack.Redis
String
String是最常用的一種數(shù)據(jù)類(lèi)型,普通的key/value存儲(chǔ)都可以歸為此類(lèi)膝舅,value其實(shí)不僅是String嗡载,也可以是數(shù)字:比如想知道什么時(shí)候封鎖一個(gè)IP地址(訪(fǎng)問(wèn)超過(guò)幾次)。INCRBY命令讓這些變得很容易仍稀,通過(guò)原子遞增保持計(jì)數(shù)洼滚。
var client = new RedisClient("127.0.0.1", 6379);
client.Set<string>("name", "Bobby");
string userName = client.Get<string>("name");
Console.WriteLine(userName);
// 訪(fǎng)問(wèn)次數(shù)
client.Set<int>("IpAccessCount", 0);
// 次數(shù)遞增
client.Incr("IpAccessCount");
Console.WriteLine(client.Get<int>("IpAccessCount"));
Console.Read();
Bobby
1
Hash
一個(gè)hashid可以存儲(chǔ)多項(xiàng)信息,每一項(xiàng)信息也有自己的key技潘。
var client = new RedisClient("127.0.0.1", 6379);
client.SetEntryInHash("userInfoId", "name", "zhangsan");
client.SetEntryInHash("userInfoId", "name1", "zhangsan1");
client.SetEntryInHash("userInfoId", "name2", "zhangsan2");
client.SetEntryInHash("userInfoId", "name3", "zhangsan3");
client.GetHashKeys("userInfoId").ForEach(e => Console.WriteLine(e));
client.GetHashValues("userInfoId").ForEach(e => Console.WriteLine(e));
Console.Read();
name
name1
name2
name3
zhangsan
zhangsan1
zhangsan2
zhangsan3
List
應(yīng)用場(chǎng)景:
- Redis list的應(yīng)用場(chǎng)景非常多遥巴,也是Redis最重要的數(shù)據(jù)結(jié)構(gòu)之一。
- 我們可以輕松地實(shí)現(xiàn)最新消息排行等功能崭篡。
- Lists的另一個(gè)應(yīng)用就是消息隊(duì)列挪哄,可以利用Lists的PUSH操作,將任務(wù)存在Lists中琉闪,然后工作線(xiàn)程再用POP操作將任務(wù)取出進(jìn)行執(zhí)行迹炼。
var client = new RedisClient("127.0.0.1", 6379);
#region List類(lèi)型
client.AddItemToList("userInfoId1", "張三");
client.AddItemToList("userInfoId1", "李四");
Console.WriteLine("List數(shù)據(jù)項(xiàng)條數(shù):" + client.GetListCount("userInfoId1"));
Console.WriteLine("List數(shù)據(jù)項(xiàng)第一條數(shù)據(jù):" + client.GetItemFromList("userInfoId1", 0));
Console.WriteLine("List所有數(shù)據(jù)");
client.GetAllItemsFromList("userInfoId1").ForEach(e => Console.WriteLine(e));
#endregion
Console.WriteLine(client.GetListCount("userInfoId1"));
// 隊(duì)列先進(jìn)先出
//Console.WriteLine(client.DequeueItemFromList("userInfoId1"));
//Console.WriteLine(client.DequeueItemFromList("userInfoId1"));
// 棧后進(jìn)先出
Console.WriteLine("出棧" + client.PopItemFromList("userInfoId1"));
Console.WriteLine("出棧" + client.PopItemFromList("userInfoId1"));
Console.Read();
List數(shù)據(jù)項(xiàng)條數(shù):2
List數(shù)據(jù)項(xiàng)第一條數(shù)據(jù):張三
List所有數(shù)據(jù)
張三
李四
2 棧后進(jìn)先出
出棧李四
出棧張三
Set
應(yīng)用場(chǎng)景:
- Redis set對(duì)外提供的功能與list類(lèi)似是一個(gè)列表的功能,特殊之處在于set是可以自動(dòng)排重的颠毙,當(dāng)你需要存儲(chǔ)一個(gè)列表數(shù)據(jù)斯入,又不希望出現(xiàn)重復(fù)數(shù)據(jù)時(shí),set是一個(gè)很好的選擇蛀蜜,并且set提供了判斷某個(gè)成員是否在一個(gè)set集合內(nèi)的重要接口刻两,這個(gè)也是list所不能提供的。
- 比如在微博應(yīng)用中滴某,每個(gè)人的好友存在一個(gè)集合(set)中磅摹,這樣求兩個(gè)人的共同好友的操作,可能就只需要用求交集命令即可霎奢。
- Redis還為集合提供了求交集户誓、并集、差集等操作幕侠,可以非常方便的實(shí)
var client = new RedisClient("127.0.0.1", 6379);
client.AddItemToSet("A", "B");
client.AddItemToSet("A", "C");
client.AddItemToSet("A", "D");
client.AddItemToSet("A", "E");
client.AddItemToSet("A", "F");
client.AddItemToSet("B", "C");
client.AddItemToSet("B", "F");
// 求差集
Console.WriteLine("A,B集合差集");
client.GetDifferencesFromSet("A", "B").ToList<string>().ForEach(e => Console.Write(e + ","));
// 求集合交集
Console.WriteLine("\nA,B集合交集");
client.GetIntersectFromSets(new string[] { "A", "B" }).ToList<string>().ForEach(e => Console.Write(e + ","));
// 求集合并集
Console.WriteLine("\nA,B集合并集");
client.GetUnionFromSets(new string[] { "A", "B" }).ToList<string>().ForEach(e => Console.Write(e + ","));
Console.Read();
A,B集合差集
B,D,E,
A,B集合交集
F,C,
A,B集合并集
C,B,F,D,E,
Sort Set(排序)
應(yīng)用場(chǎng)景:
- 以某個(gè)條件為權(quán)重帝美,比如按頂?shù)拇螖?shù)排序.
- ZREVRANGE命令可以用來(lái)按照得分來(lái)獲取前100名的用戶(hù),ZRANK可以用來(lái)獲取用戶(hù)排名晤硕,非常直接而且操作容易悼潭。
- Redis sorted set的使用場(chǎng)景與set類(lèi)似,區(qū)別是set不是自動(dòng)有序的舞箍,而sorted set可以通過(guò)用戶(hù)額外提供一個(gè)優(yōu)先級(jí)(score)的參數(shù)來(lái)為成員排序舰褪,并且是插入有序的,即自動(dòng)排序疏橄。
- 比如:twitter 的public timeline可以以發(fā)表時(shí)間作為score來(lái)存儲(chǔ)占拍,這樣獲取時(shí)就是自動(dòng)按時(shí)間排好序的。
- 比如:全班同學(xué)成績(jī)的SortedSets,value可以是同學(xué)的學(xué)號(hào)刷喜,而score就可以是其考試得分,這樣數(shù)據(jù)插入集合的立砸,就已經(jīng)進(jìn)行了天然的排序掖疮。
- 另外還可以用Sorted Sets來(lái)做帶權(quán)重的隊(duì)列,比如普通消息的score為1颗祝,重要消息的score為2浊闪,然后工作線(xiàn)程可以選擇按score的倒序來(lái)獲取工作任務(wù)。讓重要的任務(wù)優(yōu)先執(zhí)行螺戳。
var client = new RedisClient("127.0.0.1", 6379);
#region "有序Set操作"
client.AddItemToSortedSet("SA", "B", 2);
client.AddItemToSortedSet("SA", "C", 1);
client.AddItemToSortedSet("SA", "D", 5);
client.AddItemToSortedSet("SA", "E", 3);
client.AddItemToSortedSet("SA", "F", 4);
// 有序集合降序排列
Console.WriteLine("\n有序集合降序排列");
client.GetAllItemsFromSortedSetDesc("SA").ForEach(e => Console.Write(e + ","));
Console.WriteLine("\n有序集合升序序排列");
client.GetAllItemsFromSortedSet("SA").ForEach(e => Console.Write(e + ","));
client.AddItemToSortedSet("SB", "C", 2);
client.AddItemToSortedSet("SB", "F", 1);
client.AddItemToSortedSet("SB", "D", 3);
Console.WriteLine("\n獲得某個(gè)值在有序集合中的排名搁宾,按分?jǐn)?shù)的升序排列");
Console.WriteLine(client.GetItemIndexInSortedSet("SB", "D"));
Console.WriteLine("\n獲得有序集合中某個(gè)值得分?jǐn)?shù)");
Console.WriteLine(client.GetItemScoreInSortedSet("SB", "D"));
Console.WriteLine("\n獲得有序集合中,某個(gè)排名范圍的所有值");
client.GetRangeFromSortedSet("SA", 0, 3).ForEach(e => Console.Write(e + ","));
#endregion
Console.Read();
有序集合降序排列
D,F,E,B,C,
有序集合升序序排列
C,B,E,F,D,
獲得某個(gè)值在有序集合中的排名倔幼,按分?jǐn)?shù)的升序排列
2
獲得有序集合中某個(gè)值得分?jǐn)?shù)
3
獲得有序集合中盖腿,某個(gè)排名范圍的所有值
C,B,E,F,
Redis操作的幫助類(lèi)庫(kù) Tdf.RedisCache
Tdf.RedisCache,用到了PooledRedisClientManager連接池來(lái)獲取RedisClient损同,同時(shí)用到了讀寫(xiě)分離的概念翩腐,可以直接拿來(lái)使用。
使用很簡(jiǎn)單膏燃,幾行代碼
using System;
using Tdf.RedisCache;
namespace Tdf.RedisCacheTest
{
class Program
{
static void Main(string[] args)
{
RedisBase.Hash_Set<string>("PooledRedisClientManager", "UserId", "6A671BD3-8A5B-4E04-B88E-C9A1A64214A6");
RedisBase.Hash_Set<string>("PooledRedisClientManager", "UserName", "Bobby");
var userId = RedisBase.Hash_Get<string>("PooledRedisClientManager", "UserId");
var userName = RedisBase.Hash_Get<string>("PooledRedisClientManager", "UserName");
Console.WriteLine(userId + "," + userName);
Console.Read();
}
}
}
配置文件
<appSettings>
<!--Redis寫(xiě)入服務(wù)器地址,可以添加多個(gè)服務(wù)器通過(guò);分隔-->
<add key="readWriteHosts" value="127.0.0.1:6379" />
<!--Redis讀服務(wù)器地址,可以添加多個(gè)服務(wù)器通過(guò);分隔-->
<add key="readOnlyHosts" value="127.0.0.1:6380" />
</appSettings>
可以使用redis desktop manager管理工具查看服務(wù)器緩存中的數(shù)據(jù)
Sentinel 哨兵
Sentinel(哨兵)是Redis 的高可用性解決方案:由一個(gè)或多個(gè)Sentinel 實(shí)例 組成的Sentinel 系統(tǒng)可以監(jiān)視任意多個(gè)主服務(wù)器茂卦,以及這些主服務(wù)器屬下的所有從服務(wù)器,并在被監(jiān)視的主服務(wù)器進(jìn)入下線(xiàn)狀態(tài)時(shí)组哩,自動(dòng)將下線(xiàn)主服務(wù)器屬下的某個(gè)從服務(wù)器升級(jí)為新的主服務(wù)器等龙。
例如:
在Server1 掉線(xiàn)后:
升級(jí)Server2 為新的主服務(wù)器:
Sentinel的作用:
- Master 狀態(tài)監(jiān)測(cè);
- 如果Master 異常伶贰,則會(huì)進(jìn)行Master-slave 轉(zhuǎn)換蛛砰,將其中一個(gè)Slave作為Master,將之前的Master作為Slave幕袱;
- Master-Slave切換后暴备,master_redis.conf、slave_redis.conf和sentinel.conf的內(nèi)容都會(huì)發(fā)生改變们豌,即master_redis.conf中會(huì)多一行slaveof的配置涯捻,sentinel.conf的監(jiān)控目標(biāo)會(huì)隨之調(diào)換。
Sentinel的工作方式:
- 每個(gè)Sentinel以每秒鐘一次的頻率向它所知的Master望迎,Slave以及其他 Sentinel 實(shí)例發(fā)送一個(gè) PING 命令障癌;
- 如果一個(gè)實(shí)例(instance)距離最后一次有效回復(fù) PING 命令的時(shí)間超過(guò) down-after-milliseconds 選項(xiàng)所指定的值, 則這個(gè)實(shí)例會(huì)被 Sentinel 標(biāo)記為主觀(guān)下線(xiàn)辩尊;
- 如果一個(gè)Master被標(biāo)記為主觀(guān)下線(xiàn)涛浙,則正在監(jiān)視這個(gè)Master的所有 Sentinel 要以每秒一次的頻率確認(rèn)Master的確進(jìn)入了主觀(guān)下線(xiàn)狀態(tài);
- 當(dāng)有足夠數(shù)量的 Sentinel(大于等于配置文件指定的值)在指定的時(shí)間范圍內(nèi)確認(rèn)Master的確進(jìn)入了主觀(guān)下線(xiàn)狀態(tài), 則Master會(huì)被標(biāo)記為客觀(guān)下線(xiàn)轿亮;
- 在一般情況下疮薇, 每個(gè) Sentinel 會(huì)以每 10 秒一次的頻率向它已知的所有Master,Slave發(fā)送 INFO 命令
- 當(dāng)Master被 Sentinel 標(biāo)記為客觀(guān)下線(xiàn)時(shí)我注,Sentinel 向下線(xiàn)的 Master 的所有 Slave 發(fā)送 INFO 命令的頻率會(huì)從 10 秒一次改為每秒一次
- 若沒(méi)有足夠數(shù)量的 Sentinel 同意 Master 已經(jīng)下線(xiàn)按咒, Master 的客觀(guān)下線(xiàn)狀態(tài)就會(huì)被移除;若 Master 重新向 Sentinel 的 PING 命令返回有效回復(fù)但骨, Master 的主觀(guān)下線(xiàn)狀態(tài)就會(huì)被移除励七。
緩存系統(tǒng)的三大問(wèn)題
- 緩存穿透 Cache Penetration: 緩存設(shè)立的目的是為了一定層面上截獲到數(shù)據(jù)庫(kù)存儲(chǔ)層的請(qǐng)求。穿透的意思就在于這個(gè)截獲沒(méi)有成功奔缠,請(qǐng)求最終還是去到了數(shù)據(jù)庫(kù)掠抬,緩存沒(méi)有產(chǎn)生應(yīng)有的價(jià)值。
- 緩存擊穿 Hotspot Invalid: 如果把緩存理解成一面擋在數(shù)據(jù)庫(kù)面前的墻壁校哎,為數(shù)據(jù)庫(kù)“抵御”查詢(xún)請(qǐng)求两波,所謂擊穿,就是在這面墻壁上打出了一個(gè)洞贬蛙。一般發(fā)生在某個(gè)熱點(diǎn)數(shù)據(jù)緩存到期雨女,而此時(shí)針對(duì)該數(shù)據(jù)的大量查詢(xún)請(qǐng)求來(lái)臨,大家一股腦的懟到了數(shù)據(jù)庫(kù)阳准。
- 緩存雪崩 Cache Avalanche: 理解了擊穿氛堕,那雪崩就更好理解了。俗話(huà)說(shuō)得好野蝇,擊穿是一個(gè)人的雪崩讼稚,雪崩是一群人的擊穿。如果緩存這堵墻上處處都是洞绕沈,那這面墻還如何屹立锐想?
windows系統(tǒng)配置redis密碼
設(shè)置密碼,修改配置文件redis.windows.conf,其中123456是設(shè)置的密碼
requirepass 123456 //此處注意乍狐,行前不能有空格
啟動(dòng)redis服務(wù)赠摇,雙擊redis-server.exe或者在redis文件夾下運(yùn)行
redis-server.exe redis.windows.conf
重新登錄獲取操作權(quán)限
redis-cli.exe -h 127.0.0.1 -p 6379 -a 123456 //需添加密碼參數(shù)
config get requirepass
redis使用IP地址訪(fǎng)問(wèn)
找到redis.windows.conf配置文件
- 將protected-mode 參數(shù)改為no。
- 注釋掉bind 127.0.0.1
- 重啟redis服務(wù)