C# 实操高并发分布式缓存解决方案

1. CAP 原则

CAP 原则也称为布鲁尔定理,由 Eric Brewer 在 2000 年提出,描述了分布式系统中的三个核心属性:一致性(Consistency)、可用性(Availability)、分区容错性(Partition Tolerance)。CAP 原则指出在分布式系统中,无法同时保证这三个属性,最多只能满足其中的两个。

  • 一致性(Consistency):系统对外表现为单个节点上的数据总是最新的,所有读请求都能获取到最近一次写入的数据。例如,像银行的交易系统,这种系统必须保持严格的一致性。

  • 可用性(Availability):系统能够始终对请求做出响应,即使部分节点故障,系统依然可以继续服务。例如,像亚马逊和谷歌这样的电商和搜索引擎系统,即使部分服务器出现问题,依然能保证大部分用户的访问。

  • 分区容错性(Partition Tolerance):当分布式系统的不同节点之间发生网络分区时,系统能够继续工作,而不发生崩溃或错误。例如,跨地域的分布式数据库系统需要在网络分区的情况下,仍保持系统的高可用性。

实际案例

  • 一致性优先:像银行系统、股票交易系统,这些系统需要确保每次查询的结果都是准确无误的,所以更注重数据一致性。
  • 可用性优先:像电商、视频网站等,在这种场景下,哪怕数据可能不是最新的,也需要保证系统的响应速度和用户体验。
  • 分区容错性优先:跨地域的社交网络、全球范围内的支付系统等,由于其涉及多个地理区域,网络延迟和网络分区问题普遍存在,系统需要具备分区容错性。

2. 实战准备

我们将创建一个 MySQL 数据库和 Redis 缓存,并设计两个操作:

  • 更新数据:数据库和缓存都需要更新。
  • 查询数据:优先从缓存中查询,如果缓存不存在,再从数据库中查询。
  • 删除缓存:当缓存过期时或数据被更新时,需要删除缓存。
示例准备:
// 安装 MySql.Data 和 StackExchange.Redisusing MySql.Data.MySqlClient;
using StackExchange.Redis;
using System;
using System.Threading;class CacheWithDatabase
{private static MySqlConnection dbConnection;private static ConnectionMultiplexer redisConnection;private static IDatabase redisCache;static void Main(string[] args){// 初始化数据库连接string dbConnectionString = "Server=localhost;Database=testdb;Uid=root;Pwd=password;";dbConnection = new MySqlConnection(dbConnectionString);dbConnection.Open();// 初始化 Redis 连接redisConnection = ConnectionMultiplexer.Connect("localhost");redisCache = redisConnection.GetDatabase();// 模拟缓存与数据库的操作UpdateData("key1", "new value");string value = GetData("key1");Console.WriteLine("Retrieved value: " + value);// 删除缓存DeleteCache("key1");}// 更新数据方法static void UpdateData(string key, string newValue){// 更新数据库string query = "UPDATE test_table SET value = @newValue WHERE key = @key";using (MySqlCommand cmd = new MySqlCommand(query, dbConnection)){cmd.Parameters.AddWithValue("@newValue", newValue);cmd.Parameters.AddWithValue("@key", key);cmd.ExecuteNonQuery();}// 更新缓存redisCache.StringSet(key, newValue);Console.WriteLine("Updated cache with key: " + key);}// 查询数据方法static string GetData(string key){// 先查询缓存string cachedValue = redisCache.StringGet(key);if (cachedValue != null){Console.WriteLine("Cache hit: " + key);return cachedValue;}// 如果缓存没有命中,则查询数据库string query = "SELECT value FROM test_table WHERE key = @key";using (MySqlCommand cmd = new MySqlCommand(query, dbConnection)){cmd.Parameters.AddWithValue("@key", key);string dbValue = (string)cmd.ExecuteScalar();if (dbValue != null){redisCache.StringSet(key, dbValue); // 将数据缓存Console.WriteLine("Cache miss. Database hit: " + key);return dbValue;}return null;}}// 删除缓存方法static void DeleteCache(string key){redisCache.KeyDelete(key);Console.WriteLine("Cache deleted for key: " + key);}
}

3. 缓存更新策略分析

缓存更新策略在高并发场景下尤为重要,避免不一致性和性能瓶颈的挑战,常见的缓存更新策略有以下几种:

  • 写回缓存:在写数据时同时更新缓存和数据库。
  • 缓存失效:缓存与数据库更新保持异步,数据变更时仅删除缓存,让后续查询自行更新。
  • 定时刷新:定期刷新缓存的数据。

4. 方案 1 - 先更新缓存,再更新数据库

在此方案中,首先更新缓存,再更新数据库,这种方式可以保证较高的系统性能,因为用户查询时可以快速获得缓存中的最新数据。

多线程示例

static void UpdateDataWithPriority(string key, string newValue)
{Thread cacheThread = new Thread(() => {// 更新缓存redisCache.StringSet(key, newValue);Console.WriteLine("Cache updated with priority for key: " + key);});Thread dbThread = new Thread(() =>{// 更新数据库string query = "UPDATE test_table SET value = @newValue WHERE key = @key";using (MySqlCommand cmd = new MySqlCommand(query, dbConnection)){cmd.Parameters.AddWithValue("@newValue", newValue);cmd.Parameters.AddWithValue("@key", key);cmd.ExecuteNonQuery();}Console.WriteLine("Database updated for key: " + key);});cacheThread.Start();dbThread.Start();cacheThread.Join();dbThread.Join();
}

5. 方案 2 - 先更新数据库,再更新缓存

此策略的优点是保证数据持久化安全性,先将数据存入数据库,减少丢失数据的风险。

static void UpdateDataAfterDB(string key, string newValue)
{Thread dbThread = new Thread(() =>{// 更新数据库string query = "UPDATE test_table SET value = @newValue WHERE key = @key";using (MySqlCommand cmd = new MySqlCommand(query, dbConnection)){cmd.Parameters.AddWithValue("@newValue", newValue);cmd.Parameters.AddWithValue("@key", key);cmd.ExecuteNonQuery();}Console.WriteLine("Database updated for key: " + key);});Thread cacheThread = new Thread(() => {// 更新缓存redisCache.StringSet(key, newValue);Console.WriteLine("Cache updated after database update for key: " + key);});dbThread.Start();dbThread.Join();  // 确保数据库先更新cacheThread.Start();
}

6. 方案 3 - 先删除缓存,再更新数据库

static void UpdateAfterCacheDeletion(string key, string newValue)
{Thread cacheDeletionThread = new Thread(() => {// 删除缓存redisCache.KeyDelete(key);Console.WriteLine("Cache deleted for key: " + key);});Thread dbUpdateThread = new Thread(() =>{// 更新数据库string query = "UPDATE test_table SET value = @newValue WHERE key = @key";using (MySqlCommand cmd = new MySqlCommand(query, dbConnection)){cmd.Parameters.AddWithValue("@newValue", newValue);cmd.Parameters.AddWithValue("@key", key);cmd.ExecuteNonQuery();}Console.WriteLine("Database updated for key: " + key);});cacheDeletionThread.Start();dbUpdateThread.Start();cacheDeletionThread.Join();dbUpdateThread.Join();
}

7. 方案 4 - 先更新数据库,再删除缓存

static void UpdateAfterDBThenDeleteCache(string key, string newValue)
{Thread dbUpdateThread = new Thread(() =>{// 更新数据库string query = "UPDATE test_table SET value = @newValue WHERE key = @key";using (MySqlCommand cmd = new MySqlCommand(query, dbConnection)){cmd.Parameters.AddWithValue("@newValue", newValue);cmd.Parameters.AddWithValue("@key", key);cmd.ExecuteNonQuery();}Console.WriteLine("Database updated for key: " + key);});Thread cacheDeletionThread = new Thread(() => {// 删除缓存redisCache.KeyDelete(key);Console.WriteLine("Cache deleted for key: " + key);});dbUpdateThread.Start();dbUpdateThread.Join();  // 确保数据库更新后再删除缓存cacheDeletionThread.Start();
}

8. 最终方案

结合上述方案,在高并发场景下,我们倾向于采用先更新数据库,再删除缓存的方式。这样可以保证数据的一致性,避免缓存中的脏数据,同时提升系统性能。以下是方案的流程图。

流程图
在这里插入图片描述

上面是基于高并发分布式缓存更新策略的流程图。此方案遵循先更新数据库,再删除缓存的逻辑,并采用多线程处理。在高并发情况下,确保了数据库和缓存的一致性及系统的高可用性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/882296.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

OpenCV高级图形用户界面(15)注册一个回调函数来处理鼠标事件的函数setMouseCallback()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 为指定的窗口设置鼠标处理器。 setMouseCallback 是 OpenCV 中的一个功能,允许开发者注册一个回调函数来处理鼠标事件。当用户在窗口…

ORACLE 19C安装 RAC报错

1. 问题描述 在Oracle 19C RAC的安装过程中,使用克隆方式在两个节点上部署集群。当第一个节点配置好基础服务后,关机并克隆节点。当尝试在第二个节点上通过页面进行RAC安装时,出现以下错误: [INS-32070] Could not remove the n…

React01 开发环境搭建

React 开发环境搭建 一、创建 React 项目二、项目精简 一、创建 React 项目 执行下述命令创建 react 项目 blu-react-basis npx create-react-app blu-react-basis项目目录结构如下: 执行下述命令启动项目 npm run start启动效果如下: 二、项目精简 …

Vue3:同一项目同一浏览器只允许打开一个标签页

说明: 阻止同一浏览器打开多个项目标签页,防止多标签页重复时间统计累加,适用于基于微信公众号页面或指定浏览器的计时统计等项目活动,计时在线学习时间统计等。 效果: main.js import {createApp} from vue import…

【Linux系统编程】第三十四弹---使用匿名管道构建简易Linux进程池

✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、引言 2、进程池的基本概念 3、管道在进程池中的应用 4、进程池的实现 4.1、master类定义 4.2、测试信道 4.3、通过cha…

OpenCV高级图形用户界面(12)用于更改指定窗口的大小函数resizeWindow()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 cv::resizeWindow() 函数用于更改指定窗口的大小。这使得你可以根据需要调整窗口的宽度和高度。 注释 指定的窗口大小是指图像区域的大小。工具栏…

Linux 手撕线程池

前言 线程池 是 池化技术 中很典型的一个,它旨在高效的管理和复用线程资源!在现在的计算机体系中,线程是执行任务(调度)的基本单位。然而,频繁的创建和销毁线程也会带来较大的开销,包括系统资源…

区块链开发入门: 原理、技术与实践

随着区块链技术的迅猛发展,它不仅改变了金融领域,还对供应链、医疗、身份认证等多个行业产生了深远影响。对于想要进入区块链开发领域的初学者而言,了解区块链的基本原理、相关技术以及实际应用场景至关重要。本文将为您提供一份全面的区块链…

网络变压器在PCIe网口应用的案例

PCIe(Peripheral Component Interconnect Express)是一种高速串行计算机总线标准,用于连接计算机主板上的设备,如显卡、网络适配器、存储控制器等。H82422S 网络变压器(Ethernet Transformer),在…

[Git]一文速通

概述 Git是一个分布式版本控制工具,主要用于管理开发过程中的源代码文件(Java类、xml文件、html页面等, )在软件开发过程中被广泛使用 Git的作用 代码回溯版本切换多人协作远程备份 通过Git 仓库来存储和管理代码 文件,Git 仓库分为两种: 本地仓库: 开…

循序渐进丨在 MogDB 数据库中实现 Oracle ASH能力

我们都知道,当 Oracle 数据库出现性能故障后,一般会在线上实时诊断数据库性能问题,特别是资源突然打高的场景,这个时候用到ASH的数据,就能很大程度上准确定位问题所在。 Oracle ASH 在 Oracle 数据库中,实…

基于微博评论的自然语言处理情感分析

目录 一、项目概述 二、需要解决的问题 三、数据预处理 1、词汇表构建(vocab_creat.py) 2、数据集加载(load_dataset.py) 四、模型构建(TextRNN.py) 1、嵌入层(Embedding Layer&#xff…

【学术会议论文投稿】ECMAScript标准:塑造现代Web开发的基石

https://www.ais.cn/attendees/index/MVNV3U 更多学术会议论文投稿请看:https://ais.cn/u/nuyAF3 目录 引言 ECMAScript的历史背景与版本更新 ECMAScript的核心特性 1. 变量和数据类型 2. 变量声明 3. 运算符 4. 控制流语句 5. 函数 6. 错误处理 7. 模板…

github学生认证(Github Copilot)

今天想配置一下Github Copilot,认证学生可以免费使用一年,认证过程中因为各种原因折腾了好久,记录一下解决方法供大家参考。 p.s.本文章只针对Github学生认证部分遇到的问题及解决方法,不包括配置copilot的全部流程~ 1、准备工作…

构建后端为etcd的CoreDNS的容器集群(六)、编写自动维护域名记录的代码脚本

本文为系列测试文章,拟基于自签名证书认证的etcd容器来构建coredns域名解析系统。 一、前置文章 构建后端为etcd的CoreDNS的容器集群(一)、生成自签名证书 构建后端为etcd的CoreDNS的容器集群(二)、下载最新的etcd容…

Github 2024-10-19 Rust开源项目日报 Top10

根据Github Trendings的统计,今日(2024-10-19统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Rust项目10Python项目2Dart项目1TypeScript项目1Vue项目1CUE项目1RustDesk: 用Rust编写的开源远程桌面软件 创建周期:1218 天开发语言:Rust, …

敏捷Scrum项目管理方法,如何做好敏捷项目管理❓

在当今快速变化的商业环境中,项目管理方法的选择对于项目的成功至关重要。敏捷Scrum作为一种轻量级、迭代式的项目管理方法,因其灵活性和高效性而备受推崇。作为项目经理,掌握敏捷Scrum项目管理方法不仅有助于提升项目交付效率,还…

2024年软件设计师中级(软考中级)详细笔记【7】面向对象技术(上)(分值10+)

目录 前言第7章 面向对象技术 (上)7.1 面向对象基础(3-4分)7.1.1 面向对象的基本概念7.1.2 面向对象分析(熟记)7.1.3 面向对象设计7.1.4 面向对象程序设计7.1.5 面向对象测试 7.2 UML(3~4分)7.2.1 事务7.2.2 关系7.2.2…

Qt 支持打包成安卓

1. 打开维护Qt,双击MaintenanceTool.exe 2.登陆进去,默认是添加或移除组件,点击下一步, 勾选Android, 点击下一步 3.更新安装中 4.进度100%,完成安装,重启。 5.打开 Qt Creator,编辑-》Preferences... 6.进…

Windows下配置Nginx和PHP

之前在Windows开发php项目用的是phpstudy,好用的很。但是phpstudy好久没有更新了,感觉PHP像没有人再用了一样。但是PHP拿来开发小系统,还是很高效的,今天记录如何在Windows环境下配置Nginx和PHP。 1. 配置nginx Nginx软件下载解压…