SQL Server 负责存储核心业务数据(保证持久性和复杂查询能力),Redis 负责缓存高频访问数据(提升读取性能)或处理特定场景(如计数器、排行榜)。

一、核心结合模式:缓存加速查询

这是最常见的场景:用 Redis 缓存 SQL Server 中查询频繁但修改较少的数据(如商品信息、用户基本信息),减少 SQL Server 的查询压力。

实现流程:

  1. 查询数据时:先查 Redis → 命中则直接返回;未命中则查 SQL Server,同时将结果存入 Redis。
  2. 更新数据时:先更新 SQL Server → 再更新或删除 Redis 中的缓存(保证数据一致性)。

代码示例(.NET):

// 假设这是操作 SQL Server 的仓储类
public class SqlUserRepository
{
    private readonly DbContext _dbContext; // EF Core 上下文

    public async Task<User?> GetByIdAsync(int id)
    {
        return await _dbContext.Users.FindAsync(id);
    }

    public async Task UpdateAsync(User user)
    {
        _dbContext.Users.Update(user);
        await _dbContext.SaveChangesAsync();
    }
}

// 结合 Redis 的业务服务
public class UserService
{
    private readonly SqlUserRepository _sqlRepo; // SQL Server 操作
    private readonly ICacheRepository _redisCache; // Redis 缓存(封装后的接口)

    public UserService(SqlUserRepository sqlRepo, ICacheRepository redisCache)
    {
        _sqlRepo = sqlRepo;
        _redisCache = redisCache;
    }

    // 1. 查询用户(先查缓存,再查数据库)
    public async Task<User?> GetUserAsync(int userId)
    {
        var cacheKey = $"user:{userId}";
        
        // 步骤1:查 Redis 缓存
        var cachedUser = await _redisCache.GetAsync<User>(cacheKey);
        if (cachedUser != null)
        {
            Console.WriteLine("从 Redis 缓存获取数据");
            return cachedUser;
        }

        // 步骤2:缓存未命中,查 SQL Server
        var user = await _sqlRepo.GetByIdAsync(userId);
        if (user != null)
        {
            // 步骤3:将数据库结果存入 Redis(设置1小时过期)
            await _redisCache.SetAsync(cacheKey, user, TimeSpan.FromHours(1));
            Console.WriteLine("从 SQL Server 获取数据,并更新到 Redis");
        }

        return user;
    }

    // 2. 更新用户(先更数据库,再更缓存)
    public async Task UpdateUserAsync(User user)
    {
        // 步骤1:先更新 SQL Server
        await _sqlRepo.UpdateAsync(user);

        // 步骤2:更新或删除 Redis 缓存(避免缓存与数据库不一致)
        var cacheKey = $"user:{user.Id}";
        await _redisCache.DeleteAsync(cacheKey); // 简单处理:直接删除旧缓存
        // 或更优:更新缓存为新值
        // await _redisCache.SetAsync(cacheKey, user, TimeSpan.FromHours(1));
    }
}

其他结合场景

1. 计数器场景(Redis 擅长,减轻 SQL 压力)

例如文章阅读量:用 Redis 的 INCR 实时累加,定期同步到 SQL Server 持久化。

csharp

// 1. 每次访问文章,用 Redis 计数(高效,支持高并发)
public async Task IncreaseReadCountAsync(int articleId)
{
    var countKey = $"article:read:{articleId}";
    await _redisCache.StringIncrementAsync(countKey); // Redis 原子自增
}

// 2. 定时任务(如每小时)将 Redis 计数同步到 SQL Server
public async Task SyncReadCountToSqlAsync()
{
    // 从 Redis 批量获取计数
    var articleIds = new List<int> { 1001, 1002, 1003 }; // 实际可通过扫描获取所有键
    foreach (var id in articleIds)
    {
        var countKey = $"article:read:{id}";
        var count = await _redisCache.StringGetAsync<long>(countKey);
        if (count > 0)
        {
            // 更新到 SQL Server(如累加至 Article 表的 ReadCount 字段)
            await _sqlRepo.IncreaseReadCountAsync(id, count);
            // 重置 Redis 计数(或保留用于下一次累加)
            await _redisCache.DeleteAsync(countKey);
        }
    }
}

2. 会话存储(替代 SQL Server 的 Session 表)

传统 ASP.NET 会话可能存在 SQL Server 中,高并发时性能差。可改用 Redis 存储会话:

  1. 安装 NuGet 包:Microsoft.AspNetCore.Session + Microsoft.Extensions.Caching.StackExchangeRedis
  2. 配置 Startup(.NET 6+ 为 Program.cs):
builder.Services.AddSession(options =>
{
    options.IdleTimeout = TimeSpan.FromMinutes(30);
});
// 使用 Redis 存储会话
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "localhost:6379";
    options.InstanceName = "session:"; // 会话键前缀
});

// 中间件启用会话
app.UseSession();
  1. 使用方式不变,但会话数据实际存在 Redis 中,性能远高于 SQL Server。

3. 热点数据临时存储(Redis 抗高并发)

例如秒杀活动的库存:用 Redis 的 DECR 预减库存(防止超卖),最终库存变更同步到 SQL Server。

// 1. 秒杀时先查 Redis 库存(高并发下性能好)
public async Task<bool> SeckillAsync(int productId)
{
    var stockKey = $"seckill:stock:{productId}";
    
    // 预减库存(原子操作,防止超卖)
    var remaining = await _redisCache.StringDecrementAsync(stockKey);
    if (remaining < 0)
    {
        // 库存不足,恢复计数
        await _redisCache.StringIncrementAsync(stockKey);
        return false;
    }

    // 2. 库存充足,记录订单(异步同步到 SQL Server)
    await _sqlRepo.CreateOrderAsync(productId);
    return true;
}

// 初始化:活动开始前从 SQL Server 加载库存到 Redis
public async Task InitSeckillStockAsync(int productId)
{
    var stock = await _sqlRepo.GetProductStockAsync(productId);
    await _redisCache.StringSetAsync($"seckill:stock:{productId}", stock);
}

三、数据一致性保障

SQL Server 与 Redis 结合的核心挑战是数据一致性,需根据业务场景选择策略:

  1. Cache-Aside 模式(最常用):

    • 读:先查缓存 → 未命中查数据库并更新缓存
    • 写:先更数据库 → 再删缓存(避免旧缓存残留)
  2. 过期时间兜底
    给 Redis 缓存设置合理的过期时间(如 1 小时),即使缓存与数据库短暂不一致,过期后也会自动更新。

  3. 分布式锁(强一致性场景):
    对并发写场景(如库存),用 Redis 分布式锁保证同一时间只有一个线程更新数据。

四、总结

SQL Server 与 Redis 结合的核心思路是:

  • SQL Server 做 “存储”:存核心数据、支持复杂查询(JOIN、事务等)。
  • Redis 做 “加速” 和 “扩展”:缓存高频读数据、处理高并发计数 / 排序场景、存储临时会话等。

通过这种分工,既能保证数据的持久性和一致性(依赖 SQL Server),又能提升系统的读写性能和并发能力(依赖 Redis),是互联网项目的经典架构模式。

二、其他结合场景

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐