转自春天的旁边微信公众号
1.总原则
一些正确但稍显废话的原则,但能指导后面每个章节的优化,所以还是要啰嗦一次。
-
可扩展性架构,堆机器能不能解决问题是最最优先考虑的问题
-
去中心化的点对点通信,优于通过中心代理的通信
-
池化的长连接,优于短连接
-
二进制数据,优于文本数据
-
尽量减少交互,一次调用的粗粒度聚合接口 优于 多次调用的细粒度接口
-
尽量减少交互,批量接口 优于 循环调用
-
尽量只交互必要的数据
-
尽量就近访问
-
尽量使用缓存
-
总是设定超时
-
在合适的场景,并行化执行
-
在合适的场景,异步化执行
2.环境准备
保证符合自家各种规范(没有的话赶紧回家写一个),尤其线下压测服务器的配置要与生产环境一致。
2.1 操作系统
-
自家规范调优应包含TCP内核参数,网卡参数及多队列绑定,IO&swap内核参数,ulimit资源限制等。
2.2 JVM与应用服务器
-
使用JDK7.0 u80 或 JDK8 最新版。
-
检查JVM启动参数已按自家规范调优,见《关键业务系统的JVM参数推荐》
-
检查应用服务器(Tomcat或微服务容器) 已按自家指南调优,如线程数等。
2.3 周边依赖系统
-
检查数据库,缓存,消息系统,已按自家指南调优。
2.4 后台辅助程序
-
检查日志收集,系统监控等,已使用最新版本,最优配置。
-
最好其最大消耗已被控制(通过cgroup,taskset等方式)。
2.5 测试程序
-
压测工具如JMeter,启动参数要参考真实客户端优化(如JVM参数,Netty参数等)。
-
测试脚本和客户端程序经过review,不存在影响性能的步骤,不存在System.out.println()等明显的瓶颈。
2.6 流量模型
-
扇入模型:平时与高峰期的流量估算,各接口的流量比例,响应时间要求
-
扇出模型:各接口对远程服务、数据库、缓存、消息系统的调用比例,响应时间估算,缓存的命中率,击穿时的访问模式。
大家在心里都有这么一个大概的模型,但很少认真写出来。
行文到此,大家大概可以感受到这份checklist的风格,都是大家明白的道理,但可能一时也会忘掉的,这里啰啰嗦嗦的給写下来。
3.数据库
仅以MySQL举例,特别鸣谢,我司DBA,如聂超大侠,文中大量观点均来自他们。
3.1 拓扑
根据扩展性原则考虑:
-
垂直拆分:按业务将表拆分到不同的库。
-
水平拆分:分表分库。
-
读写分离:在业务允许的情况下,在从库读取非实时数据。
3.2 Schema
自家规范应包含:
-
统一的存储引擎,主键策略。
-
禁用存储过程,函数,触发器,外键约束。
-
列类型永远越短越好,建议:布尔/枚举:tinyint,日期与时间戳:timestamp或int,char/text/blob: 尽量用符合实际长度的varchar(n),小数及货币:移位转为int 或 decimal,IP地址:int。
-
索引策略:索引字段的顺序需要考虑字段值去重之后的个数,较多的放前面,合理创建联合索引,避免冗余索引,合理利用覆盖索引等。
3.3 SQL
1. 自家规范应包含:
-
禁止多于3表的join,禁用子查询
-
禁止where子句中对字段施加函数使索引失效,如to_date(add_time)>xxxxx
-
不建议使用%前缀模糊查询影响索引使用,模糊查询较多时建议使用ElasticSearch
-
避免隐式类型转化,如ISENDED=1 与 ISENDED='1'
根据尽量少数据原则与尽量少交互的原则来设计SQL:
-
禁止select *
-
复杂度合理的SQL语句,减少交互次数。
根据扩展性原则,将负载放在更容易伸缩的应用服务实例上:
-
尽量不要做数学运算,函数运算, 或者输出格式转换等非必要操作
-
避免count(*),计数统计实时要求较强使用memcache或者redis,非实时统计使用定时更新的单独统计表。
-
甚至排序都是不鼓励的,尽量在应用侧进行。另外避免多余的排序,使用GROUP BY 时,默认会进行排序,当你不需要排序时,可以使用order by null。
2. 联系DBA进行MySQL统计的慢查询的Review,解析SQL查询计划时尽量避免extra列出现:Using File Sort,Using Temporary
3.4 DAO框架
-
根据尽量少交互与尽量少数据的原则,需使用对SQL完全可控的DAO框架,建议为MyBatis 或 Spring JDBC Template。
-
必须使用prepareStatement,提升性能与防注入。
-
根据一切皆有超时的原则,配置SQL执行的超时。可在连接池里设置default值,可在MyBatis的Mapper定义里设置每个请求的超时,可惜规范是秒级的。
-
JDBC driver 规范本身不支持异步模式,如果一定要异步,可以像Quasar那样把请求封装成Callable交给另外的线程池执行,但注意其额外开销。
3.5 事务
-
不使用事务,连接池设置autocommit,使用其他方式来保持数据一致性。
-
通过@Transaction控制事务,事务跨度尽量短,把非事务范围内的业务逻辑剔除到被标注的函数之外。
-
只读事务可以不加事务标注。
3.6 连接池
选型:
-
在水平分库分表时,根据点对点通信优先的原则,尽量使用客户端分片的实现。功能不满足时才用MyCat中央代理。
-
推荐使用性能最高HikariCP,或者Druid,或者Tomcat JDBC,不推荐c3p0与DBCP。
连接池的配置:
-
配置连接初始值,再联系DBA获得线上数据库支持的连接数,计算最大连接数。
-
连接有效性的检查,只在连接空闲检测时执行,不在获取和归还连接时执行。直接使用数据库的Ping方案,不要配置检查的SQL。
-
根据总是设置超时的原则,配置获取连接的超时时间。
-
配置合理的空闲连接回收间隔和空闲时间。
4.缓存
4.1 多级缓存
-
根据缓存原则, 缓存 > 数据库/远程调用
-
根据就近原则, 堆内缓存 > 堆外缓存 > 集中式缓存
-
堆内缓存受大小限制,并影响GC
-
堆内缓存与堆外缓存,分布在每一台应用服务器上,所以刷新方式比集中式的复杂
-
堆外缓存与集中式缓存,需要序列化/反序列化对象
-
集中式缓存,有网络传输的成本,特别是数据超过一个网络包的大小。如果一次获取多个键时,在有分区的情况下,更需要收发多个网络包。
使用上述条件选择合适的缓存方案,或同时使用多级缓存,逐层回源。
4.2 公共
-
需要对回源进行并发控制,当key失效时,只有单一线程对该key回源。
-
基于二进制优于文本数据的原则,JSON的序列化方案较通用与更高的可读性。
而较大,结构较复杂的对象,基于Kyro,PB,Thrift的二进制序列化方案的性能更高,见后面的序列化方案部分。
4.3 堆内缓存
选型:
-
推荐Guava Cache。
-
Ehcache较重,性能也较差。更不要使用存在严重并发bug的Jodd Cache。
GuavaCache:
-
正确设置并行度等参数。
-
重载load()函数,实现单一线程回源。
-
Guava Cache能在后台刷新,在刷新的过程中,依然使用旧数据响应请求,不会造成卡顿,但需要重载实现reload()函数。
-
Guava Cache同时还支持并发安全版的WeakHashMap。
4.4 堆外缓存
选型:
-
推荐Cassandra的OHC 或者 OpenHFT的Chronical map2。
-
OHC够简单,其实R大不喜欢Chronical,玩的太深,换个JDK都可能跑不起来。
-
Chronical map3的license则较不友好,复杂度高且要求JDK8。
-
其他如Ehcache的Terracota Offheap 一向不喜欢。
4.5 Memcached
客户端:
-
基于点对点通信优于网关的原则,使用客户端一致性哈希分区。
-
推荐Spymemcached。 XMemcached 太久没更新,Folsom知名度不高。
-
注意Spymemcached为单线程单连接架构(一个SpyClient只有一条IO线程,与每个Memcached只有一条连接),必要时可多建几个SpyClient随机选择,但不要用Commons Pool去封装它,把Spy原本的设计一笔抹杀。
-
根据在合适场景使用并发的原则,Spymemcached支持异步API。
-
根据一切皆设超时的原则,可在ConnectionFactory中设置最大超时数,默认值两秒半太长。
数据结构:
-
Key必须设置失效时间。
-
Key必须有长度限制。
-
Value长度需要控制,以不超过1个网络包(MTU 1500byte)为佳。
-
Value大小差别较大的缓存类型,建议拆分到不同MC集群,否则会造成低使用率并且产生踢出。
4.6 Redis as Cache
Redis拓扑,基于点对点通信优于网关的原则:
-
无HA的普通分片:由Jedis客户端完成分片路由。
-
Redis Cluster:同样由Jedis客户端封装分区,跳转,重试等逻辑。最好使用最新的Jedis版本,旧版太多bug。
服务端:
-
Cache节点与持久化数据节点不要混用。
-
Cache节点是否需要持久化要衡量。
-
对热键进行监控,发现不合理的热健要进行分拆等处理。
客户端:
-
Jedis基于Apache Commons Pool进行了多连接的封装,正确配置总连接数不超过Redis Server的允许连接数。
-
性能考虑,空闲连接检查不要过于频繁(建议30秒以上),另不要打开testOnBorrow等测试参数。
-
根据一切皆有超时的原则,设定统一的调用超时(默认2秒),获取连接的最长等待时间参数,重试次数。
-
根据在合适的地方异步的原则,Jedis本身没有异步API,只在PipleLine模式下支持。
数据结构:
-
Key必须设置失效时间。
-
Key必须有长度限制。
-
Value长度需要控制,以不超过1个网络包(MTU 1500byte)为佳。另外set和sorted的elements不要超过5000个。
-
除了使用序列化的String,可以考虑用Hash来存储对象,注意内部结构为ZipList与HashTable时,hmget 与hgetall的不同复杂度。
命令:
-
慎用的命令:LANGE(0, -1), HGETALL, SMEMBER
-
高复杂度的命令: ZINTERSTORE, SINTERSTORE, ZUNIONSTORE, ZREM
-
尽量使用多参数的命令:MGET/MSET,HMGET/HMSET, LPUSH/RPUSH等
-
根据减少交互的原则,尽量使用pipeline
-
根据减少交互的原则,必要时可使用Redis的Lua脚本
5.服务调用
5.1 接口设计
1. 尽量少交互的原则:
支持批量接口,最大的批量,综合考虑调用者的需求与 后端存储的能力。
支持粗粒度接口,在支持原子细粒度接口的同时,支持粗粒度接口/聚合层接口,将多个数据源的获取,多个动作,合并成一个粗粒度接口。
2. 尽量少数据的原则:
在提供返回所有数据的大接口的同时,提供只提供满足部分调用者需要的轻量接口。
最好再提供能定制返回字段的接口。
3. 二进制数据优于文本数据
同样是一个简单通用性,与性能的选择,特别是大数据量时。
5.2 RESTful
仅以Apache HttpClient为例,大部分Restful框架都是对Apache HttpClient的封装。
另外OkHttp也值得看看。
-
不要重复创建ApacheClient实例,使用连接池,正确配置连接池的连接数。
-
连接池总是有锁,针对不同的服务,使用不同的Apache HttpClient实例,将锁分散开来。在高并发时比使用全局单例的ApacheClient,有很大的性能提升。
-
根据一切调用皆有超时的原则,每次调用均设置超时时间。RequestConfig里共有Connect Timeout, Socket Timout 和 从Pool中获取连接的Timeout三种超时。
-
需要异步或并行的场景,使用Apache AsyncHttpClient项目。但要注意AsyncHttpClient项目,检查调用超时的周期默认为1秒。
5.3 自家RPC框架
每家的RPC框架特性不同,但考虑点都类似。
6.消息异步
6.1 选型
-
根据就近原则,可以先尝试用JVM内的队列来解决,然后再考虑中央消息系统。
-
可靠性要求极高的选择RabbitMQ,可支持单条消息确认。
-
海量消息场景,允许极端情况下少量丢失则使用Kafka。
6.2 Kafka
-
根据扩展性原则,RabbitMQ本身没有分片功能,但可以在客户端自行分片。
-
在同步和异步之间做好权衡,异步批量发送可以极大的提高发送的速度。
-
关注消费者如下参数:commitInterval(自动提交offset间隔),prefetchSize(指单次从服务器批量拉取消息的大小),过大和过小都会影响性能,建议保持默认。
6.3 RabbitMQ
-
根据扩展性原则,RabbitMQ本身没有分片功能,但可以在客户端自行分片。
-
如非必要情况,应该保持默认的同步发送模式。
-
关注消费者如下参数:autocommit(自动提交确认,默认false) ,在消息拉取到本地即认为消费成功,而不是真正消费成功后提交。prefetchCount(预取消息条数,默认64条)
-
生产者在必要时也可以临时降级不进行confirm。
7. 日志
7.1 综述
-
Log4j2或logback,不要再使用Log4j。
-
除了应用启停日志,不允许使用超慢的System.out.println() 或 e.printStack();
-
严格控制日志量避免过高IO,对海量日志,应该有开关可以动态关停。
-
如果可能出现海量异常信息,可仿效JDK的优化,用RateLimiter进行限流,丢弃过多的异常日志。
7.2 内容
-
严格控制日志格式,避免出现消耗较大的输出如类名,方法名,行号等。
-
业务日志不要滥用toJSONString()来打印对象,尽量使用对象自身的toString()函数,因为JSON转换的消耗并不低。
-
在生产环境必定输出的日志,不要使用logger.info("hello {}", name)的模式,而是使用正确估算大小的StringBuilder直接拼装输出信息。
7.3 异步日志
-
同步日志的堵塞非常严重,特别是发生IO的时候,因此尽量使用异步日志。
-
Logback的异步方案存在一定问题,需要正确配置Queue长度,阀值达到多少时丢弃Warn以下的日志,最新版还可以设置如果队列已满,是等待还是直接丢弃日志。
-
如果觉得Logback的异步日志每次插入都要询问队列容量太过消耗,可重写一个直接入列,不成功则直接丢弃的版本。
8. 工具类
8.1 JSON
-
使用Jackson 或 FastJSON。GSON的性能较前两者为差,尤其是大对象时。
-
超大对象可以使用Jackson或FastJSON的Streaming API进行处理。
-
将不需要序列化的属性,通过Annotation排除掉。
FastJson:
-
尽量使用最新的版本。
-
SerializerFeature.DisableCircularReferenceDetect 关闭循环引用检查。
Jackson:
-
设置参数,不序列化为Null的属性,等于默认值的属性。
-
除了jackson-databinding,可试用简化版没那么多花样的jackon-jr。
8.2 二进制序列化
需要定义IDL的PB与Thrift,不需要定义的Storm等用的Kyro 都可选择,其他一些比较旧
就算了。
8.3 Bean复制
在VO,BO之间复制时,使用Orika(生成代码) 或 Dozer(缓存反射),不要使用需要每次进行反射的Apache BeanUitls,Spring BeanUtils。
8.4 日期
JDK的日期类与字符串之间的转换很慢且非线程安全。
继续用Date不想大动作的,就用Commons Lang3的FastDateFormat。
能大动作就用joda time的Date,或者JDK8的新日期API。
9.Java代码优化 与 业务逻辑优化
参考《Java调优指南1.8版》,对内存使用,并发与锁等方面进行优化。
规则前置,将消耗较大的操作放后面,如果前面的条件不满足时可。
另外前面提到的一堆原则,比如尽量缓存,尽量少交互,尽量少数据,并行,异步等,都可在此使用。
相关推荐
一、脚本优化checklist 在进行脚本优化时,需要注意以下几点: * 登陆事件:登陆事件是系统的入口,需要对登陆事件进行优化,减少响应时间。 * 统计登陆的响应时间:需要统计登陆事件的响应时间,以便了解系统的...
7. **性能优化**:当数据量较大时,一次性加载所有选项可能会影响性能。此时,可以考虑使用虚拟化技术,只在需要时加载可视区域内的项,以提高界面响应速度。 8. **异常处理**:在数据填充或处理用户操作时,应考虑...
【Android性能优化典范 - 第6季 - 胡凯】主要涵盖了Android应用性能优化的关键方面,特别是关于程序启动时间和安装包大小的优化。以下是详细的知识点解析: 1. **程序启动时间优化**: - **启动时间的重要性**:...
4. 数据结构与算法选择:优化性能,避免后期问题。 三、编码阶段 1. 代码规范:遵循团队或行业标准,保持代码风格一致。 2. 注释:提供足够的注释,以便他人理解代码。 3. 错误处理:对异常和边界条件进行处理,...
5. **性能优化**:在保证代码可读性的前提下,考虑性能优化,如减少不必要的计算、合理使用数据结构和算法等。 6. **安全编程**:防止常见的安全漏洞,如SQL注入、跨站脚本攻击等,确保代码的安全性。 二、...
此清单旨在帮助开发团队识别并解决潜在的问题点,避免因未预见的技术难题而导致的应用崩溃或其他性能问题。 #### 二、设备碎片化 1. **兼容性测试**:针对市场上主流的操作系统版本(如Android 5.0至最新版本)...
性能测试是软件开发过程中的重要环节,用于评估和优化系统的处理能力、稳定性和资源消耗。以下是一个基于给定信息的详细性能测试用例参考模板及其关键知识点: **用例名称**:学校基本信息修改性能测试 **用例描述...
在IT行业中,保证代码质量是开发过程中的关键环节。"checklist"正是一款旨在提升代码质量的...对于开发者来说,理解和掌握checklist的使用不仅能优化个人的工作流程,也能为整个团队的协作和项目的成功带来积极影响。
Oracle DBA Checklist 是一份用于指导数据库管理员(DBA)进行日常、周度和月度检查Oracle数据库状态的详细...通过执行这些检查,DBA可以及时发现并解决问题,预防潜在的故障,同时优化数据库性能,提升整体服务质量。
15. 优化(Optimization):合作伙伴应具备在AWS环境中持续优化性能、成本和运营的能力。 AWS MSP计划的验证清单(Checklist)要求合作伙伴根据自己的能力评估填写,并将作为与AWS进行能力审核讨论的基础。审核过程...
CMMI分为五个成熟度级别,从初始级(Level 1)到优化级(Level 5),每个级别都设定了特定的目标和实践,以帮助组织逐步提升其过程管理能力。本文将深入解析CMMI的第一级——初始级(Initial Level)所涵盖的过程域...
2. **设计特性**:明确设计的应用领域,如通信、计算或消费电子。统计设计中的单元数量(可放置对象)。确定设计语言是Verilog还是VHDL,以及网络列表是扁平化还是层次化。评估是否存在RTL源代码,是否有特殊数据...
- 使用指针和下标访问数组元素,两者在大多数情况下性能相近,但在某些优化器的帮助下,使用下标可能略快。 #### 结构体 **学习时间:1周** **知识点Checklist** 1. **位域结构体定义与使用:** - 位域结构体...
检查内容可能涵盖DRAM的电源、时钟、地址、数据和控制信号的正确连接,以及内存的热管理,以防止过热导致的性能下降或损坏。 3. **PMIC**(电源管理集成电路):PMIC负责为整个系统提供稳定、高效的电源。检查应...
这份清单涵盖了硬件设计、软件开发、性能优化等多个方面,旨在帮助设计人员发现并解决潜在问题,提高最终产品的质量和可靠性。 #### 四、硬件设计注意事项 1. **电源管理**:确保电源电路设计合理,能够支持处理器...
性能测试结果的分析是性能优化的关键步骤,它可以帮助开发者、测试者和操作者快速地定位性能瓶颈,找到性能问题的根源,并采取相应的优化措施以提高系统的性能。 1. 监控步骤 在性能测试结果的分析中,监控步骤是...
配置Findbugs通常涉及指定要扫描的代码路径、选择要应用的检测级别、设置报告输出格式等步骤,具体配置方法可参考Findbugs的官方文档或相关教程。在实际使用中,开发团队应根据项目需求和团队规范来定制Findbugs的...
3. **查询优化**:尽量避免全表扫描,优化查询性能。 4. **索引策略**:合理设置索引,提高查询效率。 5. **元数据管理**:确保每张表都包含必要的元数据,如创建人、创建时间等。 #### 七、方案 1. **问题解决**...