如何开发一个性能好的软件系统?(多年工作经验,很有价值)

发布于:2023-01-11 ⋅ 阅读:(464) ⋅ 点赞:(0)

开发一个软件系统,想要性能好,需要好的技术架构、以及代码要写得好。以下是我的一些工作经验。

一、数据库方面

1 索引 [重点]

请注意,一定要重视,一定要知道索引的重要性。否则,上线后系统肯定卡,要么炸。

什么时候建索引?建什么样的索引?

可以在产品需求确认后、编码前、统一建表时把索引设定好确认好,写代码时围绕着这些索引去写查询条件。后期根据实际情况还会有些调整。 也可以在项目代码全部写完后,再统一定索引。根据代码的查询条件以及慢查询,做调整。

理论上,一个软件系统,一定要有主键索引、肯定会有单索引、肯定会有联合索引。常用查询,必须建索引。举个例子,一个订单表,可能就需要 商户id+用户id+创建时间 作为联合索引:alter table order add index u_m(`merchant_id`,`uid`,`create_time`) USING BTREE;

如果把索引弄得好,一张表5000万行数据,没什么压力。比如订单表、账变表、积分流水表。但一定要慎重、小心、认真,因为以后处理问题处理数据的时候会非常耗时,容易搞崩。

注意事项:索引越少越好,千万别说唉呀我每个字段都建了索引,软件怎么还这么慢呢?

关于索引的原理,可以看我另一篇文章:一文掌握MySQL的索引 -- 数据结构(认真排版、简洁易懂)

关于SQL的性能分析,可以看我这篇文章:如何分析MySQL语句的性能(MySQL Explain详解)

2 数据归档

数据量大的表,可以做归档,比如只提供3个月的数据查询。比如移动电话账单,只能查近6个月。

3 写代码查数据时,别用 select * ,请指定要查哪些字段。

原因是:(1) 少查一个字段,内存的节约、速度的提升,都会好不少。(2) select … 指定全部字段,也比select * 的性能要好。(3) 可能会使用覆盖索引,性能极速提升。

4 如果能不用 select count() 就别用。这个性能很差。

前台的接口,提供给用户翻页,用户下拉翻一页,不需要知道总页数。

就算是普通的页码分页组件,也应该问一声,是否可以让用户一直点下一页,点到没有了就是最后一页。

5 慎用 limit

比如 limit 100000000,10 这样系统会直接卡死,性能非常差。有时可以不用 limit, 换一种写法,用主键判断再配合 limit 使用,性能提升非常多,如:where id > 9000000 limit 100

使用场景,这里说两个。
(1) 比如有一个数据中心系统,提供接口给别的系统采集数据,别接受页码、第几页等参数去数据库分页,可以接受一个“last_id”作为参数,然后where id > $last_id limit 100; 
(2) 分页

6 默认一个时间范围的查询条件

去数据库查询时,尽量带上默认时间范围。比如后台订单报表,一打开界面,如果默认展示的数据时间范围是全部,这性能会很差,而且特别影响系统的整体性能,影响终端用户的使用体验。

7 善用事务

如果你不用事务,数据很难保证一致性,也很难保证不出现脏数据错误数据。但注意别什么都往事务里塞,容易死锁。

8 读写分离

访问量一多,必须考虑读写分离,一主多从。数据库的压力就会降下来。但一定要注意,更新数据后又查询时,如果时时性要求高,自己注意这是否会查主库,如果不会,请自己指定去主库查询。比如采集到商品数据后,丢入队列进行一些额外处理,队列里判断数据是否存在,如果不存在就返回false,这就会有问题。

一般的ORM的底层有自动处理好,同一事务里,会自动连接主库。

Thinkphp的框架还有个配置 read_master ,写入操作之后的读数据库,会自动去主库读取。这是个很好的功能。

9 分库分表

如果短时间之内会有大量数据,或者不能做数据归档。可以使用分表,甚至分库。

注:我经历过分表。没经历过分库。

10 多注意看日志

平常没事儿干的时候多看看服务器的慢查询,以及错误日志。这可以让你在问题暴露之前发现问题,处理问题。

二、缓存方面

1 正常一个企业的软件系统,都需要上缓存。请重视。否则,你的系统上线后,不是卡就是炸。

2 单机部署,linux系统一般可能只有1000来个连接,需要配置内核网络参数。单节点的吞吐量(QPS)在5W左右,如果机器性能好可以达到八九万。

3 如果规模非常大,就用分布式部署。比如Redis,可以考虑哨兵模式、集群模式,建议选用集群模式,比如三主三从。如果有兴趣了解三主三从,可以看我这篇文章:docker容器化搭建 redis7.0.4 三主三从集群

4 缓存key的长度,尽量减短。

5 缓存的内容,尽量减短,并使用压缩。别什么都往里面丢。

6 必须设置过期时间。否则内存会有爆的一天。不能过期不能随时清除的缓存,请在后端代码里自行维护。

7 避免在同一时间大量key过期。你可以根据业务情况,随机过期时间。

8 注意避免缓存穿透。数据库无值时,别一直查库。请先判断是否存在此key

9 注意热点KEY的问题。如何发现热点KEY,提供几个方案:代码访问时封装一个统一入口、用redis提供的命令。解决方案,提供几个方案:人工拆分、主从节点key、二级缓存。

三 、 代码方面

1 尽量保证每个接口在本地测试时的耗时在100ms以内。

2 及时性要求不高的任务,特别是耗时任务,请用队列。比如前面说的记录日志,比如下单成功后给用户短信通知、计划报表等。

3 减少http接口的数量。这样可以减少你的各种连接数。

4 不要在循环里查数据库。这性能特别差。

5 不要在循环里查缓存。这性能也挺差。

6 一些复杂数据,能在代码里计算就在代码里计算。从数据库取出简单数据,然后用代码进行计算,别直接用sql语句去数据库里处理。

7 严格记录日志,用于日后排查问题,并且免于日后扯皮。

8 该用锁的地方要用锁。比如定时将任务丢入队列,如果队列里其中一个任务卡住了,这个时候会依然持续不断的丢入新的任务到队列里,这样会把队列撑爆。

10 调试性能的时候,调试bug的时候,多看开发手册,多看框架底层代码。比如数据量大的时候、索引、读写分离,以及一些特殊的BUG和性能需求。

我以前遇到一个问题,用的Thinkphp5.1,command命令行里自动在主库update了数据,然后丢到队列进行其它业务处理,队列里自动去从库查询数据,因为主从同步有延迟,这时查不到数据。现在解决方案是要让所有队列(或队列里某个任务)去主库读取数据,怎么实现呢?这里不贴代码了,有兴趣的可以自己研究下看看。

11 前端资源,能使用CDN的就使用CDN

12 图片,不要直接存到当前项目,请使用单独的图片服务器,比如公司自己部署的图片服务器,或者第三方存储商。

13 当我们直接new一个对象的时候,请考虑它的性能,系统访问量有点大的时候,内存的不断创建和回收会影响性能。可以考虑使用单例模式。也可以使用容器,比如thinkphp laravel提供的容器,这是个注册树模式,它给我们提供了很好的代码架构,对系统性能也会有很大提升。

四、Nginx

1 访问量一多,必须搭建服务器的负载均衡。可以自己配置多台服务器。用Nginx的反向代理,也可以使用阿里云等云服务商提供的负载均衡服务。代码服务器的压力就可以降下来。

2 单节点的QPS能达到10万左右。

五、websocket

1 频繁请求的接口,请使用websocket进行推送。

2 请不要使用前端ajax轮询,请用websocket推送

3 单节点一般能对付大部分场景,我用的workerman,QPS在二三十万,甚至更多。

五、升级到HTTP 2.0

主要有这几个方面的提升:

多路复用。做到同一个连接并发处理多个请求,从头到尾只用一个TCP连接,解决了TCP慢启动的问题;

头部数据压缩。一般我们一个页面,会有很多很多请求,每个请求都带一堆请求头数据,很影响性能和带宽。2.0解决了这个问题。

服务器推送。我们访问一个页面时,这个页面通常还需要一堆额外的请求,浏览器会主动把这些资源推给浏览器,大大提升了性能。

我们部署一个网站,默认的HTTP协议版本是HTTP1.1,并且大部分网站使用的也是1.1版本。升级到2.0版,可以大大提升性能。具体情况,自行百度搜索HTTP每个版本的区别、它是如何提升网络速度的、如何升级到HTTP2.0。

六、前端效果(交互欺骗)

1 当一个页面信息元素比较多时,你可以先加载主要数据,后加载次要数据。给用户的感觉是,哇,一点就打开了,真快。其实有一些他没那么快注意到的数据,还没展示出来。

2 有些复杂的交互,可以拆分成两步甚至多步。

本文含有隐藏内容,请 开通VIP 后查看