记一次性能优化问题
参与架构
代码设计
我们总假设,温度是一次次上报的,且点与点之间没有先后的顺序
很容易写出以下代码
For(EachTimeUploadData)
Pipe.collect(in,out,now)
遇到的问题
我们的不仅需要记录温度,还要记录压力和流量,而这些属性是归属于管道
这个时候会遇到一个巨大的问题,数据库的DeadLock
我们默认聚合根是Pipe,那么为了防止聚合根在两个线程中出现,故而数据库中是要对这一行开启事务的,意味着根所在一行是要锁行的,而测试中如果遇上同一根管道的首尾同时上报数据,那么就会发生DeadLock,更不要提某些情况下是要沿着管道将沿途所有的管道锁定的问题。
解决死锁
- 我们不能允许数据丢失
- 不能够对架构做大改动
要解决死锁的问题,最简单的办法时,我们只用一个线程来操作,在不考虑集群的情况下,将原有方案变更为,数据上报时候顺序写入文件,然后开启一个线程从文件头开始反序列化数据然后逐一写入数据库。
集群环境
在集群环境下,依旧有锁冲突的问题,那么冲突时候策略就可以考虑以下了,我选择了,冲突之后将数据放入队列尾部,这个时候代码逻辑上是没什么问题了
性能
可是上线之后发现,其写入性能仅在15s,注意是s,是秒,才完成50条入队消息的消费
在各种断点之后,依旧发现瓶颈在数据库与应用中
- 每次查询流量系统地址,30~50ms
- 每次查询流量,30~50ms
- 每次查询管道链条300~500ms
在一些支管上,单次插入可以达到最大5000ms
解决
- 增加管道标记父节点字段,而非靠查找所有管道再通过计算得出父节点
- 增加本地缓存和Redis缓存切换,以其在没有redis环境下可以降低查询流量系统地址的时延
- 优化流量和压力的Opc客户端,减少数据传输量
- 将于数据库交互中的插入和更新变为Batch形式
测试结果
消费50条数据仅需要300ms左右,