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

业务简述:

为了提高站点性能,部署了一台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);
}


读取数据库数据填充到缓存的问题,及修复方案一则,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。