技术梳理

61 阅读1分钟

1.仪表项目 数据库使用CK 和 ES --有800多个字段,开发字段的条件过滤 等于不等于 包含不包含 大于小于 曾是不曾是等运算符, 字段使用连接符 and 或 or
支持分组类似于sql中的小括号
用户保存配置好就是一个卡片,生成对应的图表: 饼图 柱状图 折线图 堆叠图 表格 多线图 多级表格等 用户的仪表盘一般包含几十个卡片,多的有几百个卡片;

实现逻辑 在代码中完成配置条件的解析拼接为sql 直接 $传给mybatis; 一套代码完成所有的解析 这套代码针对不同的需求,后续通过配置就可以完成;
例如缺陷关闭率

	{
		"key": "关闭率(%)",
		"value": "case when sum(denominator)=0 then 0 else round(sum(numerator)*100/sum(denominator),2) end",
		"target": 1
	},
	{
		"key": "未关闭数(个)",
		"value": "sum(denominator)-sum(numerator)",
		"extCondition1": "denominator=1 and numerator=0"
	},
	{
		"key": "GAP",
		"value": "{\"gapNumerator\":\"sum(numerator)\",\"gapDenominator\":\"sum(denominator)\"}"
	}
]

有多个柱子可选 关闭率 未关闭个数 已关闭个数 GAP(GAP值 要设置目标值) 数据源 指标视图都是可以配置的

这里说一个业务场景吧, 有数次迭代的过程, 对于我的开发思路体现,比较有代表性;

第一次的需求场景是这样,有一个自助查询数据库是ES,也是和仪表盘差不多可以配置一堆条件,保存自己的查询,页面效果的是数据详情的分页列表

用户在自助查询配置查询,不想在仪表盘再配置一遍,希望用他配置的自助查询数据作为数据源,再这个基础上做卡片的配置; 等于 在这边做指标的逻辑过滤,和数据分组的聚合操作等

最终采用的方案是 调用自助查询将数据拼接为id_collectionName 作为一条数据写入CK 我在将这条数据分隔,行转列

join (select
	query_id,
	arrayElement(splitByString('_',
	b.rids_collection),
	1) as id,
	arrayElement(splitByString('_',
	b.rids_collection),
	2) as collection_name
from
	(
	select
		query_id,
		trim(arrayJoin(rids)) as rids_collection
	from
		(
		select
			query_id,
			row_number () over (partition by query_id
		order by
			create_time desc) as row_id,
			splitByString(',',
			result_ids) as rids
		from
			gsdp_dw.ads_kanban_custom_query_result_ids
		where
			query_id in ( %s )
		limit 1 by query_id ) a ) b ) c on
c.id = toString(rev.id)
and c.collection_name = rev.collection_name;


因为主要考虑到 如果通过http请求将id collectionName数据拿到,作为参数传给数据库,担心传入的数据过大,数据库扛不住;

那这样上线之后,因为我要调用外部接口,自助查询有一个写CK过程,因为CK是分布式的数据库,有时会发生我查的时候,有些CK节点还没有同步,造成数据不完整 而且CK在用户使用高峰期,遇到任务调度(实时写操作),经常发生CK连接数不够用,造成卡片一直loading无数据返回,用户会频繁刷新页面,造成问题更加剧

所以我就做了一个套新指标的逻辑, 将指标过滤的逻辑配置为一个DSL,将这个DSL配置我新指标的一个字段, 分子计算的逻辑,配置两个字段,一个是用到的字段,一个是字段的值, 计算规则用枚举值表示配置为一个字段

image.png

这样直接组装出DSL查询ES,拿到过滤后的明细数据,用case when 根据传入的枚举 做百分比的计算,关闭或未关闭的计算等 (先按照图形分 组装不同的数据结构) 这套通用逻辑大概1000多行,对于多维表格(递归分组 和 统计行和列) 这样省去了写入CK 和 连表查询的过程,提升查询效率

有一种情况会配置多个查询,查询反馈比较忙,对于这种情况用completableFuture做异步编排,提升查询效率 (1.开异步在 join之前一定要先收集,否则会造成阻塞 等同没开子线程,测试反馈查询变慢 发现有人改了)

工作中常见的线上问题基本都处理过

1.直接创建子线程完成oom,改造服务启动时给每个区域建立线程池
2.老接口,查所有数据完成服务oom,通过eclips分析dump
3.数据库死锁,当更新一条记录的时候即使没有显式的加锁,如果用到普通索引作为条件,那么会先获取普通索引的锁,然后再尝试获取主键索引的锁。

可以在应用程序中设置一个规定的索引获取顺序,例如,只能按照主键索引->普通索引的顺序获取锁,这样就可以避免不同的线程出现获取不同顺序锁的情况, 进而避免死锁的发生(靠SQL保证)

  • 所有update都通过主键ID进行。
  • 在同一个事务中,避免出现多条update语句修改同一条记录。

查询时多用主键索引,其次是索引覆盖 和 索引下推

当发生热点更新时,也就是同时更新同一条数据, 容易造成数据库cpu打满, 主要原因是数据库线程一直是繁忙状态,不断的自旋, 导致数据库CPU打满; 像阿里有个数据库优化,将热点数据更新 优化为类似于reentrantLock阻塞队列排队,不会自旋; 一般我们都是使用分布式锁,来扛并发,因为数据库的连接是很珍贵的。

服务的数据库飙高一般都是因为并发执行了一个耗时的操作,导致线程不断的上线文切换,cpu飙高;一般来说像死锁,都会进入阻塞队列,cpu使用率反而不高

执行top命令

$top PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1893 admin 20 0 7127m 2.6g 38m S 181.7 32.6 10:20.26 java

查看进程中哪个线程占用cpu高

$top -Hp 1893 PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 4519 admin 20 0 7127m 2.6g 38m R 18.6 32.6 0:40.11 java

转16进制

$printf '%x\n' 4519 11a7

jstack 1893 |grep -A 200 11a7 打印线程的栈信息