将开发人员Lauz 标为隆哥蒙 名品该文第三天数读
他们好,我是爱写标识符的Lauz!
USB性能优化对专门从事后端合作开发的老师而言,的确再熟识但是了,有的是子公司将USB操控性加入到考核中。那时和他们谈谈合作开发中常见的USB强化形式。
检索
对查阅他们具体而言会想不到 sql 与否加检索了或是与否走检索了。
他们能透过 explain 指示看下检索的采用情形
explain select * from usero whereuserId like%234;
SQL强化形式
创建检索准则参照:MySQL检索
深巨集难题
MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回 N 行,那当offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行 SQL改写。
标签记录法
select id,name FROM user where id > 100000 limit10;
延迟关联法
延迟关联法,就是把条件转移到主键检索树,然后减少回表。强化后的SQL如下:
select a.id,a.name,a.balance FROM user a INNER JOIN (SELECT b.id FROM user b WHERE b.create_time >2022-05-19 limit100000, 10) AS c on a.id= c.id;
先透过create_time二级检索树查阅到满足条件的主键ID,再与原表透过主键ID内连接,这样后面直接走了主键检索了,同时也减少了回表。
采用缓存
缓存是一种空间换天数的思想,把要查的数据,放好到缓存里面,需要时,直接查缓存,而避免去查数据库或是计算的过程。
缓存通常有两种:一种是本地缓存也就是 JVM 缓存,另一种是 Memcached/Redis 类的集中式缓存。
采用缓存也能采用预取思想,提前把将来可能需要的数据计算好,放到缓存中,等需要的时候,去缓存取就行。能透过定时任务定时去更新缓存。
采用缓存需要考虑几个难题:
缓存的高可用难题。如果缓存宕机,与否会压垮数据库?缓存穿透。虽然缓存没有宕机,但是某些 Key 发生了大量查阅,并且这些Key 都不在缓存里,导致短天数内大量请求压垮数据库。缓存击穿。指两个热点 Key,大并发集中对这两个点进行访问,当这个 Key在失效的瞬间,持续的大并发就穿破缓存,直接请求数据库。大量的热 Key 过期。某些 Key 失效,大量请求在短天数内写入并压垮数据库,也就是缓存雪崩。缓存和数据库一致性难题。池化思想
池化思想解决的难题就是避免重复创建对象或创建连接,能重复利用,避免不必要的损耗,毕竟创建销毁也会占用天数。他们常见的线程池、数据库连接池都是池化思想。
他们在写标识符时候能利用这种思想来强化USB操控性,凡是不阻碍主流程的业务逻辑,都能异步化,放到后台去做。
异步处理
异步思想:针对耗时比较长且不是结果必须的逻辑,能考虑放到异步执行,这样能降低USB耗时。
比如:用户注册成功后,短信邮件通知,也是能异步处理
异步的实现形式,能用线程池、异步注解、异步调用,也能用消息队列实现。
List<List<Long>> allIds = Lists.partition(ids,200);
final List
allIds.stream().forEach((batchIds) -> {
CompletableFuture.supplyAsync(() -> {
result.addAll(remoteCallUser(batchIds));
returnBoolean.TRUE;
}, executor);
})
串行改并行
串行就是,当前执行逻辑必须等上两个执行逻辑结束之后才执行,并行就是两个执行逻辑同时进行。
如果是同步调用,则所消耗的总天数 T=T1+T2 +T3;如果是异步调用,则所消耗的总天数 T=Max(T1, T2,T3)。
串行条件:3 个调用之间没有耦合关系,能并行。如果必须在拿到第 1 个调用的结果之后,根据结果再去调用第 2、第 3 个USB,就不能做异步调用了。
批量处理
批量操作数据库,主要是针对同时保存多个数据。
强化前:
//for循环单笔入库
for(User user:userList){
insert(user);
}
强化后:
batchInsert(userList);
避免大事务
大事务就是运行天数长的事务。由于事务一致不提交,就会导致数据库连接被占用,即并发场景下,数据库连接池被占满,影响到别的请求访问数据库,影响别的USB操控性,甚至有可能拖垮数据库。
因此,为了强化USB,他们要规避大事务难题。他们能透过这些方案来规避大事务:
RPC远程调用不要放到事务里面查阅相关的操作,尽量放到事务之外事务中避免处理太多数据锁粒度避免过粗
锁一般是为了在高并发场景下保护共享资源采用的一种手段,但是如果锁的粒度太粗,会很影响USB操控性。
关于锁粒度:就是要锁的范围有多大,不管是jvm锁、redis分布式锁还是数据库锁,只需要在临界资源处加锁即可,不涉及共享资源的,不必要加锁。
错误形式
//非共享资源 private void notShare(){
}
//共享资源 private void share(){
}
private int wrong(){
synchronized (this) {
share();
notShare();
}
}
正确形式:
//非共享资源 private void notShare(){
}
//共享资源 private void share(){
}
private int right(){
notShare();
synchronized (this) {
share();
}
}
强化程序结构
强化程序逻辑、程序标识符,是能节省耗时的。比如,程序创建多不必要的对象、或是程序逻辑混乱,多次重复查数据库、又或是实现逻辑算法不是最高效的,甚至出现死循环等等。
复杂的逻辑条件,有时候调整一下顺序,就能让程序更加高效。
有时候死循环是他们自己写的,例如下面这段标识符:
while(true) {
if(condition) {
break;
}
System.out.println(“do samething”);
}
如果condition条件非常复杂,一旦出现判断不正确,或是少写了一些逻辑判断,就可能在某些场景下出现死循环的难题。
分库分表
当系统发展到一定的阶段,用户并发量大,会有大量的数据库请求,需要占用大量的数据库连接,同时会带来磁盘IO的操控性瓶颈难题。
此外,随着用户数量越来越多,产生的数据也越来越多,由于数据量太大,sql语句查阅数据时,即使走了检索也会非常耗时。
这时候就要分库分表了。
分库分表主要有两个方向:垂直和水平。
在水平方向(即数据方向)上,分库和分表的作用,其实是有区别的,不能混为一谈。
分库:是为了解决数据库连接资源不足难题,和磁盘IO的操控性瓶颈难题。分表:是为了解决单表数据量太大,sql语句查阅数据时,即使走了检索也非常耗时问题。此外还能解决消耗cpu资源难题。分库分表:能解决 数据库连接资源不足、磁盘IO的操控性瓶颈、检索数据耗时 和 消耗cpu资源等难题。