总结几个问题啦

来源:互联网 发布:沙宝亮唱功 知乎 编辑:程序博客网 时间:2024/06/08 12:38

第一个问题

一个用户下了单,然后用户无奈的发现自己支付成功的订单变成已失效了。
其中代码的流程是:
支付回调时序图

支付系统将支付结果 回调传给SOA服务,SOA服务去更改用户订单状态为 已支付。这一过程要失败了,定时任务就会将这个订单状态置为 已失效,然后用户就懵逼了,明明支付成功了,却变成无效订单了。要是我,肯定急眼了。

导致的问题:

支付系统在回调失败时是会基于一定策略继续尝试的。
1. 第一次回调在第6步时没有查到数据,程序抛出一个空指针异常终止了程序;
2. 在进行第二次回调时,查到了流水数据,但在第7步没有查到订单数据,程序没有抛出异常或告知支付系统回调失败,没有更改订单状态。但更新了流水表,订单表没有更新,导致两个表的订单状态不一致了。

原因:

数据库是主从集群配置的,SELECT的SQL会发往从库,INSERT、UPDATE、DELETE的SQL发往了主库,后面从DBA了解到主从是有一些时间delay的。这就能解释为什么查不到数据了。

改进措施:

  1. 对于这种涉及到金钱的,并且数据一致性要求高的,可以直接去查询主库,避免出现这种意外的情况;
  2. 在没有查到数据的时候,程序及时抛出异常并报警,或告知支付系统回调失败,方便排查问题;
  3. 将更新流水表和更新订单表放在一个事务里,保证两个表 订单的状态是一致的。

哈哈,缅怀下那个定律:

Anything that can go wrong will go wrong!

第二个问题

来段伪代码(在工作中就写了这样一段代码):

  @Transactional(rollbackFor = Exception.class)  public void invokeRemote() {      insert();   // 数据库插入操作 加锁      update();   // 更新操作 加锁      insert();   // 插入操作 加锁      executePost(URL, data);  // 发送https的请求  }

一旦这个事务方法被调用,insert和update都会加锁,然后发送https的POST请求,不靠谱的https接口超时了,让这个事务一直不能commit, 如果这个事务方法被并发调用就惨了,多个事务一直挂着等待锁然后就超时报错了。还会影响到其他的业务,由于锁一直未释放,其他业务请求获取不到锁出现超时或死锁。

改进1

  @Transactional(rollbackFor = Exception.class)  public void invokeRemote() {      executePost(URL, data);  // 发送https的请求      insert();   // 数据库插入操作 加锁      update();   // 更新操作 加锁      insert();   // 插入操作 加锁  }

将https的请求提前,因为时间耗费都在这一步,这样数据库层面就不会先去加锁,避免后续事务的等待。

改进2

public void invokeRemote() {      executePost(URL, data);  // 发送https的请求      oneService.operateDb()}@Transactional(rollbackFor = Exception.class)public void operateDb() {      insert();   // 数据库插入操作 加锁      update();   // 更新操作 加锁      insert();   // 插入操作 加锁}

这种方式的改进比较好一些,事务方法中不要出现网络调用、比较耗时的处理程序,事务中数据库的连接是不会释放的,如果每个事务的处理时间都非常长,那宝贵的数据库连接资源将消耗殆尽。

第三个问题

 <sql id="where">     <where>         <if test="id != null">             id = #{id}         </if>     </where> </sql> <select id="selectOrderById" resultType="me.jiaocchong.Order">     select * from tb_order <include refid="where" /> </select>

这是mybatis的mapper里写的一段sql, 如果代码里面传的id为null会发生什么?意外就是tb_order表里数据全查出来堆在了内存里(tb_order表上千万的数据),这时看见的错误就是OutOfMemoryError了。很可怕,直接导致线上机器宕机。
所以写代码一定要细心。

参考链接:
http://tech.meituan.com/innodb-lock.html
https://my.oschina.net/hcliu/blog/397281
http://tech.lede.com/2017/02/06/rd/server/SpringTransactional/

原创粉丝点击