高并发解决方案

概述

  • 高并发是一个不可避免的问题,要从多个维度来解决高并发问题

在并发中经常提及的两个常用名词TPS和QPS

TPS秒级事务并发量 QPS秒级并发查询量

解决高并发

硬件

从硬件方向下手

单体应用垂直扩容

  • CPU从32位提升为64
  • 内存提升
  • 磁盘提升机械硬盘到固态硬盘
  • 磁盘扩容
  • 千兆网卡提升为万兆网卡
    • 解决流量问题

缓存

做缓存也是解决高并发的一个很好的解决办法

浏览器缓存

  • 第一次访问返回200之后访问 返回304表示页面没有被修改过 不需要再次请求 直接使用缓存

  • 把资源缓存到浏览器里面 可以节省服务器的压力但是会消耗客户端内存

  • 设置响应头可以把数据等缓存到浏览器中

Nginx缓存

  • 把稳定的进行缓存,缓存一些本地的静态资源

CDN缓存

  • 工作中基本上碰不到
  • CDN : 内容分发网络
    • 概念: 使 用户就近访问,降低网络阻塞
    • 本身是一个缓存,把后端应用的数据缓存起来,用户要访问的时候,直接CDN上获取,不需要走后端的Nginx,以及具体应用服务器Tomcat。作用:加速数据的传输,提高稳定性,如果CDN上没有获取到数据,再走后端的Nginx缓存,Nginx上也没有,就走后端的应用服务器,CDN主要缓存静态资源

应用缓存

内存缓存

  • 速度快,效率高,容易丢失 容易被清理

磁盘缓存

  • 在磁盘缓存数据,读取效率较内存缓存低,应用重启缓存不会丢失

多级缓存

浏览器->CDN->Nginx->Redis->DB(数据库)

缓存使用场景

  • 经常要读取的数据
  • 频繁访问的数据
  • 热点数据缓存
    • 经常被访问的数据,热点数据 ,随着我们的使用突然之间出现
  • IO瓶颈数据
    • 输入输出的最大限制
  • 计算昂贵的数据
    • 计算比较耗时的数据
  • 无需实时更新的数据

缓存的目的是减少对后端服务的访问,降低后端服务的压力 缓存不适合频繁修改

如果需要实时修改, 直接修改缓存慢慢修改数据库。

集群

集群部署已经成为主流
单一应用,当访问流量很大无法支撑,那么可以集群部署,也叫单体应用水平扩容,原来由一台服务器提供服务,改为多部署几台。

加以负载均衡提高访问效率需要session共享

拆分

应用拆分: 分布式

把单体应用进行拆分 按照模块拆分

数据库拆分

水平拆分

垂直拆分

静态化

  • 直接定时生成静态的html页面,提供前端访问。
    • 定期去更新页面适用于网站首页面
    • 将图片等放到静态资源Nginx

动静分离

  • 用Nginx存储静态资源
  • 用Tomcat处理动态请求

消息队列

  • 使用队列进行流量消峰、解耦

池化

对象池

  • 通过复用对象,减少对象创建和垃圾收集器回收对象的资源开销
  • 通常是无状态对象

数据库连接池

线程池

HttpClient连接池

优化

JVM优化

  • 设置JVM参数

-server -Xmx4g -Xm4g -Xmn256m

Tomcat优化

Java程序优化

  • 养成良好的编程习惯
  • 不要重复创建太多对象
  • 流/文件/连接 记得使用完关闭
  • 少用重量级同步锁synchronized ,采用Lock
  • 不要在循环体中使用try/catch
  • 多定义局部变量,少定义成员变量

数据库优化

数据库服务器优化

  • 修改数据库服务器的配置文件参数,DBA(数据库管理员)来做

数据库架构优化

  • 将数据库服务器和应用服务器分离
  • 读写分离:通过数据库主从架构解决,写数据时操作主库,读数据时操作从库,分摊读写压力
  • 分库分表:扩容数据库,解决数据量容量问题

数据库索引优化

  • 建立合适的索引
  • 建立索引的字段尽量的小,最好是数值
  • 尽量在唯一性高的字段上创建索引、主键、序号等
  • 不要在性别这种唯一性很低的字段上创建索引

SQL优化

在sql查询中为了提高查询效率,我们常常会采取一些措施对查询语句进行sql优化,下面总结的一些方法,有需要的可以参考参考。

1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。

2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null
可以在num上设置默认值0,确保表中num列没有null值,然后这样查询:
select id from t where num=0

3.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。

4.应尽量避免在 where 子句中使用 or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20
可以这样查询:
select id from t where num=10
union all
select id from t where num=20

5.in 和 not in 也要慎用,否则会导致全表扫描,如:
select id from t where num in(1,2,3)
对于连续的数值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3

6.下面的查询也将导致全表扫描:
select id from t where name like ‘%abc%’

7.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100
应改为:
select id from t where num=100*2

8.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)=‘abc’–name以abc开头的id
应改为:
select id from t where name like ‘abc%’

9.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

10.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,
否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。

11.不要写一些没有意义的查询,如需要生成一个空表结构:
select col1,col2 into #t from t where 1=0
这类代码不会返回任何结果集,但是会消耗系统资源的,应改成这样:
create table #t(…)

12.很多时候用 exists 代替 in 是一个好的选择:
select num from a where num in(select num from b)
用下面的语句替换:
select num from a where exists(select 1 from b where num=a.num)

13.并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,
如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。

14.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,
因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。
一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。

15.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。
这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

16.尽可能的使用 varchar 代替 char ,因为首先变长字段存储空间小,可以节省存储空间,
其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。

17.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。

18.避免频繁创建和删除临时表,以减少系统表资源的消耗。

19.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。

20.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,
以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。

21.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。

22.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。

23.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。

24.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。
在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。
25.尽量避免大事务操作,提高系统并发能力。26.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。

采用数据搜索引擎

ES solr等

SQL执行计划

explain + sql语句 可以检测出SQL语句是否优秀

Nginx优化

  • 调整配置文件参数

Linux优化

网络优化

  • 机房、宽带、路由器等方面优化
  • 网络架构更合理

前端优化

js优化

  • 压缩变小
  • 压缩工具
  • 多个js文件合并成一个js文件

css优化

压缩变小

多个css合并成一个

HTML页面优化

  • 不要加载太多的js和css
  • js和css加载放在页面的尾部,从用户体验角度考虑
  • 页面上减少到服务器的请求数

压测

  • 系统上线之前,需要对系统各个环节进行压力测试,发现系统的瓶颈点,然后对系统的瓶颈点,进行调优。调优完成后,还需要考虑另外一些风险因素
  • 测试工具:JMeter 等

版权声明:本文为qq_55624776原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。