接口优化及问题

目录

总体分析

前言:

关于sql优化

关于代码优化 

实例

部分细节优化

工作总结

问题总结

数据库事务问题(一)

数据库事务问题(二) 

每次工作总结

第一次

第二次

第三次

第四次

第五次

第六次


总体分析

前言:

一般可以使用Arthas大概分析具体哪个步骤耗时过长,根据耗时过长的代码进行分析,一般都是sql执行时间过长

关于sql优化

  • 如果数据库执行时间不长的话

    • 需要看一下返回的数据类型和代码中定义的字段类型是否相同

    • 其次可以看一下是否因为返回太多字段信息,有些sql会把所有字段信息进行返回,并且这些字段也要进行序列化和反序列化进行映射也要耗时

  • 如果数据库执行时间长的话

    • 就看一下sql执行计划,是否用到索引,没有的话就需要加上索引,这样修改代价降到最低

    • 加索引的话,尽可能加上复合索引

    • 如果加上索引之后,索引失效就需要看看sql是否有必要进行改写了,尽可能使用上索引

    • 如果加上索引之后,索引生效,但是耗时还是很长的话,就跟本身sql没啥关系,应该也不会出现这种情况(就是走索引速度还不快)

拓展一下复合索引的好处:

建了一个(a,b,c)的复合索引,那么实际等于建了(a),(a,b),(a,b,c)三个索引,因为每多一个索引,都会增加写操作的开销和磁盘空间的开销。对于大量数据的表,这可是不小的开销!

覆盖索引。同样的有复合索引(a,b,c),如果有如下的sql: select a,b,c from table where a=1 and b = 1。那么MySQL可以直接通过遍历索引取得数据,而无需回表,这减少了很多的随机io操作。减少io操作,特别的随机io其实是dba主要的优化策略。所以,在真正的实际应用中,覆盖索引是主要的提升性能的优化手段之一。   

关于代码优化 

循环中有数据库查询,不断与数据库进行交互耗时是最严重的

  • 如果想不修改循环中的代码逻辑,就只能需要进行异步批量查询数据库,就是多线程的方式

  • 直接修改代码逻辑,尽量避免循环里面对数据库进行查询

  • 数据查询的慢,根本原因还是一次性查询的数据太多了,可以做分页查询,或者分批调用接口

实例

 从20s优化到500ms,我用了这三招_苏三说技术的博客-CSDN博客

 上面这个文章主要就是加索引,多线程,分页查询这三步

部分细节优化

工作总结

1. 如果对大量数据进行操作的时候,提升优化就是分批操作,例如我们这边es数据查询就是每批50个 

2. 比方说根据大量服务id请求对应的数据,因为数据量太大,对请求也进行分批操作,就是分批id包装为请求体发送

3. 有些耗时,但是不影响用户的功能就可以走异步实现

4. 他们认为加上本地缓存会更快

备注:一个一个数据进行操作,跟批量数据操作的话,应该是批量操作的速度要快一些吧,主要是节约了频繁与数据库或者与redis进行连接这个时间(就算你用连接池的话,也是需要与数据库或者redis服务端建立连接的过程)

问题总结

数据库事务问题(一)

问题场景:排序修改,数据库中一个数据的排序发生改变,其他的顺序都跟着变动,比方说原来序号为2的数据,现在改为10,那么3-9的数据序号都要进行-1操作

实际发生情况:偶现,多个数据序号重复,序号之间会有间隔(原来的需求就是为了序号之间没有间隔)

发生原因:我感觉是不可重复读,可能同时有两个事务都在修改数据,只不过一个事务读到了另一个事务提交后的数据,它本来要读的是另一个数据提交前的数据,就是每次读到的数据不一样,这不就是不可重复读嘛,就那个数据读的是另一个事务提交后的数据,其他数据读的都是正常的数据,就会导致重复,也可能存在幻读,因为有可能读到提交后的数据

解决方案:等待中,我感觉更改排序的时候加上读写锁吧,这样改的话并发量大的话,会引起阻塞,我看他们有一个方案是,拿更改之后的全部数据,写到库里面,这个操作就太扯了,正常的话应该加上行锁,尽可能减小锁的粒度

拓展:就像这种批量修改数据,并行执行事务,对数据准确性要求比较严格的话,就会有问题,正常情况下,数据都是更新为其他不同的值,与你拿到的是更新前或者更新后的值无关,只看最后更新成啥,但是你更新的值与你之前获得的值相关的话,就会出现并发问题

数据库事务问题(二) 

问题场景:事务还没有执行完,执行业务逻辑,读取到的是老数据 

举例说明:

如果不加锁的话,事务还没有执行完,别的线程进来拿数据,拿的肯定是老数据,这样是不行的,所以需要将事务单独将一个方法写出来,一定要确保事务执行完成

举一个加锁的例子:

这锁在和@Transational同一个方法一起使用的情况下会出现,锁已经去除,但是事务还没提交的情况,造成脏读和数据不一致性等情况.假设你事务里面执行时间长了,**由于事务的提交在方法运行结束之后,并且事务真正启动在执行到它们之后的第一个操作 InnoDB 表的语句的时候**,看下面这张图,就是线程a执行test这个方法的时候,线程a已经解锁了,但是事务可能刚刚提交数据还没有变更,线程b这个时候进来执行的话,读取的还是原来的数据,这样就是有问题的

@Transactional(rollbackFor = Exeception.class)
public void test() {
    lock.lock();
    try {
      // 业务逻辑
    } finally {
      lock.unlock();
    }
  }

解决:可以把@Transational和业务逻辑的代码单独提到一个service里

@Transactional
    public void update(int id) {
        /*
          业务代码
         */
    }

这样的话呢,就是update这个方法是个事务,这个事务执行完成后,才会去解锁,数据会保持一致性。重点是@transactional这个方法执行完才会提交事务,而上面那种的锁已经释放,还没有提交事务的话,就会引起数据不一致问题

还有一点就是看看上面是业务代码都在一个方法里面,所以@Transactional这个注解一般放在service层,之前做业务的时候,还有这样的问题,功能是这样的,先将原来数据status置0,然后插入数据,另一个表的数据也有置0操作,这样的业务逻辑都放在@Transactional方法里面的话,不加锁,线程a假设正在提交事务的话,此时线程b进来插入数据,线程a有可能把线程b插进来的数据失效了,按道理来说应该加把锁

每次工作总结

第一次

  • 判空不细致 

第二次

  • 事务要保证
  • try-catch要牢记具体在哪个位置去处理
  • 建表的时候注意索引添加

第三次

  • 注意筛选条件
  • 灵活使用sql函数
  • 改动的东西对其他代码的影响
  • 注意判空和脏数据,导致代码报错,try-catch

第四次

  • 修改人没加上
  • 筛选数据不仔细
  • 代码冗余严重,起始还好
  • 设计文档详细一点
  • 他人需求:
  • 就是什么时候用on duplicate key update,什么时候用update,大多数时候用update,当你传的值为空值的时候有意义,就不能用这个了,假设你用update那个,一般为空的话这个值应该是不改变数据库数据的,但是因为这个值需要传空,而且还要写入数据库中,那好,你不写判空处理的话,就是你传什么我改什么,在修改或者删除的时候,更改字段,expire这个字段你会传空,这样的话,就把原来不为空的数据改为空了,这样就不能用update动态sql了,还有就是on duplicate key update 自带安全的,就是如果多线程增加或者插入在一个接口操作,或者在不同接口操作的话,就是同时判断库里面没有数据,就会在数据库里创建两个相同的数据在表中,用这个的话就可以避免这个现象
  • 修改落入库的数据一定要对一下,保证数据正确性,入库正确就行,展示样式,用户体验也要关注一下
  • 测试发现的有问题的数据,一定先看一下这样的数据到底是个啥,这个是什么数据,然后再去判断,筛选条件一定要准确
  • 就是能简单就简单,思考一下怎么解耦方便,后续拓展方不方便

第五次

  • 就是优化的时候,sql查询条件能少尽量少,返回字段能少尽量少,条件和返回字段看细致一点
  • 然后就是关联条件也是影响到索引的
  • sql优化:1.就是加索引,explain进行判断 2.就是看业务代码,有没有循环查询数据库的 3.业务代码有没有冗余字段进行精简 4.数据库的数量是否过大,修改业务逻辑进行提升
  • 详设里面具体改哪些东西还是要列清楚,例:我某次把启用停用的修改人就没有添加上
  • 发现校验每次都不成功的时候,看看@valiated注解有没有加上
  • 入参有id一定要去查一下数据库里面,校验

第六次

  • 就是需求任务可以理解成,测试需要怎么测
  • 涉及修改的地方一定要写到详设里面

你可能感兴趣的:(工作方面,java)