一波简单的订单优化

背景

系统中,下单流程较长,业务逻辑复杂,再加上分布式锁的使用,导致下单效率很低,耗时较长,但由于对接的OTA要求500ms以内

而我负责的目的地系统下单只是其中的一个环节,所以必须控制在300ms以内,于是有了这一波优化

系统使用hibernate jpa

开始

要优化,自然先找出耗时较长的函数调用,简单的方法是使用System.currentMilliseconds()函数打印时间。

更进一步,使用切面,对Service层的方法调用做切面,在切面里打印每一个函数调用时间

实际时两种方法混用

涉及到的优化

批量添加,事物包裹

有一段保存操作日志到数据库的代码,是对每一个订单项依次循环,保存日志。

修改为先循环构建要保存的对象,再调用批量保存的接口,批量保存接口添加@Transaction注解

修改后耗时接近只保存一次的时间,修改前耗时与订单项数量成正比

对于只需要数量结果的,使用count而非查询出List之后用size

显然,使用count传输的数据数量为常量,而List很大时,传输数据量也会很大

索引

多使用索引几乎没什么问题,特别是库存价格相关的表,毕竟这些表写少读多

sql中不适用in

使用explain看了一下带有in条件的查询语句,显示的为seq scan,顺序扫描,即便是有索引的情况依然如此

对于这种问题,想办法消除in

在我的问题中,查询一个订单对应的所有订单项相关的表,但是表中只记录了订单项id,而没有订单id
想办法增加订单id字段,同时对订单id增加索引,然后使用订单id查询

1
2
3
4
-- 修改前
select * from xx where item_id in (1,2,3);
-- 修改后
select * from xx where order_id=1;

jpa管理的Entity对象,存在级联关系时,可考虑使用级联查询,不单独查询

这个不绝对,在我的情况中,是由于jpa一级缓存还存在,但是优化前却使用条件查询,查询了一遍数据库
而优化后使用级联查询,会直接从一级缓存中获取数据

也存在另外一个问题,在保存数据的时候,一定要手动的把级联关系双向都设置上, 否则级联获取的为空

由于是在循环中,所以优化时间可观

流程合并

订单下单和确认为两个流程,会存在两次加锁,由于锁的耗时较大,而又存在自动确认的情况(即一次下单,两个流程都会跑一边),
所以在自动确认的情况下,将这两个流程合并, 仅使用一次加锁

能放在循环外面的,尽可能不要放在循环内

一次循环1ms,10次循环就是10ms,但是每次执行结果完全相同,则将此代码提到循环体外

ps: 优化的时候,就是这样一点点的扣时间的

避免数据重复查询

开始查询一次,后边有需要,都使用开始查询出来的数据,即便hibernate有一级缓存, 也会提升一些效率

锁的优化

对于一个订单,优化前,是一个资源一天一个锁,这样当下单包含多个资源时,会存在很多个锁

优化后,将所有资源和日期的数据整体作为获取锁的参数,只返回一个锁,大大降低锁的数量, 优化明显

具体优化逻辑不清楚,不是自己做的

更多思考

如果还要优化,效果最明显,最可行的也就是使用hibernate的二级缓存,但是因为对其理解不深,为避免出现数据不一致的情况
暂时未用。当然也可以手动的,将数据在redis等内存数据库中做一份缓存。

更进一步,想办法消除锁,按照锁购买的资源分队列,相同资源的顺序下单

还有很多。自己不知道的优化。。

文章目录
  1. 1. 背景
  2. 2. 开始
  3. 3. 涉及到的优化
    1. 3.1. 批量添加,事物包裹
    2. 3.2. 对于只需要数量结果的,使用count而非查询出List之后用size
    3. 3.3. 索引
    4. 3.4. sql中不适用in
    5. 3.5. jpa管理的Entity对象,存在级联关系时,可考虑使用级联查询,不单独查询
    6. 3.6. 流程合并
    7. 3.7. 能放在循环外面的,尽可能不要放在循环内
    8. 3.8. 避免数据重复查询
    9. 3.9. 锁的优化
  4. 4. 更多思考
,