项目是springboot应用。 jvm运行内存 512M,并发超过5内存溢出。
项目是公司内部项目,由于历史数据巨大,每次查询数据库时间比较长(有的能到20分钟获取结果),所以希望对用户查询结果时间优化。
想法:用户查询时间优化:优化数据库(历史金融数据,没人敢动),优化表结构索引(关联很多系统,索引和结构认为很难再优化),优化sql语句(sql拼接各种条件和分组,用户选择不容易控制),备份数据库数据(数据太大,而且实时更新)
结论:所以用户查询时间很难优化,所以从另一个角度来想优化方案,如果无法避免用户第一次查询时间,是不是可以减少接下来的排序和翻页数据搜索时间,顺着这个思虑考虑。
第一次
次级想法:如果用户查询出第一次数据,我们缓存起来,如果用户排序和翻页,直接从缓存中取数据。
关于缓存在redis、mysql,排序如何实现,缓存时间,实时性数据如何使用缓存等进行测试
缓存方式:
mysql缓存数据 缺点:增加表缓存表,记录区分不同条件数据难,增加、定时删除过期数据资源占用比较大(实时性)
mysql缓存数据 优点:对内存需求不大,存储硬盘,可以使用 数据库 排序功能。
redis缓存数据 缺点:数据存储于内存消耗大,不能排序
redis缓存数据 优点:只需要一个key + 有效时间 将结果存入到redis 即可(1w条数据存储占1M内存)
最终方案:
根据 查询条件 排序后MD5 获取字符串作为 本条件查询结果的key,然后使用key去redis获取,如果获取成功,则重置到期时间,如果没有查询到,则 使用条件去查询数据库,获取结果后存储到redis。返回数据 使用内存排序和转义 后返回结果级。(查询条数设置5000条,超过5000条的查询结果级不缓存,要求用户精简搜索条件)
本方案妥协:公司内部系统,并发量不用很高,基本上不会超过5个人同时操作,一般只有值班的人员操作。
第二次
问题介绍
产生原因:5000条限制的时候大约并发量20左右(最耗内存的查询),完全可以满足他们的需求,后台用户要切将缓存数据5000条 提高到5w条,然后就崩了。当 查询65w条数据,使用程序做排序的时候把jvm的内存溢出。6(次) 5W(行) 50(Key-value值) 10(平均字符数) > 512 M jvm内存
思考解决办法:
优化代码,减少多余内存占用(实际效果不明显,主要是 大数据在内存时占用大问题,不操作)
限制用户并发数,并发超过极限,剩下的排队等待。 (可行,用户体验差)
增加 jvm 内存(512M增加至1G),增加 负载均衡(平均分配)
解决方案:
设置两个参数 ①并发数量 ②控制条数 含义:当这个查询超过控制条数(例如1w条),我们就把这个线程加入到并发数量控制中,如果小于控制条数,我们就不用控制并发。提供两个参数 是因为可以更具用户实际使用情况直接调整两个参数到最合适的数值。
第三次
问题介绍:
按照上面的方法修改后 512内存 可以并发5以上,完全可以满足用户使用。但是 由于有一张表 字段特别多(140个字段)所以他的5w行数据相当于其他表(50个字段以内)3倍以上。所以试了一下这张表 并发2 的时候。有的时候就 内存溢出,有的时候可以正常返回。这种情况导致 由于这一个特殊表 导致整体并发量降到1,及用户查询大数据量 只能一个一个查询,不能并发。这个用户很难接受。
解决方案:
首先设置并发使用最大内存数量,然后内阁请求进来根据他的 查询条数、字段数量、字段长度 大概判断出 占用内存数量,然后比较是否超过最大内存,超过则等待,没超过则 执行,返回结果后释放该记录。
效果:用户查询5w 50字段的 可以并发8 ,用户查询5w 10字段 可以并发20+,用户查询5w 140字段的可以并发1(1G内存后可以并发3)。这样 用户可以 并发查询 1条 5w 140+2次5w50 + 5次 5w10 的,用户使用几乎无感受。