sdb报告创建线程失败问题定位

在sdb 的集群环境中,如果面对的是一个高并发的操作场景,有时候会莫名其妙地报告 -10 错误。

这个-10 错误,在认真查阅节点的 diaglog 日志后,发现是操作系统 create thread 失败的问题。

那么本文就是指导大家如何定位这个问题。

sdb 节点的 diaglog 的错误日志信息

错误日志信息总,通过会有类似如下的关键信息

Failed to create new agent: boost::thread_resource_error: Resource temporaily unavailable
Failed to create new agent, probe = 30
Failed to create subagent thread, rc = -10
Failed to start session EDU, rc = -10


一般操作系统在创建线程时,会受限于哪些参数呢,主要有两个

  1. 文件句柄数限制
  2. 内存资源
  • 我们先来介绍文件句柄数

在linux 操作系统中,号称一切皆为文件,无论是进程、线程、socker 还是其他,最终都会被操作系统归为文件操作。操作系统或者进程,每申请一个资源,例如线程、socker,都会打开一个文件,那么这个文件打开状态,就可以简单理解为文件句柄。

那么句柄数限制又是什么呢?其实就是操作系统,或者某个进程所能够打开的最多文件的数量的限制。

大家有了这个概念后,我们再来看操作系统是如何对文件句柄数进行限制的。

在操作系统中,有一个神奇的命令 - ulimit ,专门设置这些奇奇怪怪的限制值,进程文件句柄数就是其中之一。

例如我们可以查看 root 用户的 ulimit 输出, -n open file = 1024 就是root 用户允许进程打开的最大文件句柄数。

这里有一个小细节需要大家注意的。

由于root 用户是linux 中的管理员用户,所以如果root 用户的 ulimit open file 设置成 1024, 那么其他的用户,例如:test、mysql 用户等,想将 ulimit opon file 设置成 大于 1024,是不行的。

这个大家一定要知道,如果希望将普通用户的 ulimit 值修改得很大,一定要先修改 root 用户的值。

另外,关于句柄数的限制,还不单单是进程中的句柄数限制,还有整个操作系统的句柄数限制,因为一个操作系统,总不能无限地打开句柄的。所以又引入另外一个设置,操作系统最大打开的句柄数限制。

这个值在 centos 7 中,是被保存在 /proc/sys/fs/file-max 文件中

如果操作系统总的句柄数已经达到上限,那么即使进程还没有启动几个线程,也会出现句柄不够的情况。

如果希望临时修改操作系统最大句柄数的设置,可以直接执行 echo 2000000 >  /proc/sys/fs/file-max 即可。

如果希望永久修改操作系统最大句柄数的设置,可以编辑 /etc/sysctl.conf 文件,增加 fs.file-max = 2000000 内容,然后在root 用户中执行 sysctl -p 即可。

  • 我们接着来介绍内存资源

因为在创建线程时,在linux 中,是需要给它预先分配内存的 – 也叫 栈大小,用来存储线程中数据的值。

这里再科普另外一个知识,一个程序中,内存主要分为两个大的部分,一个称为 “堆”,一个称为“栈”。“堆”是程序用来保存常量和变量名字的,“栈”则是程序来用保存具体的变量数字的。

好,背景知识介绍完毕后,开始进入正题。

开始时说到,如果系统内存不足,也是无法创建线程的。这个原因就是在于创建线程时,操作系统需要分配一块内存给线程,这个内存是多大呢,就是 ulimit 中 -s stack size 的大小。如果操作系统连 stack size 大小的内容都无法拿出来了,创建线程就会失败。

有一些读者可能会奇怪,为啥这么一点内存都没有了?

其实如果仔细查看操作系统,你就会发现,那么多进程,每个进程又是那么多线程在运行,每个线程都在申请内存(注意,这块的内存是物理内存),内存不足正常的很。这个也容易让人联想到JVM 的OOM ,但是他们真的不是一回事,大家千万不要误会。

要解决这个问题也比较简单 – 直接粗暴?就是将 ulimit 中 -s stack size 调小一点,每个线程不要申请那么多内存了,操作系统的内存资源就会更加的充裕。毕竟程序、线程这些,都是用完就完了,不可能都永久占用内存的。

  • 其他的知识点

因为有一些读者在解决这类问题时,执行 ulimit -a 命令,发现参数都设置正常,为啥还是不行?

这里就需要和读者说道说道。

你看见 ulimit -a 是好的,但是你怎么知道进程用的就是你设置的值呢?

所以眼见为实,读者解决问题时,应该要真正确认sdb 进程的ulimit 参数是啥。

方式有两个

  1. 在sdb的较新版本中,节点启动时的diaglog 日志,会打印它自己的 ulimit 参数,读者可以去翻翻日志
  2. 另外一种就更加直接,直接查看 linux 的系统记录。例如知道 11910 进程的 PID 是 123456,就直接打开 /proc/123456/limits 文件,查看里面的内容,这样想不知道,都难
  • 关于句柄数和线程的命令

查看 某个进程总共开启了多少个 线程,可以

  1. cat /proc/$PID/status | grep Threads
  2. pstree -p $PID ,然后+1,因为还有主进程
  3. top -Hp $PID,然后查看头部 “Threads”参数
  4. ps hH p $PID | wc -l

查看linux 目前总打开的句柄数

lsof -n|awk ‘{print $2}‘|sort|uniq -c|sort -nr|awk ‘{print $1}‘ | awk ‘{sum += $1};END {print sum}‘

查看某个进程打开的总句柄数

lsof -n|awk ‘{print $2}‘|sort|uniq -c|sort -nr |  grep  $PID

原文地址:https://www.cnblogs.com/chenfool/p/11237960.html

时间: 2024-11-06 18:17:11

sdb报告创建线程失败问题定位的相关文章

SequoiaDB报告创建线程失败的解决办法

1.问题背景 对于分布式数据库和分布式环境,高并发和高性能压力的情况下,出现线程创建失败等等问题也是十分常见的,这时候就十分考虑数据库管理员的经验,需要能快速的定位到问题和瓶颈所在,快速解决.本文也是作为一个最佳实践,告诉大家如何在高并发情况下定位问题,排除问题,解决瓶颈. 2.问题定位 SequoiaDB在集群环境中的 -10 错误码,在认真查阅节点的 diaglog 日志后,发现是操作系统 create thread 失败的问题. 如我们的测试环境下,SequoiaDB节点的 diaglog

线程创建偶尔失败问题

一.创建一个线程,里面设置一个死循环,在销毁线程时间循环标志设置为false,并return,该线程是退出了,但是有没有将资源自动释放呢? 所遇问题:在多个视频循环播放的时候,程序运行十个小时左右并挂掉,提示创建线程失败,可能原因是在主线程退出之后该线程并没有释放资源,导致下次重新创建同一个线程而失败. 分析:linux线程执行有两种状态,分别是joinable状态和unjoinable状态,这与windows是有区别的: 1.如果线程是joinable状态,当线程函数自己返回退出时或pthre

线程的基本操作,创建线程,结束线程,新线程跟原线程共享变量

#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <pthread.h> // 包含线程库 #include <string.h> void *thread_function(void *arg); // 定义线程函数原型 //char message[] = "THREAD_TEST"; // 定义公用的内存空间 char message[

远程创建线程注入DLL

HANDLE WINAPI CreateRemoteThread(__in HANDLE hProcess,__in LPSECURITY_ATTRIBUTES lpThreadAttributes,__in SIZE_T dwStackSize,__in LPTHREAD_START_ROUTINE lpStartAddress,__in LPVOID lpParameter,__in DWORD dwCreationFlags,__out LPDWORD lpThreadId);hProce

线程池ThreadPoolExecutor分析: 线程池是什么时候创建线程的,队列中的任务是什么时候取出来的?

带着几个问题进入源码分析: 线程池是什么时候创建线程的? 任务runnable task是先放到core到maxThread之间的线程,还是先放到队列? 队列中的任务是什么时候取出来的? 什么时候会触发reject策略? core到maxThread之间的线程什么时候会die? task抛出异常,线程池中这个work thread还能运行其他任务吗? 至少在new ThreadPoolExecutor()时,Thread对象并没有初始化. 这里仅仅指定了几个初始参数 一段基础代码,进入分析 pu

hive创建表失败,drop表失败

一.hive创建表失败,报错: CREATE TABLE pokes (foo INT, bar STRING);FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.DDLTask. MetaException(message:javax.jdo.JDODataStoreException: An exception was thrown while adding/validating class(

多线程_创建线程_继承Thread类

public class ThreadDemo {   public static void main(String[] args){         Demo d = new Demo();   d.start();      for(int i = 0;i < 100;i++){      System.out.println("MainThread" + i);   }   } } class Demo extends Thread {   public void run(

Java并发编程:如何创建线程

一.Java中关于应用程序和进程相关的概念 在Java中,一个应用程序对应着一个JVM实例(也有地方称为JVM进程),一般来说名字默认为java.exe或者javaw.exe(windows下可以通过任务管理器查看).Java采用的是单线程编程模型,即在我们自己的程序中如果没有主动创建线程的话,只会创建一个线程,通常称为主线程.但是要注意,虽然只有一个线程来执行任务,不代表JVM中只有一个线程,JVM实例在创建的时候,同时会创建很多其他的线程(比如垃圾收集器线程). 由于Java采用的是单线程编

Java多线程——创建线程的两种方式

创建线程方式一:继承Thread类. 步骤:1,定义一个类继承Thread类.2,覆盖Thread类中的run方法.3,直接创建Thread的子类对象创建线程.4,调用start方法开启线程并调用线程的任务run方法执行. 可以通过Thread的getName获取线程的名称 Thread-编号(从0开始)主线程的名字就是main. 例: class Demo extends Thread { private String name; Demo(String name) { super(name)