国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

15158846557 在線咨詢 在線咨詢
15158846557 在線咨詢
所在位置: 首頁 > 營銷資訊 > 網(wǎng)站運(yùn)營 > .net 有沒有比較好用的redis 集群解決方案?

.net 有沒有比較好用的redis 集群解決方案?

時間:2023-10-23 13:12:01 | 來源:網(wǎng)站運(yùn)營

時間:2023-10-23 13:12:01 來源:網(wǎng)站運(yùn)營

.net 有沒有比較好用的redis 集群解決方案?:該原文是Ayende Rahien大佬業(yè)余自己在使用C# 和 .NET構(gòu)建一個簡單、高性能兼容Redis協(xié)議的數(shù)據(jù)庫的經(jīng)歷。首先這個"Redis"是非常簡單的實(shí)現(xiàn),但是他在優(yōu)化這個簡單"Redis"路程很有趣,也能給我們在從事性能優(yōu)化工作時帶來一些啟示。

原作者:Ayende Rahien

原鏈接:https://ayende.com/blog/197473-C/high-performance-net-building-a-redis-clone-architecture

構(gòu)建Redis克隆版-架構(gòu)

在之前的文章中,我們嘗試用最簡單的方式來完成一個Redis克隆版。打開一個套接字來監(jiān)聽,為每個客戶端單獨(dú)分配一個Task來從網(wǎng)絡(luò)讀取數(shù)據(jù),解析命名并執(zhí)行它。雖然在流水線上有一些小的改進(jìn),但也只僅此而已。

讓我們退一步來構(gòu)建一個與Redis架構(gòu)更為接近的Redis克隆版。為此,我們需要在一個線程中完成所有工作。這在C#中是比較難實(shí)現(xiàn)的,沒有用于執(zhí)行Redis那樣工作類型的API。更確切的來說是有Socket.Select()方法,但是需要我們自己在此基礎(chǔ)上構(gòu)建一切(比如我們必須寫代碼處理緩沖、字符串等等)。

考慮到這是通往最終建議的架構(gòu)的一個中途站,我決定完全跳過這個。相反,我將首先專注于消除系統(tǒng)中的主要瓶頸,即ConcurrentDictionary。

分析器的結(jié)果表明,我們這最大的開銷就是ConcurrentDictionary的可伸縮性。即使我使用了1024個分片的鎖,它仍然占用50%的時間開銷。問題是,我們能做得更好嗎?我們可以嘗試一個更好的選擇,就是我們不再使用ConcurrentDictionary,而是直接使用單獨(dú)的Dictionary來分片,這樣的話每個Dictionary都不需要并發(fā)就可以訪問。

Vue3+.NET6+C#10,最近優(yōu)質(zhì)前后端分離項目匯總

據(jù)說80%的WEB開發(fā)都是管理后臺,一套開源的優(yōu)秀管理后臺開發(fā)模板堪稱福音!分享一套Vue3+ Axios+ TS+ Vite +Element Plus+ .NET 6 WebAPI+ JWT+ SqlSugar的前后端分離架構(gòu)的通用管理后臺源碼+數(shù)據(jù)庫腳本,還有與之配套錄制的一組視頻教程,全部打包免費(fèi)分享!

核心功能概覽

管理后臺千千萬,核心幾乎沒咋變,權(quán)限模塊、數(shù)據(jù)增刪改查、日志預(yù)警、流程審批、數(shù)據(jù)報表等,剩下的就是填充各種業(yè)務(wù)。本后臺是提供了部分功能的實(shí)現(xiàn),剩下的可以參考實(shí)現(xiàn)了,后續(xù)也會升級。

全套資料預(yù)覽

光有代碼和工具是不夠的,真的要學(xué)會,就一定得動手!這里是全套的視頻課件代碼打包分享的,強(qiáng)烈建議對照視頻教程動手試試!

資料免費(fèi)自?。?/h3>由于內(nèi)容過多不便呈現(xiàn),需要視頻教程和配套源碼的小伙伴,可點(diǎn)擊這里,添加我本站主頁個人說明處號碼 免費(fèi)分享

也可直接點(diǎn)擊下方卡片:點(diǎn)擊后可自動復(fù)制威芯號,并跳轉(zhuǎn)到威芯。得辛苦大家自行搜索威芯號添加。內(nèi)容已做打包,添加后直接發(fā)送注意查收!

興致上來了做了張圖,也是干貨清單,需要的小伙伴直接來領(lǐng)就是了。

包含VS2022安裝包 / C#基礎(chǔ) .NET6/WPF/Winform零基礎(chǔ)到各類實(shí)戰(zhàn)!

免費(fèi)資料免費(fèi)送!完整還附源碼...

有看中的趕緊領(lǐng),真不要錢!
我的想法是這樣的,我們將為客戶端提供常規(guī)的讀寫操作。但是,我們不會直接在I/O上處理這些命令,而是將其路由到一個專用的線程(使用它自己的Dictionary)來完成這項工作。因為我是16核的機(jī)器,我將創(chuàng)建10個這樣的線程(假設(shè)它們每個都能分配到1個核心),并且我能夠?qū)/O處理放到其余的6個核心上。

以下是更改后的結(jié)果:

請注意,我們現(xiàn)在跑分的數(shù)據(jù)是125w/s,比上一次幾乎增長了25%。下面是這一次新代碼的分析器結(jié)果:

因此在本例中,花費(fèi)了大量的時間來處理各種各樣的字符串,等待GC(大約占30%)。集合的成本下降了很多。還有一些其它的開銷出現(xiàn)在我眼前,看看這里:

對于“簡單”屬性查找來說,這個開銷非常驚人。另外SubString函數(shù)的調(diào)用開銷也很大,超過整個系統(tǒng)開銷的6%。在研究系統(tǒng)其它部分時,看到了這個:

這真的很有趣,因為我們花了很多的時間在等待隊列中是否有新的元素,其實(shí)我們可以做更多的事情,而不是就在那干等著。

我還嘗試了其它的線程數(shù)量,如果只運(yùn)行一個ExecWorker,我們的運(yùn)行速度是40w/s,兩個線程,我們的運(yùn)行速度是70w/s。當(dāng)使用4個專用于處理請求的線程時,我們的運(yùn)行速度是106w/s。

因此,很明顯,我們需要重新考慮這種方案,我們不能夠正確地擴(kuò)展到合適的數(shù)值。注意,這種方法也不利用流水線。我們分別處理每個命令和其他命令。我的下一步是添加對使用這種方法的流水線的支持,并測量這種影響。

從另一方面來說,我們現(xiàn)在的性能還是100w/s,考慮到我只花了很少的時間來實(shí)現(xiàn)方案,從這個方案可以獲得25w/s的性能提升,這是令人激動人心的。從側(cè)面說,我們還有更多的事情可以做,但我想把重點(diǎn)放在修復(fù)我們第一個方案上。

下面是當(dāng)前的狀態(tài),因此您可以與原始代碼比較。

using System.Collections.Concurrent;using System.Net.Sockets;using System.Threading.Channels;var listener = new TcpListener(System.Net.IPAddress.Any, 6379);listener.Start();var redisClone = new RedisClone();while (true){ var client = listener.AcceptTcpClient(); var _ = redisClone.HandleConnection(client); // run async}public class RedisClone{ ShardedDictionary _state = new(Environment.ProcessorCount / 2); public async Task HandleConnection(TcpClient tcp) { var _ = tcp; var stream = tcp.GetStream(); var client = new Client { Tcp = tcp, Dic = _state, Reader = new StreamReader(stream), Writer = new StreamWriter(stream) { NewLine = "/r/n" } }; await client.ReadAsync(); }}class Client{ public TcpClient Tcp; public StreamReader Reader; public StreamWriter Writer; public string Key; public string? Value; public ShardedDictionary Dic; List<string> Args = new(); public async Task ReadAsync() { try { Args.Clear(); var lineTask = Reader.ReadLineAsync(); if (lineTask.IsCompleted == false) { await Writer.FlushAsync(); } var line = await lineTask; if (line == null) { using (Tcp) { return; } } if (line[0] != '*') throw new InvalidDataException("Cannot understand arg batch: " + line); var argsv = int.Parse(line.Substring(1)); for (int i = 0; i < argsv; i++) { line = await Reader.ReadLineAsync(); if (line == null || line[0] != '$') throw new InvalidDataException("Cannot understand arg length: " + line); var argLen = int.Parse(line.Substring(1)); line = await Reader.ReadLineAsync(); if (line == null || line.Length != argLen) throw new InvalidDataException("Wrong arg length expected " + argLen + " got: " + line); Args.Add(line); } switch (Args[0]) { case "GET": Key = Args[1]; Value = null; break; case "SET": Key = Args[1]; Value = Args[2]; break; default: throw new ArgumentOutOfRangeException("Unknown command: " + Args[0]); } Dic.Run(this); } catch (Exception e) { await HandleError(e); } } public async Task NextAsync() { try { if (Value == null) { await Writer.WriteLineAsync("$-1"); } else { await Writer.WriteLineAsync($"${Value.Length}/r/n{Value}"); } await ReadAsync(); } catch (Exception e) { await HandleError(e); } } public async Task HandleError(Exception e) { using (Tcp) { try { string? line; var errReader = new StringReader(e.ToString()); while ((line = errReader.ReadLine()) != null) { await Writer.WriteAsync("-"); await Writer.WriteLineAsync(line); } await Writer.FlushAsync(); } catch (Exception) { // nothing we can do } } }}class ShardedDictionary{ Dictionary<string, string>[] _dics; BlockingCollection<Client>[] _workers; public ShardedDictionary(int shardingFactor) { _dics = new Dictionary<string, string>[shardingFactor]; _workers = new BlockingCollection<Client>[shardingFactor]; for (int i = 0; i < shardingFactor; i++) { var dic = new Dictionary<string, string>(); var worker = new BlockingCollection<Client>(); _dics[i] = dic; _workers[i] = worker; // readers new Thread(() => { ExecWorker(dic, worker); }) { IsBackground = true, }.Start(); } } private static void ExecWorker(Dictionary<string, string> dic, BlockingCollection<Client> worker) { while (true) { var client = worker.Take(); if (client.Value != null) { dic[client.Key] = client.Value; client.Value = null; } else { dic.TryGetValue(client.Key, out client.Value); } var _ = client.NextAsync(); } } public void Run(Client c) { var reader = _workers[c.GetHashCode() % _workers.Length]; reader.Add(c); }}學(xué)習(xí)離不開答疑交流,跳槽高薪離不開信息分享,這里給大家推薦個學(xué)習(xí)群,里面有大佬技術(shù)答疑、有直播技術(shù)分享、有資料定期分享、還有高薪內(nèi)推資源,強(qiáng)烈建議大家進(jìn)群一起學(xué)習(xí)交流,來年拿高薪。也歡迎大家加入最活躍的編程技術(shù)交流群 (.NET/WPF )進(jìn)學(xué)習(xí)群,一起學(xué)習(xí)進(jìn)步!

關(guān)鍵詞:解決,方案

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點(diǎn)擊下載Chrome瀏覽器
關(guān)閉