ASP.NET Redis 開發

文件併發(日誌處理)--隊列--Redis+Log4Nethtml

Redis簡介

Redis是一個開源的,使用C語言編寫,面向「鍵/值」對類型數據的分佈式NoSQL數據庫系統,特色是高性能,持久存儲,適應高併發的應用場景。Redis純粹爲應用而產生,它是一個高性能的key-value數據庫,而且提供了多種語言的APIgit

性能測試結果表示SET操做每秒鐘可達110000次,GET操做每秒81000次(固然不一樣的服務器配置性能不一樣)。github

Redis目前提供五種數據類型:string(字符串),list(鏈表), Hash(哈希),set(集合)及zset(sorted set)  (有序集合)redis

Redis開發維護很活躍,雖然它是一個Key-Value數據庫存儲系統,但它自己支持MQ功能,因此徹底能夠當作一個輕量級的隊列服務來使用。對於RabbitMQ和Redis的入隊和出隊操做,各執行100萬次,每10萬次記錄一次執行時間。測試數據分爲128Bytes、512Bytes、1K和10K四個不一樣大小的數據。實驗代表:入隊時,當數據比較小時Redis的性能要高於RabbitMQ,而若是數據大小超過了10K,Redis則慢的沒法忍受;出隊時,不管數據大小,Redis都表現出很是好的性能,而RabbitMQ的出隊性能則遠低於Redis。數據庫

RedisMemcached的比較.

1.Memcached是多線程,而Redis使用單線程.json

2.Memcached使用預分配的內存池的方式,Redis使用現場申請內存的方式來存儲數據,而且能夠配置虛擬內存。windows

3.Redis能夠實現持久化,主從複製,實現故障恢復。數組

4.Memcached只是簡單的keyvalue,可是Redis支持數據類型比較多。緩存

Redis的存儲分爲內存存儲、磁盤存儲 .從這一點,也說明了RedisMemcached是有區別的。Redis Memcached同樣,爲了保證效率,數據都是緩存在內存中。區別的是redis會週期性的把更新的數據寫入磁盤或者把修改 操做寫入追加的記錄文件,而且在此基礎上實現了master-slave(主從)同步。安全

Redis有兩種存儲方式,默認是snapshot方式,實現方法是定時將內存的快照(snapshot)持久化到硬盤,這種方法缺點是持久化以後若是出現crash則會丟失一段數據。所以在完美主義者的推進下做者增長了aof方式。aofappend only mode,在寫入內存數據的同時將操做命令保存到日誌文件,在一個併發更改上萬的系統中,命令日誌是一個很是龐大的數據,管理維護成本很是高,恢復重建時間會很是長,這樣致使失去aof高可用性本意。另外更重要的是Redis是一個內存數據結構模型,全部的優點都是創建在對內存複雜數據結構高效的原子操做上,這樣就看出aof是一個很是不協調的部分。

其實aof目的主要是數據可靠性及高可用性.

Redis安裝

文章的最後我提供了下載包,固然你也能夠去官網下載最新版本的Redis https://github.com/dmajkic/redis/downloads 

將服務程序拷貝到一個磁盤上的目錄,以下圖:

文件說明:

redis-server.exe:服務程序  

redis-check-dump.exe:本地數據庫檢查 

redis-check-aof.exe:更新日誌檢查 

redis-benchmark.exe:性能測試,用以模擬同時由N個客戶端發送M個 SETs/GETs 查詢.

redis-cli.exe: 服務端開啓後,咱們的客戶端就能夠輸入各類命令測試了

1、打開一個cmd窗口,使用cd命令切換到指定目錄(F:\Redis)運行 redis-server.exe redis.conf 

2、從新打開一個cmd窗口,使用cd命令切換到指定目錄(F:\Redis)運行 redis-cli.exe -h 127.0.0.1 -p 6379,其中 127.0.0.1是本地ip6379redis服務端的默認端口 (這樣能夠開啓一個客戶端程序進行特殊指令的測試).

能夠將此服務設置爲windows系統服務,下載Redis服務安裝軟件,安裝便可。(https://github.com/rgl/redis/downloads 

若是你的電腦是64bit系統,能夠下載redis-2.4.6-setup-64-bit.exe

安裝完成Redis服務後,咱們會在計算機的服務裏面看到

而後啓動此服務。

接下來在使用Redis時,還須要下載C#驅動(也就是C#開發庫),以下圖:

綠色版安裝

redis-server --service-install redis.windows.conf --loglevel verbose --service-name redis --port 6379

Redis經常使用數據類型

使用Redis,咱們不用在面對功能單調的數據庫時,把精力放在如何把大象放進冰箱這樣的問題上,而是利用Redis靈活多變的數據結構和數據操做,爲不一樣的大象構建不一樣的冰箱。

 Redis最爲經常使用的數據類型主要有如下五種: 

  •    String
  •    Hash
  •    List
  •    Set
  •    Sorted set

String類型

String是最經常使用的一種數據類型,普通的key/value存儲均可以歸爲此類 。一個Key對應一個Value,string類型是二進制安全的。Redisstring能夠包含任何數據,好比jpg圖片(生成二進制)或者序列化的對象。基本操做以下:

[csharp] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1.  var client = new RedisClient("127.0.0.1", 6379);  
  2.             client.Set<int>("pwd", 1111);  
  3.             int pwd=client.Get<int>("pwd");  
  4.             Console.WriteLine(pwd);  
  5.    
  6. UserInfo userInfo = new UserInfo() { UserName = "zhangsan", UserPwd = "1111" };//</span>(底層使用json序列化 )  
  7. client.Set<UserInfo>("userInfo", userInfo);  
  8. UserInfo user=client.Get<UserInfo>("userInfo");  
  9. Console.WriteLine(user.UserName);  
  10.   
  11. List<UserInfo> list = new List<UserInfo>() { new UserInfo(){UserName="lisi",UserPwd="111"},new UserInfo(){UserName="wangwu",UserPwd="123"} };  
  12. client.Set<List<UserInfo>>("list",list);  
  13. List<UserInfo>userInfoList=client.Get<List<UserInfo>>("list");  
  14.   
  15.  foreach (UserInfo userInfo in userInfoList)  
  16.    {  
  17.        Console.WriteLine(userInfo.UserName);  
  18.    }  

Hash類型

Hash是一個string 類型的fieldvalue的映射表。hash特別適合存儲對象。相對於將對象的每一個字段存成單個string 類型。一個對象存儲在hash類型中會佔用更少的內存,而且能夠更方便的存取整個對象。

做爲一個key value存在,不少開發者天然的使用set/get方式來使用Redis,實際上這並非最優化的使用方法。尤爲在未啓用VM狀況下,Redis所有數據須要放入內存,節約內存尤爲重要.

增長了序列化/反序列化的開銷,而且在須要修改其中一項信息時,須要把整個對象取回 

Redis爲單進程單線程模式,採用隊列模式將併發訪問變爲串行訪問。Redis自己沒有鎖的概念,Redis對於多個客戶端鏈接並不存在競爭 .. redis是個單線程的程序,爲何會這麼快呢 ?

一、大量線程致使的線程切換開銷

二、鎖、

3、非必要的內存拷貝。 

4. Redis多樣的數據結構,每種結構只作本身愛作的事.

Hash對應的Value內部實際就是一個HashMap,實際這裏會有2種不一樣實現,這個HashMap的成員比較少時,Redis爲了節省內存會採用相似一維數組的方式來緊湊存儲,而不會採用真正的HashMap結構,當成員量增大時會自動轉成真正的HashMap. 

Key仍然是用戶ID, value是一個Map,這個Mapkey是成員的屬性名,value是屬性值,這樣對數據的修改和存取均可以直接經過其內部MapKey(Redis裏稱內部Mapkeyfield), 也就是經過 key(用戶ID) + field(屬性標籤就能夠操做對應屬性數據了,既不須要重複存儲數據,也不會帶來序列化和反序列化

[csharp] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. client.SetEntryInHash("user", "userInfo", "aaaaaaaaaa");  
  2. List<string> list = client.GetHashKeys("user");  
  3. List<string> list = client.GetHashValues("userInfo");//獲取值  
  4. List<string> list = client.GetAllKeys();//獲取全部的key。  

Redis爲不一樣數據類型分別提供了一組參數來控制內存使用,咱們在前面提到過的Redis Hashvalue內部是一個

HashMap,若是該Map的成員比較少,則會採用一維數組的方式來緊湊存儲該MAP,省去了大量指針的內存開銷,這個參數在redis,conf配置文件中下面2項。

Hash-max-zipmap-entries 64

Hash-max-zipmap-value 512.

含義是當value這個Map內部不超過多少個成員時會採用線性緊湊格式存儲,默認是64,即value內部有64個如下的成員就是使用線性緊湊存儲,超過該值自動轉成真正的HashMap.

Hash-max-zipmap-value含義是當value這個MAP內部的每一個成員值長度不超過多少字節就會採用線性緊湊存儲來節省空間。以上兩個條件任意一個條件超過設置值都會轉成真正的HashMap,也就不會再節省內存了,這個值設置多少須要權衡,HashMap的優點就是查找和操做時間短。

一個key可對應多個field,一個field對應一個value   

這裏同時須要注意,Redis提供了接口(hgetall)能夠直接取到所有的屬性數據,可是若是內部Map的成員不少,那麼涉及到遍歷整個內部Map的操做,因爲Redis單線程模型的緣故,這個遍歷操做可能會比較耗時,而另其它客戶端的請求徹底不響應,這點須要格外注意 

建議使用對象類別和ID構成鍵名,使用字段表示對象屬性,字

段值存儲屬性值,例如:car:2 price 500

List類型

list是一個鏈表結構,主要功能是push,pop,獲取一個範圍的全部的值等,操做中key理解爲鏈表名字。 Redislist類型其實就是一個每一個子元素都是string類型的雙向鏈表。咱們能夠經過push,pop操做從鏈表的頭部或者尾部添加刪除元素,這樣list既能夠做爲棧,又能夠做爲隊列。Redis list的實現爲一個雙向鏈表,便可以支持反向查找和遍歷,更方便操做,不過帶來了部分額外的內存開銷,Redis內部的不少實現,包括髮送緩衝隊列等也都是用的這個數據結構

[csharp] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. //隊列使用  
  2.   
  3.  client.EnqueueItemOnList("name", "zhangsan");  
  4.            client.EnqueueItemOnList("name", "lisi");  
  5.            int count= client.GetListCount("name");  
  6.            for (int i = 0; i < count; i++)  
  7.            {  
  8.               Console.WriteLine(client.DequeueItemFromList("name"));  
  9.            }  
  10.   
  11. //棧使用  
  12.             client.PushItemToList("name2", "wangwu");  
  13.             client.PushItemToList("name2", "maliu");  
  14.             int count = client.GetListCount("name2");  
  15.             for (int i = 0; i < count; i++)  
  16.             {  
  17.                 Console.WriteLine(client.PopItemFromList("name2"));  
  18.             }  

Set類型

它是string類型的無序集合。set是經過hash table實現的,添加,刪除和查找,對集合咱們能夠取並集,交集,差集.

[csharp] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. //對Set類型進行操做  
  2.             client.AddItemToSet("a3", "ddd");  
  3.             client.AddItemToSet("a3", "ccc");  
  4.             client.AddItemToSet("a3", "tttt");  
  5.             client.AddItemToSet("a3", "sssh");  
  6.             client.AddItemToSet("a3", "hhhh");  
  7.             System.Collections.Generic.HashSet<string>hashset=client.GetAllItemsFromSet("a3");  
  8.             foreach (string str in hashset)  
  9.             {  
  10.                 Console.WriteLine(str);  
  11.             }  
  12.   
  13. //求並集  
  14. client.AddItemToSet("a3", "ddd");  
  15.             client.AddItemToSet("a3", "ccc");  
  16.             client.AddItemToSet("a3", "tttt");  
  17.             client.AddItemToSet("a3", "sssh");  
  18.             client.AddItemToSet("a3", "hhhh");  
  19.             client.AddItemToSet("a4", "hhhh");  
  20.             client.AddItemToSet("a4", "h777");  
  21.  System.Collections.Generic.HashSet<string>hashset=  client.GetUnionFromSets(new string[] { "a3","a4"});  
  22.   
  23.           foreach (string str in hashset)  
  24.           {  
  25.               Console.WriteLine(str);  
  26.           }  
  27.   
  28. //求交集  
  29.  System.Collections.Generic.HashSet<string> hashset = client.GetIntersectFromSets(new string[] { 「a3」, 「a4」 });  
  30.   
  31. //求差集.  
  32.  System.Collections.Generic.HashSet<string> hashset = client.GetDifferencesFromSet("a3",new string[] { "a4"});  

返回存在於第一個集合,可是不存在於其餘集合的數據。差集

Sorted Set類型

sorted set set的一個升級版本,它在set的基礎上增長了一個順序的屬性,這一屬性在添加修改   .元素的時候能夠指定,每次指定後,zset(表示有序集合)會自動從新按新的值調整順序。能夠理解爲有列的表,一列存 value,一列存順序。操做中key理解爲zset的名字.

Redis sorted set的使用場景與set相似,區別是set不是自動有序的,而sorted set能夠經過用戶額外提供一個優先級(score)的參數來爲成員排序,而且是插入有序的,即自動排序。當你須要一個有序的而且不重複的集合列表,那麼能夠選擇sorted set數據結構, 

[csharp] view plain copy 在CODE上查看代碼片 派生到個人代碼片
  1. client.AddItemToSortedSet("a5", "ffff");  
  2.            client.AddItemToSortedSet("a5","bbbb");  
  3.            client.AddItemToSortedSet("a5", "gggg");  
  4.            client.AddItemToSortedSet("a5", "cccc");  
  5.            client.AddItemToSortedSet("a5", "waaa");  
  6.            System.Collections.Generic.List<string> list =client.GetAllItemsFromSortedSet("a5");  
  7.            foreach (string str in list)  
  8.            {  
  9.                Console.WriteLine(str);  
  10.            }  

給Redis設置密碼

安裝redis服務器後,不能讓其在外網環境下面裸奔啊,咱們能夠爲其設置一個複雜的訪問密碼,最好20位以上,這樣能夠有效防止別人暴力破解。

找到redis的配置文件,默認在這個地方:C:\Program Files\Redis\conf\redis.conf,查找requirepass選項配置

把這個選項前面的#註釋幹掉,而後在後面添加一個複雜的密碼

# use a very strong password otherwise it will be very easy to break.
#
requirepass 2016@Test.88210_yujie

# Command renaming.

帶密碼的訪問

  var client = new RedisClient("192.168.2.154", 6379); //ip和端口
  client.Password = "2016@Test.88210_yujie"; //redis訪問密碼

ServiceStack.Redis源碼:https://github.com/ServiceStack/ServiceStack.Redis

注:此文使用的redis C#驅動是1.0版本,目前最新版本爲4.0,並且已經開源。

4.0版操做Demo以下:

/* ==============================================================================
   * 功能描述:ReidsDemo  
   * 創 建 者:Zouqj
   * 建立日期:2016/4/14/11:40
   * 更多redis相關技術請參考個人博文:http://www.cnblogs.com/jiekzou/p/4487356.html
   ==============================================================================*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//添加以下引用
using ServiceStack.Redis;
using ServiceStack.Redis.Generic;
using System.Web.Script.Serialization;

namespace RedisConsoleApplication1
{
    // 更多redis相關技術請參考個人博文:http://www.cnblogs.com/jiekzou/p/4487356.html
    class Program
    {
        #region static field
        static string host = "192.168.2.154";/*訪問host地址*/
        static string password = "2016@Msd.1127_kjy";/*實例id:密碼*/
        static readonly RedisClient client = new RedisClient(host, 6379, password);
        //static readonly RedisClient client = new RedisClient("xxxxx.m.cnsza.kvstore.aliyuncs.com", 6379, "dacb71347ad0409c:xxxx"); //49正式環境
        static IRedisTypedClient<InStoreReceipt> redis = client.As<InStoreReceipt>();
       
        #endregion

        static void Main(string[] args)
        {
            try
            {
                RedisTestApp();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        #region redis Test
        public static void RedisTestApp()
        {
            StringTest(); //字符串測試

            HashTest(); //Hash測試

            ObjectTest(); //實體對象測試

            SingleObjEnqueueTest(); //單個對象隊列測試

            ListObjTest(); //對象列表測試

            QueueTest(); //隊列和棧測試

            Console.ReadKey();
        }

        #region static method
        /// <summary>
        /// 隊列和棧測試
        /// </summary>
        private static void QueueTest()
        {
            Console.WriteLine("*******************隊列 先進先出********************");
            
            client.EnqueueItemOnList("test", "饒成龍");//入隊。
            client.EnqueueItemOnList("test", "周文傑");
            long length = client.GetListCount("test");
            for (int i = 0; i < length; i++)
            {
                Console.WriteLine(client.DequeueItemFromList("test"));//出隊.
            }
            Console.WriteLine("*********************棧 先進後出*****************");
            client.PushItemToList("name1", "鄒瓊俊");//入棧
            client.PushItemToList("name1", "周文傑");
            long length1 = client.GetListCount("name1");
            for (int i = 0; i < length1; i++)
            {
                Console.WriteLine(client.PopItemFromList("name1"));//出棧.
            }
            Console.ReadKey();
        }
        /// <summary>
        /// 單個對象隊列測試
        /// </summary>
        private static void SingleObjEnqueueTest()
        {
            Console.WriteLine("******************實體對象隊列操做********************");
            Student _stu = new Student { Name = "張三", Age = 21 };
            JavaScriptSerializer json = new JavaScriptSerializer();
            client.EnqueueItemOnList("stu", json.Serialize(_stu));
            _stu = json.Deserialize<Student>(client.DequeueItemFromList("stu"));
            Console.WriteLine(string.Format("姓名:{0},年齡{1}", _stu.Name, _stu.Age));
            Console.ReadKey();
        }

        /// <summary>
        /// List對象測試
        /// </summary>
        public static void ListObjTest()
        {
            List<InStoreReceipt> list = new List<InStoreReceipt>() { new InStoreReceipt() { IdentityID = 1, ReceiptStatus = 1, ReceiptTime = DateTime.Now, ReceiptMessage = "test1" },
            new InStoreReceipt() { IdentityID = 2, ReceiptStatus = 1, ReceiptTime = DateTime.Now, ReceiptMessage = "test2" },new InStoreReceipt() { IdentityID = 3, ReceiptStatus = 1, ReceiptTime = DateTime.Now, ReceiptMessage = "test3" }};
            AddInStoreInfo(list);
            var rList = redis.GetAllItemsFromList(redis.Lists["InStoreReceiptInfoList"]);
            rList.ForEach(v => Console.WriteLine(v.IdentityID + "," + v.ReceiptTime + "," + v.ReceiptMessage));
            redis.RemoveAllFromList(redis.Lists["InStoreReceiptInfoList"]);
            Console.ReadKey();
        }

        /// <summary>
        /// 實體對象測試
        /// </summary>
        private static void ObjectTest()
        {
            Console.WriteLine("**************實體對象,單個,列表操做*****************");
            UserInfo userInfo = new UserInfo() { UserName = "zhangsan", UserPwd = "1111" };//</span>(底層使用json序列化 )  
            client.Set<UserInfo>("userInfo", userInfo);
            UserInfo user = client.Get<UserInfo>("userInfo");
            Console.WriteLine(user.UserName);

            //List<UserInfo> list = new List<UserInfo>() { new UserInfo() { UserName = "lisi", UserPwd = "222" }, new UserInfo() { UserName = "wangwu", UserPwd = "123" } };
            //client.Set<List<UserInfo>>("list", list);
            List<UserInfo> userInfoList = client.Get<List<UserInfo>>("list");
            userInfoList.ForEach(u => Console.WriteLine(u.UserName));
            client.Remove("list");

            Console.ReadKey();
        }

        /// <summary>
        /// Hash測試
        /// </summary>
        private static void HashTest()
        {
            Console.WriteLine("********************Hash*********************");
            client.SetEntryInHash("userInfoId", "name", "zhangsan");
            var lstKeys= client.GetHashKeys("userInfoId");
            lstKeys.ForEach(k => Console.WriteLine(k));
            var lstValues=client.GetHashValues("userInfoId");
            lstValues.ForEach(v => Console.WriteLine(v));
            client.Remove("userInfoId");
            Console.ReadKey();
        }
        /// <summary>
        /// 字符串測試
        /// </summary>
        private static void StringTest()
        {
            #region 字符串類型
            Console.WriteLine("*******************字符串類型*********************");
            client.Set<string>("name", "zouqj");
            string userName = client.Get<string>("name");
            Console.WriteLine(userName);
            Console.ReadKey();
            #endregion
        }
        /// <summary>
        /// 添加須要回執的進倉單信息到Redis
        /// </summary>
        /// <param name="lstRInStore">進倉單回執信息列表</param>
        private static void AddInStoreInfo(List<InStoreReceipt> inStoreReceipt)
        {
            IRedisList<InStoreReceipt> rlstRInStore = redis.Lists["InStoreReceiptInfoList"];
            rlstRInStore.AddRange(inStoreReceipt);
        } 
        #endregion
        #endregion
    }
    /// <summary>
    /// 進倉單回執信息(對應清關係統)
    /// </summary>
    public class InStoreReceipt
    {
        /// <summary>
        /// 主鍵ID
        /// </summary>
        public int IdentityID { get; set; }
        /// <summary>
        /// 回執狀態
        /// </summary>
        public int ReceiptStatus { get; set; }
        /// <summary>
        /// 回執時間
        /// </summary>
        public DateTime ReceiptTime { get; set; }
        /// <summary>
        /// 回執信息
        /// </summary>
        public string ReceiptMessage { get; set; }
    }
    public class Student
    {
        /// <summary>
        /// 姓名
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 年齡
        /// </summary>
        public int Age { get; set; }
    }
    public class UserInfo
    {
        public string UserName { get; set; }
        public string UserPwd { get; set; }
    }
}
View Code

RedisHelper類以下:

   public static class RedisHelper
    {
        #region static field

        static string host = "192.168.2.154";/*訪問host地址*/
        static string password = "2016@Msd.1127_kjy";/*實例id:密碼*/
        static readonly RedisClient client = new RedisClient(host, 6379, password);

        #endregion
        #region
        /// <summary>
        /// 獲取信息
        /// </summary>
        /// <typeparam name="T">對象類型</typeparam>
        /// <returns>對象</returns>
        public static T Get<T>(string token) where T : class,new()
        {
            return client.Get<T>(token);
        }
       /// <summary>
       /// 設置信息
       /// </summary>
       /// <typeparam name="T">對象類型</typeparam>
       /// <param name="token">key</param>
       /// <param name="obj">對象</param>
       public static void Set<T>(string token,T obj) where T:class,new()
       {
         client.Set<T>(token, obj);
       }
       /// <summary>
       /// 設置指定Key的過時時間
       /// </summary>
       /// <param name="token">具體的key值</param>
       /// <param name="seconds">過時時間,單位:秒</param>
       public static void Expire(string token,int seconds)
       {
           client.Expire(token, seconds);
       }
        #endregion
    }

特別說明:ServiceStack.Redis從4.0開始商用,有每小時6000次訪問的限制,若是要在生產環境使用,建議使用低版本。

Redis工具和所需資料代碼全下載,地址:http://pan.baidu.com/s/155F6A

相關文章
相關標籤/搜索