简介
最近项目中遇到一个问题,服务器使用tcp与电池检测设备相连,电池检测设备挂载了大量的通道,这些通道会产生电池检测数据(平均1秒产生一条数据,一条数据平均130字节,最快100毫秒产生一条数据),一个测试流程可连续运行几天置一年不等,那么服务端如何检验这些设备发送的检测数据是否有漏发丢发或者是重复发送?第一解决方案就是将这些数据存储在数据库中,每次上传数据的时候去数据库中查询这些数据是不是已经发送过,但是一个测试数据可能有百万甚至上千万条,且在上千个通道并发运行的情况下如果将这些数据全部存在数据库的一张表中,查询效率可想而知,结合数据算法的一些知识,我采用了位图去解决这个问题。
前置条件
从tcp传输校验位得到的启发,在设备发送消息时,我让设备开发人员每次发送消息时带上一个整型标志位,从0依次递归增长,这样如果数据是正常的没有丢失的,那么从0遍历到结尾数据应该是连续的,如果一个报文与之前发送的标志位重复了(比如收到了两个2,那么这部我认为第二个是重复消息,将其丢弃)
核心思想与设计
解决这个问题的核心是使用位图在内存中去存储前置条件中的标志位,位图的知识可自行百度,还是比较简单的,之所以使用位图的思路是在如此大量的数据的情况下,将所有数据存储在数据库那么查询效率肯定不会太好,所有我选择将主要数据使用位图这种数据结构使用少量的内存空间构成一个过滤器来过来数据是否发送过。举例,在java中int类型是4个字节,那么我们要存储2500万个数字的话,如果我们使用散列表,或者是数组去存储的话需要100m的存储空间,如果我们这里使用位图来存储的话,同样使用100m空间可以存储8亿个数字。那么我们要做的就是将强制条件带上来的校验位,来构建一个位图过滤器来校验数据是否发送过。但是假设检测数据有8亿条的话,1千个通道那么公共需要使用100g的内存去构建过滤器,对于我们现有服务器的条件来说是吃不消的,于是我又将数据分段比如10万一个段,如果上传了10万零1的话,我就讲之前1-10万这个数据刷入磁盘,当后续后1-10万的数据补存进来的时候从磁盘将其读出,将对于的位图标志位值1,然后再写入磁盘,这样就可以将上千个通道并发传数据的内存使用控制在M这个数量级,完全是可接受的。
上代码
https://github.com/liushprofessor/BitMapFilter