排版素材网站,分级会员管理系统网站开发,网站开发官网,广州网站建设服务电话1. CAP 原则
CAP 原则也称为布鲁尔定理#xff0c;由 Eric Brewer 在 2000 年提出#xff0c;描述了分布式系统中的三个核心属性#xff1a;一致性#xff08;Consistency#xff09;、可用性#xff08;Availability#xff09;、分区容错性#xff08;Partition Tol…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 Serverlocalhost;Databasetestdb;Uidroot;Pwdpassword;;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. 最终方案
结合上述方案在高并发场景下我们倾向于采用先更新数据库再删除缓存的方式。这样可以保证数据的一致性避免缓存中的脏数据同时提升系统性能。以下是方案的流程图。
流程图
上面是基于高并发分布式缓存更新策略的流程图。此方案遵循先更新数据库再删除缓存的逻辑并采用多线程处理。在高并发情况下确保了数据库和缓存的一致性及系统的高可用性。