zxdsb666. 发表于 2021-9-9 07:30

Mysql专栏 - Linux底层交互和Raid存储架构

# Mysql专栏 - Linux底层交互和Raid存储架构

# 前言

​        在专栏之前的几篇文章中,我们总结了缓冲池,缓存页,redo log,undo log,以及数据页和数据行在底层是如何进行存储的,后续介绍了表空间,段,区等概念。这一节比较特殊,讲述的是和Linux有关的交互原理,因为多数的mysql都是部署在linux的服务器上面,本节会简单介绍一下linux是如何处理mysql的请求的,以及linux系统会带来哪些问题



# 概述

+ 介绍mysql的随机读写和顺序读写,那一部分是随机读写,那一部分是顺序读写
+ linux的系统分层逻辑结构,以及和mysql系统的交互步骤。
+ Raid的存储结构简单了解,以及他对于mysql服务的影响。
+ 补充关于too many connections问题的产生原因和解决办法。



# mysql的随机读写和顺序读写

​        mysql在执行增删改查的时候,会从表空间的磁盘文件里面读取数据页,首先我们需要明白一点,虽然在表空间的磁盘文件的数据页上进行磁盘的时是**随机读写**,但是包括**Redo log**、 **bin log** 等这些日志文件其实在磁盘上是**顺序读写**的。



## 磁盘的随机读

​        随机读取一个数据页到缓存就是磁盘的随机读操作,因为数据页的位置并不是固定的可能在磁盘的任意一位置,所以整个读写性能十分差的,根据之前的介绍,当数据页读取之后放到buffer pool里面。

​        为了IO的性能,这里需要了解两个重要的性能指标:**IOPS**和**响应延迟**。IOPS 指的是存储系统每秒可以执行多少次磁盘读写操作,底层磁盘支持每秒执行1000个随机读写和200个差距是很大的。响应延迟说的则是一次IO请求之后多少时间进行响应,也是十分大的影响的,一秒200个读写是10MS完成和一次200读写却在1S完成响应差距也是非常大的。



## redo日志是顺序写

​        虽然对于磁盘是随机读写到缓冲池,但是在缓存页更新了之后,这个数据是需要按照顺序写到redo log日志的,顺序写意味着读取的时候也可以顺序读。(关于redo log的细节将会在后续的文章介绍)磁盘顺序写的性能其实是很重要的,某种程度上来高速的顺序磁盘访问差不多可以赶上内存的读写性能,尤其是在数据库里用了**os cache机制**的情况下,也就是说就是redo log顺序写入磁盘之前,先是进入**os cache**,就是操作系统管理的**内存缓存**里。

> 关于os cache可以翻一翻之前的文章,他还有一个作用是如果innodb引擎中设置:**innodb_flush_log_at_trx_commit** 为 2,会在写入redo日志的时候把数据显写到os cache当中,并且在1S之后(可能)再把数据刷新回redo log的磁盘文件,但是这样在mysql宕机的时候会出现数据丢失的隐患....
>
> 另外这里在学习的时候查找了一下关于os cache以及application cache的速度消耗对比,在答案中讨论了缓存的有效性判定条件,以及什么时候才需要使用cache。感兴趣可以查看一下具体的文章内容:
>
> https://stackoverflow.com/questions/13091453/system-os-caching-vs-application-caching

​        最后我们根据上面的文字总结下面的图:

![](https://gitee.com/lazyTimes/imageReposity/raw/master/img/20210908132627.png)



根据上面的说明,我们大致可以总结出下面两点:

+ 写redo log的速度越快说明查询的sql性能就越高。
+ 磁盘每秒可以写入越多的数据,也说明越高的性能。

# Linux系统分层

​        下面我们进入正题,简单来说Linux的存储系统分为**VFS层、文件系统层、Page Cache缓存层、通用Block层、IO调度层、Block设备驱 动层、Block设备层**,如下图:



## mysql和linux交互步骤:

​        下面是mysql和llinux交互的大致流程:

1. VFS层:当mysql发起一次数据页的随机读写,一次redo log顺序读写的时候,实际上会把io请求交给linux的vfs层面。
2. 文件系统:vfs会把io请求需按照对应的文件管理系统进行操作和读写。比如ext1、ext2、ext3
3. Page cache:文件系统会在page cache基于内存的缓存找所需的数据,基于**内存缓存**来执行读写,如果还没有此时会交给block层
4. Block层:在一层把你对文件的io请求转换为block io
5. io调度层:默认用的是CFS 公平调度算法,io调度完成之后,会决定那个io请求先执行那个后执行。

> CFS调度器:采用完全公平调度算法,引入虚拟运行时间概念;
>
> Deadline调度器:使用**红黑树**,把进程按照绝对截止期限进行排序,选择最小进程进行调度运行
>
> 实际情况下执行多个数据库请求的时候多个sql同时在io,所以建议生产环境把io调度算法换成 **deadline调度**,核心思想就是任何一个io不能随心所欲一直等待,在**指定范围**内他必须执行。

6. Block设备驱动层:此时可以执行io的请求的就会交给block设备驱动层,最后把驱动交给设备层

7. Block设备层:硬件设备完成了IO读写操作之后要不然是写要不然是读,最后就把响应经过上面的层级反向依次返回。

​        最终 MySQL可以得到本次IO读写操作的结果,最终的结构图如下所示:

!(https://gitee.com/lazyTimes/imageReposity/raw/master/img/20210908214703.png)



## ERROR 1040(HY000): Too many connections问题

​        下面补充一个比较常见的linux相关问题。

​        在专栏的第一篇介绍中我们知道数据库是存在连接池的概念,但是连接池的容量总归是有限的,出现这个报错说明连接池已经满了。

​        默认情况下每一台机器默认的连接池大小是**200**,每台java系统最多建立200个连接,如果是2台机器,最多400个连接。

​        关于这个设置的关键的参数是**max_connections**:比如最大连接数目是800.

实际上,在运行过程中通常会发现:`show variables like 'max_connections'`在线上的设置只有**214**,这是什么情况?为什么mysql会自动重置呢?其实是因为**linux打开句柄的数量是1024导致只能连接214个**。



### 如何解决上面的问题

​        在linux当中:执行命令`Ulimit -HSn 65535`可以修改句柄的配置,为了验证是否成功,可以通过下面的命令查看句柄的配置:

​        `Cat /ect/security/limits.conf`

​        `Cat /etc/rc.load`

​        为了保险起见,也可以在`my.cnf` 的内部确保了 `mac_connections` 参数配置。



### 为什么最大连接数是214呢?

​        linux默认会限制你每个进程对机器资源的使用的,包括可以打开的文件句柄的限制,可以打开的子进程数的限制,网络缓存的限制,最大可以锁定的内存大小。

​        为了让mysql发挥最大的功效,句柄的设置值通常都是**65535**。

        所以我们平时可以用`ulimit`命令来设置每个进程被限制使用的资源量,用ulimit -a就可以看到进程被限制使用的各种资 源的量比如 core file size 代表的进程崩溃时候的转储文件的大小限制,max locked memory就是最大锁定内存大小,open files就是最大可以打开的文件句柄数量,max user processes就是最多可以拥有的子进程数量。

​        设置之后,我们要确保变更落地到/etc/security/limits.conf文件里,永久性的设置进程的资源限制所以执行`ulimit -HSn 65535`命令后,要用如下命令检查一下是否落地到配置文件里去了。

​        `cat /etc/security/limits.conf` 以及 `cat /etc/rc.local`

# Raid存储架构的初步了解

​        了解完系统分层下面我们来聊聊Raid的存储架构。

## 进程操作系统和服务器的关系

​        mysql说白了也是一门编程语言,所以也是依赖操作系统执行的,他需要使用cpu内存和硬盘这些硬件进行存储。

**磁盘冗余阵列简单理解:**

​        磁盘冗余阵列,最简单的理解就是说使用一块硬盘管理多块硬盘的技术,在存储的层面上使用多个磁盘进行管理。那么磁盘RAID的硬盘有什么用?

## RAID硬盘介绍:

1. **数据冗余机制 - 备份**:当多块磁盘中有一块硬盘挂了的时候,RAID硬盘可以对于其中一块的硬盘数据冗余到其他的磁盘。
2. **多磁盘管理技术**:RAID 0 RAID 1 …....
3. **RAID 卡缓存**:类似于服务器的主内存的模式,和内存类似的SDRAM,大致认为基于内存进行存储。

对于RAID硬盘,内部通常会有一块叫做SDRAM的空间,用于保存操作系统的内存缓存,但是这一块类似内存的东西一旦断电怎么办,这样数据如果没有刷新到硬盘不就丢失?所以RAID基于**锂电池供电**运行把缓存的数据写入到磁盘阵列的磁盘。

​        然而锂电池也是要休息的,也存在性能衰退,所以需要在30 - 90天之内自动进行充放电一次,这样才可以延长锂电池的寿命和校准电池容量。

​        由于充放电的问题,通常充电放电的时候需要把数据写入磁盘而不是写入到缓存中防治数据丢失,但是也是因为这个问题定期充电放电导致的会性能大幅度的波动,在实际的体验上是定期的数据库线上波动。

​        至于如何解决上面的问题,我们放到下文介绍,我们先来补充一下raid的关系。



### Raid 0 和 raid 1是什么关系?

​        在raid硬盘当中,有一块硬盘出现问题,此时就会在1和0之间形成一个数据冗余,一块磁盘坏了另一块上面也会有数据在上面。raid磁盘意味着每两块磁盘形成一个互为镜像的架构,一共有三组,所以会形成3组镜像的数据冗余。

!(https://gitee.com/lazyTimes/imageReposity/raw/master/img/20210908224906.png)

### raid锂电池的充放电问题处理办法:

1. 锂电池换成电容,支持透明的冲带你放电,但是电容易老化,(**不常用**)
2. **手动充放电**,在业务的低峰时期,关闭raid自动的充电放电,脚本触发**(通常解决办法)**
3. 充电放电的时候不关闭write back,不降低缓存的级别为write through。**(配合第二个策略使用)**



# 总结

​        本节内容也是偏向理论为主,简单介绍了mysql的磁盘随机写和磁盘顺序写,并且简单介绍了linux的系统分层的逻辑结构,最后我们了解了Raid的存储架构,以及RAID的锂电池的细节。



# 写在最后

​        本篇内容同样比较基础,如果有意见或者建议欢迎指导。



# 历史文章:

第一篇:(https://juejin.cn/post/7001327889895407623)

第二篇:(https://juejin.cn/post/7001755108820123662)

上一篇:(https://juejin.cn/post/7004302789400592398)

xlndql 发表于 2021-9-9 14:13

mysql到这个深度确实牛啊

振兴 发表于 2021-9-10 12:18

看的有点蒙了
页: [1]
查看完整版本: Mysql专栏 - Linux底层交互和Raid存储架构