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

在 sdb 的错误列表中,-10 错误代表:系统错误。

这是一个笼统的错误描述,也是一个让人迷惑的错误描述。"系统错误"?什么鬼的系统错误,究竟是什么地方导致了系统错误?

作者通过分析大量的 -10 错误案例,发现大部分情况下,都是由于线程创建失败。

一般抛出 -10 错误的都是数据节点,并且一般数据节点的 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
```

# linux 线程创建失败,原因分析

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

* 文件句柄数限制
* 内存资源

## 系统句柄

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

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

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

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

### 进程最大文件句柄数

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

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

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

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

### 用户最大进程数
一个系统用户,也有它最大的进程数,参数为 ulimit -u 命令。

该参数的官方解释如下:

```
Linux itself has a Max Processes per user limit. This feature allows us to control the number of processes an existing user on the server may be authorized to have
```

虽然解释是一个用户的进程限制,但是从使用效果分析,对线程数也有明显限制效果,毕竟线程是从进程中派生出来的资源。

一个用户的进程数限制,修改的方式和 ulimit -n 的方式一样。

### 系统总句柄限制

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

以 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 大小的内存都无法 malloc 了,创建线程就会失败。

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

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

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

在centos 7 的系统中,默认的 stack size 是8MB,一般使用默认值,都不会出现 "创建线程" 的错误,只有当极限情况才会对用户造成影响。

### 内存地址检查

在linux 系统中,内存管理是一个细致活。

其实在linux 的系统内存管理中,有两层

* 物理内存
* 虚拟内存

物理内存很好理解,就是用户为操作系统配置的RAM 大小,也是人们日常所说的服务器内存大小。

虚拟内存,是操作系统为了更好管理物理内存而设计出来的一层虚拟资源。因为操作系统在运行过程中,很多进程都在使用、释放内存,真实 free 的物理内存必然是不连续的。

所以操作系统为了让程序在使用内存时,更加方便的使用物理内存,构造了一层虚拟内存。

进程在向操作系统 malloc 一段连续的内存时,实际从虚拟内存中申请了一段连续的地址。在操作系统的内存映射表中,记录了虚拟内存地址指向真实的物理内存地址。

同时,即使程序 malloc 了内存,但实际上在 malloc 后,并不会真的立马占用了物理内存,只有当真正开始保存数据时,才会实际使用物理内存。

介绍到这里,读者们可能就对内存 malloc 有所了解了。

提问:如果操作系统真的是没有内存了而导致无法创建新的线程,那么如何定位这个问题呢?

在linux 操作系统中,实际上时刻都在记录内存的使用情况。

用户可以通过以下命令获得重要信息

```
cat /proc/meminfo | grep Commit
```

该命令将打印两个重要的数值
* CommitLimit
* Committed_AS

CommitLimit 参数,是操作系统最大可以 malloc 的内存的大小。

Committed_AS 参数,是操作系统当前已经被 malloc 了的内存的大小。

所以简单的总结,就是当 Committed_AS 参数的值无限接近 CommitLimit 参数的值时,证明当前的操作系统,真的没有太多可用内存可以继续给 malloc 了。

### 内核参数中的内存管理
针对linux 系统的内存控制,实际上真的是一个细致活。

它对内存的管理,各种参数有着交叉互相影响关系,作者在这里尝试使用毕生功力,尽量为读者解释明白。读者如果还有疑问,请咨询谷姐和度娘,作者本人可能会对大家热情的提问,累觉不爱

最新文章

  1. SignalR快速入门 ~ 仿QQ即时聊天,消息推送,单聊,群聊,多群公聊(基础=》提升)
  2. Cordova系列(一)
  3. 将一个div始终定位在屏幕中心
  4. code vs1262 不要把球传我(组合数学) 2012年CCC加拿大高中生信息学奥赛
  5. Jellycons – iOS 8 图标下载(PNG, SKETCH)
  6. git 如何恢复只是提交到本地的文件(或者commit)
  7. GRIB格式转换心得(转自博客:http://windforestwing.blog.163.com/blog/static/19545412007103084743804/)
  8. MyBatis+Spring 事务管理
  9. 使用linux的GDB打印STL(vector,map,set..................)
  10. Android隐藏标题栏
  11. 【set&&sstream||floyed判环算法】【UVa 11549】Calculator Conundrum
  12. 从0到1一步步搭建代码质量检测系统~iOS
  13. Linux - 请允许我静静地后台运行
  14. iOS 如何优化 App 的启动时间
  15. 分布式服务框架Dubbo
  16. Access denied for user ‘root’@‘localhost’(using password: YES)的解决方法
  17. 原博客地址http://blog.chinaunix.net/uid/20656672.html不再维护(10年前数百篇oracle/teradata性能优化、故障处理案例)
  18. Django之发送邮件
  19. JavaScript 字符串(String)对象
  20. 关于Suppressing notification from package com.xxx.xxx by user request.的异常

热门文章

  1. JavaScript 高级程序设计(第3版)第二章 (在html中使用js)
  2. Spring cloud学习--Zuul02
  3. dp(过河问题)
  4. bfs(最短路径)
  5. 报错: no such table:django_session解决方式
  6. 使用form提交到搜狗浏览器示例
  7. 根据配置RedisProperties自动获取RedisConnectionFactory
  8. psfgettable - 从控制台字体中提取出嵌入的Unicode字符表
  9. elk相关启动脚本-shell编写
  10. 工作中常用的linux命令大全