一:背景

1. 讲故事

记的在上一家公司做全内存项目的时候,因为一些关键表会在程序 startup 的时候全量灌入到内存中,但随着时间的推移,内存和数据库的同步偶尔会出现数据差异的情况,伴随着就是运营那边报过来的 bug,检查数据库的数据完整性很简单,直接写一些 sql 验证一下就好了,但校验内存中的数据就非常麻烦了,因为你不能像写 sql 一样直接去查生产中的内存集合,那怎么办呢? 为了方便演示问题,先上一段演示代码:


class Program
{
static void Main(string[] args)
{
var tradeList = new List<Trade>()
{
new Trade(){TradeID=1, TradeTitle="交易1", Created=Convert.ToDateTime("2020/8/1"), CustomerID=1},
new Trade(){TradeID=2, TradeTitle="交易2", Created=Convert.ToDateTime("2020/8/5"),CustomerID=2},
new Trade(){TradeID=3, TradeTitle="交易3", Created=Convert.ToDateTime("2020/8/10"), CustomerID=3}
};
}
} class Trade
{
public int TradeID { get; set; } public string TradeTitle { get; set; } public DateTime Created { get; set; } public int CustomerID { get; set; }
}

上面的 tradeList 就是内存中的集合,现在有一个问题,我想查询一下 trade 表中 CustomerID in (1,2,10) && Created <= '2020-08-01' 的记录是否和内存中的 tradelist 一致。

用 sql 验证太简单了,直接在查询分析器里面写一下sql 搞定,如下图:

那在 UI 上 怎么验证呢?

二: 寻找解决方法

1. 在UI上自定义高级查询

这个也是大家最容易想到的,使用多个 if 叠加查询条件,如下代码所示:


static void Main(string[] args)
{
var tradeList = new List<Trade>()
{
new Trade(){TradeID=1, TradeTitle="交易1", Created=Convert.ToDateTime("2020/8/1"), CustomerID=1},
new Trade(){TradeID=2, TradeTitle="交易2", Created=Convert.ToDateTime("2020/8/5"),CustomerID=2},
new Trade(){TradeID=3, TradeTitle="交易3", Created=Convert.ToDateTime("2020/8/10"), CustomerID=3}
}; IEnumerable<Trade> query = tradeList; //UI
var queryCustomerIDList = new List<int>() { 1, 2, 10};
var queryCreated = "2020-08-01"; if (queryCustomerIDList.Count > 0)
{
query = query.Where(m => queryCustomerIDList.Contains(m.CustomerID));
}
if (string.IsNullOrEmpty(queryCreated))
{
query = query.Where(m => m.Created <= Convert.ToDateTime(queryCreated));
} //最后的结果
var list = query.ToList();
}

问题貌似是可以解决,但是这种用 if 叠加的方式不觉得太不灵活了吗? 如果客户心情不好,又来了一个 TradeID between 1 and 10 的筛选条件,那上面的代码是不是还得加一个 TradeID 的判断 ? 太麻烦了,还得继续寻找更灵活的姿势。

2. 使用DataTable

哈哈,大家看到 DataTable 是不是有一点懵逼,可不要小瞧这玩意,人家可是直接支持 sql 查询的哦,这灵活性不容小觑哈,上一段代码说话:


static void Main(string[] args)
{
var tradeList = new List<Trade>()
{
new Trade(){TradeID=1, TradeTitle="交易1", Created=Convert.ToDateTime("2020/8/1"), CustomerID=1},
new Trade(){TradeID=2, TradeTitle="交易2", Created=Convert.ToDateTime("2020/8/5"),CustomerID=1},
new Trade(){TradeID=3, TradeTitle="交易3", Created=Convert.ToDateTime("2020/8/10"), CustomerID=3}
}; var table = CopyToDataTable(tradeList); var query = table.Select("CustomerID in (1,2,10) and Created <= '2020-08-01' and TradeID >= 1 and TradeID <= 10")
.Select(m => new Trade()
{
TradeID = Convert.ToInt32(m[0]),
TradeTitle = Convert.ToString(m[1]),
Created = Convert.ToDateTime(m[2]),
CustomerID = Convert.ToInt32(3)
}).ToList();
} public static DataTable CopyToDataTable<T>(IEnumerable<T> array)
{
var ret = new DataTable();
foreach (PropertyDescriptor dp in TypeDescriptor.GetProperties(typeof(T)))
ret.Columns.Add(dp.Name);
foreach (T item in array)
{
var Row = ret.NewRow();
foreach (PropertyDescriptor dp in TypeDescriptor.GetProperties(typeof(T)))
Row[dp.Name] = dp.GetValue(item);
ret.Rows.Add(Row);
}
return ret;
}

是不是很强大,直接将文本化的 sql 塞入到 DataTable 中,你想什么样的查询你就写什么样的 sql 就 ok 啦,当然,理论归理论,在我的场景中肯定是不会这么玩的,毕竟内存中的 trade 有上千万行,转成 DataTable 不是给自己挖坑嘛,那有没有其他的方式呢?

3. 使用 表达式树 (ExpressionTree)

我想很多人看到 表达式树 都会退避三舍,虽然这玩意很强大,但是太复杂了,它会将你的查询语句拆解成树中的节点从而构建一棵非常复杂的树结构,其实 DataTable 对 sql语句的解析也是在内存中构建了一棵解析树,所以这玩意太反人类了,比如你要构建 i > 5 的查询,你需要下面这样的硬编码,这还是非常简单的哈,复杂的会让你吐血。

            ParameterExpression param = Expression.Parameter(typeof(int), "i");
ConstantExpression constExp = Expression.Constant(5, typeof(int));
BinaryExpression greaterThan = Expression.GreaterThan(param, constExp);
Expression<Func<int, bool>> f = Expression.Lambda<Func<int, bool>>(greaterThan, param);
Func<int, bool> mydelegate = f.Compile();
Console.WriteLine(mydelegate(5));

从图中可以看到,5>5 = False 是没有问题的,既然表达式树是可以解决类似这样的场景,聪明的你应该会想到,开源社区是否又类似封装好的 ExpressionTree 开发包呢? 说实话,还真有。。。

4. DynamicExpresso 开发工具包

开源大法好,github地址:https://github.com/davideicardi/DynamicExpresso , 这玩意实现了 将文本化的 C# 语句 动态转换成 delegate,这句话是什么意思呢? 大家可以看一下这张图:

从上图可以看到,你可以 写一些文本化的 C# 语句,然后经过 DynamicExpresso 处理后转换成了可执行 delegate,如果你没看懂,我用代码表示一下,如下图:

其中: 30 = 5 * 8 / 2 + 10 ,重点在于这里的 数学表达式 是文本的,有了这个思路,那我是不是也可以将 tradeList 的查询条件文本化表示,如下代码:


var interpreter = new Interpreter(); interpreter.Reference(typeof(System.Linq.Enumerable)); interpreter.SetVariable("arr", new int[] { 1, 2, 10 }); string whereExpression = "(trade.CustomerID == 1 || trade.CustomerID==2 || trade.CustomerID==10) && " +
"trade.Created <= Convert.ToDateTime(\"2020-08-01\") &&" +
"trade.TradeID >= 1 && " +
"trade.TradeID <=10"; Func<Trade, bool> queryFunc = interpreter.ParseAsDelegate<Func<Trade, bool>>(whereExpression, "trade"); var list = tradeList.Where(queryFunc).ToList(); var i = Enumerable.Contains(new int[] { 1, 2, 3 }, 3);

问题搞定,还是比较完美的

如何校验内存数据的一致性,DynamicExpresso 算是帮上大忙了的更多相关文章

  1. 使用percona-toolkit校验主从数据的一致性

    主从数据校验使用percona-toolkit工具集的以下两个工具(主库上使用): pt-table-checksum  #检查主从数据是否一致, pt-table-sync #把主库数据同步到从库 ...

  2. Linux就这个范儿 第15章 七种武器 linux 同步IO: sync、fsync与fdatasync Linux中的内存大页面huge page/large page David Cutler Linux读写内存数据的三种方式

    Linux就这个范儿 第15章 七种武器  linux 同步IO: sync.fsync与fdatasync   Linux中的内存大页面huge page/large page  David Cut ...

  3. java内存模型-顺序一致性

    数据竞争与顺序一致性保证 当程序未正确同步时,就会存在数据竞争.java 内存模型规范对数据竞争的定义如下: 在一个线程中写一个变量, 在另一个线程读同一个变量, 而且写和读没有通过同步来排序. 当代 ...

  4. 关于内存数据与 JSON

    闲话: 正在用 WebBroker 写一个小网站,感受颇多: 1.如果是写一个小东西,应该先考虑下 WebBroker,因为它是分分钟即可上手的. 2.如果要写一个大东西,或许也应该考虑下 WebBr ...

  5. MySQL pt-table-checksum及pt-table-sync校验及修复主从一致性

    [pt-table-checksum]pt-table-checksum是percona-toolkit系列工具中的一个, 可以用来检测主. 从数据库中数据的一致性.其原理是在主库上运行, 对同步的表 ...

  6. ZeroMQ接口函数之 :zmq_send_const – 从一个socket上发送一个固定内存数据

    ZeroMQ API 目录 :http://www.cnblogs.com/fengbohello/p/4230135.html ——————————————————————————————————— ...

  7. 如何解决分布式系统数据事务一致性问题(HBase加Solr)

    如何解决分布式系统数据事务一致性问题 (HBase加Solr) 摘要:对于所有的分布式系统,我想事务一致性问题是极其非常重要的问题,因为它直接影响到系统的可用性.本文以下所述所要解决的问题是:对于入H ...

  8. Linux就这个范儿 第18章 这里也是鼓乐笙箫 Linux读写内存数据的三种方式

    Linux就这个范儿 第18章  这里也是鼓乐笙箫  Linux读写内存数据的三种方式 P703 Linux读写内存数据的三种方式 1.read  ,write方式会在用户空间和内核空间不断拷贝数据, ...

  9. NoSQL数据库:数据的一致性

    NoSQL数据库:数据的一致性 读取一致性 强一致性 在任何时间访问集群中任一结点,得到的数据结果一致: 用户一致性 对同一用户,访问集群期间得到的数据一致: 解决用户一致性:使用粘性会话,将会话绑定 ...

随机推荐

  1. 将音频文件转二进制分包存储到Redis(奇淫技巧操作)

    功能需求: 一.获取本地音频文件,进行解析成二进制数据音频流 二.将音频流转化成byte[]数组,按指定大小字节数进行分包 三.将音频流分成若干个包,以List列表形式缓存到redis数据库中 四.从 ...

  2. websocket推送进度条百分比给前台

    说明:后台springboot项目 前台vue+element-UI 直接放代码: //别忘了开启springboot的websocket <dependency> <groupId ...

  3. vue学习(六) 事件修饰符 stop prevent capture self once

    //html <div id="app"> <div @click="divHandler" style="height:150px ...

  4. Springboot(二)springboot之jsp支持

    参考恒宇少年的博客:https://www.jianshu.com/p/90a84c814d0c springboot内部对jsp的支持并不是特别理想,而springboot推荐的视图是Thymele ...

  5. .NET CORE HttpClient使用

    自从HttpClient诞生依赖,它的使用方式一直备受争议,framework版本时代产生过相当多经典的错误使用案例,包括Tcp链接耗尽.DNS更改无感知等问题.有兴趣的同学自行查找研究.在.NETC ...

  6. Oracle DataGuard故障转移(failover)后使用RMAN还原失败的主库

    (一)DG故障转移后切换为备库的方法 在DG执行故障转移之后,主库与从库的关系就被破坏了.这个时候如果要恢复主从关系,可以使用下面的3种方法: 将失败的主库重新搭建为备库,该方法比较耗时: 使用数据库 ...

  7. Skill 脚本演示 ycSetNetColor.il

    https://www.cnblogs.com/yeungchie/ ycSetNetColor.il 将原理图中某一 netName 的所有连线同时修改为一种 颜色 / 线宽. 回到目录

  8. Python程序设计pdf|网盘下载内附提取码

    点击此处下载提取码:5o7z 本书提出了以理解和运用计算生态为目标的Python语言教学思想,不仅系统讲解了Python语言语法,同时介绍了从数据理解到图像处理的14个Python函数库,向初学Pyt ...

  9. Java主类的装载

    在JavaMain()函数中调用LoadMainClass()函数加载Java主类.LoadMainClass()函数的实现如下: /* * Loads a class and verifies th ...

  10. 《分享》Koa2源码分析

    曾经在公司内部做的一起关于koa源码的分享,希望对你有帮助: koa2 源码分析整理 koa2(2.4.1版本)源码主要包含四个js,包括application.js, context.js, req ...