当前位置: 首页 > news >正文

TiDB 优雅关闭

背景

今天使用tiup做实验的事后,将tidb节点从2个缩到1个,发现tiup返回成功但是tidb-server进程还在。

这就引发的我的好奇心,why?

实验复现

启动集群

#( 07/31/23@ 8:32下午 )( happy@ZBMAC-f298743e3 ):~/docker/tiup/tiproxytiup playground v6.4.0 --db 2 --kv 1 --pd 1 --tiflash 0 --without-monitor --db.config tidb.toml
tiup is checking updates for component playground ...
Starting component `playground`: /Users/happy/.tiup/components/playground/v1.12.5/tiup-playground v6.4.0 --db 2 --kv 1 --pd 1 --tiflash 0 --without-monitor --db.config tidb.toml
Start pd instance:v6.4.0
Start tikv instance:v6.4.0
Start tidb instance:v6.4.0
Start tidb instance:v6.4.0
Waiting for tidb instances ready
127.0.0.1:4000 ... Done
127.0.0.1:4001 ... Done🎉 TiDB Playground Cluster is started, enjoy!Connect TiDB:   mysql --comments --host 127.0.0.1 --port 4000 -u root
Connect TiDB:   mysql --comments --host 127.0.0.1 --port 4001 -u root
TiDB Dashboard: http://127.0.0.1:2379/dashboard

查看节点信息

#( 07/31/23@ 8:32下午 )( happy@ZBMAC-f298743e3 ):~tiup playground display
tiup is checking updates for component playground ...
Starting component `playground`: /Users/happy/.tiup/components/playground/v1.12.5/tiup-playground display
Pid    Role  Uptime
---    ----  ------
10113  pd    49.376485092s
10114  tikv  49.32262974s
10115  tidb  49.283144092s
10116  tidb  49.245069308s

缩掉一个tidb节点

#( 07/31/23@ 8:34下午 )( happy@ZBMAC-f298743e3 ):~tiup playground scale-in --pid 10115
tiup is checking updates for component playground ...
Starting component `playground`: /Users/happy/.tiup/components/playground/v1.12.5/tiup-playground scale-in --pid 10115
scale in tidb success

这里可以看到已经返回了 scale in tidb success

查看进程

#( 07/31/23@ 8:34下午 )( happy@ZBMAC-f298743e3 ):~ps -ef | grep 10115502 11371 99718   0  8:34下午 ttys001    0:00.00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox 10115502 10115 10111   0  8:32下午 ttys005    0:04.29 /Users/happy/.tiup/components/tidb/v6.4.0/tidb-server -P 4000 --store=tikv --host=127.0.0.1 --status=10080 --path=127.0.0.1:2379 --log-file=/Users/happy/.tiup/data/TlaeoSj/tidb-0/tidb.log --config=/Users/happy/.tiup/data/TlaeoSj/tidb-0/tidb.toml

进程还是存在

分析

于是查看了 v6.4.0 的 tidb-server 代码。首先想到去main函数看下close的流程

main

func main() {//..signal.SetupSignalHandler(func(graceful bool) {svr.Close()cleanup(svr, storage, dom, graceful)cpuprofile.StopCPUProfiler()close(exited)})// ...
}

在这里发现两个重要的逻辑 svr.Close(),cleanup(svr, storage, dom, graceful)

svr.Close()

// Close closes the server.
func (s *Server) Close() {s.startShutdown()s.rwlock.Lock() // prevent new connectionsdefer s.rwlock.Unlock()if s.listener != nil {err := s.listener.Close()terror.Log(errors.Trace(err))s.listener = nil}if s.socket != nil {err := s.socket.Close()terror.Log(errors.Trace(err))s.socket = nil}if s.statusServer != nil {err := s.statusServer.Close()terror.Log(errors.Trace(err))s.statusServer = nil}if s.grpcServer != nil {s.grpcServer.Stop()s.grpcServer = nil}if s.autoIDService != nil {s.autoIDService.Close()}if s.authTokenCancelFunc != nil {s.authTokenCancelFunc()}s.wg.Wait()metrics.ServerEventCounter.WithLabelValues(metrics.EventClose).Inc()
}func (s *Server) startShutdown() {s.rwlock.RLock()logutil.BgLogger().Info("setting tidb-server to report unhealthy (shutting-down)")s.inShutdownMode = trues.rwlock.RUnlock()// give the load balancer a chance to receive a few unhealthy health reports// before acquiring the s.rwlock and blocking connections.waitTime := time.Duration(s.cfg.GracefulWaitBeforeShutdown) * time.Secondif waitTime > 0 {logutil.BgLogger().Info("waiting for stray connections before starting shutdown process", zap.Duration("waitTime", waitTime))time.Sleep(waitTime)}
}

从上面的逻辑可以看到,close的时候先startShutdown再进行资源回收。而在执行startShutdown的时候,居然有个time.Sleep(waitTime)。

然后研究下 graceful-wait-before-shutdown 参数,发现参数是0,不是此处导致的。

在 TiDB 等待服务器关闭期间,HTTP 状态会显示失败,使得负载均衡器可以重新路由流量
默认值:0
指定关闭服务器时 TiDB 等待的秒数,使得客户端有时间断开连接。

cleanup()

在 cleanup 中看到了 GracefulDown 和 TryGracefulDown 两个方法

func cleanup(svr *server.Server, storage kv.Storage, dom *domain.Domain, graceful bool) {if graceful {done := make(chan struct{})svr.GracefulDown(context.Background(), done)} else {svr.TryGracefulDown()}plugin.Shutdown(context.Background())closeDomainAndStorage(storage, dom)disk.CleanUp()topsql.Close()
}

TryGracefulDown

研究发现使用 SIGHUP 终止进程时使用 TryGracefulDown 方法,其他时候使用 GracefulDown。对比 TryGracefulDown 和 GracefulDown 实现, TryGracefulDown 只是多个15s的超时处理,底层逻辑还是 GracefulDown

var gracefulCloseConnectionsTimeout = 15 * time.Second// TryGracefulDown will try to gracefully close all connection first with timeout. if timeout, will close all connection directly.
func (s *Server) TryGracefulDown() {ctx, cancel := context.WithTimeout(context.Background(), gracefulCloseConnectionsTimeout)defer cancel()done := make(chan struct{})go func() {s.GracefulDown(ctx, done)}()select {case <-ctx.Done():s.KillAllConnections()case <-done:return}
}

GracefulDown

下面是 GracefulDown 实现,原来在这里会间隔1s,一直判断客户端连接是否存在,如果不存在才退出。

// GracefulDown waits all clients to close.
func (s *Server) GracefulDown(ctx context.Context, done chan struct{}) {logutil.Logger(ctx).Info("[server] graceful shutdown.")metrics.ServerEventCounter.WithLabelValues(metrics.EventGracefulDown).Inc()count := s.ConnectionCount()for i := 0; count > 0; i++ {s.kickIdleConnection()count = s.ConnectionCount()if count == 0 {break}// Print information for every 30s.if i%30 == 0 {logutil.Logger(ctx).Info("graceful shutdown...", zap.Int("conn count", count))}ticker := time.After(time.Second)select {case <-ctx.Done():returncase <-ticker:}}close(done)
}

ConnectionCount

判断连接个数的逻辑也很简单,就是对算下 s.clients 的 length

// ConnectionCount gets current connection count.
func (s *Server) ConnectionCount() int {s.rwlock.RLock()cnt := len(s.clients)s.rwlock.RUnlock()return cnt
}

其中还有一个奇怪的函数 kickIdleConnection,这个是做什么的?

kickIdleConnection

看逻辑是收集可以被close的会话然后close掉。

func (s *Server) kickIdleConnection() {var conns []*clientConns.rwlock.RLock()for _, cc := range s.clients {if cc.ShutdownOrNotify() {// Shutdowned conn will be closed by us, and notified conn will exist themselves.conns = append(conns, cc)}}s.rwlock.RUnlock()for _, cc := range conns {err := cc.Close()if err != nil {logutil.BgLogger().Error("close connection", zap.Error(err))}}
}

那么什么样的会话可以被close呢?

ShutdownOrNotify

有三类:

  • client 状态处于 ServerStatusInTrans;
  • 状态处于 connStatusReading
  • 以及处于 connStatusDispatching 在 clientConn.Run 方法中被回收
// ShutdownOrNotify will Shutdown this client connection, or do its best to notify.
func (cc *clientConn) ShutdownOrNotify() bool {if (cc.ctx.Status() & mysql.ServerStatusInTrans) > 0 {return false}// If the client connection status is reading, it's safe to shutdown it.if atomic.CompareAndSwapInt32(&cc.status, connStatusReading, connStatusShutdown) {return true}// If the client connection status is dispatching, we can't shutdown it immediately,// so set the status to WaitShutdown as a notification, the loop in clientConn.Run// will detect it and then exit.atomic.StoreInt32(&cc.status, connStatusWaitShutdown)return false
}const (connStatusDispatching int32 = iotaconnStatusReadingconnStatusShutdown     // Closed by server.connStatusWaitShutdown // Notified by server to close.
)

破案

通过上面的分析,我们注意到了处于 ServerStatusInTrans 状态的连接不会被关闭,然后连接该节点执行show processlist发现的确有个处于事务中的会话

mysql> show processlist;
+---------------------+------+-----------------+------+---------+------+----------------------------+------------------+
| Id                  | User | Host            | db   | Command | Time | State                      | Info             |
+---------------------+------+-----------------+------+---------+------+----------------------------+------------------+
| 7794237818187809175 | root | 127.0.0.1:61293 | a    | Query   |    0 | in transaction; autocommit | show processlist |
+---------------------+------+-----------------+------+---------+------+----------------------------+------------------+
1 row in set (0.00 sec)

平时mysql使用的多,mysql在关闭的时候不管会话处于什么阶段,不管不顾直接停服,而tidb的这样处理着实让我想不到。

总结

本文简短的分析了下 tidb 进程关闭的处理流程,最终定位到进程没有及时关闭的原因。

对比于mysql的停服行为,让我们对tidb的处理方式有了不一样的理解。

对于 “graceful-wait-before-shutdown 参数”、“停服时等待事务结束的逻辑”的确需要在实践中才能积累。

相关文章:

TiDB 优雅关闭

背景 今天使用tiup做实验的事后&#xff0c;将tidb节点从2个缩到1个&#xff0c;发现tiup返回成功但是tidb-server进程还在。 这就引发的我的好奇心&#xff0c;why&#xff1f; 实验复现 启动集群 #( 07/31/23 8:32下午 )( happyZBMAC-f298743e3 ):~/docker/tiup/tiproxy…...

食品厂能源管理系统助力节能减排,提升可持续发展

随着全球能源问题的日益突出&#xff0c;食品厂作为能源消耗较大的行业&#xff0c;如何有效管理和利用能源成为了一项重要任务。引入食品厂能源管理系统可以帮助企业实现节能减排&#xff0c;提高能源利用效率&#xff0c;同时也符合可持续发展的理念。 食品厂能源管理系统的…...

ABAP读取文本函数效率优化,read_text --->zread_text

FUNCTION zread_text. *“---------------------------------------------------------------------- "“本地接口&#xff1a; *” IMPORTING *” VALUE(CLIENT) LIKE SY-MANDT DEFAULT SY-MANDT *" VALUE(ID) LIKE THEAD-TDID *" VALUE(LANGUAGE) LIKE THEAD-…...

Spring Data Repository 使用详解

8.1. 核心概念 Spring Data repository 抽象的中心接口是 Repository。它把要管理的 domain 类以及 domain 类的ID类型作为泛型参数。这个接口主要是作为一个标记接口&#xff0c;用来捕捉工作中的类型&#xff0c;并帮助你发现扩展这个接口的接口。 CrudRepository 和 ListCr…...

[ MySQL ] — 数据库环境安装、概念和基本使用

目录 安装MySQL 获取mysql官⽅yum源 安装mysql yum 源 安装mysql服务 启动服务 登录 方法1&#xff1a;获取临时root密码 方法2&#xff1a;无密码 方法3&#xff1a;跳过密码认证 配置my.cnf 卸载环境 设置开机启动(可以不设) 常见问题 安装遇到秘钥过期的问题&…...

Apache Thrift C++库的TThreadPoolServer模式的完整示例

1. 本程序功能 1) 要有完整的request 和 response; 2) 支持多进程并行处理任务; 3)子进程任务结束后无僵尸进程 2.Apache Thrift C++库的编译和安装 见 步步详解:Apache Thrift C++库从编译到工作模式DEMO_北雨南萍的博客-CSDN博客 3.框架生成 数据字段定义: cat D…...

图解java.util.concurrent并发包源码系列——深入理解ReentrantLock,看完可以吊打面试官

图解java.util.concurrent并发包源码系列——深入理解ReentrantLock&#xff0c;看完可以吊打面试官 ReentrantLock是什么&#xff0c;有什么作用ReentrantLock的使用ReentrantLock源码解析ReentrantLock#lock方法FairSync#tryAcquire方法NonfairSync#tryAcquire方法 Reentrant…...

【计算机网络】网络基础(上)

文章目录 1. 网络发展认识协议 2.网络协议初识协议分层OSI七层模型 | TCP/IP网络传输基本流程情况1&#xff1a;同一个局域网(子网)数据在两台通信机器中如何流转协议报头的理解局域网通信原理(故事版本)一般原理数据碰撞结论 情况2&#xff1a;跨一个路由器的两个子网IP地址与…...

51单片机(普中HC6800-EM3 V3.0)实验例程软件分析 实验四 蜂鸣器

目录 前言 一、原理图及知识点介绍 1.1、蜂鸣器原理图&#xff1a; 二、代码分析 前言 第一个实验:51单片机&#xff08;普中HC6800-EM3 V3.0&#xff09;实验例程软件分析 实验一 点亮第一个LED_ManGo CHEN的博客-CSDN博客 第二个实验:51单片机&#xff08;普中HC6800-EM…...

无向图-已知根节点求高度

深搜板子题&#xff0c;无向图&#xff0c;加边加两个&#xff0c;dfs输入两个参数变量&#xff0c;一个是当前深搜节点&#xff0c;另一个是父节点&#xff08;避免重复搜索父节点&#xff09;&#xff0c;恢复现场 ///首先完成数组模拟邻接表#include<iostream> #incl…...

RIP动态路由协议 (已过时,逐渐退出舞台)

RIP 路由更新&#xff1a;RIP1/2 每30秒钟广播(255.255.255.255)/组播 &#xff08;224.0.0.9&#xff09;一次超时&#xff1a;180秒未收到更新&#xff0c;即标记为不可用&#xff08;跳数16&#xff09;&#xff0c;240秒收不到&#xff0c;即从路由表中删除 &#xff1b;跳…...

C++ operator关键字的使用(重载运算符、仿函数、类型转换操作符)

目录 定义operator重载运算符operator重载函数调用运算符operator类型转换操作符 定义 C11 中&#xff0c;operator 是一个关键字&#xff0c;用于重载运算符。通过重载运算符&#xff0c;您可以定义自定义类型的对象在使用内置运算符时的行为。 operator重载用法一般可以分为…...

深度学习笔记-暂退法(Drop out)

背景 在机器学习的模型中&#xff0c;如果模型的参数太多&#xff0c;而训练样本又太少&#xff0c;训练出来的模型很容易产生过拟合的现象。在训练神经网络的时候经常会遇到过拟合的问题&#xff0c;过拟合具体表现在&#xff1a;模型在训练数据上损失函数较小&#xff0c;预…...

使用自适应去噪在线顺序极限学习机预测飞机发动机剩余使用寿命(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…...

实验5-7 使用函数求1到10的阶乘和 (10 分)

实验5-7 使用函数求1到10的阶乘和 &#xff08;10 分&#xff09; 本题要求实现一个计算非负整数阶乘的简单函数&#xff0c;使得可以利用该函数&#xff0c;计算1!2!⋯10!的值。 函数接口定义&#xff1a; double fact( int n ); 其中n是用户传入的参数&#xff0c;其值不超过…...

kafka部署

1.kafka安装部署 1.1 kafaka下载 https://archive.apache.org/dist/kafka/2.4.0/kafka_2.12-2.4.0.tgz Binary downloads是指预编译的软件包,可供直接下载和安装,无需手动编译。在计算机领域中,二进制下载通常指预构建的软件分发包,可以直接安装在系统上并使用 "2.…...

Spring Security6入门及自定义登录

一、前言 Spring Security已经更新到了6.x,通过本专栏记录以下Spring Security6学习过程&#xff0c;当然大家可参考Spring Security5专栏对比学习 Spring Security5专栏地址&#xff1a;security5 Spring Security是spring家族产品中的一个安全框架&#xff0c;核心功能包括…...

开放式蓝牙耳机哪个品牌好用?盘点几款很不错的开放式耳机

​相比传统入耳式耳机&#xff0c;开放式耳机因其不入耳不伤耳的开放设计&#xff0c;不仅带来了舒适的佩戴体验&#xff0c;还创造了一种与周围环境互动的全新方式&#xff0c;户外运动过程时也无需担心发生事故&#xff0c;安全性更高。我整理了几款比较好用的开放式耳机给大…...

WebGL: 几个入门小例子

本文罗列几个WebGL入门例子&#xff0c;用于帮助WebGL学习。 一、概述 WebGL (Web Graphics Library)是一组基于Open ES、在Web内渲染3D图形的Javascript APIs。 Ref. from Khronos Group: WebGL WebGL™ is a cross-platform, royalty-free open web standard for a low-lev…...

PAT(Advanced Level)刷题指南 —— 第一弹

一、1001 A+B Format 1. 问题重述 给两个整数,输出这两个数的加和的结果,每三位用逗号分隔。 2. Sample Input -1000000 93. Sample Output -999,9914. 题解 思路:直接将两个整数相加,判断是否为负,是负数则直接输出负号并转为正数;然后将正数转为字符串,按规则每…...

谷歌浏览器插件

项目中有时候会用到插件 sync-cookie-extension1.0.0&#xff1a;开发环境同步测试 cookie 至 localhost&#xff0c;便于本地请求服务携带 cookie 参考地址&#xff1a;https://juejin.cn/post/7139354571712757767 里面有源码下载下来&#xff0c;加在到扩展即可使用FeHelp…...

MySQL 隔离级别:脏读、幻读及不可重复读的原理与示例

一、MySQL 隔离级别 MySQL 提供了四种隔离级别,用于控制事务之间的并发访问以及数据的可见性,不同隔离级别对脏读、幻读、不可重复读这几种并发数据问题有着不同的处理方式,具体如下: 隔离级别脏读不可重复读幻读性能特点及锁机制读未提交(READ UNCOMMITTED)允许出现允许…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

(二)TensorRT-LLM | 模型导出(v0.20.0rc3)

0. 概述 上一节 对安装和使用有个基本介绍。根据这个 issue 的描述&#xff0c;后续 TensorRT-LLM 团队可能更专注于更新和维护 pytorch backend。但 tensorrt backend 作为先前一直开发的工作&#xff0c;其中包含了大量可以学习的地方。本文主要看看它导出模型的部分&#x…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...