在前面介紹了三篇關(guān)于MongoDB數(shù)據(jù)庫(kù)的開(kāi)發(fā)使用文章,嚴(yán)格來(lái)講這個(gè)不能歸類于MongoDB數(shù)據(jù)庫(kù)開(kāi)發(fā),不過(guò)Redis又有著和MongoDB數(shù)據(jù)庫(kù)非常密切的關(guān)系,它們兩者很接近,Redis主要是內(nèi)存中的NoSQL數(shù)據(jù)庫(kù),用來(lái)提高性能的;MongoDB數(shù)據(jù)庫(kù)則是文件中的NoSQL數(shù)據(jù)庫(kù),做數(shù)據(jù)序列號(hào)存儲(chǔ)使用的,它們兩者關(guān)系密切又有所區(qū)別。本篇主要介紹Redis的安裝及使用,為后面Redis和MongoDB數(shù)據(jù)庫(kù)的聯(lián)合使用先鋪下基礎(chǔ)。
1、Redis基礎(chǔ)及安裝
Redis是一個(gè)開(kāi)源的使用ANSI C語(yǔ)言編寫、支持網(wǎng)絡(luò)、可基于內(nèi)存亦可持久化的日志型、Key-Value數(shù)據(jù)庫(kù),和Memcached類似,它支持存儲(chǔ)的value類型相對(duì)更多,包括string(字符串)、list(鏈表)、set(集合)、zset(sorted set --有序集合)和hash(哈希類型)。在此基礎(chǔ)上,redis支持各種不同方式的排序。與memcached一樣,為了保證效率,數(shù)據(jù)都是緩存在內(nèi)存中。區(qū)別的是redis會(huì)周期性的把更新的數(shù)據(jù)寫入磁盤或者把修改操作寫入追加的記錄文件,并且在此基礎(chǔ)上實(shí)現(xiàn)了master-slave(主從)同步。
Redis的代碼遵循ANSI-C編寫,可以在所有POSIX系統(tǒng)(如Linux, *
BSD, Mac OS X, Solaris等)上安裝運(yùn)行。而且Redis并不依賴任何非標(biāo)準(zhǔn)庫(kù),也沒(méi)有編譯參數(shù)必需添加。
1)Redis支持兩種持久化方式:
(1):snapshotting(快照)也是默認(rèn)方式.(把數(shù)據(jù)做一個(gè)備份,將數(shù)據(jù)存儲(chǔ)到文件)
(2)Append-only file(縮寫aof)的方式
快照是默認(rèn)的持久化方式,這種方式是將內(nèi)存中數(shù)據(jù)以快照的方式寫到二進(jìn)制文件中,默認(rèn)的文件名稱為dump.rdb.可以通過(guò)配置設(shè)置自動(dòng)做快照持久化的方式。我們可以配置redis在n秒內(nèi)如果超過(guò)m個(gè)key鍵修改就自動(dòng)做快照.
aof方式:由于快照方式是在一定間隔時(shí)間做一次的,所以如果Redis意外down掉的話,就會(huì)丟失最后一次快照后的所有修改。aof比快照方式有更好的持久化性,是由于在使用aof時(shí),redis會(huì)將每一個(gè)收到的寫命令都通過(guò)write函數(shù)追加到文件中,當(dāng)redis重啟時(shí)會(huì)通過(guò)重新執(zhí)行文件中保存的寫命令來(lái)在內(nèi)存中重建整個(gè)數(shù)據(jù)庫(kù)的內(nèi)容。
2)Redis數(shù)據(jù)結(jié)構(gòu)
Redis 的作者antirez曾稱其為一個(gè)數(shù)據(jù)結(jié)構(gòu)服務(wù)器(data structures server),這是一個(gè)非常準(zhǔn)確的表述,Redis的所有功能就是將數(shù)據(jù)以其固有的幾種結(jié)構(gòu)保存,并提供給用戶操作這幾種結(jié)構(gòu)的接口。我們可以想象我們?cè)诟鞣N語(yǔ)言中的那些固有數(shù)據(jù)類型及其操作。
Redis目前提供四種數(shù)據(jù)類型:string,list,set及zset(sorted set)和Hash。
string是最簡(jiǎn)單的類型,你可以理解成與Memcached一模一個(gè)的類型,一個(gè)key對(duì)應(yīng)一個(gè)value,其上支持的操作與Memcached的操作類似。但它的功能更豐富。
list是一個(gè)鏈表結(jié)構(gòu),主要功能是push、pop、獲取一個(gè)范圍的所有值等等。操作中key理解為鏈表的名字。
set是集合,和我們數(shù)學(xué)中的集合概念相似,對(duì)集合的操作有添加刪除元素,有對(duì)多個(gè)集合求交并差等操作。操作中key理解為集合的名字。
zset是set的一個(gè)升級(jí)版本,他在set的基礎(chǔ)上增加了一個(gè)順序?qū)傩?,這一屬性在添加修改元素的時(shí)候可以指定,每次指定后,zset會(huì)自動(dòng)重新按新的值調(diào)整順序??梢岳斫饬擞袃闪械膍ysql表,一列存value,一列存順序。操作中key理解為zset的名字。
Hash數(shù)據(jù)類型允許用戶用Redis存儲(chǔ)對(duì)象類型,Hash數(shù)據(jù)類型的一個(gè)重要優(yōu)點(diǎn)是,當(dāng)你存儲(chǔ)的數(shù)據(jù)對(duì)象只有很少幾個(gè)key值時(shí),數(shù)據(jù)存儲(chǔ)的內(nèi)存消耗會(huì)很小.更多關(guān)于Hash數(shù)據(jù)類型的說(shuō)明請(qǐng)見(jiàn): http://code.google.com/p/redis/wiki/Hashes
3)Redis數(shù)據(jù)存儲(chǔ)
Redis的存儲(chǔ)分為內(nèi)存存儲(chǔ)、磁盤存儲(chǔ)和log文件三部分,配置文件中有三個(gè)參數(shù)對(duì)其進(jìn)行配置。
save seconds updates,save配置,指出在多長(zhǎng)時(shí)間內(nèi),有多少次更新操作,就將數(shù)據(jù)同步到數(shù)據(jù)文件。這個(gè)可以多個(gè)條件配合,比如默認(rèn)配置文件中的設(shè)置,就設(shè)置了三個(gè)條件。
appendonly yes/no ,appendonly配置,指出是否在每次更新操作后進(jìn)行日志記錄,如果不開(kāi)啟,可能會(huì)在斷電時(shí)導(dǎo)致一段時(shí)間內(nèi)的數(shù)據(jù)丟失。因?yàn)閞edis本身同步數(shù)據(jù)文件是按上面的save條件來(lái)同步的,所以有的數(shù)據(jù)會(huì)在一段時(shí)間內(nèi)只存在于內(nèi)存中。
appendfsync no/always/everysec ,appendfsync配置,no表示等操作系統(tǒng)進(jìn)行數(shù)據(jù)緩存同步到磁盤,always表示每次更新操作后手動(dòng)調(diào)用fsync()將數(shù)據(jù)寫到磁盤,everysec表示每秒同步一次。
4)Redis的安裝
Redis可以在不同的平臺(tái)運(yùn)行,不過(guò)我主要基于Windows進(jìn)行開(kāi)發(fā)工作,所以下面主要是基于Windows平臺(tái)進(jìn)行介紹。
Redis可以安裝以DOS窗口啟動(dòng)的,也可以安裝為Windows服務(wù)的,一般為了方便,我們更愿意把它安裝為Windows服務(wù),這樣可以比較方便管理。下載地址:https://github.com/MSOpenTech/redis/releases下載,安裝為Windows服務(wù)即可。
當(dāng)前可以下載到最新的Windows安裝版本為3.0,安裝后作為Windows服務(wù)運(yùn)行,安裝后可以在系統(tǒng)的服務(wù)里面看到Redis的服務(wù)在運(yùn)行了,如下圖所示。

安裝好Redis后,還有一個(gè)Redis伴侶Redis Desktop Manager需要安裝,這樣可以實(shí)時(shí)查看Redis緩存里面有哪些數(shù)據(jù),具體地址如下:http://redisdesktop.com/download
下載屬于自己平臺(tái)的版本即可

下載安裝后,打開(kāi)運(yùn)行界面,如果我們往里面添加鍵值的數(shù)據(jù),那么可以看到里面的數(shù)據(jù)了。

2、Redis的C#使用
Redis目前提供四種數(shù)據(jù)類型:string,list,set及zset(sorted set)和Hash。因此它在C#里面也有對(duì)應(yīng)的封裝處理,而且有很多人對(duì)他進(jìn)行了封裝,提供了很多的響應(yīng)開(kāi)發(fā)包,具體可以訪問(wèn)http://redis.io/clients#c 進(jìn)行了解。一般建議用ServiceStack.Redis的封裝驅(qū)動(dòng)比較好,具體的使用可以參考https://github.com/ServiceStack/ServiceStack.Redis。
我們開(kāi)發(fā)C#代碼的時(shí)候,可以在NuGet程序包上面進(jìn)行添加對(duì)應(yīng)的ServiceStack.Redis引用,如下所示。

在彈出的NuGet程序包里面,輸入ServiceStack.Redis進(jìn)行搜索,并添加下面的驅(qū)動(dòng)引用即可。

這樣會(huì)在項(xiàng)目引用里面添加了幾個(gè)對(duì)應(yīng)的程序集,如下所示。

在C#里面使用Redis,首先需要實(shí)例化一個(gè)Redis的客戶端類,如下所示。
//創(chuàng)建一個(gè)Redis的客戶端類
RedisClient client = new RedisClient("127.0.0.1", 6379);
在使用前,我們需要清空所有的鍵值存儲(chǔ),使用FushAll方法即可,如下所示
//Redis FlushAll 命令用于清空整個(gè) Redis 服務(wù)器的數(shù)據(jù)(刪除所有數(shù)據(jù)庫(kù)的所有 key )。
client.FlushAll();
根據(jù)上面的驅(qū)動(dòng),可以為不同類型的處理編寫一些演示代碼,下面代碼是摘錄網(wǎng)上的案例進(jìn)行介紹。
#region string類型的測(cè)試代碼
client.Add<string>("StringValueTime", "帶有有效期的字符串", DateTime.Now.AddMilliseconds(10000));
while (true)
{
if (client.ContainsKey("StringValueTime"))
{
Console.WriteLine("String.鍵:StringValue, 值:{0} {1}", client.Get<string>("StringValueTime"), DateTime.Now);
Thread.Sleep(10000);
}
else
{
Console.WriteLine("鍵:StringValue, 值:已過(guò)期 {0}", DateTime.Now);
break;
}
}
client.Add<string>("StringValue", " String和Memcached操作方法差不多");
Console.WriteLine("數(shù)據(jù)類型為:String.鍵:StringValue, 值:{0}", client.Get<string>("StringValue"));
Student stud = new Student() { id = "1001", name = "李四" };
client.Add<Student>("StringEntity", stud);
Student Get_stud = client.Get<Student>("StringEntity");
Console.WriteLine("數(shù)據(jù)類型為:String.鍵:StringEntity, 值:{0} {1}", Get_stud.id, Get_stud.name);
#endregion
#region Hash類型的測(cè)試代碼
client.SetEntryInHash("HashID", "Name", "張三");
client.SetEntryInHash("HashID", "Age", "24");
client.SetEntryInHash("HashID", "Sex", "男");
client.SetEntryInHash("HashID", "Address", "上海市XX號(hào)XX室");
List<string> HaskKey = client.GetHashKeys("HashID");
foreach (string key in HaskKey)
{
Console.WriteLine("HashID--Key:{0}", key);
}
List<string> HaskValue = client.GetHashValues("HashID");
foreach (string value in HaskValue)
{
Console.WriteLine("HashID--Value:{0}", value);
}
List<string> AllKey = client.GetAllKeys(); //獲取所有的key。
foreach (string Key in AllKey)
{
Console.WriteLine("AllKey--Key:{0}", Key);
}
#endregion
#region List類型的測(cè)試代碼
/*
* list是一個(gè)鏈表結(jié)構(gòu),主要功能是push,pop,獲取一個(gè)范圍的所有的值等,操作中key理解為鏈表名字。
* Redis的list類型其實(shí)就是一個(gè)每個(gè)子元素都是string類型的雙向鏈表。我們可以通過(guò)push,pop操作從鏈表的頭部或者尾部添加刪除元素,
* 這樣list既可以作為棧,又可以作為隊(duì)列。Redis list的實(shí)現(xiàn)為一個(gè)雙向鏈表,即可以支持反向查找和遍歷,更方便操作,不過(guò)帶來(lái)了部分額外的內(nèi)存開(kāi)銷,
* Redis內(nèi)部的很多實(shí)現(xiàn),包括發(fā)送緩沖隊(duì)列等也都是用的這個(gè)數(shù)據(jù)結(jié)構(gòu)
*/
client.EnqueueItemOnList("QueueListId", "1.張三"); //入隊(duì)
client.EnqueueItemOnList("QueueListId", "2.張四");
client.EnqueueItemOnList("QueueListId", "3.王五");
client.EnqueueItemOnList("QueueListId", "4.王麻子");
long q = client.GetListCount("QueueListId");
Console.WriteLine(client.GetItemFromList("QueueListId", 0));
for (int i = 0; i < q; i++)
{
Console.WriteLine("QueueListId出隊(duì)值:{0}", client.DequeueItemFromList("QueueListId")); //出隊(duì)(隊(duì)列先進(jìn)先出)
}
q = client.GetListCount("QueueListId");
Console.WriteLine(q);
client.PushItemToList("StackListId", "1.張三"); //入棧
client.PushItemToList("StackListId", "2.張四");
client.PushItemToList("StackListId", "3.王五");
client.PushItemToList("StackListId", "4.王麻子");
long p = client.GetListCount("StackListId");
for (int i = 0; i < p; i++)
{
Console.WriteLine("StackListId出棧值:{0}", client.PopItemFromList("StackListId")); //出棧(棧先進(jìn)后出)
}
q = client.GetListCount("StackListId");
Console.WriteLine(q);
#endregion
#region Set無(wú)序集合的測(cè)試代碼
/*
它是string類型的無(wú)序集合。set是通過(guò)hash table實(shí)現(xiàn)的,添加,刪除和查找,對(duì)集合我們可以取并集,交集,差集
*/
client.AddItemToSet("Set1001", "小A");
client.AddItemToSet("Set1001", "小B");
client.AddItemToSet("Set1001", "小C");
client.AddItemToSet("Set1001", "小D");
HashSet<string> hastsetA = client.GetAllItemsFromSet("Set1001");
foreach (string item in hastsetA)
{
Console.WriteLine("Set無(wú)序集合ValueA:{0}", item); //出來(lái)的結(jié)果是無(wú)須的
}
client.AddItemToSet("Set1002", "小K");
client.AddItemToSet("Set1002", "小C");
client.AddItemToSet("Set1002", "小A");
client.AddItemToSet("Set1002", "小J");
HashSet<string> hastsetB = client.GetAllItemsFromSet("Set1002");
foreach (string item in hastsetB)
{
Console.WriteLine("Set無(wú)序集合ValueB:{0}", item); //出來(lái)的結(jié)果是無(wú)須的
}
HashSet<string> hashUnion = client.GetUnionFromSets(new string[] { "Set1001", "Set1002" });
foreach (string item in hashUnion)
{
Console.WriteLine("求Set1001和Set1002的并集:{0}", item); //并集
}
HashSet<string> hashG = client.GetIntersectFromSets(new string[] { "Set1001", "Set1002" });
foreach (string item in hashG)
{
Console.WriteLine("求Set1001和Set1002的交集:{0}", item); //交集
}
HashSet<string> hashD = client.GetDifferencesFromSet("Set1001", new string[] { "Set1002" }); //[返回存在于第一個(gè)集合,但是不存在于其他集合的數(shù)據(jù)。差集]
foreach (string item in hashD)
{
Console.WriteLine("求Set1001和Set1002的差集:{0}", item); //差集
}
#endregion
#region SetSorted 有序集合的測(cè)試代碼
/*
sorted set 是set的一個(gè)升級(jí)版本,它在set的基礎(chǔ)上增加了一個(gè)順序的屬性,這一屬性在添加修改.元素的時(shí)候可以指定,
* 每次指定后,zset(表示有序集合)會(huì)自動(dòng)重新按新的值調(diào)整順序。可以理解為有列的表,一列存 value,一列存順序。操作中key理解為zset的名字.
*/
client.AddItemToSortedSet("SetSorted1001", "1.劉仔");
client.AddItemToSortedSet("SetSorted1001", "2.星仔");
client.AddItemToSortedSet("SetSorted1001", "3.豬仔");
List<string> listSetSorted = client.GetAllItemsFromSortedSet("SetSorted1001");
foreach (string item in listSetSorted)
{
Console.WriteLine("SetSorted有序集合{0}", item);
}
#endregion
對(duì)于具體類型的類對(duì)象,也可以使用As方法進(jìn)行轉(zhuǎn)換為對(duì)應(yīng)的處理對(duì)象進(jìn)行處理,如下所示
IRedisTypedClient<Phone> phones = client.As<Phone>();
具體的測(cè)試代碼如下所示。
/// <summary>
/// Redis對(duì)對(duì)象類的處理例子
/// </summary>
private void btnTypeValue_Click(object sender, EventArgs e)
{
IRedisTypedClient<Phone> phones = client.As<Phone>();
Phone phoneFive = phones.GetValue("5");
if (phoneFive == null)
{
Thread.Sleep(50);
phoneFive = new Phone
{
Id = 5,
Manufacturer = "Apple",
Model = "xxxxx",
Owner = new Person
{
Id = 1,
Age = 100,
Name = "伍華聰",
Profession = "計(jì)算機(jī)",
Surname = "wuhuacong"
}
};
phones.SetEntry(phoneFive.Id.ToString(), phoneFive);
}
client.Store<Phone>(
new Phone
{
Id = 2,
Manufacturer = "LG",
Model = "test-xxx",
Owner = new Person
{
Id = 2,
Age = 40,
Name = "test",
Profession = "teacher",
Surname = "wuhuacong"
}
});
var message = "Phone model is " + phoneFive.Manufacturer + ",";
message += "Phone Owner Name is: " + phoneFive.Owner.Name;
Console.WriteLine(message);
}
以上就是關(guān)于Redis的安裝以及簡(jiǎn)單的例子使用說(shuō)明,在具體中,我們可以利用Redis的高性能特性,來(lái)構(gòu)建我們的緩存數(shù)據(jù),并且可以利用Redis和MongoDB數(shù)據(jù)庫(kù)的完美銜接,可以整合一起做的更好,為相關(guān)的后臺(tái)提供更高效的數(shù)據(jù)處理操作,畢竟在互聯(lián)網(wǎng)的大環(huán)境下,性能是非常重要的。