1、NoSQL簡介html
NoSQL並非No SQL(再也不須要SQL),而是指Not Only SQL(不只僅只有SQL)。NoSQL並非用來替代關係型數據庫的,而是在某些使用關係型數據庫不合適的場景中,可使用NoSQL數據庫進行優化,而在系統中主要的、常規的數據仍然使用關係型數據庫。
經常使用的NoSQL數據庫有Memcached、Redis、MongoDB等,其中前二者屬於鍵值對數據庫,後者屬於文檔數據庫。它們都有各自的優缺點以及使用場景。
算法
2、Memcached介紹與安裝數據庫
Memcached是一個專門用來作緩存的數據庫,緩存的數據都是在內存當中,當數據庫重啓以後,數據也就都丟失了。其至關於一個Dictionary鍵值對集合,根據Key值取Value值。緩存
從網上下載Memcached-win64-1.4.4-14.zip安裝包。解壓後,經過管理員權限執行以下命令,可將其安裝成服務:服務器
G:\MemcachedAfterInstall>memcached.exe -d install
二、Memcached可視化工具併發
TreeNMS是一款Redis、Memcached可視化客戶端工具,實現基於Web方式對Redis、Memcached數據庫進行管理、維護。可經過以下連接http://www.treesoft.cn/dms.html進行下載。async
下圖即該工具的使用界面:memcached
3、在.Net Core中的使用工具
一、組件包的安裝與使用優化
在.Net Core項目中調用Memcached數據庫的資源,可使用EnyimMemcachedCore客戶端組件包。
經過該組件向Memcached數據庫存入數據有三種模式,分別以下:
1)Set:存在則覆蓋,不存在則新增;
2)Replace:若是存在則覆蓋,而且返回true;若是不存在則不處理,而且返回false;
3)Add:若是不存在則新增,而且返回true;若是存在則不處理,而且返回false;
故,根據以上可知,若是沒有特殊要求通常用Set便可。
注意:Memcached數據庫中的key的長度最高爲250個字符,value的值最大爲1M。
二、案例演示
爲了演示方便,建立了一個基於.Net Core3.1版本的WPF項目,界面以下:
1)存儲數據、讀取數據
public partial class MainWindow : Window { private readonly MemcachedClient _memcachedClient; private static readonly ILoggerFactory _loggerFactory = new LoggerFactory(); public MainWindow() { InitializeComponent(); var options = new MemcachedClientOptions(); options.AddServer("127.0.0.1", 11211); _memcachedClient = new MemcachedClient(_loggerFactory, new MemcachedClientConfiguration(_loggerFactory, options)); } } private void btnStorage_Click(object sender, RoutedEventArgs e) { var result = _memcachedClient.Store(StoreMode.Set, "supersnow", "yao"); MessageBox.Show($"保存成功?{result}"); } private void btnRead_Click(object sender, RoutedEventArgs e) { var result = _memcachedClient.Get("supersnow"); MessageBox.Show($"讀取結果:{result}"); }
2)存儲類數據、讀取類數據
private async void btnStorage_Class_Click(object sender, RoutedEventArgs e) { var person = new Person { Name = "SuperSnow", Age = 25 }; var result = await _memcachedClient.StoreAsync(StoreMode.Set, "person_key", person, TimeSpan.FromSeconds(3600)); MessageBox.Show($"保存Person成功?{result}"); }
private async void btnRead_Class_Click(object sender, RoutedEventArgs e) { var result = await _memcachedClient.GetAsync<Person>("person_key"); if (result.Value == null) MessageBox.Show("數據已經失效"); else MessageBox.Show($"讀取Person結果:{result.Value.Name}:{result.Value.Age}"); } public class Person { public string Name { get; set; } public int Age { get; set; } }
3)讀取數據異常
對於讀取數據失敗時,返回結果爲false。可是,此時並不知道失敗的緣由是什麼,能夠經過以下方法獲取失敗緣由:
private void btnRead_Exception_Click(object sender, RoutedEventArgs e) { var result = _memcachedClient.ExecuteRemove("abc"); MessageBox.Show($"結果:{result.Success},Message:{result.Message},Exception:{result.Exception},StatusCode:{result.StatusCode},InnerResult:{result.InnerResult.Message}"); }
4)Memcached Cas操做
Cas操做相似於關係型數據庫中的「樂觀鎖」,查詢的時候會順帶查出一個Cas值,在寫入的時候會帶着這個Cas值,若是發現Cas值改變了,則說明已經有其餘操做提早修改了相應的值。本質上來講Cas就是用來解決併發問題,經過讀取一個值,而後作一些處理或判斷,而後再寫回到數據庫中,這種操做有可能產生併發問題。
private CasResult<Person> casResult; private void btnA_Read_Click(object sender, RoutedEventArgs e) { casResult = _memcachedClient.GetWithCas<Person>("person_key"); if (casResult.Result != null) MessageBox.Show($"讀取Person結果:{casResult.Result.Name}:{casResult.Result.Age}"); else MessageBox.Show("沒有結果"); } private void btnB_Save_Click(object sender, RoutedEventArgs e) { var result = _memcachedClient.GetWithCas<Person>("person_key"); if (result.Result.Age >= 25) result.Result.Age++; var tag = _memcachedClient.Cas(StoreMode.Set, "person_key", result.Result, result.Cas); if (tag.Result) MessageBox.Show("修改爲功"); else MessageBox.Show("修改失敗"); } private void btnA_Save_Click(object sender, RoutedEventArgs e) { if (casResult.Result.Age >= 25) casResult.Result.Age++; else casResult.Result.Age++; var tag = _memcachedClient.Cas(StoreMode.Set, "person_key", casResult.Result, casResult.Cas); if (tag.Result) MessageBox.Show("修改爲功"); else MessageBox.Show("修改失敗"); }
5)Memcached集羣
Memcached重啓以後,在短期內大量的請求會涌入數據庫,給數據庫形成巨大壓力,解決此類問題的方法就是使用集羣,即便用多Memcached服務器提供服務。
除此以外,Memcached還有可能面臨「雪崩」問題,若是全部緩存設置過時時間是同樣的,或者失效時間在一個短距離的範圍內。那麼每隔一段時間就會形成一次數據庫訪問的高峯。此類問題的解決方法就是將不一樣緩存的緩存失效時間設置成不同,如對失效時間加上隨機數。
Memcached集羣的節點之間不用進行通信和數據同步,只須要在多個服務器上啓動多個Memcached數據庫便可。客戶端決定了將數據寫入不一樣的Memcached實例,不用作主從複製操做。
節點定位算法有不少種,最經常使用的就是Ketama算法,該算法根據Key算出一個hash值,而後根據hash值再得出服務器。以下例所示:
private readonly MemcachedClient _memcachedClient; private static readonly ILoggerFactory _loggerFactory = new LoggerFactory(); public MainWindow() { InitializeComponent(); var options = new MemcachedClientOptions(); options.AddServer("127.0.0.1", 11211); options.AddServer("127.0.0.1", 11212); options.NodeLocatorFactory = new DefaultNodeLocatorFactory(11211); _memcachedClient = new MemcachedClient(_loggerFactory, new MemcachedClientConfiguration(_loggerFactory, options)); } private void btnA_Save_Cluster_Click(object sender, RoutedEventArgs e) { var person = new Person { Name = "張三", Age = 25 }; var result = _memcachedClient.Store(StoreMode.Set, "1", person); MessageBox.Show($"保存Person成功?{result}"); } private void btnA_Read_Cluster_Click(object sender, RoutedEventArgs e) { var result = _memcachedClient.Get<Person>("1"); if (result == null) MessageBox.Show("數據已經失效"); else MessageBox.Show($"讀取Person結果:{result.Name}:{result.Age}"); } private void btnB_Save_Cluster_Click(object sender, RoutedEventArgs e) { var person = new Person { Name = "李四", Age = 23 }; var result = _memcachedClient.Store(StoreMode.Set, "2", person); MessageBox.Show($"保存Person成功?{result}"); } private void btnB_Read_Cluster_Click(object sender, RoutedEventArgs e) { var result = _memcachedClient.Get<Person>("2"); if (result == null) MessageBox.Show("數據已經失效"); else MessageBox.Show($"讀取Person結果:{result.Name}:{result.Age}"); }
經過以下命令分別啓動兩個Memcached實例:
G:\MemcachedAfterInstall>memcached.exe -p 11212 G:\MemcachedAfterInstall>memcached.exe -p 11211
在TreeNMS系統中配置兩個數據庫鏈接:
因爲Key=1和Key=2的hash值不一樣,因此緩存數據分別存在於兩個Memcached實例中,以下圖所示: