一、NoSQL簡介 NoSQL並不是No SQL(不再需要SQL),而是指Not Only SQL(不僅僅只有SQL)。NoSQL並不是用來替代關係型資料庫的,而是在某些使用關係型資料庫不合適的場景中,可以使用NoSQL資料庫進行優化,而在系統中主要的、常規的數據仍然使用關係型資料庫。 常用的NoS ...
一、NoSQL簡介
NoSQL並不是No SQL(不再需要SQL),而是指Not Only SQL(不僅僅只有SQL)。NoSQL並不是用來替代關係型資料庫的,而是在某些使用關係型資料庫不合適的場景中,可以使用NoSQL資料庫進行優化,而在系統中主要的、常規的數據仍然使用關係型資料庫。
常用的NoSQL資料庫有Memcached、Redis、MongoDB等,其中前兩者屬於鍵值對資料庫,後者屬於文檔資料庫。它們都有各自的優缺點以及使用場景。
二、Memcached介紹與安裝
Memcached是一個專門用來做緩存的資料庫,緩存的數據都是在記憶體當中,當資料庫重啟之後,數據也就都丟失了。其相當於一個Dictionary鍵值對集合,根據Key值取Value值。
1、Memcached安裝
從網上下載Memcached-win64-1.4.4-14.zip安裝包。解壓後,通過管理員許可權執行如下命令,可將其安裝成服務:
G:\MemcachedAfterInstall>memcached.exe -d install
2、Memcached可視化工具
TreeNMS是一款Redis、Memcached可視化客戶端工具,實現基於Web方式對Redis、Memcached資料庫進行管理、維護。可通過如下鏈接http://www.treesoft.cn/dms.html進行下載。
下圖即該工具的使用界面:
三、在.Net Core中的使用
1、組件包的安裝與使用
在.Net Core項目中調用Memcached資料庫的資源,可以使用EnyimMemcachedCore客戶端組件包。
通過該組件向Memcached資料庫存入數據有三種模式,分別如下:
1)Set:存在則覆蓋,不存在則新增;
2)Replace:如果存在則覆蓋,並且返回true;如果不存在則不處理,並且返回false;
3)Add:如果不存在則新增,並且返回true;如果存在則不處理,並且返回false;
故,根據以上可知,如果沒有特殊要求一般用Set即可。
註意:Memcached資料庫中的key的長度最高為250個字元,value的值最大為1M。
2、案例演示
為了演示方便,創建了一個基於.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實例中,如下圖所示: