国外网站模版免费下载,南通建设网站公司,做内网网站教程,官网网站优化公司作者#xff1a;田杰 在数据库的日常使用中#xff0c;来自应用的高并发场景并不罕见#xff0c;其标志性的表现为 高新连接创建速率#xff08;CPS#xff0c;比如 PHP 短连接#xff09;、发送大量请求到 DB 数据库层。
如同 海啸#xff0c;大量的新建连接和请求猛烈…作者田杰 在数据库的日常使用中来自应用的高并发场景并不罕见其标志性的表现为 高新连接创建速率CPS比如 PHP 短连接、发送大量请求到 DB 数据库层。
如同 海啸大量的新建连接和请求猛烈的冲击考验着 DB 层的处理能力非常容易出现数据库被冲击 hang 住或响应极其缓慢的情况想象下无预知无缓冲的短时间内突然工作量翻涨数倍会不会立时被忙哭了 ^_^。 而数据库通常作为架构最下端的数据存取汇聚单元其性能表现和稳定性往往决定了应用的最终表现和使用体验可谓业务生死之大事不可不察。 由此我们一起看一下 “海啸” 场景下可以用来 “保命” 的各种解决方案。 注 • 本文目标是总结高并发场景下的应对处理方法而应对热点更新秒杀场景的“招数”会另文介绍。 • 本文的主旨在于方便数据库的使用者理解业务高并发请求场景下的保障 DB 可用性和稳定性的机制和方法非机制的全面深度技术细节介绍。
1. 线程池
1.1 模型 我们举一个生活中的例子方便大家理解 线程池Thread Pool。 比如有个银行有 10 个窗口实例规格 CPU 数量)官方说可以容纳 10 人Client Thread。平时呢人也不多一直顺畅。稍微忙一点呢大家挤挤。这个 10 人的地方挤个 50 人也可以不是每个人时刻都在窗口办业务。效率也挺高。 年底发工资、公司结算、发行纪念币来了一大帮人大家一起挤谁也不让就把银行挤满了大家接踵摩肩动也动不了再发生些争抢那这银行谁也办不了业务了。 好了来个保安Timer Thread搞了个队伍机制10 个队列 loose_thread_pool_size 10按规定执行一次放 10 个人。这个效率也不错。 当然了如果一下子来了 1000 个人那么门口等待的队伍会很长虽然不致于把银行撑爆但是后面的同学要等很长时间有的会去抱怨了应用侧等待超过自身定义的超时时间后返回错误。 问题来了有的同学搞不清楚买哪种纪念币一直在看看停停保安看他们也不像马上能决定的样子而且窗口柜员也不是非常忙保安就又搞了个规则叫 “stall_limit”。 看一些同学犹豫超过 stall_limit 定义的时间那么就算他们 stall 了可以再放 1 个人进去oversubscribing。但去窗口办业务的人数是有上限的最多 50 个人10 个窗口每个窗口 5 个人 loose_thread_pool_oversubscribe 4。 之后只能出一个进一个; 如果都不出来那也 hang 了。这个时候至少要让保安能进去把这些太慢的同学赶出来几个让等待的队列动起来。 还有有的同学在里面发现忘带证件了需要等送进来。他们找地方等lock wait。那么他们是在等待了这个是不算 oversubscribing 数量的所以保安也可以放人一直放到 thread_pool_max_threads 个人。 如果证件还没送来那么银行就被这些等证件的霸占了hang 了。另外如果一下子证件都送来那这个银行一下子忙起来也爆了热点更新。 当然如果这个银行没有大量客户同时办业务的场景是可以不需要搞个保安不需要搞个队伍的loose_thread_pool_enabled OFF)。这个银行本身最多可以 50 个人但是保安只让 10 个人进去那效率就会低了。 还有门口等待队伍长了这个可以有 3 种可能 • 顾客动作慢慢 SQL建议考虑优化 SQL 降低执行成本。 • 银行小 窗口数量少实例规格小建议扩店升级实例规格。 • 窗口动作慢物理机问题、数据库 bug不在本文讨论范围内。 从上面的例子中我们可以看到 Thread Pool 是通过队列机制限制数据库的 Client Thread 的并发度控制 Running Thread 数量避免大量的争抢和创建 Client Thread 的开销来提升 CPU 使用 效率保障吞吐的在应用给与 DB 的访问压力不断增加的情况下保持 DB 吞吐处理能力。
1.2 适用场景
如果我们仔细品位下上面的例子可以发现 Thread Pool 的适用场景 • 每个要办的业务简短OLTP 场景且性能瓶颈在 CPU 资源上 • 场景中不存在 大量 需要长时间执行且无停顿可以暂时不使用 CPU的 SQL • 能够接受一定损失错误/开销的业务启用 Thread Pool 后需要一定开销存在简单的查询比不启用 Thread Pool 的情况下执行时间增加的可能比如被分配到了 stall 的 thread group 而要花时间等待执行
1.3 小结
参数开放修改默认值说明1loose_thread_pool_enabledYes是否启用 Thread Pool2loose_thread_pool_oversubscribeYes每个 Thread Group 在出现 Stall Thread 的情况下可以额外同时执行active的线程个数线程池最多可以同时执行active的线程数 thread_pool_oversubscribe 1* thread_pool_size建议 33tloose_thread_pool_sizeYes (RDS)Thread Pool 中分组Thread Group的个数建议设置为实例规格 CPU 个数4thread_pool_max_threadsNoThread Pool 中最大线程数量到达这个数量后无法再创建新的 thread5thread_pool_idle_timeoutNoThread Group 中空闲的线程退出前的空闲等待idle时间6thread_pool_stall_limitNoTimer Thread 检查 “Stall” 情况的间隔避免一个 thread 长时间霸占一个 thread group
那么面对存在长时间执行的查询除了优化 SQL 降低执行成本外有时不具有可操作性当然如果该查询对数据时效性不敏感可以考虑转移到只读实例上执行是否还有其他招数可用 请看下一招“限流”。
2. 限流
如果“海啸”来的异常猛烈并且在“海啸”中能够定义出一批带有同样特征的查询比如 Redis 缓存被击穿大量相似重复查询打到 DB 层或者如上例 Thread Pool 中的长时间执行的查询那么在业务支持/允许降级的情况下我们可以通过对这批请求采取限流的方式来“保命”。 相对 thread pool 这种对“海啸” 全方位覆盖的应对机制限流更像是集力量于一点的定向打击。
2.1 Statement Concurrency Control
对于 RDS for MySQL 8.0 和 PolarDB for MySQL我们可以通过“语句并发控制”Statement Concurrency Control特性来实现针对指定语句的限流。 比如发现下面的查询在高并发的场景下拖累了整个实例的性能和业务核实业务可以接受该查询被限流。
# 高成本慢查询
select count(*)
from jacky.mytab
where cid 90363
or uid ???
Copy 确定 SQL 语句后可以根据语句特征来调用 dbms_ccl 工具包创建规则进行限流。
# 增加限流规则限制最多 1 个并发执行
call dbms_ccl.add_ccl_rule(select,jacky,mytab,1,cid;uid);
# 显示当前的限流规则
call dbms_ccl.show_ccl_rule();
-----------------------------------------------------------------------------------------------------
| ID | TYPE | SCHEMA | TABLE | STATE | ORDER | CONCURRENCY_COUNT | MATCHED | RUNNING | WAITTING | KEYWORDS |
-----------------------------------------------------------------------------------------------------
| 2 | SELECT | jacky | mytab | Y | N | 1 | 116 | 1 | 26 | cid;uid |
-----------------------------------------------------------------------------------------------------
Copy 限流规则添加后超过定义的并发度的 SQL 请求在 Concurrency control waiting 状态 限流前后对比可以看到限流后 CPU 使用率从 100% 降低到 50% 左右有效恢复业务可用性。
2.2 DAS 限流
对于 RDS for MySQL 5.6 和 5.7 控制台的 CloudDBA 功能直接集成了 SQL 限流功能。 我们来看一个真实生活中的例子某客户在业务高峰期出现大量的集中请求导致高配实例 CPU 完全打满由于实例响应极其缓慢能采集到的监控数据显示当时 活动会话达到 14700 。 在业务层反复调整无法恢复的情况下 在 2020.3.24 21:35 通过设置 SQL 限流恢复了业务可用性。 RDS 实例会话情况 RDS 实例 CPU 使用率情况
3. 御敌于外
上面介绍的都是数据库层面的应对之策那么是否我们一定要被动的在数据库层面“兵来将挡”呢有没有主动“御敌于外”的办法呢
3.1 名词解释
名称说明1短连接通信双方有数据交互时就建立一个 TCP 连接数据发送完成后则断开此 TCP 连接通常基于 PHP 语言的应用采用短连接方式访问数据库2长连接通信双方有数据交互时首先尝试复用已有空闲 TCP 连接如果没有空闲 TCP 连接则尝试创建新连接数据发送完成后通常不断开此 TCP 连接以便后续复用通常基于 Java 语言的应用采用长连接方式访问数据库3syn queue用于存储接收到的 syn 请求的连接 socket 队列TCP 协议栈接收到 syn 后系统内核自动回复 syn,ack 同时将 syn 代表的连接放入到 syn queue 队列中并管理是否需要重传 syn,ack其长度由 tcp_max_syn_backlog 或 somaxconnLinux 内核参数确定4accept queue用于存储完成 TCP 三次握手的连接 socket 队列当 MySQL 调用 accept() 时从该队列取走一个 socket 处理其长度由 应用设置的 backlog 参数和内核参数 somaxconn 的较小值决定5ListenOverFlow由于 syn queue 已经打满新收到的 syn 请求不被处理而丢弃的场景发生数量6ListenDrops由于 accept queue 已经打满完成 TCP 三次握手的连接不被处理而丢弃的场景发生数量
3.2 短连接优化
首先我们来看看一个普通的 SQL 请求是如何被从应用通过网络发送给 DB 层进而得到处理的。 仔细看一下上述时序图就会发现如果应用和数据库之间在没有可用的网络连接情况下需要首先建立起一条基于 TCP/IP 协议栈的 MySQL 网络连接才能够将 SQL 请求发送给数据库实例并获取到处理的结果集。 在应用采用短连接机制比如基于 PHP 语言开发的应用的情况下每个 SQL/Query 都需要和数据库实例创建一个 TCP 网络连接需要消耗数据库实例和其所在物理机的 CPU 资源。 在“海啸”的场景下采用短连接机制的应用会保持很高的新连接创建速率CPS大于等于 QPS这样在高负载QPS 的基础上进一步消耗数据库实例的 CPU 资源拉高 CPU 使用率降低 CPU 使用效率进入恶性循环容易触发数据库雪崩式崩溃。 在 CPU 资源紧张的情况下会出现大量连接请求积压无法处理而触发 ListenOverFlow 和 ListenDrops 情况出现。 这里我们看一个真实世界中的例子。 客户在 13:30 将应用从长连接模式调整为短连接模式由于短连接模式的高并发新建连接请求速率CPS - 每秒新建连接数修改后实例 CPU 使用率总体上升 25% 左右业务侧出现大量连接失败错误并感知 RDS 实例响应缓慢。 部分 CPU 被完全打满无法满足处理高连接请求的需求而出现 ListenOverFlow / ListenDrops。 线程池 Thread Pool 是数据库层对该场景较好的解决方案而启用了数据库独立代理RDS for MySQL 读写分离地址 和 PolarDB for MySQL 的集群地址的实例还可以选择启用“短连接优化”的链路层解决方案。 当应用断开连接后数据库独享代理会判断之前的连接是否为空闲idle连接如果是空闲连接代理会将代理与数据库之间的连接保留在连接池内一段时间仅释放应用与代理之间的连接。 在保留连接的这段时间内如果应用发起新连接代理会直接从连接池里使用保留的连接从而减少与数据库建立连接的开销。 官方文档短连接优化
原文链接 本文为云栖社区原创内容未经允许不得转载。