读取数据库数据填充到缓存的问题,及修复方案一则

业务简述:

为了提高站点性能,部署了一台Redis,把资源从SqlServer数据库中同步到Redis,站点由原来的读取数据库,变更为读取Redis,以利用Redis的高并发提升站点性能目的。

环境说明:
1、假设表名为softs, 记录的更新时间字段名为 updateTime;
2、不考虑数据库的DELETE操作,只考虑INSERT和UPDATE操作;
3、流程中所有时间,都以数据库时间为准,以避免不同服务器之间的时间误差,导致数据遗漏。
4、重要的前提条件:数据库执行INSERT和UPDATE操作时,把记录的updateTime字段更新为SqlServer的当前时间GETDATE()

旧的业务流程:
1、数据同步程序第一次启动时:
    a、读取数据库当前时间GETDATE(),作为下一次增量更新的起始时间变量:LastUpdateTime;
    b、全量填充:读取SqlServer的全部数据,填充到Redis中。
2、全量填充完成后,通过DataReader获取增量数据,填充到Redis中:
3、保存这一批增量数据中,最大的那个updateTime,作为下一次增量数据的最小时间:LastUpdateTime;
4、休眠一分钟,重复执行步骤2
流程代码如下:

DateTime lastUpdateTime = DateTime.MinValue;
while(true)
{
    string sql = "SELECT * FROM softs with(nolock) WHERE updateTime >= @LastUpdateTime ORDER BY updateTime";
    var sqlPara = new SqlParaMeter("@LastUpdateTime", SqlDataType.DateTime){Value = lastUpdateTime};
    using(var reader = SqlHelper.ExecuteReader(sql, connectionString, sqlPara)
    {
        while(reader.Read())
        {
            DateTime dt = Convert.ToDateTime(reader["updateTime"]);
            if(dt > lastUpdateTime)
                lastUpdateTime = dt;
            // 获取数据,new数据实体,并Protobuf序列化后,填充到Redis
        }
    }
    Thread.Sleep(60000);
}

存在的问题:
DataReader始终读取到数据库的实时数据,即 sql执行前,时间为1;sql执行后,数据读取前,把时间更新为2;数据读取后,读取到的时间是2,而不会是1;
假设这种情况:
1、LastUpdateTime为:2014-7-7 13:54:12,SQL读取到10条记录,这10条记录中,updateTime最大值为:2014-7-7 13:54:14
2、程序循环读取并处理这10条记录,当读取到第3条记录,并处理时;
3、数据库更新了第1条记录和第10条记录,并把这2条记录的updateTime更新为:2014-7-7 13:54:16
4、程序继续循环处理完成,此时得到的下一次待处理的LastUpdateTime为:2014-7-7 13:54:16
问题出现了:第3步更新了2条记录,第10条记录成功处理了,第1条记录的修改,没有映射到Redis,导致脏数据的产生

新的业务流程:
1、等同于旧流程;
2、全量填充完成后,读取数据库当前时间,作为下一次增量数据的最小时间:LastUpdateTime;
3、通过DataReader获取增量数据,填充到Redis中;
4、休眠一分钟,重复执行步骤2
注:重点是把执行SQL前的数据库时间,当成下一次的增量起始时间,缺点是会有一定的数据重复填充,现在Redis压力不大,可以接受,如果希望忽略重复填充,可以在代码里做去重处理。
代码如下:

DateTime lastUpdateTime;
while(true)
{
    DateTime nextUpdateTime = Convert.ToDateTime(SqlHelper.ExecuteScalar("SELECT GETDATE()", connectionString));
    string sql = "SELECT * FROM softs with(nolock) WHERE updateTime >= @LastUpdateTime ORDER BY updateTime";
    var sqlPara = new SqlParaMeter("@LastUpdateTime", SqlDataType.DateTime){Value = lastUpdateTime};
    using(var reader = SqlHelper.ExecuteReader(sql, connectionString, sqlPara)
    {
        while(reader.Read())
        {
            // 获取数据,new数据实体,并Protobuf序列化后,填充到Redis
        }
    }
    lastUpdateTime = nextUpdateTime;
    Thread.Sleep(60000);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

游北亮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值