如何校验内存数据的一致性,DynamicExpresso 算是帮上大忙了
一:背景
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 算是帮上大忙了的更多相关文章
- 使用percona-toolkit校验主从数据的一致性
主从数据校验使用percona-toolkit工具集的以下两个工具(主库上使用): pt-table-checksum #检查主从数据是否一致, pt-table-sync #把主库数据同步到从库 ...
- 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 ...
- java内存模型-顺序一致性
数据竞争与顺序一致性保证 当程序未正确同步时,就会存在数据竞争.java 内存模型规范对数据竞争的定义如下: 在一个线程中写一个变量, 在另一个线程读同一个变量, 而且写和读没有通过同步来排序. 当代 ...
- 关于内存数据与 JSON
闲话: 正在用 WebBroker 写一个小网站,感受颇多: 1.如果是写一个小东西,应该先考虑下 WebBroker,因为它是分分钟即可上手的. 2.如果要写一个大东西,或许也应该考虑下 WebBr ...
- MySQL pt-table-checksum及pt-table-sync校验及修复主从一致性
[pt-table-checksum]pt-table-checksum是percona-toolkit系列工具中的一个, 可以用来检测主. 从数据库中数据的一致性.其原理是在主库上运行, 对同步的表 ...
- ZeroMQ接口函数之 :zmq_send_const – 从一个socket上发送一个固定内存数据
ZeroMQ API 目录 :http://www.cnblogs.com/fengbohello/p/4230135.html ——————————————————————————————————— ...
- 如何解决分布式系统数据事务一致性问题(HBase加Solr)
如何解决分布式系统数据事务一致性问题 (HBase加Solr) 摘要:对于所有的分布式系统,我想事务一致性问题是极其非常重要的问题,因为它直接影响到系统的可用性.本文以下所述所要解决的问题是:对于入H ...
- Linux就这个范儿 第18章 这里也是鼓乐笙箫 Linux读写内存数据的三种方式
Linux就这个范儿 第18章 这里也是鼓乐笙箫 Linux读写内存数据的三种方式 P703 Linux读写内存数据的三种方式 1.read ,write方式会在用户空间和内核空间不断拷贝数据, ...
- NoSQL数据库:数据的一致性
NoSQL数据库:数据的一致性 读取一致性 强一致性 在任何时间访问集群中任一结点,得到的数据结果一致: 用户一致性 对同一用户,访问集群期间得到的数据一致: 解决用户一致性:使用粘性会话,将会话绑定 ...
随机推荐
- Pollard-Rho
\(code:\) ll pri[12]={2,3,5,7,11,13,17,19}; ll mul(ll x,ll y,ll mod) { ll c=(long double)x*y/mod+0.5 ...
- 小白在使用ISE编写verilog代码综合时犯得错误及我自己的解决办法
一:错误原因,顶层信号声明类别错误 错误前 更改后 二:综合时警告 更改前: 错误原因:调用子模块时 输出端口只能用wire类型变量进行映射 这是verilog语法规定的 tx_done在uart_t ...
- 前缀和线性基HDU6579
Operation 题解:看到区间最大异或和,首先想到的是线性基: 线性基可以处理的操作是: 在数列末尾插入一个数 查询全局的子集异或最大值 由于线性基的长度很短,因此我们可以将数列所有前缀的线性基保 ...
- 【NeurlPS2019】Positional Normalization 位置归一化
作者提出,当前的BatchNorm, GroupNorm, InstanceNorm在空间层面归一化信息,同时丢弃了统计值.作者认为这些统计信息中包含重要的信息,如果有效利用,可以提高GAN和分类网络 ...
- Html5 表单元素基础
表单元素 1.定义: 表单是提供让读者在网页上输入,勾选和选取数据,以便提交给服务器数据库的工具.(邮箱注册,用户登录,调查问卷等) 2.表单元素(下拉框,输入框……) 3.表单主结构: <fo ...
- Ribbon 负载规则替换
1 添加规则类: 注意: 官方文档明确给出了警告: 这个自定义配置类不能放在 @ComponentScan 所扫描的当前包下以及子包下,否则自定义的配置类就会被所有的 Ribbon 客户端所共享,达不 ...
- SpringBoot整合Mail发送邮件&发送模板邮件
整合mail发送邮件,其实就是通过代码来操作发送邮件的步骤,编辑收件人.邮件内容.邮件附件等等.通过邮件可以拓展出短信验证码.消息通知等业务. 一.pom文件引入依赖 <dependency&g ...
- Oracle数据库出现[23000][2291] ORA-02291: integrity constraint (SIMTH.SYS_C005306) violated异常
参考链接 这个异常发生在往中间表中插入数据时,这时出现异常是因为关联的某个表没有插入数据,所以给没有插入数据的关联表插入数据,再给中间表插入数据此时异常就会解决.
- PHP xml_set_external_entity_ref_handler() 函数
定义和用法 xml_set_external_entity_ref_handler() 函数规定当解析器在 XML 文档中找到外部实体时被调用的函数. 如果成功,该函数则返回 TRUE.如果失败,则返 ...
- MediaStreamConstraints对象
MediaStreamConstraints对象作用是在调用getUserMedia()时用于指定应在返回的MediaStream中包括哪些轨道,以及(可选)为这些轨道的设置约束. 属性 audio布 ...