正文
分布式事务提交慢的一次总结和思考
背景
分布式事务未提交 是应用程序出现宕机异常的很重要的一原因.
应用宕机主要可以分为:
1. 内存泄露导致的OOM宕机. 表现在系统越来越慢, 应用的内存和CPU占用量越来越高. 最终达到无响应的状态, 此时数据库一般是正常的.
2. 分布式事务未提交导致的宕机, 表现在无法建立新的连接, 会出现连接池满,或者超时等的现象. 数据库的压力会比较大, 应用的压力一般不会很高.
3. 线程池泄露导致的栈溢出问题, 一般表现在线程/线程池处理不规范, 创建后没有做清理处理, 导致线程堆积, 非堆区超过上下, 出现栈溢出或者是OOM溢出.
这种现象下堆区内存较为正常, 但是非堆区会比较异常.
4. 异常代码导致的宕机,比如死循环, 笛卡尔积, 大数据量下的未分页,全部积压到JVM的堆区内存中.
5. 第三方库的异常, 比如json解析过大, 影像图片解析异常, excel解析异常(错误的格式或者是16k*20k的渲染), 错误文件导致的解析死循环.
6. 服务器系统配置不规范,应用配置不规范, 比如nofile, 内存限制, 过低,过于严格, IO突然的降低导致延迟增加或者是延迟热加载时文件加载失败(损坏,不完整等),或者是重复加载. 内存,文件连接不释放
问题与思考
本次问题出现的范围比较广, 原因也比较复杂, 这里仅分析与思考一下一个事务相关的问题.
其他的比如 数据库参数配置, 大表的批量更新与删除, 索引的过多,不完整等问题暂时不深入.
OLTP的环境下, 事务的处理必须是短促的. 要占用尽可能少的数据库时间与资源.
在提交事务后, 应该尽快的执行commit 或者是 rollback.
避免占用过多,过久的数据库的资源.
TCP连接/Session/Process 是数据库资源
Lock/latch/ 也是数据库资源.
数据文件写入/redo/undo的写入, 归档的写入, 更数据库资源.
SQL解析,SQL执行,排序,关联 等等 都是数据库的资源.
事务未提交, 主要会占用 网络,session, 以及lock相关的资源.
他会锁住要执行变更的部分行信息.
并且因为不提交, 这个数据库连接不能被复用. 应用服务器的连接池值能够继续创建新的连接.
这就会导致:
1. 创建TCP连接.数据库创建process. 创建session 是很重的操作, 会耗时比较久, 高并发的情况下基本上是秒级操作.
2. 提交事务的时间越久, 其他用户需要的连接越多, 建立的连接的效率就会越低. 会进入螺旋上升的性能屏障.
3. 建立Process和Session, 数据库会分配内存和CPU资源, 会导致数据库的负载上升, 响应能力下降.
4. 数据库的性能下降会导致本来可以很快提交的动作变的更慢. 导致真个流程变的更加漫长.
优化思路
分布式事务未提交是不可接受的.
事务提交逻辑复杂也是不可接受的.
1. 开启事务后要尽快提交, 不要有太过复杂的逻辑处理, 不要一次性修改更多的数据, 尤其不要有大表的not in 批量处理等.
2. 开启事务后, 必须有finally , 能够catch 住异常, 并且执行rollback等操作, 避免事务处理时异常数据导致不提交,出现异常卡顿.
3. 不建议在开启事务后有非关键核心的表处理, 比如临时表, 排序,大表的数据检索与验证等. 避免提交事务变慢. 需要插入修改删除的数据需要再事务开启之间准备好.
4. 开启连接池,并且适当设置连接池的大小. 尤其不要过高, 过高在遇到事务问题时会将数据库的资源耗尽, 过低会导致无法满足业务需求.
关于线程数和连接数
应用服务器的线程数可以理解为干活的人数.
对应数据库的连接数可以理解为工人交付工作成果的窗口.
干活的人数跟车间的大小相关(CPU和内存) 设置的太小了, 吞吐量会比较小.
但是设置的太大了, 会出现线程上下文切换, 出现等待事件,并且占用比较多的CPU和内存资源.
所以线程池的设置 要根据应用服务器的配置, 以及业务场景的逻辑来进行设置.
如果资源比较少, 并且业务逻辑处理比较快, 并发要求不是特别高, 可以适当减少线程池的大小.
如果对并发和吞吐量要求特别高, 那么必须要加大配置. 增加线程池.
连接池就像是线程池干完活后用于提交工作成果的.
如果成果比较简单, 可以快速的提交和释放, 那么数据库连接池可以比较小一些.
如果提交的工作比较复杂, 时间很久, 需要较高的连接池数量.
需要注意, 如果连接池多了, 应用服务器需要关联较多的对象, 内存,CPU和切换起来都可能会慢.
数据库对应更多的应用, 他会产生更大的压力. 虽然有epoll等网络模型
但是100个并发和1000个并发对数据库产生的压力是非常不一样的.
所有放之四海皆准的配置, 需要根据业务场景,数据量, 应用和数据库的配置进行关联思考设置出最合适的配置来.