如何提高项目的稳定性和开发效率呢?
前言
除了保证项目如期上线,如何保证项目上线后的运行速度,如何提高容灾能力,减少 bug 同样是我们需要考虑的问题。
我们从以下几个方面来探究思考,抛砖引玉,看看大家是否有其他维度来提高项目,欢迎在评论区留言。
1.语言选择方面
开发效率和运行效率的平衡点
我入行时做的安卓开发,使用 Java 语言,觉得入门门槛比较高:异常处理,IO,集合,JDBC 等等,除了基础概念不好理解之外,代码量也比较大。
使用 Java 开发了 2 年安卓之后,开始使用 PHP 做接口开发,觉得 PHP 开发真的比 Java 开发代码量少太多了,而且 PHP 的数组实在是太简单了。那时候满脑子想的就是“PHP 是最好的语言”
去年接触了 Go,发现 Go 对并发处理实在是太友好了,而且代码量像 PHP 一样少,甚至更少。而且 Go 打包编译时自动格式化代码,比 PHP 更规范。Go 性能比肩 Java,对开发者的友好程序甚至略好于 PHP(我个人的体会)
所以,对开发语言的选择对项目至关重要,我们要结合自己的业务场景,选择合适的语言进行开发。我们目前采用的是 PHP+Go,内部服务采用 RPC 的方式来通讯。
2.框架选择方面(开发速度、项目性能)
我们来探究一下相较于其他框架,为什么 swoole 的速度更快?swoole 是如何做到的?
swoole 是 PHP 的一个 extension 扩展,但又不仅是一个扩展,因为普通的扩展只是提供了库函数。而 swoole 扩展在运行后是可以接管 PHP 的控制权,进入事件循环的;
swoole 使用纯 C 语言编写,定位就是一个网络通信和异步 IO 的基础库,不依赖其他三方,定位纯粹;
swoole 底层内置了异步非阻塞、多线程的网络 IO 服务器,当 IO 事件发生后,swoole 会自动回调相应的 php 函数
总结:异步处理,提高对 IO 密集型场景并发处理
swoole 框架相比于 fpm 等,主要节省了 PHP 框架和全局对象每次请求创建销毁带来的性能消耗:与 http 请求无关的全局对象只需要构造一次,并且 swoole 还支持对某些对象和数据做缓存控制
总结:swoole 常驻内存,避免重复创建销毁,重复加载带来的性能消耗
之前一个浅薄的认识:认为 swoole 之所以比 laravel 这类框架性能高,是因为 laravel 做了大量封装,引入中间件,每次请求都需要额外走一些非必要的流程。认为“程序开发效率和程序运行效率肯定是对立面”这种理论,有点浅薄了,还是要深入理解原理呀。
3.数据库存储引擎方面(性能)
我们通过对 MySQL 的 MyISAM 和 InnoDB 存储引擎做对比,来分析数据库对项目性能的影响
首先介绍一下 MySQL 区别其他数据库的一个重点特点:插件式的表存储引擎;注意,MySQL 的存储引擎是基于表的,不是基于数据库的
下面根据特性对比分析 2 个存储引擎
事务处理:
MyISAM 不支持事务处理
InnoDB 支持事务处理,支持 4 个事务隔离级别,支持多版本读
所以对事物有要求的业务场景需要使用 InnoDB
表级锁定
MyISAM 锁定机制是表级索引,表级索引实现的成本很小但是大大降低了并发性能
InnoDB 支持行级锁定,通过索引实现;InnoDB 很好的解决了并发问题,可以说 InnoDB 就是为处理大数据量高并发设计的
InnoDB 不仅支持行锁,也支持外键
读写互相阻塞
MyISAM 不仅会在写入的时候阻塞读取,也会在读取的时候阻塞写入;但是读本身不会阻塞另外的读
InnoDB 支持非锁定读(默认读操作不会产生锁)
缓存
MyISAM 只会缓存索引,MyISAM 可以通过 key_buffer 缓存以大大提高访问性能较少磁盘 IO,但是缓存原理是只缓存索引,不缓存数据
InnoDB 既能缓存索引,也能缓存数据,具有非常高效的缓存特性
InnoDB 的高阶新特性
InnoDB 可以通过多版本控制(MVCC)来获得高并发
实现了 4 种隔离级别,默认为 Repeatable read 级别
支持通过 next-key Locking 策略避免幻读(phantom)现象的产生
插入缓存(insert buffer)
二次写(double write)
自适应哈希索引(adaptive hash index,AHI)
预读(read ahead)
试用场景
MyISAM
不需要支持事务处理
并发较低
数据修改较少
以读为主
InnoDB
需要支持事务处理的场景
大数据量、高并发的场景
对数据一致性要求高的场景
注意:可以利用 InnoDB 较好的缓存能力提高内存利用率,减少磁盘 IO
4.算法方面(性能)
我们通过深入了解算法相关的概念来理解如何衡量自己设计的算法是否高效,如何优化。
首先说明算法是什么?
算法(Algorithm)是指用来操作数据,解决程序问题的一组方法。
时间复杂度 &空间复杂度
对于同一个问题,使用不同的算法,在执行过程中消耗的资源和时间是有很大区别的,我们引入了“时间复杂度”和“空间复杂度”来衡量不同算法之间的优劣
时间复杂度
时间维度:执行当前算法所消耗的时间,通常用【时间复杂度】来描述
大 O 符号表示法:考虑到在不通配置的机器上运行或者数据规模的区别,我们不能简单的通过算法具体的运行时间来描述“时间复杂度”,我们引入了【大 O 符号表示法】
在大 O 符号表示法中,时间复杂度的公式是:T(n)=O(f(n)),其中 f(n)表示每行代码执行次数之和,而 O 表示正相关关系,这个公式的全称是:算法的渐进时间复杂度。
大 O 符号不是真实代表算法执行时间的,它是用来表示代码执行时间的增长变化趋势的。
常见的时间复杂度量级:(时间复杂度越来越大,执行效率越来越低)
常数阶 O(1)
对数阶 O(logN)
线性阶 O(n)
线性对数阶 O(nlogN)
平方阶 O(n²)
立方阶 O(n³)
K 次方阶 O(n^k)
指数阶 O(2^n)
空间复杂度
空间维度:执行当前算法需要占用的内存空间,通常使用【空间复杂度】来描述
既然时间复杂度不是用来计算程序具体耗时的,空间复杂度也不是用来计算程序实际占用空间的。
空间复杂度是对一个算法在运行过程中临时占用存储空间大小的一个量度,同样反映一个趋势,我们用 S(n)来定义
常见的空间复杂度量级:(空间复杂度越来越大,执行效率越来越低)
常数阶 O(1)
线性阶 O(n)
平方阶 O(n²)
5.函数设计方面(性能、稳定性)
我们以递归和迭代的区别是什么来抛转引玉,请大家思考如何结合自己的业务场景设计合适的函数
递归的基本概念就是调用自身,直接或者间接的调用自己,通常把一个大型问题转化为一个和原问题相似的、规模较小的问题来解决。可以简单理解为 A 调用 A
迭代的基本概念是利用变量的原值推算出变量的新值,可以简单的理解为 A 调用 B
各自优点:
递归的优点是可以把大问题转成小问题,可以精简代码,可读性号
迭代的优点是执行效率高,空间复杂度低(因为迭代的时间只和循环次数呈一个线性关系,没有额外的空间花销)
各自缺点:
递归浪费空间,递归太深会造成堆栈溢出
迭代代码比递归代码复杂,不够简洁,可读性差
应用场景分别是什么?
递归中一定有迭代的概念,但是迭代中不一定有递归,大部分都是可以相互转换的
理论上能用迭代的不用递归,因为递归函数浪费内存空间,可能造成堆栈溢出
实际项目中还要考虑代码的可读性,不止是方便别人,也方便自己,我们使用递归时,可以根据业务场景设置一个递归层级的最大值。
6.网络请求方面(安全性)
我们通过对比 http 与 https 的区别,探讨一下如何保证数据传输的安全性
https 协议需要到 CA 申请证书,一般免费证书较少,因而需要一定费用。
http 是超文本传输协议,信息是明文传输,https 则是具有安全性的 ssl/tls 加密传输协议。
http 和 https 使用的是完全不同的连接方式,用的端口也不一样,前者是 80,后者是 443。
http 的连接很简单,是无状态的;
HTTPS 协议是由 SSL/TLS+HTTP 协议构建的可进行==加密传输、身份认证==的网络协议,比 http 协议安全。
7.会话方面(安全性)
我们通过探讨 session 和 cookie 的最佳实践,来探讨一下会话方面的安全性
session 数据存储在服务器,cookie 数据存储在客户端浏览器上
cookie 不是很安全,我们可以查询伪造存储再客户端的 cookie 进行欺骗请求,考虑到安全应该使用 session
session 会在一定时间内保存在服务器,当访问量增多时,会比较占用服务器性能,考虑到性能时可以使用 cookie
单个 cookie 保存的数据不能超过 4kb,很多浏览器会限制一个站点最多保存的 cookie 数
cookie 具有不可跨域名性
会话 cookie 和持久 cookie:
会话 cookie 即不设置过期时间,会随着浏览器关闭就消失的 cookie,一般存储在内存中;
持久 cookie 即设置了过期时间,即使关闭了浏览器也不会消失的 cookie,一般存在硬盘中;再次打开浏览器仍然有效,直到达到过期时间。
session 共享:
对于多网站单服务器(同一父域名不同子域名)如何解决不同网站之间的 SessionId 共享问题?由于域名不同(a.test.com,b.test.com),而 sessionId 又分别存储再不同的 cookie 中,我们的思路就是改变 cookie 的存储范围到父域名,达到共享 cookie 的目的,从而实现 SessionId 的共享。
由此带来的弊端是子站之间的 cookie 信息也被共享了
比较好的实践是:把登录信息等敏感数据保存到 session 中,其他非敏感数据保存在 cookie 中
8.总结
不管是强大如微信,还是微小如一个小插件,影响其性能和稳定性的维度都很多,需要我们夯实基础,有理有据的进行程序设计和优化调优。
9.一起学习,升级打怪
我们搞了一个对学 Go 真正有帮助的群 ,欢迎加入:
公众号:程序员升级打怪之旅
微信号:wangzhongyang1993
版权声明: 本文为 InfoQ 作者【王中阳Go】的原创文章。
原文链接:【http://xie.infoq.cn/article/8252a3c2785b097a92b42c88b】。文章转载请联系作者。
评论 (1 条评论)