精华内容
下载资源
问答
  • 近期服务器出现大量time_wait的TCP连接造成服务器连接数过多而最终导致tomcat假死状态。连接服务器查看连接数的时候提示如下。 [root@test apache-tomcat-7.0.53]# netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a...
  • 解决TIME_WAIT过多造成的问题
  • 3(深入理解Wait、Notify和Wait与sleep区别).rar
  • 对于服务器挂起中的CLOSE_WAIT & FIN_WAIT2 解决方案。
  • Linux TCP Finwait2/Timewait状态要义浅析

    千次阅读 2018-08-11 08:37:17
    最直观的解法就是,将进入timewait的TCP socket按照进入timewait时间点的FIFO原则排队,仅仅启动一个timer,设置队头的timewait到期时间为timer到期的时间。这是Linux内核标准的实现方式。 然而这里面的trick在于...

    我讨厌TCP。但是我的工作中总是要接触TCP!
    近期三四个礼拜,接连碰到三三两两TCP的问题,这些都无关拥塞控制,这些都是状态机方面的问题,但无论怎样,我是非常讨厌的,以至于恶心,我释放大量的感情色彩在TCP协议…

    但这不能否认我对TCP的理解以及对其性情的掌握已经超出了大多数人,我在咒骂中成长。我咒骂着TCP,同时也可能被别人鄙视着…那就来吧。

    本周本来不想写技术文章的,很多的pending的事情尘埃落定,我想干点别的,我哪能有心思搞什么我最烦的TCP!然而非也…

    除了我最烦的TCP,我还能搞什么?无路可走!不如再写一些感悟,期待能帮助一些需要帮助的人。

    那就再说说TCP吧。


    首先我们来看RFC793上标准的TCP状态图:

    这里写图片描述

    再看一下这个标准RFC的状态图中潜在的风险:

    这里写图片描述

    标准归标准,实现归实现。

    标准需要完成一个闭环,而实现则要兼顾各种限制条件。在TCP状态机上看,RFC793实现了单一CLOSED出口,虽然TIMEWAIT状态本身就不完美,但至少它是主动关闭方唯一的出口。然而如图所示,在实现上,这里会遇到潜在的DoS风险,如何规避这个风险呢?

    Linux内核TCP实现是这么做的:

    这里写图片描述

    你可以说,这个状态机违反了RFC793所述原汁原味的全双工独立控制的TCP协议。是的,它违反了,有谁规定我不能单独关闭一个方向的数据传输而在另一个方向继续呢?

    然而,事实上没有人这么做,没有人单独关闭一个方向的数据传输,当你关闭一个方向的数据传输时,潜规则是,告诉对方,你也关闭吧。如果你真的想关闭一个方向的数据传输,你不发任何数据便是,事实上很多协议不都是单方向传输数据的吗?比如文件下载,除了一开始的几个交互外,后续几乎就是单方向的。

    历史地看问题,在1970-1980年代,人们认为交互会被平等的分为两类,以Telnet双向交互和文件下载准单向传输为代表,如此环境设计出来的TCP协议,当然要让二者平分秋色了。可以说,应用模式的事情,TCP帮着做了太多。

    TCP过渡设计了。

    到了1990年代,一直持续到21世纪,Linux时代和TCP/IP初上舞台的时代已经有大不同。此时人们根据现实的要求,不光要兼顾应用对传输协议的使用,还要应对公共无界无障碍的Internet上的各种攻击,当人们认识到现实的攻击带来的危害其影响远重要于理论上的闭环自洽的时候,这个状态机就被改变了,至少是在Linux内核是被改变了。


    这里不得不闲扯几句。

    被改变的不止TCP的状态机,还包括TCP Timewait的时间,不再是RFC所说的2MSL即120秒,而是简单的60秒,为什么?因为这会在不伤大雅的情况下,节省资源!

    在Linux内核inet_twsk_schedule函数中,有下面的注释请阅读:

    /* timeout := RTO * 3.5
    *
    * 3.5 = 1+2+0.5 to wait for two retransmits.
    *
    * RATIONALE: if FIN arrived and we entered TIME-WAIT state,
    * our ACK acking that FIN can be lost. If N subsequent retransmitted
    * FINs (or previous seqments) are lost (probability of such event
    * is p^(N+1), where p is probability to lose single packet and
    * time to detect the loss is about RTO*(2^N - 1) with exponential
    * backoff). Normal timewait length is calculated so, that we
    * waited at least for one retransmitted FIN (maximal RTO is 120sec).
    * [ BTW Linux. following BSD, violates this requirement waiting
    * only for 60sec, we should wait at least for 240 secs.
    * Well, 240 consumes too much of resources 8)
    * ]
    * This interval is not reduced to catch old duplicate and
    * responces to our wandering segments living for two MSLs.
    * However, if we use PAWS to detect
    * old duplicates, we can reduce the interval to bounds required
    * by RTO, rather than MSL. So, if peer understands PAWS, we
    * kill tw bucket after 3.5*RTO (it is important that this number
    * is greater than TS tick!) and detect old duplicates with help
    * of PAWS.
    */

    同时,如果你看HTTP的标准的话,也是没有规定HTTP头部的长度,然而这会让Web服务器端在读到\r\n\r\n之前不断的执行malloc-strncpy循环,这会耗尽服务器的内存,于是在实现上,Web服务器会对HTTP头长度进行限制!


    理论上的分析应该可以到底为止了。在具体的实现上,如果你撸代码的话,还是有不少难以用文字表达的trick。


    首先我们简单想一下,Linux会为每一个进入finwait2状态或者timewait状态的socket都启动一个timer来计时吗?非也。同样的设计和TCP不会为每一个数据包启动一个重传定时器一样。

    最直观的解法就是,将进入timewait的TCP socket按照进入timewait时间点的FIFO原则排队,仅仅启动一个timer,设置队头的timewait到期时间为timer到期的时间。这是Linux内核标准的实现方式。

    然而这里面的trick在于,Linux对finwait2状态的实现和timewait的实现进行了复用,这个在代码的清晰度上减了分。

    具体来讲,Linux内核用tcp_time_wait一个函数,实现了两个状态:
    这里写图片描述

    请撸下面的代码:

    /*
     * Move a socket to time-wait or dead fin-wait-2 state.
     */
    void tcp_time_wait(struct sock *sk, int state, int timeo)
    {
        ...
    
        if (tcp_death_row.sysctl_tw_recycle && tp->rx_opt.ts_recent_stamp)
            recycle_ok = tcp_remember_stamp(sk); // trick!!
    
        ...
    
        if (tw != NULL) {
            ...
    
            /* Get the TIME_WAIT timeout firing. */
            if (timeo < rto)
                timeo = rto;
    
            if (recycle_ok) {
                tw->tw_timeout = rto; // about 3.5 rto
            } else {
                tw->tw_timeout = TCP_TIMEWAIT_LEN;
                if (state == TCP_TIME_WAIT)
                    timeo = TCP_TIMEWAIT_LEN;
            }
            // 事实上是两类timer:
            // 1. 标准的TIMEWAIT-TIMEOUT=60ms的timer
            // 2. 非标准的TIMEOUT=3.5RTO的TIMER
            inet_twsk_schedule(tw, &tcp_death_row, timeo,
                       TCP_TIMEWAIT_LEN);
            inet_twsk_put(tw);
        } else {
            ...
        }
        ...
    }

    你以为这就结束了吗?非也。

    我想表达的是Linux在实现TCP timewait的时候,它真的是能预先判定什么情况下可以违背一点规则。就比如说,什么情况下可以让一个处于timewait状态的TCP连接不在那个状态待60秒(已经是一个违反原则的优化后的值了),而是可以马上释放。

    用什么可以check?答案是PAWS!具体详见我在很久以前写的一篇文章,必须看哦:
    TCP的TIME_WAIT快速回收与重用https://blog.csdn.net/dog250/article/details/13760985
    有理论分析,有实验,没有代码…不过我觉得2013年的文章(并不严谨,但就是那个意思),还可以了,当时我记得是同事北京出差回来,一个问题没有搞定在讨论,当时大家叫我老湿,同时大家的讨论也被我听到了,我当时也不知道到底为什么,后来大家把这个问题淡化以后,我默默做了几个实验,终于明白了原因…事后就写了上面那篇文章。

    一直以来,我都是很讨厌TCP的,但是我就是可以搞定TCP的任何问题,上天作弄!


    现在来看Linux关于PAWS的优化。

    摘录一段上述我自己在2013年写的文章的一个段落

    于是需要有一定的手段避免这些危险。什么手段呢?虽然曾经连接的tuple信息没有了,但是在IP层还可以保存一个peer信息,注意这个信息不单单是用于TCP这个四层协议的,路由逻辑也会使用它,其字段包括但不限于:
    对端IP地址
    peer最后一次被TCP触摸到的时间戳

    在快速释放掉TIME_WAIT连接之后,peer依然保留着。丢失的仅仅是端口信息。不过有了peer的IP地址信息以及TCP最后一次触摸它的时间戳就足够了,TCP规范给出一个优化,即一个新的连接除了同时触犯了以下几点,其它的均可以快速接入,即使它本应该处在TIME_WAIT状态(但是被即快速回收了):
    1.来自同一台机器的TCP连接携带时间戳;
    2.之前同一台peer机器(仅仅识别IP地址,因为连接被快速释放了,没了端口信息)的某个TCP数据在MSL秒之内到过本机;
    3.新连接的时间戳小于peer机器上次TCP到来时的时间戳,且差值大于重放窗口戳。

    同时满足上述3个条件,方可拒绝一个连接,否则,即便一个timewait还没有结束的连接也依然可以被重用(前提是你要打开tw reuse这个sysctl参数)

    我曾经想过,为什么这个优化没有写在TCP的RFC中。

    因为你可以看到,这个优化完全基于IP层dst_entry,完全基于peer这个IP层的东西,如果要设计一个协议的话,是绝对不应该依赖其它的层级的,也就是说,你设计的协议越闭环越好,越不依赖别的层级越好!

    然而,在实现上,那就没有限制了。在Linux内核中,到处都有这些违法原则的实现。也许吧,Linux是一个宏内核,什么东西都懂…


    好了,我总结有三:

    • Linux TCP的timewait超时60秒
    • Linux TCP的finwait2和timewait共用一套实现
    • Linux TCP状态机有大不同
    • Linux TCP针对PAWS是依赖IP层的


    这周末本来不想写技术文章的,但今日深圳大雨到暴雨,后半夜无眠,听雨作文,成就这篇!其实我本来是想写一篇文章抨击一下戾气或者说杠精,然而在我自己获得认可之后,所有的戾气突然间成了美好,我也就没有什么好抨击的了,只有感激。

    PS:温州皮鞋厂老板(这是我的朋友,一个真实的人,不是虚构的)终于AT要成功了,我当然很欣慰。然而这也意味着温州皮鞋厂老板再一次离我而去…这我很伤心。

    接下来还想喷一下burst和pacing,有谁前来讨论吗?哈哈。。。

    展开全文
  • wait方法是当前线程等待,释放锁,这是Object的方法。同时要注意,Java 14 之后引入的 inline class 是没有 wait 方法的 Sleep()原理 public static native void sleep(long millis) throws InterruptedException; ...

    一句话总结:sleep方法是当前线程休眠,让出cpu,不释放锁,这是Thread的静态方法;wait方法是当前线程等待,释放锁,这是Object的方法。同时要注意,Java 14 之后引入的 inline class 是没有 wait 方法的

    Sleep()原理

    public static native void sleep(long millis) throws InterruptedException;
    

    sleep()是Thread中的static方法,也是native实现。就是调用底层的 sleep 函数实现:

    void THREAD_sleep(int seconds) {
    #ifdef windows
        Sleep(1000L * seconds);
    #else
        sleep(seconds);
    #endif
    }
    

    linux的sleep函数参考 sleep: https://man7.org/linux/man-pages/man3/sleep.3.html

    wait(), notify(), notifyAll()

    这些属于基本的Java多线程同步类的API,都是native实现:

    public final native void wait(long timeout) throws InterruptedException;
    public final native void notify();
    public final native void notifyAll();
    

    那么底层实现是怎么回事呢?
    首先我们需要先明确JDK底层实现共享内存锁的基本机制。
    每个Object都有一个ObjectMonitor,这个ObjectMonitor中包含三个特殊的数据结构,分别是CXQ(实际上是Contention List),EntryList还有WaitSet;一个线程在同一时间只会出现在他们三个中的一个中。首先来看下CXQ:
    这里写图片描述
    一个尝试获取Object锁的线程,如果首次尝试(就是尝试CAS更新轻量锁)失败,那么会进入CXQ;进入的方法就是CAS更新CXQ指针指向自己,如果成功,自己的next指向剩余队列;CXQ是一个LIFO队列,设计成LIFO主要是为了:

    1. 进入CXQ队列后,每个线程先进入一段时间的spin自旋状态,尝试获取锁,获取失败的话则进入park状态。这个自旋的意义在于,假设锁的hold时间非常短,如果直接进入park状态的话,程序在用户态和系统态之间的切换会影响锁性能。这个spin可以减少切换;
    2. 进入spin状态如果成功获取到锁的话,需要出队列,出队列需要更新自己的头指针,如果位于队列前列,那么需要操作的时间会减少
      但是,如果全部依靠这个机制,那么理所当然的,CAS更新队列头的操作会非常频繁。所以,引入了EntryList来减少争用:
      这里写图片描述
      假设Thread A是当前锁的Owner,接下来他要释放锁了,那么如果EntryList为null并且cxq不为null,就会从cxq末尾取出一个线程,放入EntryList(注意,EntryList为双向队列),并且标记EntryList其中一个线程为Successor(一般是头节点,这个EntryList的大小可能大于一,一般在notify时,后面会说到),这个Successor接下来会进入spin状态尝试获取锁(注意,在第一次自旋过去后,之后线程一直处于park状态)。如果获取成功,则成为owner,否则,回到EntryList中。
      这种利用两个队列减少争用的算法,可以参考: Michael Scott’s “2Q” algorithm
      接下来,进入我们的正题,wait方法。如果一个线程成为owner后,执行了wait方法,则会进入WaitSet:
      Object.wait()底层实现

    这里写图片描述

    void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
    
        //检查线程合法性
        Thread *const Self = THREAD;
        assert(Self->is_Java_thread(), "Must be Java thread!");
        JavaThread *jt = (JavaThread *) THREAD;
        DeferredInitialize();
        //检查当前线程是否拥有锁
        CHECK_OWNER();
        EventJavaMonitorWait event;
        // 检查中断位
        if (interruptible && Thread::is_interrupted(Self, true) && !HAS_PENDING_EXCEPTION) {
            if (JvmtiExport::should_post_monitor_waited()) {
                JvmtiExport::post_monitor_waited(jt, this, false);
            }
            if (event.should_commit()) {
                post_monitor_wait_event(&event, 0, millis, false);
            }
            TEVENT(Wait - ThrowIEX);
            THROW(vmSymbols::java_lang_InterruptedException());
            return;
        }
        TEVENT(Wait);
        assert(Self->_Stalled == 0, "invariant");
        Self->_Stalled = intptr_t(this);
        jt->set_current_waiting_monitor(this);
    
        //建立放入WaitSet中的这个线程的封装对象
        ObjectWaiter node(Self);
        node.TState = ObjectWaiter::TS_WAIT;
        Self->_ParkEvent->reset();
        OrderAccess::fence();
        //用自旋方式获取操作waitset的lock,因为一般只有owner线程会操作这个waitset(无论是wait还是notify),所以竞争概率很小(除非响应interrupt事件才会有争用),采用spin方式效率高
        Thread::SpinAcquire(&_WaitSetLock, "WaitSet - add");
        //添加到waitset
        AddWaiter(&node);
        //释放锁,代表现在线程已经进入了waitset,接下来要park了
        Thread::SpinRelease(&_WaitSetLock);
    
        if ((SyncFlags & 4) == 0) {
            _Responsible = NULL;
        }
        intptr_t save = _recursions; // record the old recursion count
        _waiters++;                  // increment the number of waiters
        _recursions = 0;             // set the recursion level to be 1
        exit(true, Self);                    // exit the monitor
        guarantee(_owner != Self, "invariant");
    
        // 确保没有unpark事件冲突影响本次park,方法就是主动post一次unpark
        if (node._notified != 0 && _succ == Self) {
            node._event->unpark();
        }
    
        // 接下来就是park操作了
        。。。。。。。。。
        。。。。。。。。。
    }
    

    当另一个owner线程调用notify时,根据Knob_MoveNotifyee这个值,决定将从waitset里面取出的一个线程放到哪里(cxq或者EntrySet)
    Object.notify()底层实现

    void ObjectMonitor::notify(TRAPS) {
        //检查当前线程是否拥有锁
        CHECK_OWNER();
        if (_WaitSet == NULL) {
            TEVENT(Empty - Notify);
            return;
        }
        DTRACE_MONITOR_PROBE(notify, this, object(), THREAD);
        //决定取出来的线程放在哪里
        int Policy = Knob_MoveNotifyee;
        //同样的,用自旋方式获取操作waitset的lock
        Thread::SpinAcquire(&_WaitSetLock, "WaitSet - notify");
        ObjectWaiter *iterator = DequeueWaiter();
        if (iterator != NULL) {
            TEVENT(Notify1 - Transfer);
            guarantee(iterator->TState == ObjectWaiter::TS_WAIT, "invariant");
            guarantee(iterator->_notified == 0, "invariant");
            if (Policy != 4) {
                iterator->TState = ObjectWaiter::TS_ENTER;
            }
            iterator->_notified = 1;
            Thread *Self = THREAD;
            iterator->_notifier_tid = Self->osthread()->thread_id();
    
            ObjectWaiter *List = _EntryList;
            if (List != NULL) {
                assert(List->_prev == NULL, "invariant");
                assert(List->TState == ObjectWaiter::TS_ENTER, "invariant");
                assert(List != iterator, "invariant");
            }
    
            if (Policy == 0) {       // prepend to EntryList
                if (List == NULL) {
                    iterator->_next = iterator->_prev = NULL;
                    _EntryList = iterator;
                } else {
                    List->_prev = iterator;
                    iterator->_next = List;
                    iterator->_prev = NULL;
                    _EntryList = iterator;
                }
            } else if (Policy == 1) {      // append to EntryList
                if (List == NULL) {
                    iterator->_next = iterator->_prev = NULL;
                    _EntryList = iterator;
                } else {
                    // CONSIDER:  finding the tail currently requires a linear-time walk of
                    // the EntryList.  We can make tail access constant-time by converting to
                    // a CDLL instead of using our current DLL.
                    ObjectWaiter *Tail;
                    for (Tail = List; Tail->_next != NULL; Tail = Tail->_next);
                    assert(Tail != NULL && Tail->_next == NULL, "invariant");
                    Tail->_next = iterator;
                    iterator->_prev = Tail;
                    iterator->_next = NULL;
                }
            } else if (Policy == 2) {      // prepend to cxq
                // prepend to cxq
                if (List == NULL) {
                    iterator->_next = iterator->_prev = NULL;
                    _EntryList = iterator;
                } else {
                    iterator->TState = ObjectWaiter::TS_CXQ;
                    for (;;) {
                        ObjectWaiter *Front = _cxq;
                        iterator->_next = Front;
                        if (Atomic::cmpxchg_ptr(iterator, &_cxq, Front) == Front) {
                            break;
                        }
                    }
                }
            } else if (Policy == 3) {      // append to cxq
                iterator->TState = ObjectWaiter::TS_CXQ;
                for (;;) {
                    ObjectWaiter *Tail;
                    Tail = _cxq;
                    if (Tail == NULL) {
                        iterator->_next = NULL;
                        if (Atomic::cmpxchg_ptr(iterator, &_cxq, NULL) == NULL) {
                            break;
                        }
                    } else {
                        while (Tail->_next != NULL) Tail = Tail->_next;
                        Tail->_next = iterator;
                        iterator->_prev = Tail;
                        iterator->_next = NULL;
                        break;
                    }
                }
            } else {
                ParkEvent *ev = iterator->_event;
                iterator->TState = ObjectWaiter::TS_RUN;
                OrderAccess::fence();
                ev->unpark();
            }
    
            if (Policy < 4) {
                iterator->wait_reenter_begin(this);
            }
    
            // _WaitSetLock protects the wait queue, not the EntryList.  We could
            // move the add-to-EntryList operation, above, outside the critical section
            // protected by _WaitSetLock.  In practice that's not useful.  With the
            // exception of  wait() timeouts and interrupts the monitor owner
            // is the only thread that grabs _WaitSetLock.  There's almost no contention
            // on _WaitSetLock so it's not profitable to reduce the length of the
            // critical section.
        }
        //释放waitset的lock
        Thread::SpinRelease(&_WaitSetLock);
    
        if (iterator != NULL && ObjectMonitor::_sync_Notifications != NULL) {
            ObjectMonitor::_sync_Notifications->inc();
        }
    }
    

    对于NotifyAll就很好推测了,这里不再赘述;

    最后求个投票,感激不尽。

    每日一刷,轻松提升技术,斩获各种offer:

    image

    展开全文
  • 英文版:Oracle Wait Interface: A Practical Guide to Performance Diagnostics & Tuning 内容简介 《Oracle Wait Interface性能诊断与调整实践指南》详细讲述了如何充分利用革命性的Oracle Wait Interface(OWI)...
  • 今天登陆服务器想查看一个端口的占用情况,...time_wait的作用 1 2 3 4 5 6 7 8 9 10 TIME_WAIT状态存在的理由: 1)可靠地实现TCP全双工连接的终止 ...

    原文:https://www.cnblogs.com/whx7762/p/9413787.html

    今天登陆服务器想查看一个端口的占用情况,发现好多TIME_WAIT的情况,吓我一跳。

    image.png

    如下是TCP 建立连接的示意图

    image.png

    image.png

    缘由

    time_wait的作用

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    TIME_WAIT状态存在的理由:

    1)可靠地实现TCP全双工连接的终止

       在进行关闭连接四次挥手协议时,最后的ACK是由主动关闭端发出的,如果这个最终的ACK丢失,服务器将重发最终的FIN,

    因此客户端必须维护状态信息允许它重发最终的ACK。如果不维持这个状态信息,那么客户端将响应RST分节,服务器将此分节解释成一个错误(在java中会抛出connection reset的SocketException)。

    因而,要实现TCP全双工连接的正常终止,必须处理终止序列四个分节中任何一个分节的丢失情况,主动关闭的客户端必须维持状态信息进入TIME_WAIT状态。

      

    2)允许老的重复分节在网络中消逝 

    TCP分节可能由于路由器异常而“迷途”,在迷途期间,TCP发送端可能因确认超时而重发这个分节,迷途的分节在路由器修复后也会被送到最终目的地,这个原来的迷途分节就称为lost duplicate。

    在关闭一个TCP连接后,马上又重新建立起一个相同的IP地址和端口之间的TCP连接,后一个连接被称为前一个连接的化身(incarnation),那么有可能出现这种情况,前一个连接的迷途重复分组在前一个连接终止后出现,从而被误解成从属于新的化身。

    为了避免这个情况,TCP不允许处于TIME_WAIT状态的连接启动一个新的化身,因为TIME_WAIT状态持续2MSL,就可以保证当成功建立一个TCP连接的时候,来自连接先前化身的重复分组已经在网络中消逝。

    大量TIME_WAIT造成的影响

    在高并发短连接的TCP服务器上,当服务器处理完请求后立刻主动正常关闭连接。这个场景下会出现大量socket处于TIME_WAIT状态。如果客户端的并发量持续很高,此时部分客户端就会显示连接不上。

    我来解释下这个场景。主动正常关闭TCP连接,都会出现TIMEWAIT。

    为什么我们要关注这个高并发短连接呢?有两个方面需要注意:

    1. 高并发可以让服务器在短时间范围内同时占用大量端口,而端口有个0~65535的范围,并不是很多,刨除系统和其他服务要用的,剩下的就更少了。

    2. 在这个场景中,短连接表示“业务处理+传输数据的时间 远远小于 TIMEWAIT超时的时间”的连接。

          这里有个相对长短的概念,比如取一个web页面,1秒钟的http短连接处理完业务,在关闭连接之后,这个业务用过的端口会停留在TIMEWAIT状态几分钟,而这几分钟,其他HTTP请求来临的时候是无法占用此端口的(占着茅坑不拉翔)。单用这个业务计算服务器的利用率会发现,服务器干正经事的时间和端口(资源)被挂着无法被使用的时间的比例是 1:几百,服务器资源严重浪费。(说个题外话,从这个意义出发来考虑服务器性能调优的话,长连接业务的服务就不需要考虑TIMEWAIT状态。同时,假如你对服务器业务场景非常熟悉,你会发现,在实际业务场景中,一般长连接对应的业务的并发量并不会很高。

         综合这两个方面,持续的到达一定量的高并发短连接,会使服务器因端口资源不足而拒绝为一部分客户服务。同时,这些端口都是服务器临时分配,无法用SO_REUSEADDR选项解决这个问题。

    关于time_wait的反思:

    1

    2

    3

    4

    存在即是合理的,既然TCP协议能盛行四十多年,就证明他的设计合理性。所以我们尽可能的使用其原本功能。

    依靠TIME_WAIT状态来保证我的服务器程序健壮,服务功能正常。

    那是不是就不要性能了呢?并不是。如果服务器上跑的短连接业务量到了我真的必须处理这个TIMEWAIT状态过多的问题的时候,我的原则是尽量处理,而不是跟TIMEWAIT干上,非先除之而后快。

    如果尽量处理了,还是解决不了问题,仍然拒绝服务部分请求,那我会采取负载均衡来抗这些高并发的短请求。持续十万并发的短连接请求,两台机器,每台5万个,应该够用了吧。一般的业务量以及国内大部分网站其实并不需要关注这个问题,一句话,达不到时才需要关注这个问题的访问量。

    小知识点

    1

    2

    TCP协议发表:1974年12月,卡恩、瑟夫的第一份TCP协议详细说明正式发表。当时美国国防部与三个科学家小组签定了完成TCP/IP的协议,结果由瑟夫领衔的小组捷足先登,首先制定出了通过详细定义的TCP/IP协议标准。当时作了一个试验,将信息包通过点对点的卫星网络,再通过陆地电缆

    ,再通过卫星网络,再由地面传输,贯串欧洲和美国,经过各种电脑系统,全程9.4万公里竟然没有丢失一个数据位,远距离的可靠数据传输证明了TCP/IP协议的成功。

     

    解决方案

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    使用命令:netstat -an | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'  查看连接状态统计

     

    使用命令:vim /etc/sysctl.conf

    编辑文件,加入以下内容:

    net.ipv4.tcp_syncookies = 1  (某些情况下该参数已启用)

    net.ipv4.tcp_tw_reuse = 1

    net.ipv4.tcp_tw_recycle = 1

    net.ipv4.tcp_fin_timeout = 30

    然后执行/sbin/sysctl -p让参数生效。

     

     

    net.ipv4.tcp_syncookies = 1表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;

    net.ipv4.tcp_tw_reuse = 1表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;

    net.ipv4.tcp_tw_recycle = 1表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。

    net.ipv4.tcp_fin_timeout修改系統默认的TIMEOUT时间

    效果展示

    image.png

    应该在很短的时间多执行几遍这个命令,你就会看到TIME_WAIT 数量在下降

    image.png

    参考资料

    原文地址:linux服务器出现大量TIME_WAIT的解决方法

     

    服务器大量TIME_WAIT和CLOSE_WAIT的原因及解决办法

     Linux服务器下查看网络连接的状态

       netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'    

    它会显示例如下面的信息:

    TIME_WAIT 814
    CLOSE_WAIT 1
    FIN_WAIT1 1
    ESTABLISHED 634
    SYN_RECV 2
    LAST_ACK 1

     

    常用的三个状态是:ESTABLISHED 表示正在通信,TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭。

     

    1.服务器保持了大量TIME_WAIT状态

     这种情况比较常见,一些爬虫服务器或者WEB服务器(如果网管在安装的时候没有做内核参数优化的话)上经常会遇到这个问题,这个问题是怎么产生的呢?

    从 上面的示意图可以看得出来,TIME_WAIT是主动关闭连接的一方保持的状态,对于爬虫服务器来说他本身就是“客户端”,在完成一个爬取任务之后,他就 会发起主动关闭连接,从而进入TIME_WAIT的状态,然后在保持这个状态2MSL(max segment lifetime)时间之后,彻底关闭回收资源。为什么要这么做?明明就已经主动关闭连接了为啥还要保持资源一段时间呢?这个是TCP/IP的设计者规定 的,主要出于以下两个方面的考虑:

    1.防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)
    2. 可靠的关闭TCP连接。在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会重新发fin, 如果这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。所以主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。另外这么设计TIME_WAIT 会定时的回收资源,并不会占用很大资源的,除非短时间内接受大量请求或者受到攻击。

    现在来说如何来解决这个问题。

     

    解决思路很简单,就是让服务器能够快速回收和重用那些TIME_WAIT的资源。

     

    下面是一篇文章中提到的/etc/sysctl.conf文件的修改

    1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    #对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃,不应该大于255,默认值是5,对应于180秒左右时间  

    net.ipv4.tcp_syn_retries=2 

    #net.ipv4.tcp_synack_retries=2 

    #表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为300秒 

    net.ipv4.tcp_keepalive_time=1200 

    net.ipv4.tcp_orphan_retries=3 

    #表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间 

    net.ipv4.tcp_fin_timeout=30   

    #表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。 

    net.ipv4.tcp_max_syn_backlog = 4096 

    #表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭 

    net.ipv4.tcp_syncookies = 1 

       

    #表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭 

    net.ipv4.tcp_tw_reuse = 1 

    #表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭 

    net.ipv4.tcp_tw_recycle = 1 

       

    ##减少超时前的探测次数  

    net.ipv4.tcp_keepalive_probes=5  

    ##优化网络设备接收队列  

    net.core.netdev_max_backlog=3000  

      修改完之后执行/sbin/sysctl -p让参数生效。

    net.ipv4.tcp_tw_reuse和net.ipv4.tcp_tw_recycle的开启都是为了回收处于TIME_WAIT状态的资源。

    net.ipv4.tcp_fin_timeout这个时间可以减少在异常情况下服务器从FIN-WAIT-2转到TIME_WAIT的时间。

    net.ipv4.tcp_keepalive_*一系列参数,是用来设置服务器检测连接存活的相关配置。

     

     修改方法:

     

    sudo vi /etc/sysctl.conf

     

    增加如下:

     

    net.ipv4.tcp_tw_reuse = 1 

    net.ipv4.tcp_tw_recycle = 1 
    net.ipv4.tcp_fin_timeout = 30 
    net.ipv4.ip_local_port_range = 10000 65000 
    net.ipv4.tcp_max_syn_backlog = 8192 
    net.ipv4.tcp_max_tw_buckets = 10000

     

    sudo /sbin/sysctl -p

     

    实时生效

     2.服务器保持了大量CLOSE_WAIT状态

    TIME_WAIT状态可以通过优化服务器参数得到解决,因为发生TIME_WAIT的情况是服务器自己可控的,要么就是对方连接的异常,要么就是自己没有迅速回收资源,总之不是由于自己程序错误导致的。

    但 是CLOSE_WAIT就不一样了,从上面的图可以看出来,如果一直保持在CLOSE_WAIT状态,那么只有一种情况,就是在对方关闭连接之后服务器程 序自己没有进一步发出ack信号。换句话说,就是在对方连接关闭之后,程序里没有检测到,或者程序压根就忘记了这个时候需要关闭连接,于是这个资源就一直 被程序占着。个人觉得这种情况,通过服务器内核参数也没办法解决,服务器对于程序抢占的资源没有主动回收的权利,除非终止程序运行。

    如果你使用的是HttpClient并且你遇到了大量CLOSE_WAIT的情况,那么这篇日志也许对你有用:http://blog.csdn.net/shootyou/article/details/6615051

    在那边日志里头我举了个场景,来说明CLOSE_WAIT和TIME_WAIT的区别,这里重新描述一下:

    服 务器A是一台爬虫服务器,它使用简单的HttpClient去请求资源服务器B上面的apache获取文件资源,正常情况下,如果请求成功,那么在抓取完 资源后,服务器A会主动发出关闭连接的请求,这个时候就是主动关闭连接,服务器A的连接状态我们可以看到是TIME_WAIT。如果一旦发生异常呢?假设 请求的资源服务器B上并不存在,那么这个时候就会由服务器B发出关闭连接的请求,服务器A就是被动的关闭了连接,如果服务器A被动关闭连接之后程序员忘了 让HttpClient释放连接,那就会造成CLOSE_WAIT的状态了。

    所以如果将大量CLOSE_WAIT的解决办法总结为一句话那就是:查代码。因为问题出在服务器程序里头啊。  

    你的程序有问题!

     

    展开全文
  • PostgreSQL中的等待事件wait_event

    千次阅读 2020-07-12 18:47:48
    * Wait Classes * ---------- */ #define PG_WAIT_LWLOCK 0x01000000U /* 等待LWLock */ #define PG_WAIT_LOCK 0x03000000U /* 等待Lock */ #define PG_WAIT_BUFFER_PIN 0x04000000U /* 等待访问数据缓冲...

    PostgreSQL9.6版本开始,加入了wait_event特性。通过查阅pg_stat_activity中的wait_event_type和wait_event我们可以了解到每个sql进程“当前”更详细的执行状态,无论是对于异常定位排查,还是系统优化来说都更加方便了。

    这篇博文简单讨论下wait_event相关的原理。

    一、等待事件分类

    I.事件类型 :

    共有9个分类,每一类都由不同事件组成

    /* ----------
     * Wait Classes
     * ----------
     */
    #define PG_WAIT_LWLOCK				0x01000000U   /* 等待LWLock */
    #define PG_WAIT_LOCK				0x03000000U   /* 等待Lock */
    #define PG_WAIT_BUFFER_PIN			0x04000000U   /* 等待访问数据缓冲区 */
    #define PG_WAIT_ACTIVITY			0x05000000U   /* 服务器进程处于空闲状态 */
    #define PG_WAIT_CLIENT				0x06000000U   /* 等待应用客户端程序在套接字中进行操作 */
    #define PG_WAIT_EXTENSION			0x07000000U   /* 等待扩展模块中的操作 */
    #define PG_WAIT_IPC					0x08000000U   /* 等待进程间通信 */
    #define PG_WAIT_TIMEOUT				0x09000000U   /* 等待达到超时时间 */
    #define PG_WAIT_IO					0x0A000000U   /* 等待IO操作完成 */
    
    II. 具体事件:

    每种类型和具体事件含义请参考:https://www.postgresql.org/docs/12/monitoring-stats.html

    1.PG_WAIT_LWLOCK类
    /* 等待LWLock */

    定义共有65种事件

    const char *const MainLWLockNames[] = {
    	"<unassigned:0>",
    	"ShmemIndexLock",
    	"OidGenLock",
    	"XidGenLock",
    	"ProcArrayLock",
    	"SInvalReadLock",
    	"SInvalWriteLock",
    	"WALBufMappingLock",
    	"WALWriteLock",
    	"ControlFileLock",
    	"CheckpointLock",
    	"CLogControlLock",
    	"SubtransControlLock",
    	"MultiXactGenLock",
    	"MultiXactOffsetControlLock",
    	"MultiXactMemberControlLock",
    	"RelCacheInitLock",
    	"CheckpointerCommLock",
    	"TwoPhaseStateLock",
    	"TablespaceCreateLock",
    	"BtreeVacuumLock",
    	"AddinShmemInitLock",
    	"AutovacuumLock",
    	"AutovacuumScheduleLock",
    	"SyncScanLock",
    	"RelationMappingLock",
    	"AsyncCtlLock",
    	"AsyncQueueLock",
    	"SerializableXactHashLock",
    	"SerializableFinishedListLock",
    	"SerializablePredicateLockListLock",
    	"OldSerXidLock",
    	"SyncRepLock",
    	"BackgroundWorkerLock",
    	"DynamicSharedMemoryControlLock",
    	"AutoFileLock",
    	"ReplicationSlotAllocationLock",
    	"ReplicationSlotControlLock",
    	"CommitTsControlLock",
    	"CommitTsLock",
    	"ReplicationOriginLock",
    	"MultiXactTruncationLock",
    	"OldSnapshotTimeMapLock",
    	"LogicalRepWorkerLock",
    	"CLogTruncationLock"
    };
    typedef enum BuiltinTrancheIds
    {
    	LWTRANCHE_CLOG_BUFFERS = NUM_INDIVIDUAL_LWLOCKS,
    	LWTRANCHE_COMMITTS_BUFFERS,
    	LWTRANCHE_SUBTRANS_BUFFERS,
    	LWTRANCHE_MXACTOFFSET_BUFFERS,
    	LWTRANCHE_MXACTMEMBER_BUFFERS,
    	LWTRANCHE_ASYNC_BUFFERS,
    	LWTRANCHE_OLDSERXID_BUFFERS,
    	LWTRANCHE_WAL_INSERT,
    	LWTRANCHE_BUFFER_CONTENT,
    	LWTRANCHE_BUFFER_IO_IN_PROGRESS,
    	LWTRANCHE_REPLICATION_ORIGIN,
    	LWTRANCHE_REPLICATION_SLOT_IO_IN_PROGRESS,
    	LWTRANCHE_PROC,
    	LWTRANCHE_BUFFER_MAPPING,
    	LWTRANCHE_LOCK_MANAGER,
    	LWTRANCHE_PREDICATE_LOCK_MANAGER,
    	LWTRANCHE_PARALLEL_HASH_JOIN,
    	LWTRANCHE_PARALLEL_QUERY_DSA,
    	LWTRANCHE_SESSION_DSA,
    	LWTRANCHE_SESSION_RECORD_TABLE,
    	LWTRANCHE_SESSION_TYPMOD_TABLE,
    	LWTRANCHE_SHARED_TUPLESTORE,
    	LWTRANCHE_TBM,
    	LWTRANCHE_PARALLEL_APPEND,
    	LWTRANCHE_FIRST_USER_DEFINED
    }			BuiltinTrancheIds;
    

    2. PG_WAIT_LOCK类型
    /* 等待Lock */

    共有10种事件

    /*
     * LOCKTAG is the key information needed to look up a LOCK item in the
     * lock hashtable.  A LOCKTAG value uniquely identifies a lockable object.
     *
     * The LockTagType enum defines the different kinds of objects we can lock.
     * We can handle up to 256 different LockTagTypes.
     */
    typedef enum LockTagType
    {
    	LOCKTAG_RELATION,			/* whole relation */
    	LOCKTAG_RELATION_EXTEND,	/* the right to extend a relation */
    	LOCKTAG_PAGE,				/* one page of a relation */
    	LOCKTAG_TUPLE,				/* one physical tuple */
    	LOCKTAG_TRANSACTION,		/* transaction (for waiting for xact done) */
    	LOCKTAG_VIRTUALTRANSACTION, /* virtual transaction (ditto) */
    	LOCKTAG_SPECULATIVE_TOKEN,	/* speculative insertion Xid and token */
    	LOCKTAG_OBJECT,				/* non-relation database object */
    	LOCKTAG_USERLOCK,			/* reserved for old contrib/userlock code */
    	LOCKTAG_ADVISORY			/* advisory user locks */
    } LockTagType;
    
    

    3. PG_WAIT_BUFFER_PIN类型
    /* 等待访问数据缓冲区 */

    共有1种事件

    /* ----------
     * pgstat_get_wait_event() -
     *
     *	Return a string representing the current wait event, backend is
     *	waiting on.
     */
    const char *
    pgstat_get_wait_event(uint32 wait_event_info)
    {
    	/* 省略部分代码行 */
    
    	switch (classId)
    	{
    		/* 省略其他分支 */
    		case PG_WAIT_BUFFER_PIN:
    			event_name = "BufferPin";
    			break;
    		/* 省略其他分支 */
         }
        
        return event_name;
    }
    

    4. PG_WAIT_ACTIVITY类型
    /* 当前服务器进程处于空闲状态 */

    共有14种事件,分别对应14个进程主函数

    typedef enum
    {
    	WAIT_EVENT_ARCHIVER_MAIN = PG_WAIT_ACTIVITY,
    	WAIT_EVENT_AUTOVACUUM_MAIN,
    	WAIT_EVENT_BGWRITER_HIBERNATE,
    	WAIT_EVENT_BGWRITER_MAIN,
    	WAIT_EVENT_CHECKPOINTER_MAIN,
    	WAIT_EVENT_LOGICAL_APPLY_MAIN,
    	WAIT_EVENT_LOGICAL_LAUNCHER_MAIN,
    	WAIT_EVENT_PGSTAT_MAIN,
    	WAIT_EVENT_RECOVERY_WAL_ALL,
    	WAIT_EVENT_RECOVERY_WAL_STREAM,
    	WAIT_EVENT_SYSLOGGER_MAIN,
    	WAIT_EVENT_WAL_RECEIVER_MAIN,
    	WAIT_EVENT_WAL_SENDER_MAIN,
    	WAIT_EVENT_WAL_WRITER_MAIN
    } WaitEventActivity;
    

    5. PG_WAIT_CLIENT类型
    /* 等待应用客户端程序在套接字中进行操作 */

    共有9种事件

    /* ----------
     * Wait Events - Client
     *
     * Use this category when a process is waiting to send data to or receive data
     * from the frontend process to which it is connected.  This is never used for
     * a background process, which has no client connection.
     * ----------
     */
    typedef enum
    {
    	WAIT_EVENT_CLIENT_READ = PG_WAIT_CLIENT,
    	WAIT_EVENT_CLIENT_WRITE,
    	WAIT_EVENT_LIBPQWALRECEIVER_CONNECT,
    	WAIT_EVENT_LIBPQWALRECEIVER_RECEIVE,
    	WAIT_EVENT_SSL_OPEN_SERVER,
    	WAIT_EVENT_WAL_RECEIVER_WAIT_START,
    	WAIT_EVENT_WAL_SENDER_WAIT_WAL,
    	WAIT_EVENT_WAL_SENDER_WRITE_DATA,
    	WAIT_EVENT_GSS_OPEN_SERVER,
    } WaitEventClient;
    

    6. PG_WAIT_EXTENSION类型
    /* 等待扩展模块中的操作 */

    共有1种事件

    /* ----------
     * pgstat_get_wait_event() -
     *
     *	Return a string representing the current wait event, backend is
     *	waiting on.
     */
    const char *
    pgstat_get_wait_event(uint32 wait_event_info)
    {
    	/* 省略部分代码行 */
    
    	switch (classId)
    	{
    		/* 省略其他分支 */
    		case PG_WAIT_EXTENSION:
    			event_name = "Extension";
    			break;
    		/* 省略其他分支 */
    	}
    
    	return event_name;
    }
    

    7. PG_WAIT_IPC类型
    /* 等待进程间通信 */

    共有37种事件

    typedef enum
    {
    	WAIT_EVENT_BGWORKER_SHUTDOWN = PG_WAIT_IPC,
    	WAIT_EVENT_BGWORKER_STARTUP,
    	WAIT_EVENT_BTREE_PAGE,
    	WAIT_EVENT_CLOG_GROUP_UPDATE,
    	WAIT_EVENT_CHECKPOINT_DONE,
    	WAIT_EVENT_CHECKPOINT_START,
    	WAIT_EVENT_EXECUTE_GATHER,
    	WAIT_EVENT_HASH_BATCH_ALLOCATING,
    	WAIT_EVENT_HASH_BATCH_ELECTING,
    	WAIT_EVENT_HASH_BATCH_LOADING,
    	WAIT_EVENT_HASH_BUILD_ALLOCATING,
    	WAIT_EVENT_HASH_BUILD_ELECTING,
    	WAIT_EVENT_HASH_BUILD_HASHING_INNER,
    	WAIT_EVENT_HASH_BUILD_HASHING_OUTER,
    	WAIT_EVENT_HASH_GROW_BATCHES_ALLOCATING,
    	WAIT_EVENT_HASH_GROW_BATCHES_DECIDING,
    	WAIT_EVENT_HASH_GROW_BATCHES_ELECTING,
    	WAIT_EVENT_HASH_GROW_BATCHES_FINISHING,
    	WAIT_EVENT_HASH_GROW_BATCHES_REPARTITIONING,
    	WAIT_EVENT_HASH_GROW_BUCKETS_ALLOCATING,
    	WAIT_EVENT_HASH_GROW_BUCKETS_ELECTING,
    	WAIT_EVENT_HASH_GROW_BUCKETS_REINSERTING,
    	WAIT_EVENT_LOGICAL_SYNC_DATA,
    	WAIT_EVENT_LOGICAL_SYNC_STATE_CHANGE,
    	WAIT_EVENT_MQ_INTERNAL,
    	WAIT_EVENT_MQ_PUT_MESSAGE,
    	WAIT_EVENT_MQ_RECEIVE,
    	WAIT_EVENT_MQ_SEND,
    	WAIT_EVENT_PARALLEL_BITMAP_SCAN,
    	WAIT_EVENT_PARALLEL_CREATE_INDEX_SCAN,
    	WAIT_EVENT_PARALLEL_FINISH,
    	WAIT_EVENT_PROCARRAY_GROUP_UPDATE,
    	WAIT_EVENT_PROMOTE,
    	WAIT_EVENT_REPLICATION_ORIGIN_DROP,
    	WAIT_EVENT_REPLICATION_SLOT_DROP,
    	WAIT_EVENT_SAFE_SNAPSHOT,
    	WAIT_EVENT_SYNC_REP
    } WaitEventIPC;
    

    8. PG_WAIT_TIMEOUT类型
    /* 等待达到超时时间 */

    共有3种事件

    /* ----------
     * Wait Events - Timeout
     *
     * Use this category when a process is waiting for a timeout to expire.
     * ----------
     */
    typedef enum
    {
    	WAIT_EVENT_BASE_BACKUP_THROTTLE = PG_WAIT_TIMEOUT,
    	WAIT_EVENT_PG_SLEEP,
    	WAIT_EVENT_RECOVERY_APPLY_DELAY
    } WaitEventTimeout;
    
    

    9. PG_WAIT_IO类型
    /* 等待IO操作完成 */

    共68种事件

    /* ----------
     * Wait Events - IO
     *
     * Use this category when a process is waiting for a IO.
     * ----------
     */
    typedef enum
    {
    	WAIT_EVENT_BUFFILE_READ = PG_WAIT_IO,
    	WAIT_EVENT_BUFFILE_WRITE,
    	WAIT_EVENT_CONTROL_FILE_READ,
    	WAIT_EVENT_CONTROL_FILE_SYNC,
    	WAIT_EVENT_CONTROL_FILE_SYNC_UPDATE,
    	WAIT_EVENT_CONTROL_FILE_WRITE,
    	WAIT_EVENT_CONTROL_FILE_WRITE_UPDATE,
    	WAIT_EVENT_COPY_FILE_READ,
    	WAIT_EVENT_COPY_FILE_WRITE,
    	WAIT_EVENT_DATA_FILE_EXTEND,
    	WAIT_EVENT_DATA_FILE_FLUSH,
    	WAIT_EVENT_DATA_FILE_IMMEDIATE_SYNC,
    	WAIT_EVENT_DATA_FILE_PREFETCH,
    	WAIT_EVENT_DATA_FILE_READ,
    	WAIT_EVENT_DATA_FILE_SYNC,
    	WAIT_EVENT_DATA_FILE_TRUNCATE,
    	WAIT_EVENT_DATA_FILE_WRITE,
    	WAIT_EVENT_DSM_FILL_ZERO_WRITE,
    	WAIT_EVENT_LOCK_FILE_ADDTODATADIR_READ,
    	WAIT_EVENT_LOCK_FILE_ADDTODATADIR_SYNC,
    	WAIT_EVENT_LOCK_FILE_ADDTODATADIR_WRITE,
    	WAIT_EVENT_LOCK_FILE_CREATE_READ,
    	WAIT_EVENT_LOCK_FILE_CREATE_SYNC,
    	WAIT_EVENT_LOCK_FILE_CREATE_WRITE,
    	WAIT_EVENT_LOCK_FILE_RECHECKDATADIR_READ,
    	WAIT_EVENT_LOGICAL_REWRITE_CHECKPOINT_SYNC,
    	WAIT_EVENT_LOGICAL_REWRITE_MAPPING_SYNC,
    	WAIT_EVENT_LOGICAL_REWRITE_MAPPING_WRITE,
    	WAIT_EVENT_LOGICAL_REWRITE_SYNC,
    	WAIT_EVENT_LOGICAL_REWRITE_TRUNCATE,
    	WAIT_EVENT_LOGICAL_REWRITE_WRITE,
    	WAIT_EVENT_RELATION_MAP_READ,
    	WAIT_EVENT_RELATION_MAP_SYNC,
    	WAIT_EVENT_RELATION_MAP_WRITE,
    	WAIT_EVENT_REORDER_BUFFER_READ,
    	WAIT_EVENT_REORDER_BUFFER_WRITE,
    	WAIT_EVENT_REORDER_LOGICAL_MAPPING_READ,
    	WAIT_EVENT_REPLICATION_SLOT_READ,
    	WAIT_EVENT_REPLICATION_SLOT_RESTORE_SYNC,
    	WAIT_EVENT_REPLICATION_SLOT_SYNC,
    	WAIT_EVENT_REPLICATION_SLOT_WRITE,
    	WAIT_EVENT_SLRU_FLUSH_SYNC,
    	WAIT_EVENT_SLRU_READ,
    	WAIT_EVENT_SLRU_SYNC,
    	WAIT_EVENT_SLRU_WRITE,
    	WAIT_EVENT_SNAPBUILD_READ,
    	WAIT_EVENT_SNAPBUILD_SYNC,
    	WAIT_EVENT_SNAPBUILD_WRITE,
    	WAIT_EVENT_TIMELINE_HISTORY_FILE_SYNC,
    	WAIT_EVENT_TIMELINE_HISTORY_FILE_WRITE,
    	WAIT_EVENT_TIMELINE_HISTORY_READ,
    	WAIT_EVENT_TIMELINE_HISTORY_SYNC,
    	WAIT_EVENT_TIMELINE_HISTORY_WRITE,
    	WAIT_EVENT_TWOPHASE_FILE_READ,
    	WAIT_EVENT_TWOPHASE_FILE_SYNC,
    	WAIT_EVENT_TWOPHASE_FILE_WRITE,
    	WAIT_EVENT_WALSENDER_TIMELINE_HISTORY_READ,
    	WAIT_EVENT_WAL_BOOTSTRAP_SYNC,
    	WAIT_EVENT_WAL_BOOTSTRAP_WRITE,
    	WAIT_EVENT_WAL_COPY_READ,
    	WAIT_EVENT_WAL_COPY_SYNC,
    	WAIT_EVENT_WAL_COPY_WRITE,
    	WAIT_EVENT_WAL_INIT_SYNC,
    	WAIT_EVENT_WAL_INIT_WRITE,
    	WAIT_EVENT_WAL_READ,
    	WAIT_EVENT_WAL_SYNC,
    	WAIT_EVENT_WAL_SYNC_METHOD_ASSIGN,
    	WAIT_EVENT_WAL_WRITE
    } WaitEventIO;
    

    二、等待事件原理

    等待事件较多,挑几种出来分析一下实现原理
    从9.6版本开始,加入了该特性,体现在backend共享内存结构体PGPROC中新增了uint32 wait_event_info; /* proc’s wait information */如下#82行

    struct PGPROC
    {
    	/* proc->links MUST BE FIRST IN STRUCT (see ProcSleep,ProcWakeup,etc) */
    	SHM_QUEUE	links;			/* list link if process is in a list */
    	PGPROC	  **procgloballist; /* procglobal list that owns this PGPROC */
    
    	PGSemaphore sem;			/* ONE semaphore to sleep on */
    	int			waitStatus;		/* STATUS_WAITING, STATUS_OK or STATUS_ERROR */
    
    	Latch		procLatch;		/* generic latch for process */
    
    	LocalTransactionId lxid;	/* local id of top-level transaction currently
    								 * being executed by this proc, if running;
    								 * else InvalidLocalTransactionId */
    	int			pid;			/* Backend's process ID; 0 if prepared xact */
    	int			pgprocno;
    
    	/* These fields are zero while a backend is still starting up: */
    	BackendId	backendId;		/* This backend's backend ID (if assigned) */
    	Oid			databaseId;		/* OID of database this backend is using */
    	Oid			roleId;			/* OID of role using this backend */
    
    	Oid			tempNamespaceId;	/* OID of temp schema this backend is
    									 * using */
    
    	bool		isBackgroundWorker; /* true if background worker. */
    
    	/*
    	 * While in hot standby mode, shows that a conflict signal has been sent
    	 * for the current transaction. Set/cleared while holding ProcArrayLock,
    	 * though not required. Accessed without lock, if needed.
    	 */
    	bool		recoveryConflictPending;
    
    	/* Info about LWLock the process is currently waiting for, if any. */
    	bool		lwWaiting;		/* true if waiting for an LW lock */
    	uint8		lwWaitMode;		/* lwlock mode being waited for */
    	proclist_node lwWaitLink;	/* position in LW lock wait list */
    
    	/* Support for condition variables. */
    	proclist_node cvWaitLink;	/* position in CV wait list */
    
    	/* Info about lock the process is currently waiting for, if any. */
    	/* waitLock and waitProcLock are NULL if not currently waiting. */
    	LOCK	   *waitLock;		/* Lock object we're sleeping on ... */
    	PROCLOCK   *waitProcLock;	/* Per-holder info for awaited lock */
    	LOCKMODE	waitLockMode;	/* type of lock we're waiting for */
    	LOCKMASK	heldLocks;		/* bitmask for lock types already held on this
    								 * lock object by this backend */
    
    	/*
    	 * Info to allow us to wait for synchronous replication, if needed.
    	 * waitLSN is InvalidXLogRecPtr if not waiting; set only by user backend.
    	 * syncRepState must not be touched except by owning process or WALSender.
    	 * syncRepLinks used only while holding SyncRepLock.
    	 */
    	XLogRecPtr	waitLSN;		/* waiting for this LSN or higher */
    	int			syncRepState;	/* wait state for sync rep */
    	SHM_QUEUE	syncRepLinks;	/* list link if process is in syncrep queue */
    
    	/*
    	 * All PROCLOCK objects for locks held or awaited by this backend are
    	 * linked into one of these lists, according to the partition number of
    	 * their lock.
    	 */
    	SHM_QUEUE	myProcLocks[NUM_LOCK_PARTITIONS];
    
    	struct XidCache subxids;	/* cache for subtransaction XIDs */
    
    	/* Support for group XID clearing. */
    	/* true, if member of ProcArray group waiting for XID clear */
    	bool		procArrayGroupMember;
    	/* next ProcArray group member waiting for XID clear */
    	pg_atomic_uint32 procArrayGroupNext;
    
    	/*
    	 * latest transaction id among the transaction's main XID and
    	 * subtransactions
    	 */
    	TransactionId procArrayGroupMemberXid;
       
    	uint32		wait_event_info;	/* proc's wait information */
    
    	/* Support for group transaction status update. */
    	bool		clogGroupMember;	/* true, if member of clog group */
    	pg_atomic_uint32 clogGroupNext; /* next clog group member */
    	TransactionId clogGroupMemberXid;	/* transaction id of clog group member */
    	XidStatus	clogGroupMemberXidStatus;	/* transaction status of clog
    											 * group member */
    	int			clogGroupMemberPage;	/* clog page corresponding to
    										 * transaction id of clog group member */
    	XLogRecPtr	clogGroupMemberLsn; /* WAL location of commit record for clog
    									 * group member */
    
    	/* Per-backend LWLock.  Protects fields below (but not group fields). */
    	LWLock		backendLock;
    
    	/* Lock manager data, recording fast-path locks taken by this backend. */
    	uint64		fpLockBits;		/* lock modes held for each fast-path slot */
    	Oid			fpRelId[FP_LOCK_SLOTS_PER_BACKEND]; /* slots for rel oids */
    	bool		fpVXIDLock;		/* are we holding a fast-path VXID lock? */
    	LocalTransactionId fpLocalTransactionId;	/* lxid for fast-path VXID
    												 * lock */
    
    	/*
    	 * Support for lock groups.  Use LockHashPartitionLockByProc on the group
    	 * leader to get the LWLock protecting these fields.
    	 */
    	PGPROC	   *lockGroupLeader;	/* lock group leader, if I'm a member */
    	dlist_head	lockGroupMembers;	/* list of members, if I'm a leader */
    	dlist_node	lockGroupLink;	/* my member link, if I'm a member */
    };
    
    I.详细分析一个Lock类型的事件,了解等待事件的具体实现

    如下sql进行update,等待事件类型为Lock,具体事件为transactionid

    postgres=# select pid,wait_event_type,wait_event,query from pg_stat_activity where pid=21318;
    -[ RECORD 1 ]---+-------------------------------------
    pid             | 21318
    wait_event_type | Lock
    wait_event      | transactionid
    query           | update tbl_test set content='c05a2f0059b30755727a2807a17674bq' where id=1;
    

    打印stack信息:

    [postgres@postgres_zabbix ~]$ pstack 21318
    #0  0x00007fa72ea38913 in __epoll_wait_nocancel () from /lib64/libc.so.6
    #1  0x00000000008522cd in WaitEventSetWaitBlock (set=0x207b7b0, cur_timeout=-1, occurred_events=0x7ffcb383b5b0, nevents=1) at latch.c:1080
    #2  0x00000000008521a8 in WaitEventSetWait (set=0x207b7b0, timeout=-1, occurred_events=0x7ffcb383b5b0, nevents=1, wait_event_info=50331652) at latch.c:1032
    #3  0x0000000000851a94 in WaitLatchOrSocket (latch=0x7fa72e4011f4, wakeEvents=33, sock=-1, timeout=-1, wait_event_info=50331652) at latch.c:407
    #4  0x000000000085195f in WaitLatch (latch=0x7fa72e4011f4, wakeEvents=33, timeout=0, wait_event_info=50331652) at latch.c:347
    #5  0x0000000000866a39 in ProcSleep (locallock=0x1fe46f0, lockMethodTable=0xb8d7a0 <default_lockmethod>) at proc.c:1289
    #6  0x0000000000860d04 in WaitOnLock (locallock=0x1fe46f0, owner=0x1ff6688) at lock.c:1768
    #7  0x000000000085fe2a in LockAcquireExtended (locktag=0x7ffcb383ba90, lockmode=5, sessionLock=false, dontWait=false, reportMemoryError=true, locallockp=0x0) at lock.c:1050
    #8  0x000000000085f47f in LockAcquire (locktag=0x7ffcb383ba90, lockmode=5, sessionLock=false, dontWait=false) at lock.c:713
    #9  0x000000000085e302 in XactLockTableWait (xid=501, rel=0x7fa72f6ba198, ctid=0x7ffcb383bb44, oper=XLTW_Update) at lmgr.c:658
    #10 0x00000000004c980f in heap_update (relation=0x7fa72f6ba198, otid=0x7ffcb383be60, newtup=0x208c7c8, cid=0, crosscheck=0x0, wait=true, tmfd=0x7ffcb383bd60, lockmode=0x7ffcb383bd5c) at heapam.c:3228
    #11 0x00000000004d3f63 in heapam_tuple_update (relation=0x7fa72f6ba198, otid=0x7ffcb383be60, slot=0x207b340, cid=0, snapshot=0x2045760, crosscheck=0x0, wait=true, tmfd=0x7ffcb383bd60, lockmode=0x7ffcb383bd5c, update_indexes=0x7ffcb383bd5b) at heapam_handler.c:332
    #12 0x00000000006da7ba in table_tuple_update (rel=0x7fa72f6ba198, otid=0x7ffcb383be60, slot=0x207b340, cid=0, snapshot=0x2045760, crosscheck=0x0, wait=true, tmfd=0x7ffcb383bd60, lockmode=0x7ffcb383bd5c, update_indexes=0x7ffcb383bd5b) at ../../../src/include/access/tableam.h:1261
    #13 0x00000000006dc636 in ExecUpdate (mtstate=0x2079df0, tupleid=0x7ffcb383be60, oldtuple=0x0, slot=0x207b340, planSlot=0x207b1e0, epqstate=0x2079ee8, estate=0x2079a70, canSetTag=true) at nodeModifyTable.c:1312
    #14 0x00000000006ddb1f in ExecModifyTable (pstate=0x2079df0) at nodeModifyTable.c:2223
    #15 0x00000000006b21c9 in ExecProcNodeFirst (node=0x2079df0) at execProcnode.c:445
    #16 0x00000000006a8356 in ExecProcNode (node=0x2079df0) at ../../../src/include/executor/executor.h:239
    #17 0x00000000006aa6d2 in ExecutePlan (estate=0x2079a70, planstate=0x2079df0, use_parallel_mode=false, peration=CMD_UPDATE, sendTuples=false, numberTuples=0, direction=ForwardScanDirection, dest=0x2088838, execute_once=true) at execMain.c:1646
    #18 0x00000000006a8833 in standard_ExecutorRun (queryDesc=0x207c6b0, direction=ForwardScanDirection, count=0, execute_once=true) at execMain.c:364
    #19 0x00000000006a86d8 in ExecutorRun (queryDesc=0x207c6b0, direction=ForwardScanDirection, count=0, execute_once=true) at execMain.c:308
    #20 0x000000000087ee7a in ProcessQuery (plan=0x1fecfc0,  sourceText=0x1fc6030 "update tbl_test set content='c05a2f0059b30755727a2807a17674bq' where id=1;", params=0x0, queryEnv=0x0, dest=0x2088838, completionTag=0x7ffcb383c270 "") at pquery.c:161
    #21 0x00000000008805c1 in PortalRunMulti (portal=0x202b250, isTopLevel=true, setHoldSnapshot=false, dest=0x2088838, altdest=0x2088838, completionTag=0x7ffcb383c270 "") at pquery.c:1283
    #22 0x000000000087fbfb in PortalRun (portal=0x202b250, count=9223372036854775807, isTopLevel=true, run_once=true, dest=0x2088838, altdest=0x2088838, completionTag=0x7ffcb383c270 "") at pquery.c:796
    #23 0x0000000000879fa1 in exec_simple_query (query_string=0x1fc6030 "update tbl_test set content='c05a2f0059b30755727a2807a17674bq' where id=1;") at postgres.c:1215
    #24 0x000000000087e00f in PostgresMain (argc=1, argv=0x1fef398, dbname=0x1fef258 "postgres", username=0x1fef238 "postgres") at postgres.c:4236
    #25 0x00000000007e5c42 in BackendRun (port=0x1fe6f40) at postmaster.c:4431
    #26 0x00000000007e5441 in BackendStartup (port=0x1fe6f40) at postmaster.c:4122
    #27 0x00000000007e1aef in ServerLoop () at postmaster.c:1704
    #28 0x00000000007e13af in PostmasterMain (argc=1, argv=0x1fc1d80) at postmaster.c:1377
    #29 0x000000000070ef6e in main (argc=1, argv=0x1fc1d80) at main.c:228
    [postgres@postgres_zabbix ~]$
    

    主要关注以下几行
    #8行申请锁,lockmode为5即ShareLock
    #4行设置等待事件为wait_event_info=50331652
    #0行进入epoll_wait中观察list链表,并sleep

    
    #0  0x00007fa72ea38913 in __epoll_wait_nocancel () from /lib64/libc.so.6
    #4  0x000000000085195f in WaitLatch (latch=0x7fa72e4011f4, wakeEvents=33, timeout=0, wait_event_info=50331652) at latch.c:347
    #8  0x000000000085f47f in LockAcquire (locktag=0x7ffcb383ba90, lockmode=5, sessionLock=false, dontWait=false) at lock.c:713
    

    使用框图简单描述下wait_event的设置,以及wait_event的查询显示的代码调用逻辑。

    这里蓝色框表示函数入口,绿色框表示变量

    在这里插入图片描述

    1)左半边框图表示event的设置

    可以看到该update申请Lock时,具体设置的等待事件为wait_event_info=50331652

    这里粘贴一部分gdb跟踪过程,大致可以看到wait_event_info=50331652的设置过程

    Breakpoint 3, ProcSleep (locallock=0x1fe46f0, lockMethodTable=0xb8d7a0 <default_lockmethod>) at proc.c:1065
    1065            LOCKMODE        lockmode = locallock->tag.mode;
    (gdb) 
    1066            LOCK       *lock = locallock->lock;
    (gdb) 
    1076            PGPROC     *leader = MyProc->lockGroupLeader;
    (gdb) p MyProc->wait_event_info  /*目前还未设置等待事件,值为0*/
    $4 = 0
    (gdb) n
    1083            if (leader != NULL)
    (gdb) 
    1117            if (myHeldLocks != 0)
    (gdb) 
    1179                    proc = (PGPROC *) &(waitQueue->links);
    (gdb) 
    1263                            enable_timeout_after(DEADLOCK_TIMEOUT, DeadlockTimeout);
    (gdb) 
    1282                    if (InHotStandby)
    (gdb) n  /*wait_event_info是在waitlatch函数中设置的,该函数定义最后一个形参就是wait_event_info*/
             /*在这里调用该函数时传递的实参为事件类型和具体事件或运算的结果,即1290行的 PG_WAIT_LOCK | locallock->tag.lock.locktag_type*/
    1289                            (void) WaitLatch(MyLatch, WL_LATCH_SET | WL_EXIT_ON_PM_DEATH, 0,   
    1290      PG_WAIT_LOCK | locallock->tag.lock.locktag_type);
    (gdb) p locallock->tag.lock.locktag_type
    $5 = 4 '\004'
    (gdb) 
    1291                            ResetLatch(MyLatch);
    (gdb) p MyProc->wait_event_info
    $7 = 50331652       /*可以看到wait_event_info已经被配置为50331652*/
    (gdb) p/x  MyProc->wait_event_info
    $8 = 0x03000004      /*以16进制显示为0x03000004*/
    

    2)右半边框图表示event的查询显示

    可以看到在pg_stat_activity中查询到该update语句对应的等待事件类型为Lock,具体事件为wait_event=transactionid

    主要分析下如何从wait_event_info=50331652得到wait_event=transactionid

    50331652是一个十进制数,之前有提到postgresql中等待事件类型的宏定义对应的实际值都是使用无符号16进制数来表示。

    将50331652转化为无符号16进制为0x03000004U,

    关注框图右侧部分的逻辑
    a. wait_event_type 的运算取值
    wait_event_type = wait_event_info & 0xFF000000
    = 0x03000004U & 0xFF000000
    意为保留wait_event_info的高两位,运算值为0x03000000U,这个值比较眼熟吧,正是lock类型的定义

    #define PG_WAIT_LOCK				0x03000000U
    

    b. wait_event的运算取值:
    step1 求locktag_type
    locktag_type=wait_event_info & 0x0000FFFF
    = 0x03000004U & 0x0000FFFF
    意为保留wait_event_info的的低四位,运算值为0x00000004U,对应十进制值为4

    step2 求wait_event
    wait_event=LockTagTypeNames[locktag_type]
    =LockTagTypeNames[4]

    值为LockTagTypeNames下标为4的成员,从LockTagTypeNames数组定义可以找到下标为4对应的正是transactionid

    /* This must match enum LockTagType! */
    const char *const LockTagTypeNames[] = {
    	"relation",
    	"extend",
    	"page",
    	"tuple",
    	"transactionid",
    	"virtualxid",
    	"speculative token",
    	"object",
    	"userlock",
    	"advisory"
    };
    
    II.简单列举下其他几种类型

    1.PG_WAIT_LWLOCK 类的buffer_content事件

    如下一个表进行autovacuum ,可以看到当前的等待事件类型为LWLock,具体事件为buffer_content

    -[ RECORD 1 ]---+----------------------------------------------------------------
    pid 202623
    wait_event_type | LWLock
    wait_event      | buffer_content
    query           | autovacuum: VACUUM public.apply_info_search (to prevent wraparound
    backend_type    | autovacuum worker 
    

    来看下stack信息:

    [postgres@postgres_zabbix ~]$ pstack 202623
    #0 0x002aae05c12aob in do_futex_wait.constprop.1 () from/lib64/libpthread.so 
    #1 0x002aae05c12a9f in new_sem_wait_slow.constprop.0 () from /lib64/libpthrd.so
    #2 0x002aae05c12b3b in sem_wait@@GLIBC 2.2.5 ()from /lib64/libpthread.so.0
    #3 0x0000000068a8e2 in PGSemaphoreLock()
    #4 0x000000006f2c34 in LWLockAcquire ()
    #5 0x00000000960se1 in LockBufferForcleanup () 
    #6 0x00000000964930 in btvacuumpage()
    #7 0x000000004c423f in btvacuumscan()
    #8 0x0000000066438e in btbulkdelete()
    #9 0x000009005d3ecl in lazy_vacuum_index () 
    #10 0x00000060654da5 in Lazy_vacuum_rel () 
    #11 0x0009000095d29bs in vacuum_rel ()
    #12 0x0009090005d3852 in vacuum()
    #13 0x09000009068d690 in do_autovacuum ()
    #14 0x00006606668daac in AutoVacworkerMain.isra.6 ()
    #15 0x000099000068349 in StartAutoVacworker ()
    #16 0x0000990000059aa in sigusr1_handler () 
    #17 <signal handler called>
    #18 0x0002aae7e5d783 in seLect_nocancel () from /Lib64/libc.so.6
    #19 0x000000000478ba inserverLoop ()
    #20 0x00000006669559 in PostmasterMain ()
    #21 0x0096006047972b in main ()
    

    可以看到当前进程通过LWLockAcquire 函数申请LWlock,尝试访问临界数据。目前在等锁,pg中LWlock是通过PGsemaphore实现(底层调用sem_wait等接口),最终调入futex接口进行sleep,等待临界资源可操作。

    进程strace信息:

    strace -p 202623
    strace: Process 202623 attached 
    futex(0x2aae0f901df8, FUTEX_WAIT, 0, NULL^Cstrace: Process 202623 detached
    <detached ...>
    

    使用futex是为了解决传统semaphore不必要的系统调用造成大量的性能损耗,具体可参考:Futex设计与实现

    2.PG_WAIT_IPC 类的Bgworkershutdown事件

    一个select查询,当前的等待事件类型为IPC,等待事件为Bgworkershutdown

    -[ RECORD 1 ]---+----------------------------------------------------------------
    pid 			| 20262
    wait_event_type | IPC
    wait_event      | Bgworkershutdown
    query           | select sum(mem_used) from qsump_pacloud_oscginfo_activity_detail_info_day
    
    

    stack信息:

    [postgres@postgres_zabbix ~]$ pstack 20262
    #0 0x00002aae97066903 in _epoll_wait nocancel () from /Lib64/libc.so.6
    #1 0x00000000006e156e in WaitEventsetWait () 
    #2 0x00000000006e1b97 in WaitLatchOrSocket ()
    #3 0x000000000068f89a in WaitForBackgroundworkerShutdown ()
    #4 0x00000000004ddbde in WaitForParallelworkersToExit.isra.1 ()
    #5 0x00000000004de76d in DestroyParallelContext ()
    #6 0x00000000004dec6b in AtEoxact_Parallel ()
    #7 0x00000000004e8997 in AbortTransaction() 
    #8 0x00000000004e8fc5 in AbortCurrentTransaction()
    #9 0x0000000000701501 in PostgresMain()
    #10 0x0000000000478cdf in ServerLoop () 
    #11 0x000000000069c559 in PostmasterMain ()
    #12 0x000000000047972b in Main () 
    [postgres@postgres_zabbix ~]$
    

    从stack可以看到,
    #8行 该select 进程当前在进行事务回滚
    #5行 准备销毁ParallelContext
    #3行 等待后台并行进程shutdown
    #0行 进入epoll_wait中观察list链表,并sleep

    ps可以看到目前20262有两个并行进程20273 ,202734

    [postgres@postgres_zabbix ~]$ ps -ef|grep 20262| grep -v grep 
    postgres 20262 20260 0 May18? 00:00:00 postgres: pg12: pguser postgres 127.0.0.1(35442) SELECT
    postgres 20273 20260 0 May18? 00:00:00 postgres: pg12: bgworker: parat 00:00:00 postgres: pg12: bgworker: parallel worker for PID 20262
    postgres 20274 20260 0 May18? 00:00:00 postgres: pg12: bgworker: parat 00:00:00 postgres: pg12: bgworker: parallel worker for PID 20262
    [postgres@postgres_zabbix ~]$
    

    不进行详细的代码分析了,大致场景为查询进程事务回滚,在等待并行进程退出并返回结果。也就是目前是进程间通信状态,因此等待事件的类型为IPC,事件就是Bgworkershutdown

    3.PG_WAIT_ACTIVITY 类型的CheckpointerMain事件
    4.PG_WAIT_CLIENT 类型的ClientRead 事件

    示例3和4请参考文章最后提到的另一篇博文

    三、总结

    通过以上的例子可以发现,PostgreSQL的等待事件,其实就是根据各种使用场景,自定义事件和类型,最终的“等待”基本是通过封装epoll、 futex等系统接口实现的。

    特别是epoll的使用场景最多,了解epoll相关可以参考之前记录的一篇博文:《PostgreSQL中的io多路复用–select和epoll实现》

    展开全文
  • 解决mysql出现大量TIME_WAIT
  • 解决进程的time_wait状态 查看进程连接状态 #netstat -an|awk ‘/tcp/ {print $6}’|sort|uniq -c 16 CLOSING 130 ESTABLISHED 298 FIN_WAIT1 13 FIN_WAIT2 9 LAST_ACK 7 LISTEN 103 SYN_RECV 5204 TIME_WAIT 状...
  • wait命令

    千次阅读 2020-01-02 22:17:56
    wait命令: 概念:阻塞当前进程的执行,直到指定的子进程结束后,当前线程才会继续执行。 格式:wait [进程号] 常用:wait 在shell中直接使用wait: 等待所有的子进程结束后,当前进程才继续往下走。 在...
  • 因为linux分配给一个用户的文件句柄是有限的,而TIME_WAIT和CLOSE_WAIT两种状态如果一直被保持,那么意味着对应数目的通道就一直被占着,而且是“占着茅坑不使劲”,一旦达到句柄数上限,新的请求就无法被处理了,...
  • 解决Linux TIME_WAIT过多造成的问题

    万次阅读 多人点赞 2019-04-15 21:23:35
    1、 time_wait的作用: TIME_WAIT状态存在的理由: 1)可靠地实现TCP全双工连接的终止 在进行关闭连接四次挥手协议时,最后的ACK是由主动关闭端发出的,如果这个最终的ACK丢失,服务器将重发最终的FIN, 因此...
  • 一个TCP FIN_WAIT2状态细节引发的感慨

    万次阅读 多人点赞 2018-07-28 08:14:15
    但是一位同事通过代码确认了一个不同的意见,他认为在经历了FINWAIT-2之后,即FINWAIT-2的timer到期后,连接依然会进入到TIMEWAIT状态,其通过tcp_time_wait函数的调用路径可以确认,调用参数为: tcp_time_wait...
  • waitwait_pid的使用

    千次阅读 2018-09-20 08:49:20
    进程一旦调用了wait,就立即阻塞自己,由wait自动分析是否当前进程的某个子进程已经退出,如果让它找到了这样一个已经变成僵尸的子进程,wait就会收集这个子进程的信息,并把它彻底销毁后返回;如果没有找到这样一...
  • 解决Linux服务器中TCP的FIN_WAIT2,CLOSE_WAIT状态连接过多的问题
  • TCP在FIN_WAIT1状态到底能持续多久以及TCP假连接问题

    万次阅读 多人点赞 2018-08-16 08:52:35
    而主机B上却没有任何关于主机A的连接信息,经查明,这是由于主机A和主机B的发送/接收缓冲区差异巨大,导致主机B进程退出后,主机A暂时憋住,主机B频繁发送零窗口探测,FIN_WAIT1状态超时,进而连接被销毁,然而主机A...
  • wait方法

    千次阅读 2019-03-24 10:33:14
    一.概念、原理、区别 Java中的多线程是一种抢占式的机制而不是分时机制。线程主要有以下几种状态:可运行,运行,阻塞,等待,等待超时,死亡。抢占式机制指的是有多个线程处于可运行状态,... Object的方法:wait...
  • 关于 time wait

    千次阅读 2018-07-17 12:43:31
    网上有很多关于 time wait 的问题和修改方案,究竟什么是 time wait?作用是什么?会造成什么问题?如何解决?我们接下来一点一点看一下。 一,time wait 是什么? timewait 状态是 TCP 链接的主动关闭方会有的...
  • Linux中wait()函数

    万次阅读 多人点赞 2018-11-10 22:50:53
    这里简单介绍一下系统调用函数:wait() 函数原型是 #include &lt;sys/types.h&gt; #include &lt;wait.h&gt; int wait(int *status) 函数功能是:父进程一旦调用了wait就立即阻塞自己,由...
  • Linux shell中的wait命令的使用

    千次阅读 2020-06-06 17:01:50
    Linux shell中的wait命令的使用一:wait命令作用二:使用格式三:举例3.1 案例一3.2 案例二:函数中使用wait3.3 案例三3.4 案例四四:串行执行与并行执行4.1 串行执行4.2 并行执行4.3 串行与并行执行效率对比 ...
  • netstat查看系统TIME_WAIT状态个数

    千次阅读 2020-06-08 22:06:17
    netstat-n|awk'/^tcp/{++S[$NF]}END{for(ainS)printa,S...常用的三个状态是:ESTABLISHED 表示正在通信,TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭。 具体每种状态什么意思,其实无需多说,看看下面这种...
  • 客户端用短连接的方式发送请求,会出现大量请求返回TIME_WAIT的现象。 在网上找了一些可用的解决方法,下次运维再来找,直接扔给他们。 参考链接如下: TCP/IP详解--TIME_WAIT状态详解:...
  • Wait函数详解

    千次阅读 2019-04-07 12:11:02
    博客搬家,原地址:https://langzi989.github.io/2017/05/04/Wait函数详解/ kill 头文件 sys/types.h signal.h 函数功能 注意此函数的功能是向指定进程发送信号。而不是杀死某个进程.名字为kill的原因是早期的Unix...
  • 原文地址:系统调优,你所不知道的TIME_WAIT和CLOSE_WAIT 作者:壹頁書http://mp.weixin.qq.com/s?__biz=MzA3MzYwNjQ3NA==&amp;mid=403319808&amp;idx=1&amp;sn=ddae082f5b844d040b9ab23c9c0eb778&...
  • 你遇到过TIME_WAIT的问题吗? 我相信很多都遇到过这个问题。一旦有用户在喊:网络变慢了。第一件事情就是,netstat -a | grep TIME_WAIT | wc -l 一下,哎呀妈呀,几千个TIME_WAIT。 然后,做的第一件事情...
  • TIME_WAIT过多的解决办法

    千次阅读 2018-08-09 09:44:28
    TIME_WAIT存在的两个理由: 1 可靠的实现TCP全双工连接的终止 2 允许老的重复的分节在网络上的消逝 第一个:如果客户端不维持TIME_WAIT状态,那么将响应给服务端一个RST,该分节被服务器解释成一个错误。如果TCP...
  • oraclev$sessionv$session_wait用途详解

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 943,158
精华内容 377,263
关键字:

wait