游戏电视苹果数码历史美丽
投稿投诉
美丽时装
彩妆资讯
历史明星
乐活安卓
数码常识
驾车健康
苹果问答
网络发型
电视车载
室内电影
游戏科学
音乐整形

看看别人后端API接口性能优化的11个方法,那叫一个优雅

  1。索引
  接口性能优化大家第一个想到的可能是:优化索引。
  没错,优化索引的成本是最小的。
  你通过查看线上日志或者监控报告,查到某个接口用到的某条sql语句耗时比较长。
  这时你可能会有下面这些疑问:该sql语句加索引了没?加的索引生效了没?mysql选错索引了没?1。1没加索引
  sql语句中where条件的关键字段,或者orderby后面的排序字段,忘了加索引,这个问题在项目中很常见。
  项目刚开始的时候,由于表中的数据量小,加不加索引sql查询性能差别不大。
  后来,随着业务的发展,表中数据量越来越多,就不得不加索引了。
  可以通过命令:showindexfromorder;复制代码
  能单独查看某张表的索引情况。
  也可以通过命令:showcreatetableorder;复制代码
  查看整张表的建表语句,里面同样会显示索引情况。
  通过ALTERTABLE命令可以添加索引:ALTERTABLEorderADDINDEXidxname(name);复制代码
  也可以通过CREATEINDEX命令添加索引:CREATEINDEXidxnameONorder(name);复制代码
  不过这里有一个需要注意的地方是:想通过命令修改索引,是不行的。
  目前在mysql中如果想要修改索引,只能先删除索引,再重新添加新的。
  删除索引可以用DROPINDEX命令:ALTERTABLEorderDROPINDEXidxname;复制代码
  用DROPINDEX命令也行:DROPINDEXidxnameONorder;复制代码1。2索引没生效
  通过上面的命令我们已经能够确认索引是有的,但它生效了没?此时你内心或许会冒出这样一个疑问。
  那么,如何查看索引有没有生效呢?
  答:可以使用explain命令,查看mysql的执行计划,它会显示索引的使用情况。
  例如:explainselectfromorderwherecode002;复制代码
  结果:
  通过这几列可以判断索引使用情况,执行计划包含列的含义如下图所示:
  如果你想进一步了解explain的详细用法,可以看看我的另一篇文章《explain索引优化的这把绝世好剑,你真的会用吗?》
  说实话,sql语句没有走索引,排除没有建索引之外,最大的可能性是索引失效了。
  下面说说索引失效的常见原因:
  如果不是上面的这些原因,则需要再进一步排查一下其他原因。1。3选错索引
  此外,你有没有遇到过这样一种情况:明明是同一条sql,只有入参不同而已。有的时候走的索引a,有的时候却走的索引b?
  没错,有时候mysql会选错索引。
  必要时可以使用forceindex来强制查询sql走某个索引。
  至于为什么mysql会选错索引,后面有专门的文章介绍的,这里先留点悬念。2。sql优化
  如果优化了索引之后,也没啥效果。
  接下来试着优化一下sql语句,因为它的改造成本相对于java代码来说也要小得多。
  下面给大家列举了sql优化的15个小技巧:
  由于这些技巧在我之前的文章中已经详细介绍过了,在这里我就不深入了。
  更详细的内容,可以看我的另一篇文章《聊聊sql优化的15个小技巧》,相信看完你会有很多收获。3。远程调用
  很多时候,我们需要在某个接口中,调用其他服务的接口。
  比如有这样的业务场景:
  在用户信息查询接口中需要返回:用户名称、性别、等级、头像、积分、成长值等信息。
  而用户名称、性别、等级、头像在用户服务中,积分在积分服务中,成长值在成长值服务中。为了汇总这些数据统一返回,需要另外提供一个对外接口服务。
  于是,用户信息查询接口需要调用用户查询接口、积分查询接口和成长值查询接口,然后汇总数据统一返回。
  调用过程如下图所示:
  调用远程接口总耗时530ms200ms150ms180ms
  显然这种串行调用远程接口性能是非常不好的,调用远程接口总的耗时为所有的远程接口耗时之和。
  那么如何优化远程接口性能呢?3。1并行调用
  上面说到,既然串行调用多个远程接口性能很差,为什么不改成并行呢?
  如下图所示:
  调用远程接口总耗时200ms200ms(即耗时最长的那次远程接口调用)
  在java8之前可以通过实现Callable接口,获取线程返回结果。
  java8以后通过CompleteFuture类实现该功能。我们这里以CompleteFuture为例:publicUserInfogetUserInfo(Longid)throwsInterruptedException,ExecutionException{finalUserInfouserInfonewUserInfo();CompletableFutureuserFutureCompletableFuture。supplyAsync((){getRemoteUserAndFill(id,userInfo);returnBoolean。TRUE;},executor);CompletableFuturebonusFutureCompletableFuture。supplyAsync((){getRemoteBonusAndFill(id,userInfo);returnBoolean。TRUE;},executor);CompletableFuturegrowthFutureCompletableFuture。supplyAsync((){getRemoteGrowthAndFill(id,userInfo);returnBoolean。TRUE;},executor);CompletableFuture。allOf(userFuture,bonusFuture,growthFuture)。join();userFuture。get();bonusFuture。get();growthFuture。get();returnuserInfo;}复制代码
  温馨提醒一下,这两种方式别忘了使用线程池。示例中我用到了executor,表示自定义的线程池,为了防止高并发场景下,出现线程过多的问题。3。2数据异构
  上面说到的用户信息查询接口需要调用用户查询接口、积分查询接口和成长值查询接口,然后汇总数据统一返回。
  那么,我们能不能把数据冗余一下,把用户信息、积分和成长值的数据统一存储到一个地方,比如:redis,存的数据结构就是用户信息查询接口所需要的内容。然后通过用户id,直接从redis中查询数据出来,不就OK了?
  如果在高并发的场景下,为了提升接口性能,远程接口调用大概率会被去掉,而改成保存冗余数据的数据异构方案。
  但需要注意的是,如果使用了数据异构方案,就可能会出现数据一致性问题。
  用户信息、积分和成长值有更新的话,大部分情况下,会先更新到数据库,然后同步到redis。但这种跨库的操作,可能会导致两边数据不一致的情况产生。4。重复调用
  重复调用在我们的日常工作代码中可以说随处可见,但如果没有控制好,会非常影响接口的性能。
  不信,我们一起看看。4。1循环查数据库
  有时候,我们需要从指定的用户集合中,查询出有哪些是在数据库中已经存在的。
  实现代码可以这样写:publicListUserqueryUser(ListUsersearchList){if(CollectionUtils。isEmpty(searchList)){returnCollections。emptyList();}ListUserresultLists。newArrayList();searchList。forEach(userresult。add(userMapper。getUserById(user。getId())));returnresult;}复制代码
  这里如果有50个用户,则需要循环50次,去查询数据库。我们都知道,每查询一次数据库,就是一次远程调用。
  如果查询50次数据库,就有50次远程调用,这是非常耗时的操作。
  那么,我们如何优化呢?
  具体代码如下:publicListUserqueryUser(ListUsersearchList){if(CollectionUtils。isEmpty(searchList)){returnCollections。emptyList();}ListLongidssearchList。stream()。map(User::getId)。collect(Collectors。toList());returnuserMapper。getUserByIds(ids);}复制代码
  提供一个根据用户id集合批量查询用户的接口,只远程调用一次,就能查询出所有的数据。
  这里有个需要注意的地方是:id集合的大小要做限制,最好一次不要请求太多的数据。要根据实际情况而定,建议控制每次请求的记录条数在500以内。4。2死循环
  有些小伙伴看到这个标题,可能会感到有点意外,死循环也算?
  代码中不是应该避免死循环吗?为啥还是会产生死循环?
  有时候死循环是我们自己写的,例如下面这段代码:while(true){if(condition){break;}System。out。println(dosamething);}复制代码
  这里使用了while(true)的循环调用,这种写法在CAS自旋锁中使用比较多。
  当满足condition等于true的时候,则自动退出该循环。
  如果condition条件非常复杂,一旦出现判断不正确,或者少写了一些逻辑判断,就可能在某些场景下出现死循环的问题。
  出现死循环,大概率是开发人员人为的bug导致的,不过这种情况很容易被测出来。
  还有一种隐藏的比较深的死循环,是由于代码写的不太严谨导致的。如果用正常数据,可能测不出问题,但一旦出现异常数据,就会立即出现死循环。4。3无限递归
  如果想要打印某个分类的所有父分类,可以用类似这样的递归方法实现:publicvoidprintCategory(Categorycategory){if(categorynullcategory。getParentId()null){return;}System。out。println(父分类名称:category。getName());CategoryparentcategoryMapper。getCategoryById(category。getParentId());printCategory(parent);}复制代码
  正常情况下,这段代码是没有问题的。
  但如果某次有人误操作,把某个分类的parentId指向了它自己,这样就会出现无限递归的情况。导致接口一直不能返回数据,最终会发生堆栈溢出。
  建议写递归方法时,设定一个递归的深度,比如:分类最大等级有4级,则深度可以设置为4。然后在递归方法中做判断,如果深度大于4时,则自动返回,这样就能避免无限循环的情况。5。异步处理
  有时候,我们接口性能优化,需要重新梳理一下业务逻辑,看看是否有设计上不太合理的地方。
  比如有个用户请求接口中,需要做业务操作,发站内通知,和记录操作日志。为了实现起来比较方便,通常我们会将这些逻辑放在接口中同步执行,势必会对接口性能造成一定的影响。
  接口内部流程图如下:
  这个接口表面上看起来没有问题,但如果你仔细梳理一下业务逻辑,会发现只有业务操作才是核心逻辑,其他的功能都是非核心逻辑。
  在这里有个原则就是:核心逻辑可以同步执行,同步写库。非核心逻辑,可以异步执行,异步写库。
  上面这个例子中,发站内通知和用户操作日志功能,对实时性要求不高,即使晚点写库,用户无非是晚点收到站内通知,或者运营晚点看到用户操作日志,对业务影响不大,所以完全可以异步处理。
  通常异步主要有两种:多线程和mq。5。1线程池
  使用线程池改造之后,接口逻辑如下:
  发站内通知和用户操作日志功能,被提交到了两个单独的线程池中。
  这样接口中重点关注的是业务操作,把其他的逻辑交给线程异步执行,这样改造之后,让接口性能瞬间提升了。
  但使用线程池有个小问题就是:如果服务器重启了,或者是需要被执行的功能出现异常了,无法重试,会丢数据。
  那么这个问题该怎么办呢?5。2mq
  使用mq改造之后,接口逻辑如下:
  对于发站内通知和用户操作日志功能,在接口中并没真正实现,它只发送了mq消息到mq服务器。然后由mq消费者消费消息时,才真正的执行这两个功能。
  这样改造之后,接口性能同样提升了,因为发送mq消息速度是很快的,我们只需关注业务操作的代码即可。6。避免大事务
  很多小伙伴在使用spring框架开发项目时,为了方便,喜欢使用Transactional注解提供事务功能。
  没错,使用Transactional注解这种声明式事务的方式提供事务功能,确实能少写很多代码,提升开发效率。
  但也容易造成大事务,引发其他的问题。
  下面用一张图看看大事务引发的问题。
  从图中能够看出,大事务问题可能会造成接口超时,对接口的性能有直接的影响。
  我们该如何优化大事务呢?少用Transactional注解将查询(select)方法放到事务外事务中避免远程调用事务中避免一次性处理太多数据有些功能可以非事务执行有些功能可以异步处理
  关于大事务问题我的另一篇文章《让人头痛的大事务问题到底要如何解决?》,它里面做了非常详细的介绍,如果大家感兴趣可以看看。7。锁粒度
  在某些业务场景中,为了防止多个线程并发修改某个共享数据,造成数据异常。
  为了解决并发场景下,多个线程同时修改数据,造成数据不一致的情况。通常情况下,我们会:加锁。
  但如果锁加得不好,导致锁的粒度太粗,也会非常影响接口性能。7。1synchronized
  在java中提供了synchronized关键字给我们的代码加锁。
  通常有两种写法:在方法上加锁和在代码块上加锁。
  先看看如何在方法上加锁:publicsynchronizeddoSave(StringfileUrl){mkdir();uploadFile(fileUrl);sendMessage(fileUrl);}复制代码
  这里加锁的目的是为了防止并发的情况下,创建了相同的目录,第二次会创建失败,影响业务功能。
  但这种直接在方法上加锁,锁的粒度有点粗。因为doSave方法中的上传文件和发消息方法,是不需要加锁的。只有创建目录方法,才需要加锁。
  我们都知道文件上传操作是非常耗时的,如果将整个方法加锁,那么需要等到整个方法执行完之后才能释放锁。显然,这会导致该方法的性能很差,变得得不偿失。
  这时,我们可以改成在代码块上加锁了,具体代码如下:publicvoiddoSave(Stringpath,StringfileUrl){synchronized(this){if(!exists(path)){mkdir(path);}}uploadFile(fileUrl);sendMessage(fileUrl);}复制代码
  这样改造之后,锁的粒度一下子变小了,只有并发创建目录功能才加了锁。而创建目录是一个非常快的操作,即使加锁对接口的性能影响也不大。
  最重要的是,其他的上传文件和发送消息功能,任然可以并发执行。
  当然,这种做在单机版的服务中,是没有问题的。但现在部署的生产环境,为了保证服务的稳定性,一般情况下,同一个服务会被部署在多个节点中。如果哪天挂了一个节点,其他的节点服务任然可用。
  多节点部署避免了因为某个节点挂了,导致服务不可用的情况。同时也能分摊整个系统的流量,避免系统压力过大。
  同时它也带来了新的问题:synchronized只能保证一个节点加锁是有效的,但如果有多个节点如何加锁呢?
  答:这就需要使用:分布式锁了。目前主流的分布式锁包括:redis分布式锁、zookeeper分布式锁和数据库分布式锁。
  由于zookeeper分布式锁的性能不太好,真实业务场景用的不多,这里先不讲。
  下面聊一下redis分布式锁。7。2redis分布式锁
  在分布式系统中,由于redis分布式锁相对于更简单和高效,成为了分布式锁的首先,被我们用到了很多实际业务场景当中。
  使用redis分布式锁的伪代码如下:publicvoiddoSave(Stringpath,StringfileUrl){try{Stringresultjedis。set(lockKey,requestId,NX,PX,expireTime);if(OK。equals(result)){if(!exists(path)){mkdir(path);uploadFile(fileUrl);sendMessage(fileUrl);}returntrue;}}finally{unlock(lockKey,requestId);}returnfalse;}复制代码
  跟之前使用synchronized关键字加锁时一样,这里锁的范围也太大了,换句话说就是锁的粒度太粗,这样会导致整个方法的执行效率很低。
  其实只有创建目录的时候,才需要加分布式锁,其余代码根本不用加锁。
  于是,我们需要优化一下代码:publicvoiddoSave(Stringpath,StringfileUrl){if(this。tryLock()){mkdir(path);}uploadFile(fileUrl);sendMessage(fileUrl);}privatebooleantryLock(){try{Stringresultjedis。set(lockKey,requestId,NX,PX,expireTime);if(OK。equals(result)){returntrue;}}finally{unlock(lockKey,requestId);}returnfalse;}复制代码
  上面代码将加锁的范围缩小了,只有创建目录时才加了锁。这样看似简单的优化之后,接口性能能提升很多。说不定,会有意外的惊喜喔。哈哈哈。
  redis分布式锁虽说好用,但它在使用时,有很多注意的细节,隐藏了很多坑,如果稍不注意很容易踩中。详细内容可以看看我的另一篇文章《聊聊redis分布式锁的8大坑》7。3数据库分布式锁
  mysql数据库中主要有三种锁:表锁:加锁快,不会出现死锁。但锁定粒度大,发生锁冲突的概率最高,并发度最低。行锁:加锁慢,会出现死锁。但锁定粒度最小,发生锁冲突的概率最低,并发度也最高。间隙锁:开销和加锁时间界于表锁和行锁之间。它会出现死锁,锁定粒度界于表锁和行锁之间,并发度一般。
  并发度越高,意味着接口性能越好。
  所以数据库锁的优化方向是:
  优先使用行锁,其次使用间隙锁,再其次使用表锁。
  赶紧看看,你用对了没?8。分页处理
  有时候我会调用某个接口批量查询数据,比如:通过用户id批量查询出用户信息,然后给这些用户送积分。
  但如果你一次性查询的用户数量太多了,比如一次查询2000个用户的数据。参数中传入了2000个用户的id,远程调用接口,会发现该用户查询接口经常超时。
  调用代码如下:ListUserusersremoteCallUser(ids);复制代码
  众所周知,调用接口从数据库获取数据,是需要经过网络传输的。如果数据量太大,无论是获取数据的速度,还是网络传输受限于带宽,都会导致耗时时间比较长。
  那么,这种情况要如何优化呢?
  答:分页处理。
  将一次获取所有的数据的请求,改成分多次获取,每次只获取一部分用户的数据,最后进行合并和汇总。
  其实,处理这个问题,要分为两种场景:同步调用和异步调用。8。1同步调用
  如果在job中需要获取2000个用户的信息,它要求只要能正确获取到数据就好,对获取数据的总耗时要求不太高。
  但对每一次远程接口调用的耗时有要求,不能大于500ms,不然会有邮件预警。
  这时,我们可以同步分页调用批量查询用户信息接口。
  具体示例代码如下:ListListLongallIdsLists。partition(ids,200);for(ListLongbatchIds:allIds){ListUserusersremoteCallUser(batchIds);}复制代码
  代码中我用的google的guava工具中的Lists。partition方法,用它来做分页简直太好用了,不然要巴拉巴拉写一大堆分页的代码。8。2异步调用
  如果是在某个接口中需要获取2000个用户的信息,它考虑的就需要更多一些。
  除了需要考虑远程调用接口的耗时之外,还需要考虑该接口本身的总耗时,也不能超时500ms。
  这时候用上面的同步分页请求远程接口,肯定是行不通的。
  那么,只能使用异步调用了。
  代码如下:ListListLongallIdsLists。partition(ids,200);finalListUserresultLists。newArrayList();allIds。stream()。forEach((batchIds){CompletableFuture。supplyAsync((){result。addAll(remoteCallUser(batchIds));returnBoolean。TRUE;},executor);})复制代码
  使用CompletableFuture类,多个线程异步调用远程接口,最后汇总结果统一返回。9。加缓存
  解决接口性能问题,加缓存是一个非常高效的方法。
  但不能为了缓存而缓存,还是要看具体的业务场景。毕竟加了缓存,会导致接口的复杂度增加,它会带来数据不一致问题。
  在有些并发量比较低的场景中,比如用户下单,可以不用加缓存。
  还有些场景,比如在商城首页显示商品分类的地方,假设这里的分类是调用接口获取到的数据,但页面暂时没有做静态化。
  如果查询分类树的接口没有使用缓存,而直接从数据库查询数据,性能会非常差。
  那么如何使用缓存呢?9。1redis缓存
  通常情况下,我们使用最多的缓存可能是:redis和memcached。
  但对于java应用来说,绝大多数都是使用的redis,所以接下来我们以redis为例。
  由于在关系型数据库,比如:mysql中,菜单是有上下级关系的。某个四级分类是某个三级分类的子分类,这个三级分类,又是某个二级分类的子分类,而这个二级分类,又是某个一级分类的子分类。
  这种存储结构决定了,想一次性查出这个分类树,并非是一件非常容易的事情。这就需要使用程序递归查询了,如果分类多的话,这个递归是比较耗时的。
  所以,如果每次都直接从数据库中查询分类树的数据,是一个非常耗时的操作。
  这时我们可以使用缓存,大部分情况,接口都直接从缓存中获取数据。操作redis可以使用成熟的框架,比如:jedis和redisson等。
  用jedis伪代码如下:Stringjsonjedis。get(key);if(StringUtils。isNotEmpty(json)){CategoryTreecategoryTreeJsonUtil。toObject(json);returncategoryTree;}returnqueryCategoryTreeFromDb();复制代码
  先从redis中根据某个key查询是否有菜单数据,如果有则转换成对象,直接返回。如果redis中没有查到菜单数据,则再从数据库中查询菜单数据,有则返回。
  此外,我们还需要有个job每隔一段时间,从数据库中查询菜单数据,更新到redis当中,这样以后每次都能直接从redis中获取菜单的数据,而无需访问数据库了。
  这样改造之后,能快速的提升性能。
  但这样做性能提升不是最佳的,还有其他的方案,我们一起看看下面的内容。9。2二级缓存
  上面的方案是基于redis缓存的,虽说redis访问速度很快。但毕竟是一个远程调用,而且菜单树的数据很多,在网络传输的过程中,是有些耗时的。
  有没有办法,不经过请求远程,就能直接获取到数据呢?
  答:使用二级缓存,即基于内存的缓存。
  除了自己手写的内存缓存之后,目前使用比较多的内存缓存框架有:guava、Ehcache、caffine等。
  我们在这里以caffeine为例,它是spring官方推荐的。
  第一步,引入caffeine的相关jar包dependencygroupIdorg。springframework。bootgroupIdspringbootstartercacheartifactIddependencydependencygroupIdcom。github。benmanes。caffeinegroupIdcaffeineartifactIdversion2。6。0versiondependency复制代码
  第二步,配置CacheManager,开启EnableCachingConfigurationEnableCachingpublicclassCacheConfig{BeanpublicCacheManagercacheManager(){CaffeineCacheManagercacheManagernewCaffeineCacheManager();Caffeine配置CaffeineObject,ObjectcaffeineCaffeine。newBuilder()最后一次写入后经过固定时间过期。expireAfterWrite(10,TimeUnit。SECONDS)缓存的最大条数。maximumSize(1000);cacheManager。setCaffeine(caffeine);returncacheManager;}}复制代码
  第三步,使用Cacheable注解获取数据ServicepublicclassCategoryService{Cacheable(valuecategory,keycategoryKey)publicCategoryModelgetCategory(StringcategoryKey){Stringjsonjedis。get(categoryKey);if(StringUtils。isNotEmpty(json)){CategoryTreecategoryTreeJsonUtil。toObject(json);returncategoryTree;}returnqueryCategoryTreeFromDb();}}复制代码
  调用categoryService。getCategory()方法时,先从caffine缓存中获取数据,如果能够获取到数据,则直接返回该数据,不进入方法体。
  如果不能获取到数据,则再从redis中查一次数据。如果查询到了,则返回数据,并且放入caffine中。
  如果还是没有查到数据,则直接从数据库中获取到数据,然后放到caffine缓存中。
  具体流程图如下:
  该方案的性能更好,但有个缺点就是,如果数据更新了,不能及时刷新缓存。此外,如果有多台服务器节点,可能存在各个节点上数据不一样的情况。
  由此可见,二级缓存给我们带来性能提升的同时,也带来了数据不一致的问题。使用二级缓存一定要结合实际的业务场景,并非所有的业务场景都适用。
  但上面我列举的分类场景,是适合使用二级缓存的。因为它属于用户不敏感数据,即使出现了稍微有点数据不一致也没有关系,用户有可能都没有察觉出来。10。分库分表
  有时候,接口性能受限的不是别的,而是数据库。
  当系统发展到一定的阶段,用户并发量大,会有大量的数据库请求,需要占用大量的数据库连接,同时会带来磁盘IO的性能瓶颈问题。
  此外,随着用户数量越来越多,产生的数据也越来越多,一张表有可能存不下。由于数据量太大,sql语句查询数据时,即使走了索引也会非常耗时。
  这时该怎么办呢?
  答:需要做分库分表。
  如下图所示:
  图中将用户库拆分成了三个库,每个库都包含了四张用户表。
  如果有用户请求过来的时候,先根据用户id路由到其中一个用户库,然后再定位到某张表。
  路由的算法挺多的:根据id取模,比如:id7,有4张表,则743,模为3,路由到用户表3。给id指定一个区间范围,比如:id的值是010万,则数据存在用户表0,id的值是1020万,则数据存在用户表1。一致性hash算法
  分库分表主要有两个方向:垂直和水平。
  说实话垂直方向(即业务方向)更简单。
  在水平方向(即数据方向)上,分库和分表的作用,其实是有区别的,不能混为一谈。分库:是为了解决数据库连接资源不足问题,和磁盘IO的性能瓶颈问题。分表:是为了解决单表数据量太大,sql语句查询数据时,即使走了索引也非常耗时问题。此外还可以解决消耗cpu资源问题。分库分表:可以解决数据库连接资源不足、磁盘IO的性能瓶颈、检索数据耗时和消耗cpu资源等问题。
  如果在有些业务场景中,用户并发量很大,但是需要保存的数据量很少,这时可以只分库,不分表。
  如果在有些业务场景中,用户并发量不大,但是需要保存的数量很多,这时可以只分表,不分库。
  如果在有些业务场景中,用户并发量大,并且需要保存的数量也很多时,可以分库分表。
  关于分库分表更详细的内容,可以看看我另一篇文章,里面讲的更深入《阿里二面:为什么分库分表?》11。辅助功能
  优化接口性能问题,除了上面提到的这些常用方法之外,还需要配合使用一些辅助功能,因为它们真的可以帮我们提升查找问题的效率。11。1开启慢查询日志
  通常情况下,为了定位sql的性能瓶颈,我们需要开启mysql的慢查询日志。把超过指定时间的sql语句,单独记录下来,方面以后分析和定位问题。
  开启慢查询日志需要重点关注三个参数:slowquerylog慢查询开关slowquerylogfile慢查询日志存放的路径longquerytime超过多少秒才会记录日志
  通过mysql的set命令可以设置:setglobalslowquerylogON;setglobalslowquerylogfileusrlocalmysqldataslow。log;setgloballongquerytime2;复制代码
  设置完之后,如果某条sql的执行时间超过了2秒,会被自动记录到slow。log文件中。
  当然也可以直接修改配置文件my。cnf〔mysqld〕slowquerylogONslowquerylogfileusrlocalmysqldataslow。loglongquerytime2复制代码
  但这种方式需要重启mysql服务。
  很多公司每天早上都会发一封慢查询日志的邮件,开发人员根据这些信息优化sql。11。2加监控
  为了出现sql问题时,能够让我们及时发现,我们需要对系统做监控。
  目前业界使用比较多的开源监控系统是:Prometheus。
  它提供了监控和预警的功能。
  架构图如下:
  我们可以用它监控如下信息:接口响应时间调用第三方服务耗时慢查询sql耗时cpu使用情况内存使用情况磁盘使用情况数据库使用情况
  等等
  它的界面大概长这样子:
  可以看到mysql当前qps,活跃线程数,连接数,缓存池的大小等信息。
  如果发现数据量连接池占用太多,对接口的性能肯定会有影响。
  这时可能是代码中开启了连接忘了关,或者并发量太大了导致的,需要做进一步排查和系统优化。
  截图中只是它一小部分功能,如果你想了解更多功能,可以访问Prometheus的官网:prometheus。io11。3链路跟踪
  有时候某个接口涉及的逻辑很多,比如:查数据库、查redis、远程调用接口,发mq消息,执行业务代码等等。
  该接口一次请求的链路很长,如果逐一排查,需要花费大量的时间,这时候,我们已经没法用传统的办法定位问题了。
  有没有办法解决这问题呢?
  用分布式链路跟踪系统:skywalking。
  架构图如下:
  通过skywalking定位性能问题:
  在skywalking中可以通过traceId(全局唯一的id),串联一个接口请求的完整链路。可以看到整个接口的耗时,调用的远程服务的耗时,访问数据库或者redis的耗时等等,功能非常强大。
  之前没有这个功能的时候,为了定位线上接口性能问题,我们还需要在代码中加日志,手动打印出链路中各个环节的耗时情况,然后再逐一排查。
  如果你用过skywalking排查接口性能问题,不自觉的会爱上它的。如果你想了解更多功能,可以访问skywalking的官网:skywalking。apache。org
  唠叨唠叨
  东哥在全网写了很多图解网络和操作系统的系列文章,很高兴收获到很多朋友的认可和支持,正好最近图解网络和操作系统的文章连载的有20篇了,也算有个体系了。

电视能有什么新的黑科技?SONYA80EKOLED这回又让我和其他家电不同,大屏电视近年来在智能方面亮点有限,除了高清的分辨率跟随各路载体的升级而进行适配之外,基于安卓的流畅OS,运动补偿MEMC,智能语音助手等功能都不再是什么新鲜事,……腾讯出手!入股这家A股子公司中国基金报记者忆山腾讯医疗版图再添新项目!9月19日,鱼跃医疗公告称,公司全资子公司迅捷医疗将引进战略投资者腾讯。资料显示,迅捷医疗去年11月刚成立,是鱼跃医疗急救……冬至前夜山西竹叶青女篮杀进季后赛,WCBA附加赛山西队66612月20日晚,冬至前夜,山西女篮与北京女篮迎来一场对双方而言至关重要的焦点战季后赛门票争夺战。这是一场双方球员都拼尽全力的比赛,所有人拼杀到最后一秒!最终,山西竹叶青女篮66……中超丢人丢到国外!遭韩媒大肆嘲讽又在踢少林足球2022赛季中超联赛已经过了6轮,由于各种原因导致本赛季联赛竞技水平大幅下滑,并且粗野犯规和低级失误不断,令球迷心寒。在联赛第六轮结束后,中超的粗野暴力场面甚至吸引了韩国媒体的……新疆阵容调整完毕!五大锋线齐聚,25后卫支援,阿的江目标四强新疆男篮休赛期的阵容调整工作基本结束,在外援配置方面,阿的江引进了摩尔特里和杰克逊,两大超级得分手齐聚新疆,阿的江解决了进攻端的短板问题,后防线的压力也得到了缓解。在本土人员引……噪音可以通过交叉感官干扰影响动物的配偶选择来源:【科学网】近日,中国科学院成都生物研究所动物行为与仿生项目组在国际学术期刊《环境污染》在线发表题为《噪音通过交叉感官干扰影响树蛙利用视觉线索的配偶选择》的研究论文。……12256的平民版鸿蒙手机荣耀30Pro其各方面体验如何?对于目前搭载了鸿蒙系统的华为手机,基本上都只有8GB运存的版本可选,只有像华为Mate50RS这样的顶级豪华旗舰才会给到12GB运存的版本,但是这款手机的价格也太贵了,动辄就是……尝尝保加利亚酸奶来源:经济日报世人都知道保加利亚玫瑰,但保加利亚酸奶您喝过吗?超乎大部分人的想象,酸奶的历史长到惊人。据考证,早在4000多年前,酸奶就已现身人类社会。中国北魏时期……武切维奇德罗赞拉文合砍83分,公牛大胜黄蜂在今日的常规赛中,公牛主场133119轻取黄蜂。隆佐上来就连中三分、武切维奇也中三分,公牛134开局。华盛顿连中四记三分将比分追近,之后双方交替得分,首节黄蜂以3130领……4床6座家庭旅行房车,配备1000AH锂电池组,可在新能源停一款房车的可居住人数是很重要的指标,对于家庭旅行来说,能住45人的大空间C型房车是更适合的选择,本期就给大家带来一款4床6座达到可拎包入住配置标准的赛德白鲸房车。本期这台……竹篮打水一场空!勇士这回真的要妥协了交易普尔内幕曝光勇士队要想同时留下追梦格林、维金斯和乔丹普尔是不可能的事情,众所周知,乔丹普尔在上赛季打出了高光表现后,如今他的身价已经非同凡响,因为勇士队要想留下普尔非常困难,因为追梦格林还……广东休赛期24小时,朱芳雨或亮剑,恭喜胡明轩,200万真香随着广东宏远结束本赛季的征途,回到自己大本营,赛场上也暴露了很多问题,接下来广东队也是可能要进行一次大的洗牌了,据小道消息透漏,胡明轩已经完成了和广东队续约,双方签订了一份多年……
漫议霉变竹筒装奶茶,别让网红变网黑日前,在杭州塘栖古镇景点,有网友发现一奶茶店员工将霉变竹筒简单清洗后,准备用来装奶茶卖给顾客,且洗刷竹筒的也不是流动水,清洗环境很差。目前涉事奶茶店已停业,后续调查情况将及时向……用4G热点取代家庭宽带,这现实吗?春节12天试用,告诉你结论自从4G普及之后,估计就有不少小伙伴在琢磨一件事了。手机网络这么快,基本能够覆盖日常使用的需求,那是不是家里可以不用办宽带了?到了如今5G普及,手机网速更快,那它能取代宽带吗?……太阳和地球之间温度极低,真空又不导热,热量怎么传到地球?你知道太空的温度是多少吗?地球的热量来自哪里?地球的温度变化是因为什么?很久之前,个人一直以为太阳的温度是一路从太阳表面慢慢传输到地球的,所以以太阳为中心,其热能覆盖范围……嵌入式冰箱持续走红容声冰箱引领潮流创新无界空间奥维云网日前发布数据显示,虽然2022年冰箱行业整体市场呈下滑趋势,但嵌入式冰箱销售却逆势走高,零售规模同比大增64。9,远超冰箱整体市场8。9的增幅。在大量需求冲击下,多个嵌……科美诊断第二三大股东及其一致行动人拟合计减持不超过9公司股份科美诊断11月11日公告,第二大股东横琴君联致康投资企业(以下简称横琴君联致康)及其一致行动人、第六大股东LOYALCLASSLIMITED(以下简称LOYALCLASS)拟合……反思游客不来之三思近日,游客为什么不来引起热议。游客不来是有原因的,应该从深层次去思考产生问题的原因,要从景区本身、服务质量、游客自身等方面进行综合思考。找出问题症结,对症下药、靶向治疗,让老景……ApexToys明日方舟前航远歌闪灵手办预售开启今天(7月28日)ApexToys官方在微博上宣布,热门手机塔防游戏《明日方舟》中的角色闪灵前航远歌Ver。17手办预售现已开启。预定商店页面:此次前航远歌Ver。……16项新改进,德生PL320相对于前代PL330收音机的新亮原创:收音机评论译介这两天,我一直在把玩德生新品PL320收音机,发布了两个原创视频,还分享了一组真机高清大图。但是,广大读者感觉还不过瘾,一股脑儿向我抛来很多问题,例如……去大连吃海鲜,有哪些好吃不贵的美食?好吃的就不便宜大连美食攻略,在大连如何吃海鲜〔呲牙〕新长兴经过多方打探和调查,大连人正确的吃海鲜方式是在市场买回来自己做,大排档什么的很容易吃到已经死了的。市场有两……曝皇马同意魔笛和利雅得胜利谈判,报价2年合同4500万欧沙特媒体Okaz独家报道,利雅得胜利向皇家马德里中场莫德里奇开出了一份天价报价:2年4500万欧元的合同。主教练鲁迪加西亚与俱乐部主席穆斯利穆阿迈尔和克里斯蒂亚诺罗纳尔多……去吃麻辣烫,懂行人专挑3样菜,好吃不压秤,老板见了心滴血!大家好我是傻姐,唯有美食和美景不可辜负,我是烟台爱美食的大姐,每天会分享一下美食小窍门和做法,还会分享水果的小知识,今天我分享的是出去吃麻辣烫的时候点菜的小技巧。学会了很有益处……保护视力,从娃抓起,走开眼镜侠孩子的视力发育是在外界环境刺激下形成的和自身不断修复完善的一个过程;想要孩子拥有一个炯炯有神的双眼,预防眼睛近视,在日常生活中,除了让孩子拥有良好的生活和学习习惯以外,还需要做……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网