如何在ASP.NET Web API中实现MySQL数据缓存?求类似SQLChangeMonitor的方案
在.NET Web API中实现MySQL数据缓存(类似SQLChangeMonitor的运行时缓存方案)
当然可以实现!不过得先说明:SqlChangeMonitor是微软专门给SQL Server做的缓存依赖组件,MySQL并没有原生对应的实现,但我们完全可以通过几种方式模拟出类似的数据库变更触发缓存更新的效果,完美适配.NET Web API的场景。
方案一:触发器+自定义缓存监控(最易上手)
这个思路是通过MySQL触发器记录数据变更,然后在.NET端定期检查变更日志,触发缓存失效。
步骤分解:
- 第一步:在MySQL中创建一张变更日志表,用来记录需要监控的表的变更情况
- 第二步:为目标业务表(比如
Products)创建INSERT/UPDATE/DELETE触发器,数据变更时自动写入日志表 - 第三步:在.NET Web API中实现后台监控逻辑,定期查询日志表,发现变更就清空对应缓存项
MySQL触发器示例(简化版):
-- 创建变更日志表 CREATE TABLE CacheInvalidationLog ( Id INT AUTO_INCREMENT PRIMARY KEY, TableName VARCHAR(100) NOT NULL, ChangeTime DATETIME DEFAULT CURRENT_TIMESTAMP, OperationType VARCHAR(10) NOT NULL ); -- 为Products表创建更新触发器 DELIMITER // CREATE TRIGGER trg_Products_AfterUpdate AFTER UPDATE ON Products FOR EACH ROW BEGIN INSERT INTO CacheInvalidationLog (TableName, OperationType) VALUES ('Products', 'UPDATE'); END // DELIMITER ; -- 同理可以创建INSERT和DELETE触发器
.NET端缓存监控代码(用IMemoryCache为例):
public class MySqlCacheMonitor { private readonly IMemoryCache _cache; private readonly IDbConnection _dbConnection; private readonly Dictionary<string, DateTime> _lastChangeTrackers = new(); public MySqlCacheMonitor(IMemoryCache cache, IDbConnection dbConnection) { _cache = cache; _dbConnection = dbConnection; // 初始化各表的最后变更时间 InitializeChangeTrackers(); // 启动后台定时检查任务 _ = StartChangeMonitoringAsync(); } private void InitializeChangeTrackers() { var monitoredTables = new[] { "Products", "Categories" }; foreach (var table in monitoredTables) { var lastChange = _dbConnection.QueryFirstOrDefault<DateTime>( "SELECT COALESCE(MAX(ChangeTime), '1900-01-01') FROM CacheInvalidationLog WHERE TableName = @TableName", new { TableName = table }); _lastChangeTrackers[table] = lastChange; } } private async Task StartChangeMonitoringAsync() { // 每10秒检查一次,可根据业务调整间隔 var checkInterval = TimeSpan.FromSeconds(10); while (true) { await Task.Delay(checkInterval); foreach (var table in _lastChangeTrackers.Keys) { var latestChange = await _dbConnection.QueryFirstOrDefaultAsync<DateTime>( "SELECT MAX(ChangeTime) FROM CacheInvalidationLog WHERE TableName = @TableName", new { TableName = table }); if (latestChange > _lastChangeTrackers[table]) { // 按表名前缀清空缓存,比如"Products_*" _cache.Remove($"Products_All"); _lastChangeTrackers[table] = latestChange; Console.WriteLine($"缓存项Products_All已因表变更失效"); } } } } }
注册到API中:
在Program.cs里添加:
// 注册数据库连接(这里用Dapper为例,也可以用EF Core) builder.Services.AddScoped<IDbConnection>(sp => new MySqlConnection(builder.Configuration.GetConnectionString("MySql"))); // 注册缓存监控器 builder.Services.AddScoped<MySqlCacheMonitor>(); // 程序启动时初始化监控器 var app = builder.Build(); using var scope = app.Services.CreateScope(); scope.ServiceProvider.GetRequiredService<MySqlCacheMonitor>();
方案二:监听MySQL Binlog(实时性更高)
如果你的业务对缓存失效的实时性要求很高,轮询触发器日志不够快,那可以直接监听MySQL的Binlog(二进制日志),它记录了所有数据库的变更操作,能实时捕获数据变化。
可以用第三方库MySqlConnector来实现Binlog监听,示例思路:
// 安装NuGet包:MySqlConnector public async Task StartBinlogMonitoringAsync(IMemoryCache cache, string connectionString) { using var connection = new MySqlConnection(connectionString); await connection.OpenAsync(); // 开启Binlog读取 var binlogReader = new MySqlBinlogReader(connection); await binlogReader.OpenAsync(); while (await binlogReader.ReadEventAsync()) { var binlogEvent = binlogReader.Event; // 捕获Products表的变更事件 if (binlogEvent is WriteRowsEvent writeEvent && writeEvent.Table.TableName == "Products") { cache.Remove("Products_All"); Console.WriteLine("检测到Products表插入操作,缓存已失效"); } else if (binlogEvent is UpdateRowsEvent updateEvent && updateEvent.Table.TableName == "Products") { cache.Remove("Products_All"); Console.WriteLine("检测到Products表更新操作,缓存已失效"); } else if (binlogEvent is DeleteRowsEvent deleteEvent && deleteEvent.Table.TableName == "Products") { cache.Remove("Products_All"); Console.WriteLine("检测到Products表删除操作,缓存已失效"); } } }
注意:使用Binlog需要确保MySQL开启了Binlog功能,并且连接账号有足够的权限(比如REPLICATION SLAVE权限)。
方案三:借助第三方缓存库
有些通用缓存库已经支持自定义依赖扩展,比如CacheManager,你可以基于它封装MySQL的变更监控逻辑,不用自己从零写轮询或Binlog监听代码。
示例代码(简化版):
// 安装NuGet包:CacheManager.Core、CacheManager.Microsoft.Extensions.Caching.Memory var cache = CacheFactory.Build("myCache", settings => { settings.WithMicrosoftMemoryCacheHandle() .WithExpiration(ExpirationMode.Sliding, TimeSpan.FromMinutes(30)) .AddCacheItemRemovedListener((sender, args) => { Console.WriteLine($"缓存项[{args.Key}]已被移除"); }); }); // 结合之前的触发器监控逻辑,当检测到数据库变更时调用 // cache.Remove("Products_All");
关键注意事项
- 缓存键设计:建议按业务维度命名(比如
Products_{Id}、Products_All),避免全量清空缓存,提升性能 - 并发与分布式场景:如果是分布式API集群,建议用Redis等分布式缓存,同时把变更监控逻辑放到单独的后台服务中,避免每个实例都轮询数据库
- 性能平衡:轮询间隔不要设置过短,否则会增加数据库压力;实时性要求高的场景优先选Binlog监听
内容的提问来源于stack exchange,提问作者Sujatha
相关产品推荐
相关产品推荐

