NoSql
Not Only Sql非關係型數據庫
由於關係型數據庫中數據的關係複雜 ,再就是數據讀取和寫入壓力,硬盤的速度滿足不了,尤其是一些大數據量 所以產生了NoSql了,比如Redis。
Redis
Redis特點
(1)是基於內存的,關係型數據庫則是存到硬盤中的。
(2)沒有嚴格的數據格式
(3)支持多種類型(5種)
(4)因爲Redis是基於內存的,所以萬一服務器掛了,那麼數據就會丟失。但是Redis有一個固化數據的功能,可以實現持久化保存,可以將一些不經常訪問的信息存儲到硬盤中,這個可以通過配置實現。但是如果是這個應用程序崩潰了,是不會影響到redis的,因爲redis相當於一個內存的
(5)支持集羣
Redis單線程模型
單線程模型:整個進程只有一個線程,線程就是執行流,這樣性能會低嗎?不會。最初的版本是使用的單線程模型,4.0之後加入了多線程的支持。相比於多線程,性能還是會低一點
單線程的優點:
- 第一,單線程可以簡化數據結構和算法的實現。併發數據結構實現不但困難而且開發測試比較麻
- 第二,單線程避免了線程切換和競態產生的消耗,對於服務端開發來說,鎖和線程切換通常是性能殺手。
- 單線程的問題:對於每個命令的執行時間是有要求的。如果 某個命令執行過長,會造成其他命令的阻塞,所以 redis 適用於那些需要快速執行的場景。
使用
redis的下載地址:https://github.com/MSOpenTech/redis/releases
下載之後解壓,雙擊redis-server.exe開啓redis服務:
redis-server.exe打開之後不要關閉,然後雙擊redis-cli.exe打開操作界面(6379是redis的默認端口,可以再配置界面更改):
還有一個可視化工具:
安裝之後登錄打開就可以看到設置的信息:
實際開發中肯定不是像上面那樣弄,我們可以直接使用對應的程序集:
ServiceStack(1小時3600次請求--可破解) 需要收費! StackExchange 免費
Redis支持數據類型
String: 主要是key-value形式的緩存,可以設置過期世間, value不byte(比特)超過512;Redis是單線程的,
Hash
Set
ZSet
List
ServiceStack中有內置的遞減的api,可以實現電商中的秒殺環節,如果是用程序的話,就要是多線程加鎖來實現,但是用redis的話更快更簡便。
示例(這是使用的ServiceStack):下面主要用的是5大類型中的string類型
自定義類:
namespace Richard.Redis.Interface { /// <summary> /// RedisBase類,是redis操作的基類,繼承自IDisposable接口,主要用於釋放內存 /// </summary> public abstract class RedisBase : IDisposable { /// <summary> /// Redis連接; /// </summary> public IRedisClient iClient { get; private set; } /// <summary> /// 構造時完成鏈接的打開 /// </summary> public RedisBase() { iClient = RedisManager.GetClient(); } //public static IRedisClient iClient { get; private set; } //static RedisBase() //{ // iClient = RedisManager.GetClient(); //} private bool _disposed = false; protected virtual void Dispose(bool disposing) { if (!this._disposed) { if (disposing) { iClient.Dispose(); iClient = null; } } this._disposed = true; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } public void Transcation() { using (IRedisTransaction irt = this.iClient.CreateTransaction()) { try { irt.QueueCommand(r => r.Set("key", 20)); irt.QueueCommand(r => r.Increment("key", 1)); irt.Commit(); // 提交事務 } catch (Exception ex) { irt.Rollback(); throw ex; } } } /// <summary> /// 清除全部數據 請小心 /// </summary> public virtual void FlushAll() { iClient.FlushAll(); } /// <summary> /// 保存數據DB文件到硬盤 /// </summary> public void Save() { iClient.Save();//阻塞式save } /// <summary> /// 異步保存數據DB文件到硬盤 /// </summary> public void SaveAsync() { iClient.SaveAsync();//異步save } } } namespace Richard.Redis.Service { /// <summary> /// key-value 鍵值對:value可以是序列化的數據 /// </summary> public class RedisStringService : RedisBase { #region 賦值 /// <summary> /// 設置key的value /// </summary> public bool Set<T>(string key, T value) { return base.iClient.Set<T>(key, value); } /// <summary> /// 設置key的value並設置過期時間 /// </summary> public bool Set<T>(string key, T value, DateTime dt) { return base.iClient.Set<T>(key, value, dt); } /// <summary> /// 設置key的value並設置過期時間 /// </summary> public bool Set<T>(string key, T value, TimeSpan sp) { return base.iClient.Set<T>(key, value, sp); } /// <summary> /// 設置多個key/value /// </summary> public void Set(Dictionary<string, string> dic) { base.iClient.SetAll(dic); } #endregion #region 追加 /// <summary> /// 在原有key的value值之後追加value,沒有就新增一項 /// </summary> public long Append(string key, string value) { return base.iClient.AppendToValue(key, value); } #endregion #region 獲取值 /// <summary> /// 獲取key的value值 /// </summary> public string Get(string key) { return base.iClient.GetValue(key); } /// <summary> /// 獲取多個key的value值 /// </summary> public List<string> Get(List<string> keys) { return base.iClient.GetValues(keys); } /// <summary> /// 獲取多個key的value值 /// </summary> public List<T> Get<T>(List<string> keys) { return base.iClient.GetValues<T>(keys); } #endregion #region 獲取舊值賦上新值 /// <summary> /// 獲取舊值賦上新值 /// </summary> public string GetAndSetValue(string key, string value) { return base.iClient.GetAndSetValue(key, value); } #endregion #region 輔助方法 /// <summary> /// 獲取值的長度 /// </summary> public long GetLength(string key) { return base.iClient.GetStringCount(key); } /// <summary> /// 自增1,返回自增後的值 /// 就是將key對應的值加1之後返回 /// </summary> public long Incr(string key) { return base.iClient.IncrementValue(key); } /// <summary> /// 自增count,返回自增後的值 /// 將原先的值加上count之後返回最終值 /// </summary> public long IncrBy(string key, int count) { return base.iClient.IncrementValueBy(key, count); } /// <summary> /// 自減1,返回自減後的值 /// 將原先的值減一之後返回最終值 /// </summary> public long Decr(string key) { return base.iClient.DecrementValue(key); } /// <summary> /// 自減count ,返回自減後的值 /// 將原先的值減去count之後返回最終值 /// </summary> /// <param name="key"></param> /// <param name="count"></param> /// <returns></returns> public long DecrBy(string key, int count) { return base.iClient.DecrementValueBy(key, count); } #endregion } }
自定義的redis管理中心:
namespace Richard.Redis.Init { /// <summary> /// Redis管理中心 /// </summary> public class RedisManager { /// <summary> /// redis配置文件信息 /// </summary> private static RedisConfigInfo RedisConfigInfo = new RedisConfigInfo(); /// <summary> /// Redis客戶端池化管理 /// </summary> private static PooledRedisClientManager prcManager; /// <summary> /// 靜態構造方法,初始化鏈接池管理對象 /// </summary> static RedisManager() { CreateManager(); } /// <summary> /// 創建鏈接池管理對象 /// </summary> private static void CreateManager() { // 可寫的Redis鏈接地址 就是redis的服務器IP地址 string[] WriteServerConStr = RedisConfigInfo.WriteServerList.Split(','); // 可讀的Redis鏈接地址 就是redis的服務器IP地址 string[] ReadServerConStr = RedisConfigInfo.ReadServerList.Split(','); prcManager = new PooledRedisClientManager(ReadServerConStr, WriteServerConStr, new RedisClientManagerConfig { //最大寫鏈接數 MaxWritePoolSize = RedisConfigInfo.MaxWritePoolSize, //最大讀鏈接數 MaxReadPoolSize = RedisConfigInfo.MaxReadPoolSize, //是否自動啓動 AutoStart = RedisConfigInfo.AutoStart, }); } /// <summary> /// 客戶端緩存操作對象 /// </summary> public static IRedisClient GetClient() { return prcManager.GetClient(); } } }
redis的配置類:
namespace Richard.Redis.Init { /// <summary> /// redis配置文件信息 /// 也可以放到配置文件去 /// </summary> public sealed class RedisConfigInfo { /// <summary> /// 可寫的Redis鏈接地址 /// format:ip1,ip2 /// /// 默認6379端口 /// </summary> public string WriteServerList = "127.0.0.1:6379"; /// <summary> /// 可讀的Redis鏈接地址 /// format:ip1,ip2 /// </summary> public string ReadServerList = "127.0.0.1:6379"; /// <summary> /// 最大寫鏈接數 /// </summary> public int MaxWritePoolSize = 60; /// <summary> /// 最大讀鏈接數 /// </summary> public int MaxReadPoolSize = 60; /// <summary> /// 本地緩存到期時間,單位:秒 /// </summary> public int LocalCacheTime = 180; /// <summary> /// 自動重啓 /// </summary> public bool AutoStart = true; /// <summary> /// 是否記錄日誌,該設置僅用於排查redis運行時出現的問題, /// 如redis工作正常,請關閉該項 /// </summary> public bool RecordeLog = false; } }
使用DecrementValue遞減方法實現商品秒殺:
public class OversellTest { private static bool IsGoOn = true;//是否繼續秒殺,由庫存和秒殺時間共同決定,任意一個結束都結束 public static void Show() { using (RedisStringService service = new RedisStringService()) { service.Set<int>("Stock", 10);//默認庫存庫存 } for (int i = 0; i < 5000; i++) //5000人同時併發; { int k = i; Task.Run(() =>//每個線程就是一個用戶請求 { using (RedisStringService service = new RedisStringService()) { if (IsGoOn) { long index = service.Decr("Stock");//-1並且返回 if (index >= 0) { Console.WriteLine($"{k.ToString("000")}秒殺成功,秒殺商品索引爲{index}"); //可以分隊列,去數據庫操作 } else { if (IsGoOn) { IsGoOn = false; } Console.WriteLine($"{k.ToString("000")}秒殺失敗,秒殺商品索引爲{index}"); } } else { Console.WriteLine($"{k.ToString("000")}秒殺停止......"); } } }); } Console.Read(); } }
結果:
5000人搶10件商品,這樣比寫多線程外加鎖性能快多了。
此外還有一些簡單的調用,可以看一下:
Console.WriteLine("*****************************************"); { using (RedisStringService service = new RedisStringService()) { service.FlushAll(); service.Set<string>("student1", "夢的翅膀"); Console.WriteLine(service.Get("student1")); service.Append("student1", "20180802"); Console.WriteLine(service.Get("student1")); Console.WriteLine(service.GetAndSetValue("student1", "程序錯誤")); Console.WriteLine(service.Get("student1")); //5秒鐘過期,5秒鐘之後保存在內存中的這個數據就會消失 service.Set<string>("student2", "王", DateTime.Now.AddSeconds(5)); Thread.Sleep(5100); Console.WriteLine(service.Get("student2")); service.Set<int>("Age", 32); Console.WriteLine(service.Incr("Age")); Console.WriteLine(service.IncrBy("Age", 3)); Console.WriteLine(service.Decr("Age")); Console.WriteLine(service.DecrBy("Age", 3)); } }
轉載:
https://www.cnblogs.com/anjingdian/p/15378697.html