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

1. CAP 原则

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

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

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

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

实际案例

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

2. 实战准备

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

  • 更新数据:数据库和缓存都需要更新。
  • 查询数据:优先从缓存中查询,如果缓存不存在,再从数据库中查询。
  • 删除缓存:当缓存过期时或数据被更新时,需要删除缓存。
示例准备:
// 安装 MySql.Data 和 StackExchange.Redis

using 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);
    });

    dbUpdate

Thread.Start();
    dbUpdateThread.Join();  // 确保数据库更新后再删除缓存
    cacheDeletionThread.Start();
}

8. 最终方案

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】Linux常见指令及权限理解

1.ls指令 语法 : ls [ 选项 ][ 目录或文件 ] 功能 :对于目录,该命令列出该目录下的所有子目录与文件。对于文件,将列出文件名以及其他信息。 常用选项: -a 列出目录下的所有文件,包括以 . 开头的隐含文…

Golang | Leetcode Golang题解之第492题构造矩形

题目: 题解: func constructRectangle(area int) []int {w : int(math.Sqrt(float64(area)))for area%w > 0 {w--}return []int{area / w, w} }

鸿蒙网络编程系列21-使用HttpRequest上传任意文件到服务端示例

1. 前述文件上传功能简介 在前述文章鸿蒙网络编程系列11-使用HttpRequest上传文件到服务端示例中,为简化起见,只描述了如何上传文本类型的文件到服务端,对文件的大小也有一定的限制,只能作为鸿蒙API演示使用,在实际开…

深度学习-24-基于keras的十大经典算法之残差网络ResNet

文章目录 1 残差网络(ResNet)1.1 ResNet简介1.2 ResNet结构2 模型应用2.1 加载数据2.2 构建模型SimpleResNet2.2.1 simple_resnet_block2.2.2 SimpleResNet2.2.3 实例化模型2.2.4 模型训练2.2.5 模型预测2.3 构建模型ResNet182.3.1 residual_block2.3.2 ResNet182.3.3 训练模型…

无人机之三维航迹规划篇

一、基本原理 飞行环境建模:在三维航迹规划中,首先需要对飞行环境进行建模。这包括对地形、障碍物、气象等因素进行准确的测量和分析,以获得可行的飞行路径。 飞行任务需求分析:根据无人机的任务需求,确定航迹规划的…

【进阶OpenCV】 (18)-- Dlib库 --人脸关键点定位

文章目录 人脸关键点定位一、作用二、原理三、代码实现1. 构造人脸检测器2. 载入模型(加载预测器)3. 获取关键点4. 显示图像5. 完整代码 总结 人脸关键点定位 在dlib库中,有shape_predictor_68_face_landmarks.dat预测器,这是一个…

从算盘到云计算:计算机发展的壮丽历程

早期的计算器 在计算机发展史上,早期的计算器起到了重要的作用。而其中最早的计算器便是算盘。算盘是古代中国人使用的一种计算工具,它由一根木棍和一些珠子组成。通过将珠子在木棍上移动,人们可以进行简单的加减乘除运算。虽然算盘的计算速…

[week1]Newstar Simple_encryption

找到源代码和加密后密文 a [0x47, 0x95, 0x34, 0x48, 0xA4, 0x1C, 0x35, 0x88, 0x64, 0x16, 0x88, 0x07, 0x14, 0x6A, 0x39, 0x12, 0xA2, 0x0A, 0x37, 0x5C, 0x07, 0x5A, 0x56, 0x60, 0x12, 0x76, 0x25, 0x12, 0x8E, 0x28] # 进行处理 for j in range(len(a)): …

【可答疑】基于51单片机的智能衣柜(含仿真、代码、报告、演示视频等)

✨哈喽大家好,这里是每天一杯冰美式oh,985电子本硕,大厂嵌入式在职0.3年,业余时间做做单片机小项目,有需要也可以提供就业指导(免费)~ 🐱‍🐉这是51单片机毕业设计100篇…

python基于图片内容识别的微信自动发送信息(对其中逻辑修改一些可以改为自动化回复)

1.内容基于python日常生活问题帮助 2.主要框架 import time from datetime import datetimeimport pyperclip import win32api import win32con import os import refrom Image_Content_Text_Recognition import ICTR from screenshot import img 上面是逻辑部分主要框架 i…

Axure重要元件二——内联框架

亲爱的小伙伴,在您浏览之前,烦请关注一下,在此深表感谢! 课程主题:内联框架 课程内容:认识内联框架、基本嵌入 应用场景:表单、图片、文字嵌入式场景、交互应用 一、认识内联框架 内联框架的…

开源两个月,antflow后端项目全网获近100星

从六月初开源,转眼间AntFlow已经开源将近四个月了(前端比后端早了大约2个月,后端于8.18开源).(其实准备是重构以前开源版本.前年的时候我们已经将Vue2版的流程设计器开源了.后来由于疫情原因,没有再继续持续开发.)后来有一天再打开仓库的时候,发现虽然很久没有更新了,但是不断有…

如何在算家云搭建Video-Infinity(视频生成)

一、模型介绍 Video-Infinity是一个先进的视频生成模型,使用多个 GPU 快速生成长视频,无需额外训练。它能够基于用户提供的文本或图片提示,创造出高质量、多样化的视频内容。 二、模型搭建流程 1.大模型 Video-Infinity 一键使用 基础环境…

应用商店上新:MainConcept Transcoder和Live Streaming Software App

在Akamai云计算平台上运行工作负载的你也许还不知道,为了帮助用户更容易地找到并快速部署各类解决方案,Akamai提供了一个丰富的应用商店(Marketplace),其中包含各类经过验证,可以在Akamai云计算平台上轻松部…

MoeCTF 2024 ---Misc方向WP

安全杂项 signin 题目描述: xdsec的小伙伴们和参赛者来上课,碰巧这一天签到系统坏了,作为老师的你,要帮他们 教师代签。 特殊提醒:luo同学今天好像在宿舍打游戏,不想来上课,这是严重的缺勤行为…

react18中如何监听localstorage的变化获取最新的本地缓存

有时候业务中会需要监听缓存的变化,实时更新页面的内容获取发送接口请求。这就要我们来监听对localstorage的修改,实时响应变化!!一下方法同样实用于vue项目。 同一个项目中不同页面的实现 实现效果 代码分析 修改localstoare的…

「漏洞复现」英飞达医学影像存档与通信系统 WebUserLogin.asmx 信息泄露漏洞

0x01 免责声明 请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,作者不为此承担任何责任。工具来自网络,安全性自测,如有侵权请联系删…

Redis中String类型常见的应用场景

目录 一. 缓存功能什么是缓存?Redis的工作原理热点数据的过期策略是什么? 二. 计数功能三. 会话(session)共享Session会话是用来解决什么问题的使用Redis集中管理Session 一. 缓存功能 什么是缓存? 缓存是一种用于存储数据的计算机硬件或软件组件. 缓存核心功能是加快数据…

Android上的AES加密

基础算法说明 https://www.youtube.com/watch?vlnKPoWZnNNM 虽然这个视频讲的非常详细,但是涉及到具体底层算法,大致流程 1. 将数据转成HEX或者byte array 2.将数据分层一块块等大小的数据 3.将数据和key 进行一次混合,加密之后的输出&…

AI让企业知识管理与设计产出更智序

AI与各行各业的融合程度正在不断加深。 从医疗健康到金融服务,从教育到制造业,从零售到物流,AI的应用正在推动这些行业的转型升级,提高效率,降低成本,创造新的增长点。一家专注于显示制造的企业就遇到了这…