资讯详情

mysql基本知识

一、mysql基本知识 1.mysql如何处理数据库? 1)程序员对mysql认知的现状 普通程序员是对的mysql掌握:建库建表建索引,增删查 2)工作中会遇到的问题 死、sql性能差,异常报错

3)为啥要mysql驱动 我们不能自己写代码和mysql服务器建立tcp、socket连接,因此mysql提供每种语言mysql驱动,包装底层网络通信,提供Connection连接对象。基于此Connection连接可以和mysql服务器通信,如增删改查。

4)为什么数据库要连接到池 tomcat容器是多线程的,他们去抢一个connection如果访问数据库,性能必须很低。 创建、使用和销毁每个线程connection建立网络连接需要时间。

池子的优点是: 一批建立良好连接的connection扔进池子里,用的时候去池子里拿,不用的时候还到池子里,不销毁,以后可以继续用。 并发建立得到了解决connection和connection销毁问题。

5)server为什么端要连接池? 许多客户端系统需要和谐mysql因此,服务端需要一个连接池来维持与客户端的连接 还需要对账号密码进行认证

2.mysql架构设计 1)如何处理服务端connection发来的sql语句? 服务端必须需要一个线程来监控connection,有数据来了,就从connection读取数据并分析数据生成sql、参数之类。 2)sql谁来处理句子? sql语句从connection读取后,会交给SQL接口可以理解为门面。

3)为什么需要查询解析器? 一条sql语句是人们使用的语法,mysql不能理解的,mysql需要转化为你能理解的语法。 那么mysql提供查询解析器进行分析sql语句,对sql进行拆解

比如:select id,name,age from user where id=1 mysql就拆解成 从user表查询数据 查询id=1那一行的数据 提取发现的数据id、name、age三个字段

sql分析就是符合sql语言的sql分析和拆解句子

4)为什么需要查询优化器? sql执行可能有很多路径,比如遍历表,一个一个比较,比如直接根据id定位数据。优化查询优化器,优化最佳查询路径,提高查询效率。

他会生成一个查询路径树,然后从中选择查询路径,你可以按照查询的步骤和顺序进行操作。

5)为什么需要执行器? 查询优化器给出一个sql执行计划需要有人执行 执行计划。

执行器将根据执行计划进行多次调度 存储引擎接口

执行器是一个非常核心的组件,负责与存储引擎合作完成一个SQL磁盘和内存层面的所有数据更新操作语句。

6)为什么需要存储引擎接口? 存储引擎是存储和处理数据的真实场所,数据主要存储在内存和磁盘中。 存储引擎接口是一种Facade,它为执行器提供了一种简单的调用方法,屏蔽了复杂的内部处理逻辑。 7)为什么要有不同的存储引擎? 满足性能、事务、存储限制、索引支持等不同场景的需求

三种模型: 完全基于内存存储,要求速度快,性能高,但存储容量小,数据丢失; 存储容量大,数据不丢失,但速度慢,性能低; 基于内存 考虑到上述两种优缺点,磁盘。

3.InnoDB存储引擎的架构设计 1)为什么需要缓冲池buffer pool 直接添加、删除和更改磁盘,性能肯定会比内存 因此,在内存中获得了一个缓冲池,并将一些操作扔进内存性能 在内存中完成查询和更新可以提高性能

2)怎么使用缓冲池 例如,在更新数据时,首先将数据加载到缓冲池中,然后将数据加入独家锁,然后操作更新,并在内存中修改数据。

3)事务没有提交,更新的数据回滚怎么办? 内存修改了数据,但如果需要回滚怎么办? 在更新数据之前,在磁盘上记录需要修改的字段的信息undo在日志中,回滚后提取还原。 4)事务未提交,为什么会出现脏数据? 当内存数据与磁盘上存储的数据不同时,该数据就变成了脏数据。

5)事务未提交,系统停机怎么办? 事务尚未提交,磁盘上的数据尚未修改,系统关闭,客户端将收到数据库异常,此时对系统没有影响,等等mysql恢复后,磁盘上的数据仍然是原始数据。 此时恢复了事务的回滚

6)事务提交过程中,系统宕机了怎么办? redo log prepare阶段:

已经flush redo log了,没有flush binlog,此时,crash,系统恢复后,事务回滚 已经flush redo log了,也flush binlog了,此时,crash,系统恢复后,将继续提交 7)事务提交后,系统停机怎么办? redo log commit了,commit此时更新了字段crash,也许缓冲池中的数据在刷盘之前就消失了。为了恢复缓冲池的状态,介绍了它redo日志。

redo log,这是一种偏向物理性质的重做日志,因为它记录了类似的东西, 修改数据页面中的记录和记录。

8)redo日志如何确保数据恢复? 有三种模式

A.redolog实时刷盘强制更新到磁盘

在事务中更新数据时,记录数据更新后的字段redo在日志内存缓冲池中。

提交事务时,策略innodb_flush_log_at_trx_commit=1,redo在事务成功之前,必须刷盘并强制更新到磁盘。

当系统再次恢复时,可以根据redo重做缓冲区脏数据 但是,在这种策略下,事务的写入速度就会大大下降。

B.另外两种策略:

如果innodb_flush_log_at_trx_commit=0,就代表mysql定时器每秒刷盘,强制更新到磁盘

如果innodb_flush_log_at_trx_commit=2.提交事务时刷盘,由os自己更新磁盘。

在0和2的模式下,很可能会失去停机redo日志数据。

C.flush和fsync理解如下:

innodb_flush_log_at_trx_commit = 0 就是 定时器自行维护,每秒调用 flush fsync。

innodb_flush_log_at_trx_commit = 1 实时调用 flush fsync 没法批处理,性能很低。

innodb_flush_log_at_trx_commit = 2 就是实时flush ,定时 fsync 交给OS定时器的维护。

若要保证缓冲池数据0丢失,则值为1; 若高效地写入操作,不考虑数据的一致性,则值为0; 若要高速写入,保证数据丢失少,则值为2;

9)什么是binlog? binlog是属于mysql server自己的日志文件,叫归档日志,不是InnoDB存储引擎独特的日志文件。

它是一种逻辑日志,比如一个update 语句 wherer id=1

而redo日志记录了哪个数据页面上的记录和修改

10)binlog磁盘什么时候写? 提交事务阶段:redo log flush,mysql执行器会将binlog flush到磁盘。

11)binlog刷盘策略 sync_binlog可控制参数binlog他的默认值是0 把binlog写入磁盘时,不是直接进入磁盘文件,而是进入磁盘文件os cache内存缓存。 也就是实时flush ,定时 fsync 交给OS维护定时器会丢失数据

sync_binlog=1,就是flush fsync,磁盘必须实时更新。

12)如何成功提交事务? redolog flush binlog flush 之后,还需要写入binlog以及文件名和写入位置commit标记更新到redolog。事务提交成功。

13)为什么事务提交成功要在redo日志写commit标记? 保证redolog和binlog只有redolog和binlog只有写成功,才能判断事务提交的成功。

否则只有redolog成功,binlog没有成功,或者binlog日志位置信息未写入redolog,判断事务不成功。

事务判定成功的三个步骤:redolog刷盘 binlog刷盘 commit标记写入redo日志

14)io线程刷盘内存缓冲区的脏数据 事务已经成功提交,但内存缓冲区的数据可能还没有刷新到磁盘。io将脏数据刷新到磁盘的线程。

如果系统停机,系统恢复后可以根据redo日志重做缓冲区的脏数据。io线程可以继续刷盘。

15)总结一个基于更新数据的数据innodb架构原理 InnoDB存储引擎 内存里面 有 buffer pool、redo log buffer 磁盘上有undo日志文件,redo日志文件

同时mysql server自己还有binlog日志磁盘文件。

事务commit前:flush fsync undo log、write buffer pool、write redo log buffer

事务cmmit中:flush fsync redo log、flush fsync binlog,update commit

事务commit后:io flush fsync buffer pool脏数据 4.数据库规划 1)数据库的机器配置 一台机器能抗下每秒多少请求,往往是跟你每个请求处理耗费多长时间是关联的

根据经验值而言,Java应用系统部署的时候常选用的机器配置大致是2核4G和4核8G的较多一些

4核8g,按500个请求来配置,根据任务时长,从一两百到七八百都有可能

java应用的内存运算性能是很高的,压力都在和外部通信上。

数据库部署的时候常选用的机器配置最低在8核16G以上,正常在16核32G,推荐16核以及以上的配置。

8核16G,按一两千的请求来配置,再高可能有危险,因为数据库的cpu、磁盘、io、内存负载都会很高,数据库压力过大就会宕机。

16核32G,按两三千的请求来配置,甚至三四千也是有可能,再高的话,cpu、磁盘、io、内存、网络负载都会很高,数据库也有可能会扛不住宕机。

如果是ssd磁盘,能抗的并发可能会更高一点,数据库最大的复杂就在于磁盘的io,需要大量的读写磁盘文件。 2)数据库的压测 数据库的压测和java系统的压测是两码事,首先得知道数据库能最大抗多大压力,才能去看java能抗多大压力。

有一种可能,比如你数据库可以抗下2000个请求,java才能抗下500个请求,所以不能只针对java系统,还必须对数据库进行压测,心里有个数。 3)QPS、TPS区分 QPS:每秒可以处理的请求数 TPS:每秒可以处理的事务数

TPS在事务中用的会比较多一些,他是说数据库每秒能处理多少次事务的commit和rollback。

QPS可以理解为每秒的请求数量,TPS可以理解为每秒完成的业务量,比如交易这个整体的业务行为。 4)IO压测指标 A.IOPS

机器随机IO并发处理的能力,比如说200IOPS,意思是每秒可以执行200个随机io读写请求。

这个是关键指标,因为后台IO线程去刷盘缓冲区的脏数据,是在不确定的时间里面刷回去的,是一个随机IO的过程。

如果说IOPS指标太低,说明刷回效率就比较低。

B.吞吐量

机器每秒可以读写多少字节的数据量

这个也是关键指标,提交事务的时候,会大量的写redo log之类的日志,这些日志都要写磁盘。

一台机器它的存储可以每秒读写多少字节的数据量,就决定了可以把多少redo log之类的日志写到磁盘。一般来说我们写redolog、binlog都是顺序写,那么一般磁盘的顺序写入,每秒的吞吐量可以达到200MB左右

通常 机器的磁盘吞吐量都是足够承载高并发请求的

C.latency延迟

往磁盘写入一条数据的延迟。

这个也是关键指标,执行sql的时候,写redo日志到磁盘,到底延迟到1ms还是多少,会影响到sql语句的执行性能。

磁盘写入延迟越低,数据库性能越好,sql的性能就越高。

5)CPU负载 6)网络负载 看每秒网卡的输入输出多少MB的数据,一般1000MB的网卡,最大传输100MB的数据。 7)内存负载 5.数据库压测 1)安装sysbench curl -s https://packagecloud.io/install/repositories/akopytov/sysbench/script.rpm.sh | sudo bash sudo yum -y install sysbench sysbench --version

2)构造测试表测试数据 sysbench --db-driver=mysql --time=300 --threads=10 --report-interval=1 --mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=test_user --mysql-password=test_user --mysql-db=test_db --tables=20 --table_size=1000000 oltp_read_write --db-ps-mode=disable prepare

–db-driver=mysql:基于mysql的驱动去连接mysql数据库,你要是oracle,或者sqlserver,那自然就是其他的数据库的驱动了

–time=300:这个就是说连续访问300秒

–threads=10:这个就是说用10个线程模拟并发访问

–report-interval=1:这个就是说每隔1秒输出一下压测情况

–mysql-host=127.0.0.1 --mysql-port=3306 --mysql-user=test_user --mysql-password=test_user:这一大串,就是说连接到哪台机器的哪个端口上的MySQL库,他的用户名和密码是什么

–mysql-db=test_db --tables=20 --table_size=1000000:这一串的意思,就是说在test_db这个库里,构造20个测试表,每个测试表里构造100万条测试数据,测试表的名字会是类似于sbtest1,sbtest2这个样子的

oltp_read_write:这个就是说,执行oltp数据库的读写测试

–db-ps-mode=disable:这个就是禁止ps模式

最后有一个prepare,意思是参照这个命令的设置去构造出来我们需要的数据库里的数据,他会自动创建20个测试表,每个表里创建100万条测试数据 3)压测结果分析 按照我们上面的命令,我们是让他每隔1秒都会输出一次压测报告的,此时他每隔一秒会输出类似下面的一段东西:

[ 22s ] thds: 10 tps: 380.99 qps: 7312.66 (r/w/o: 5132.99/1155.86/1321.35) lat (ms, 95%): 21.33 err/s: 0.00 reconn/s: 0.00

第22s输出的一段压测统计报告,然后是其他的一些统计字段:

thds: 10,这个意思就是有10个线程在压测

tps: 380.99,这个意思就是每秒执行了380.99个事务

qps: 7610.20,这个意思就是每秒可以执行7610.20个请求

(r/w/o: 5132.99/1155.86/1321.35),这个意思就是说,在每秒7610.20个请求中,有5132.99个请求是读请求,1155.86个请求是写请求,1321.35个请求是其他的请求,就是对QPS进行了拆解

lat (ms, 95%): 21.33,这个意思就是说,95%的请求的延迟都在21.33毫秒以下

err/s: 0.00 reconn/s: 0.00,这两个的意思就是说,每秒有0个请求是失败的,发生了0次网络重连

4)总的压测报告 SQL statistics:

queries performed:

read: 1480084 // 这就是说在300s的压测期间执行了148万多次的读请求

write: 298457 // 这是说在压测期间执行了29万多次的写请求

other: 325436 // 这是说在压测期间执行了30万多次的其他请求

total: 2103977 // 这是说一共执行了210万多次的请求

// 这是说一共执行了10万多个事务,每秒执行350多个事务

transactions: 105180( 350.6 per sec. )

// 这是说一共执行了210万多次的请求,每秒执行7000+请求

queries: 2103977 ( 7013.26 per sec. )

ignored errors: 0 (0.00 per sec.)

reconnects: 0 (0.00 per sec.)

// 下面就是说,一共执行了300s的压测,执行了10万+的事务

General staticstics:

total time: 300.0052s

total number of events: 105180

Latency (ms):

min: 4.32 // 请求中延迟最小的是4.32ms

avg: 13.42 // 所有请求平均延迟是13.42ms

max: 45.56 // 延迟最大的请求是45.56ms

95th percentile: 21.33 // 95%的请求延迟都在21.33ms以内 5)总结 在硬件的一定合理的负载范围内,把数据库的QPS提高到最大,这就是数据库压测的时候最合理的一个极限QPS值

6.机器的性能 压测的时候,需要机器的负载在比较合理的范围,28原则,不能超过80%。 1)CPU负载 按下 1,即可显示使用 CPU 核心数

top - 15:52:00 up 42:35, 1 user, load average: 0.15, 0.05, 0.01 时间 运行时长 用户数量 负载 1分钟 5分钟 15分钟

CPU负载是0.15,这就说明,4核CPU中连一个核都没用满,4核CPU基本都很空闲,没啥人在用。

如果你的CPU负载是1,那说明4核CPU中有一个核已经被使用的比较繁忙了,另外3个核还是比较空闲一些。

如果你的CPU负载是1.5,说明有一个核被使用繁忙,另外一个核也在使用,但是没那么繁忙,还有2个核可能还是空闲的。

2)内存负载 Mem: 33554432k total, 20971520k used, 12268339 free, 307200k buffers

去掉末尾6个数字就是多少GB 去掉末尾3个数字就是多少MB

总内存大概有32GB,已经使用了20GB左右的内存,还有10多G的内存是空闲的,有大概300MB左右的内存用作OS内核的缓冲区了。

3)磁盘IO负载 A.随机读写IOPS

一般来说,随机磁盘读写每秒在两三百次都是可以承受的。

dstat -r

–io/total- read writ 0.25 31.9 0 253 0 39.0

B.吞吐量

dstat -d

-dsk/total- read writ 639k 62k 32k 761k 0 0 16k 16k

每秒读取963kb,写入62kb的含义 像这个存储IO吞吐量基本上都不算多的,因为普通的机械硬盘都可以做到每秒钟上百MB的读写数据量。 4)网卡负载 dstat -n

-net/total- recv send 16k 17k

每秒钟网卡接收到流量有多少kb,每秒钟通过网卡发送出去的流量有多少kb

通常来说,如果你的机器使用的是千兆网卡,那么每秒钟网卡的总流量也就在100MB左右,甚至更低一些。 7.数据库监控 1)监控哪些指标 拿到一台机器,即使QA、DBA团队都没有提供专业的压测支持,也要自己测一下,做到心里有数。大概能承担多少QPS和TPS。

除此之外还要做好运维监控的工作,包括对CPU、内存、网络、磁盘IO、慢查询、QPS、TPS 2)node_exporter node_exporter,启动之后是个linux进程,他会自动采集这台linux机器上的CPU、磁盘、内存、网络之类的各种监控数据

本质你可以理解为通过我们之前讲解的那些linux命令,就可以采集到一切你想要的linux机器的监控数据。 A.安装 下载地址:http://cactifans.hi-www.com/prometheus/ node_exporter-0.15.2.linux-amd64.tar.gz

打开securityCRT alt + p 使用sftp上传 cd /opt

回到shell

cd /opt/node_exporter tar -zxvf node_exporter-0.18.0.linux-amd64.tar.gz mv node_exporter-0.18.0.linux-amd64 node_exporter cd node_exporter

B.启动 nohup /opt/node_exporter/node_exporter &

启动之后,通过9100端口暴露出去它的数据 3)mysqld_exporter 采集MySQL数据库自己的一些监控数据的,比如SQL性能、连接数量等

A.安装 https://github.com/prometheus/mysqld_exporter/releases/download/v0.10.0/mysqld_exporter-0.10.0.linux-amd64.tar.gz

mysqld_exporter-0.10.0.linux-amd64.tar.gz

cd /opt/ tar -zxvf mysqld_exporter-0.10.0.linux-amd64.tar.gz mv mysqld_exporter-0.10.0.linux-amd64 mysqld_exporter

B.配置环境变量

export DATA_SOURCE_NAME=‘root:root@(10.10.10.1:3306)/’ echo “export DATA_SOURCE_NAME=‘root:root@(10.10.10.1:3306)/’” >> /etc/profile source /etc/profile

C.启动 nohup /opt/mysqld_exporter/mysqld_exporter --collect.info_schema.processlist --collect.info_schema.innodb_tablespaces --collect.info_schema.innodb_metrics --collect.perf_schema.tableiowaits --collect.perf_schema.indexiowaits --collect.perf_schema.tablelocks --collect.engine_innodb_status --collect.perf_schema.file_events --collect.binlog_size --collect.info_schema.clientstats --collect.perf_schema.eventswaits &

启动之后,通过9104端口暴露出去它的数据 4)Prometheus 监控和采集数据,放到他自己的时序数据库中去。 比如它可以监控和采集node_exporter的数据到自己的库

A.安装 下载地址:http://cactifans.hi-www.com/prometheus/ prometheus-2.1.0.linux-amd64.tar.gz

打开securityCRT alt + p 使用sftp上传

tar -zxvf /opt/prometheus-2.14.0.linux-amd64.tar.gz rm -rf /opt/prometheus-2.14.0.linux-amd64.tar.gz mv /opt/prometheus-2.14.0.linux-amd64 /opt/prometheus

B.配置prometheus.yml cd /opt/prometheus vi prometheus.yml

在scrape_configs下面加入一大段自定义的配置,因为他需要去采集mysql的数据和机器的数据

完整文件如下: # my global config global:   scrape_interval:     15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.   evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.   # scrape_timeout is set to the global default (10s).

# Alertmanager configuration alerting:   alertmanagers:   - static_configs:     - targets:       # - alertmanager:9093

# Load rules once and periodically evaluate them according to the global ‘evaluation_interval’. rule_files:   # - “first_rules.yml”   # - “second_rules.yml”

# A scrape configuration containing exactly one endpoint to scrape: # Here it’s Prometheus itself. scrape_configs:   - job_name: ‘Host’     file_sd_configs:     - files:       - ‘host.yml’     metrics_path: /metrics     relabel_configs:     - source_labels: []       regex: (.)       target_label: instance       replacement: $1     - source_labels: []       regex: (.)       target_label:        replacement: $1:9100

- job_name: ‘MySQL’     file_sd_configs:     - files:         - ‘mysql.yml’     metrics_path: /metrics     relabel_configs:     - source_labels: []       regex: (.)       target_label: instance       replacement: $1     - source_labels: []       regex: (.)       target_label:        replacement: $1:9104

- job_name: ‘prometheus’     static_configs:       - targets: [‘10.10.10.1:9090’]

C.配置node_exporte的host.yml

这里配置node_exporter的访问地址

cd /opt/prometheus vi host.yml

  • labels: service: test targets:
    • 127.0.0.1:9100

D.配置mysql的mysql.yml vi /data/prometheus/mysql.yml

  • labels: service: mysql targets:
    • 127.0.0.1:9104

E.启动prometheus /opt/prometheus/prometheus --storage.tsdb.retention=30d &

F.打开界面 http://10.10.10.1:9090/graph

至此,prometheus已经可以采集机器和mysql的数据了,并通过9090端口开放数据出去

5)Grafana是什么 可视化的数据展示系统,可以把Prometheus采集的数据,展示成精美的报表,直观的看到机器和mysql的监控情况。

A.安装 下载地址:https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana-4.6.3.linux-x64.tar.gz

打开securityCRT alt + p 使用sftp上传 cd /opt/prometheus

回到shell mkdir -p /opt/prometheus/grafana tar -zxvf grafana-4.6.3.linux-x64.tar.gz -C /opt/prometheus rm -rf /opt/prometheus/grafana-4.6.3.linux-x64.tar.gz

B.启动 /opt/prometheus/grafana/bin/grafana-server &

C.打开界面 http://10.10.10.1:3000 账户秘密:admin admin

D.配置数据源 接着在Grafana左侧菜单栏里有一个Data Sources,点击里面的一个按钮是Add data source,就是添加一个数据源

然后在界面里输入你的数据源的名字是Prometheus,类型是Prometheus,HTTP URL地址是http://10.10.10.1:9090,其他的都用默认的配置就行了,接下来Grafana就会自动从Prometheus里获取监控数据和展示了。

E.安装grafana-dashboards模板

下载地址:https://github.com/percona/grafana-dashboards/archive/v1.6.1.tar.gz

解压 dashboards文件夹有一堆json文件,接着在grafana页面中,可以看到最上面有一个Home按钮,点击一下进入一个界面,你会看到一个Import Dashboard的按钮,就是说可以导入一些仪表盘,这个时候就是要导入刚才看到的一大堆的json文件。

点击Upload json file按钮,就会出现一个界面让你上传一个一个的json文件,然后你就依次上传,接着grafana中就会出现一大堆的仪表盘了,比如机器的CPU使用率的仪表盘,磁盘性能仪表盘,磁盘空间仪表盘,MySQL监控仪表盘,等等。

8.Buffer Pool 1)为什么要有buffer pool 内存和磁盘结合,提高数据库的性能

2)为什么需要配置buffer pool容量? 因为内存大小不是无限的,所以需要对buffer pool进行容量的配置

my.cnf配置文件里面可以配置大小,如下是配置了20GB,默认值是128MB [server] innodb_buffer_pool_size = 2147483648

3)数据在磁盘是如何存放的? 逻辑上,我们有库、表、行的概念,在实际物理存储的时候,每个表里面的行的数据都是按照一页一页来存储的,一页的大小是16kb。 4)数据如何存放在buffer pool? 有了buffer pool这样一个内存的容器,磁盘的数据必定需要按照一定的约定和格式存放进去。

mysql的数据在磁盘上是16kb的大小存放在一页上,定位一行数据,需要先找到这一页,那么对应的读取一页的数据,也是按照一页的形式存放在buffer pool里面的,物理和内存结构映射,就是一个一个的缓存页和物理页对应。 5)数据放到buffer pool怎么和物理数据对应? 这就有了一个描述数据,里面存放这页数据,在buffer pool的地址,以及对应的物理表空间,页编号等信息。 6)描述数据放哪里?怎么放,大小是多少? 放到bufferpool的最前面,大小是一页的5%,800个字节。

多出来的一个一个描述数据的大小,会导致buffer pool的总容量撑出来变大。

比如128MB的buffer pool可能最终大小就是130MB 7)bufferpool的初始化 mysql启动的时候,会安装buffer pool设置的大小,加上描述数据的大小,去申请一块内存作为bufferpool的内存区域。

接下来,会把一堆800字节的描述数据和16kb的缓存页在内存里面初始化,并且一字按顺序排好。 8)bufferpool会有内存碎片吗? 会有,因为buffer pool的大小是人设定的,很可能划分完所有的缓存页和描述数据块之后,还剩一点点的内存,这一点点内存放不下任何一个缓存页了,就没办法使用了。就变成了内存碎片。

同时,再划分缓存页和描述数据块的时候,会让它们紧密的挨在一起,这样能尽可能的减少内存浪费,就尽可能的减少内存碎片的产生了。

如果缓存页东一块西一块,必然有很多间隙,就形成了内存碎片。

9.Free链表 1)读取一页的数据选择哪个缓存页存放? 这个就涉及到了一个问题,哪个缓存页是空闲的。为了解决这个问题,就设计了一个free链表,所有空闲的缓存页对应的描述数据,做成一个双向链表串在一起。

当数据库启动的时候,缓存页都是没有使用的,因此,所有的描述数据,都串在了一起,组成了一个free链表。 2)如何找到free链表 要去使用free链表,就要有一块数据来描述它,就弄了一个基础节点,存储了free链表的数量、free链表的头节点,尾节点等信息。

对于free链表来说,这个基础node节点40字节,是不属于bufferpool的。相当于free链表的facade外观。 3)数据从物理页到内存页放入的全部过程? 首先读取了一页数据之后,先去free链表的外观基础节点里面去找到一个空闲的描述数据。 然后把这页数据,写入描述数据对应的内存页地址,把数据灌装进去。 然后把数据的物理表空间、页编号,更新到描述数据,完成映射。 最后把这个描述数据从free链表中摘除。 4)如何知道一页数据有没有缓存? 既然bufferpool的设计是为了提高性能,尽量在内存里面操作数据,那操作一条记录,得先去检查在bufferpool里面是否存在。

那如何快速的知道一条记录是否命中缓存呢?

mysql又设计了一个哈希表,写入kv对,key=表空间号+页编号,value=缓存页地址。 每次取数据,先从哈希表看一下,有没有对应的kv,有就直接取出来使用。

10.Flush链表 1)为什么会有脏页、脏数据的产生 对数据修改都是基于bufferpool的缓存页来操作的,那么修改的时候,必然会存在脏数据,和数据库的数据不一致,也就是产生了脏页。

2)怎么统计哪些缓存页产生了脏页? 修过缓存页的数据被修改了,那么它就是一个脏页,io线程去刷盘的时候,不可能所有缓存页都刷盘,而是只刷新脏页。

如果一个缓存页成为了脏页,它会把它的描述数据块加入到flush链表里面,flush链表也是一个双向链表,所有的脏页通过flush链表串起来。这样所有的脏页就能被统计到了。 3)从哪个源头去处理脏页 flush链表和free链表一样,也有一个不属于bufferpool的外置node节点来提供facade外观,里面提供有多少size的脏页,第一个脏页和最后一个脏页。

11.LRU链表 1)free缓存页不够使用了怎么办? 缓存页不够使用了,接下来必须要淘汰掉一个缓存页,需要刷盘并清空,成为一个新的缓存页 2)淘汰哪个缓存页呢? 原则上肯定是最少使用的先淘汰掉,因此引入LRU链表,最近最少使用算法。 有了LRU链表就知道哪些是不常用的缓存页了,需要淘汰缓存页的时候,从链表按顺序淘汰就可以了。 3)LRU工作机制 加载新缓存页和修改一个缓存页,都给它调到LRU的链表头部,让不经常修改的往后排。 4)从哪个源头去操作LRU链表呢 同样的道理,一个外置Node节点,持有LRU链表的size,第一个LRU链表和最后一个LRU链表。 5)单纯的LRU链表带来的问题 可能是不用的页被提前加载进来了,导致经常使用的缓存页排到最后面去了

  1. 一种是预读机制,触发预读机制就会加载相邻的页数据,主要是为了提升性能。

比如: a. 顺序的访问了一个区里的多个数据页,访问的数据页的数量超过了这个阈值,此时就会触发预读机制,把下一个相邻区中的所有数据页都加载到缓存里去 innodb_read_ahead_threshold,他的默认值是56

b. 一个区里的13个连续的数据页,而且这些数据页都是比较频繁会被访问的,此时就会直接触发预读机制,把这个区里的其他的数据页都加载到缓存里去

2.另外一种是全表扫描,把整个表的页数据,全部加载进来了 这个所谓的全表扫描,意思就是类似如下的SQL语句:SELECT * FROM USERS,没有where条件 6)如何解决简单LRU链表淘汰掉热点数据到问题 为了解决简单LRU链表的问题,引入优化版的LRU链表,采用的思想是冷热数据分离。链表的头部区域是热数据区域,尾部区域是冷数据区域。innodb_old_blocks_pct=37,默认冷数据占37%

第一次加载数据的时候,加载到冷区域头部。如果这个缓存页1s后,还有被使用,说明它可能经常被使用,那么就移动到热数据区域头部。

通过冷热数据分离,每次淘汰,淘汰冷数据区域就可以了。放进来1s后都没有的数据,就会变成冷数据。 7)热数据区域的链表频繁节点移动怎么优化? 热数据区域的数据本来就是热数据,访问一次就提到头部,肯定效率不是最高的。 因此 如果是热数据区域前1/4数据的访问,不再移动,只有后3/4的数据被访问了,才往前提。 8)冷数据什么时候刷盘淘汰 定时io线程,刷盘冷数据区域链表尾部的几个数据,从lru链表和flush链表移除,还到free链表中。

几个链表都没有数据了,就从冷数据末端去flush一个缓存页,从lur链表和flush链表移除,还到free链表中。 9)热点数据啥时候刷盘淘汰 冷数据知道如何淘汰了,热数据怎么处理呢,热数据必然是在flush链表里面的,是脏数据,flush链表有一个后台任务,在合适的时候,去刷盘脏页数据。从lur链表和flush链表移除,还到free链表中。

10)如何避免crud的时候,缓存页用完了,频繁倒腾冷数据刷盘 给buffer pool的内存设置大一点,即使高峰期 free消耗的速率比flush、lru刷盘的数据快,但是还是有很多内存可以使用,等高峰期过了,free又慢慢被还原了。 12.三个链表动态的运行过程 Buffer Pool在运行中被使用的时候,实际上会频繁的从磁盘上加载数据页到他的缓存页里去,然后free链表、flush链表、lru链表都会在使用的时候同时被使用。

比如数据加载到一个缓存页,free链表里会移除这个缓存页,然后lru链表的冷数据区域的头部会放入这个缓存页。

然后如果你要是修改了一个缓存页,那么flush链表中会记录这个脏页,lru链表中还可能会把你从冷数据区域移动到热数据区域的头部去。

如果你是查询了一个缓存页,那么此时就会把这个缓存页在lru链表中移动到热数据区域去,或者在热数据区域中也有可能会移动到头部去。

你一边不停的加载数据到缓存页里去,不停的查询和修改缓存数据,然后free链表中的缓存页不停的在减少,flush链表中的缓存页不停的在增加,lru链表中的缓存页不停的在增加和移动。

另外一边,你的后台线程不停的在把lru链表的冷数据区域的缓存页以及flush链表的缓存页,刷入磁盘中来清空缓存页,然后flush链表和lru链表中的缓存页在减少,free链表中的缓存页在增加。

13.多Buffer Pool来优化数据库的并发性能 1)buffer pool并发访问的时候需要加锁吗 多线程访问一个BufferPool,必然是要加锁的 比如,先加锁,然后加载数据页到缓存页,更新free链表,更新lru链表,再释放锁,接着下一个线程再执行一系列的操作。 2)buffer pool并发访问加锁影响性能吗? 大部分情况下,线程都是查询或者更新缓存里的数据,操作都是发生在内存的,微秒级,包括更新free、flush、lru链表都是内存操作,速度极快。

但是有些情况下,需要读取从磁盘读取数据到缓存页,发生了一次磁盘IO,耗时就长一些,后面排队的线程自然就多等一会。 3)多个buffer pool优化并发能力 多个buffer pool类似于分段加锁,降低了锁的冲突,多核多线程访问就成倍的提高了性能

每一个缓存页,只会放在其中一个buffer pool里面

my.cnf配置如下 [server] innodb_buffer_pool_instances = 8 innodb_buffer_pool_size = 8589934592

innodb_buffer_pool_instances代表了有几个buffer pool innodb_buffer_pool_size代表了buffer pool总共占用多少字节 14.Buffer Pool动态调整 1)bufferpool能在运行期间调整吗 可以调整,但不这么做,因为性能太低下了,极为耗时。 主要是因为bufferpool需要连续的内存,比如你8g调整到16g,就需要把8g的数据拷贝到16gb去。因此最好不要这么做 2)如何更高效的给bufferpool动态调整大小 将每个bufferpool分为n个chuck,这n个chuck共享free、flush、lru链表。需要扩容的时候,生成一个连续内存的chuck,然后交给bufferpool就可以了

每个buffer pool里多个chunk但是共用一套链表数据结构 3)buffer pool容量生成推荐配置成多少 推荐配置成总内存的50%-60%,系统和其他软件也要使用内存。 32GB,配置60%就是20GB。

buffer pool总大小 = chunk个数 * chunk容量 * buffer pool个数

比如20GB buffer pool,16个buffer pool,128MBchunk容量,10个chunk数量 比如20GB buffer pool,32个buffer pool,128MBchunk容量,5个chunk数量

15.innodb状态参数查询 SHOW ENGINE INNODB STATUS

Total memory allocated xxxx; Dictionary memory allocated xxx Buffer pool size   xxxx Free buffers       xxx Database pages     xxx Old database pages xxxx Modified db pages  xx Pending reads 0 Pending writes: LRU 0, flush list 0, single page 0 Pages made young xxxx, not young xxx xx youngs/s, xx non-youngs/s Pages read xxxx, created xxx, written xxx xx reads/s, xx creates/s, 1xx writes/s Buffer pool hit rate xxx / 1000, young-making rate xxx / 1000 not xx / 1000 Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s LRU len: xxxx, unzip_LRU len: xxx I/O sum[xxx]:cur[xx], unzip sum[16xx:cur[0] 主要讲解这里跟buffer pool相关的一些东西。

(1)Total memory allocated,这就是说buffer pool最终的总大小是多少 (2)Buffer pool size,这就是说buffer pool一共能容纳多少个缓存页 (3)Free buffers,这就是说free链表中一共有多少个空闲的缓存页是可用的 (4)Database pages和Old database pages,就是说lru链表中一共有多少个缓存页,以及冷数据区域里的缓存页数量 (5)Modified db pages,这就是flush链表中的缓存页数量 (6)Pending reads和Pending writes,等待从磁盘上加载进缓存页的数量,还有就是即将从lru链表中刷入磁盘的数量、即将从flush链表中刷入磁盘的数量 (7)Pages made young和not young,这就是说已经lru冷数据区域里访问之后转移到热数据区域的缓存页的数量,以及在lru冷数据区域里1s内被访问了没进入热数据区域的缓存页的数量 (8)youngs/s和not youngs/s,这就是说每秒从冷数据区域进入热数据区域的缓存页的数量,以及每秒在冷数据区域里被访问了但是不能进入热数据区域的缓存页的数量 (9)Pages read xxxx, created xxx, written xxx,xx reads/s, xx creates/s, 1xx writes/s,这里就是说已经读取、创建和写入了多少个缓存页,以及每秒钟读取、创建和写入的缓存页数量 (10)Buffer pool hit rate xxx / 1000,这就是说每1000次访问,有多少次是直接命中了buffer pool里的缓存的 (11)young-making rate xxx / 1000 not xx / 1000,每1000次访问,有多少次访问让缓存页从冷数据区域移动到了热数据区域,以及没移动的缓存页数量 (12)LRU len:这就是lru链表里的缓存页的数量 (13)I/O sum:最近50s读取磁盘页的总数 (14)I/O cur:现在正在读取磁盘页的数量

buffer pool的千次访问缓存命中率,这个命中率越高,说明你大量的操作都是直接基于缓存来执行的,性能越高。

第二个是你的磁盘IO的情况,这个磁盘IO越多,说明你数据库性能越差。

16.一行数据在磁盘上的物理结构 1)为什么不能直接更新磁盘 磁盘io随机读写,性能必然很差

读写内存速度快,磁盘顺序写很快,通过操作内存和顺序写才能极大的提升mysql的性能。 2)为什么要有数据页? 一条一条数据加载和写入性能低

如果操作到一页数据里面的其他数据,就可以不用再次去读取磁盘了,提高了性能

一页数据读取和后台线程flush数据页,相对单行读取和写入来说,降低了读取和写入的频率 3)一行数据在磁盘上如何存储 A.存储格式

涉及到一个概念,行格式,意思是一行存储的格式,这个格式有多个。

COMPACT格式 紧凑型

CREATE TABLE table_name (columns) ROW_FORMAT=COMPACT ALTER TABLE table_name ROW_FORMAT=COMPACT

它的存储格式如下:

变长字段的长度列表,null值列表,数据头,column01的值,column02的值,column0n的值…

B.变长字段列表

逆序 存储的是 非null值的字段长度

变长字段大小 最大不超过65535

C.NULL值列表

逆序 存储的是 每个字段是否为null,用bit来表示

D.数据头

40个bit位

第1、2个bit位预留的没有含义

第3个bit位delete_mask,标记是否删除

接下来4个bit位是n_owned记录数

接下来是13个bit位是heap_no堆位置

接下来是3个bit的record_type行记录类型,0代表的是普通类型,1代表的是B+树非叶子节点,2代表的是最小值数据,3代表的是最大值数据

最后是16个bit的next_record,这个是指向他下一条数据的指针。

“0x09 0x04 00000101 0000000000000000000010000000000000011001 00000000094C(DB_ROW_ID)00000000032D(DB_TRX_ID) EA000010078E(DB_ROL_PTR)  616161 636320 6262626262”

E.真实字段值

隐藏字段 每行字段的开头还有三个隐藏字段 DB_ROW_I D 行id DB_TRX_ID 事务id DB_ROL_PTR 回滚指针

真实字段 用户自己的字段

F.如何读取一条数据

一行记录的指针指向这行数据的真实地址,往前读,是40个bit的数据头+null值列表+可变字段长度,往右读是三个隐藏字段+真实字段数据。

那么解析一个一个字段的内容,首先需要确认字段是否固定长度?

如果是固定长度,从真实地址直接往后读

如果是可变长度字段,那么存在null和非null两种情况,那么就从null值列表bit位中解析出当前是否为null,为null就不再处理。

如果这个字段不为null,就去逆序的从右往左读取该字段的长度。

知道长度了,再从真实数据,读取这个长度的数据。

G.变长字段列表和null值列表 逆序的好处

从数据的指针开始,从左往右读,是真实字段一个一个顺序,从右往左读,也刚好是和真实字段的顺序一一对应

H.行溢出

如果一页存不下,怎么办?这种情况,存不下的数据要往下一页放,下一页也放不下,就再往下一页放,指针指向过去。指针占用20字节大小。 4)数据页的格式 数据页就跟每一行数据一样,都是由MySQL开发人员设计出来的一个特殊的存储格式。 数据页和缓存页是一一对应的。内存里面写数据就写到空闲区域,flush链表、lru链表刷盘的时候,整个缓存页,刷如磁盘。

数据页包含如下几个部分: 文件头 38个字节 数据页头 56个字节 最大记录、最小记录 26个字节 多个数据行 不固定 空闲区域 不固定 数据页目录 不固定 文件尾部 8个字节 5)什么是表空间? 我们每创建一个表,对应磁盘上就有一个 表名.ibd 的磁盘数据文件

表空间里面存储了这个表的数据,里面存放着所有的页数据 通过表空间来管理不同的表。 6)什么是数据区? 但是一个表空间管理所有的页数据,太庞大了,不便于管理,因此有了数据区和数据组的概念。

一个区里面存放64个页,是1MB 一个组里面放256个区,是256MB

表空间里面 第1组第1区里面的前3个页是存放特殊信息的。 其他组第1区里面的前2个页是存放特殊信息的。

查询数据的时候,就是从表组区里面找到页,把页信息加载到缓存页里面去。 17.随机读写和顺序读写的是什么 mysql读取一页数据,可能在磁盘上的任何一个位置,所以读取磁盘的数据只能是随机读这种方式。 随机读关注两个指标,IOPS、响应延迟 IOPS是并发随机读写的能力,一般核心数据库我们会使用ssd,来提高这种能力。 响应延迟是一次操作花费多长时间。

顺序写是追加的方式,往文件末尾追加数据。redo log就是这种方式,顺序写的性能是很高的,几乎和内存随机读写的性能差不多。 顺序读写关注吞吐量的指标。

一笔更新操作,会涉及到至少一次的随机读和一次顺序写,所以IOPS并发能力和写入吞吐量就决定了整体数据库的性能。

18.mysql读写数据在linux操作系统上是什么流程 liunx存储系统分为VFS层,文件系统层,pagecache层,通用block层,io调度层,block设备驱动层,block设备层。

通过VFS区分需要经过哪个文件系统(NFS、Ext3),如果缓冲区有则直接基于内存读写,没有则通往block层,把对文件的io请求转化为block io请求,把blockio请求转发给io调度层,有公平调度与最后期限调度,通过block驱动,对block设备进行读写

19.什么是RAID磁盘冗余阵列 很多数据库在部署的时候,存储都是搭建的RAID存储架构,叫磁盘冗余阵列。

为什么会出现这个技术呢?

1)很多情况下,我们服务器上一块磁盘是不够使用的,为了扩大我们的存储空间,我们可能需要多块磁盘来存放数据。 那么就引入了RAID技术,可以管理多块磁盘的一种磁盘阵列技术。

2)RAID还可以实现数据冗余,比如写入数据的时候,可以写入到两块磁盘上去,第二块磁盘用来冗余,如果第一块磁盘坏掉了,还可以从第二块磁盘上读取出冗余的数据。这块都是RAID自动管理的,不需要人来操心。

3)RAID技术实际上就是管理多块磁盘的一种磁盘阵列技术,他有软件层面的东西,也有硬件层买的东西,比如有RAID卡这种硬件设备。

4)RAID还可以分成不同的技术方案,比如RAID 0、RAID 1、RAID 0+1、RAID2,等等,一直到RAID 10,很多种不同的多磁盘管理技术方案。

20.RAID存储定期性能抖动 1)为了提高磁盘读写的写入性能,RAID卡提供了一个缓存 磁盘组成RAID阵列的时候,一般会有一个RAID卡,RAID卡是带有一个缓存的,它是类似于内存的SDRAM,大致就认为基于内存来存储吧。

RAID的缓存模式设置为write back的时候,所有写入磁盘的数据都是先写入到RAID卡的缓存里面,然后再慢慢的写入到磁盘里,这个写缓冲机制可以大幅度的提高磁盘的写入性能。 2)RAID卡缓存的问题 RAID卡好是非常好,但是有一个问题,服务器关闭或者服务器掉电了,缓存数据就会丢失,为了解决这个问题,就要单独给RAID卡提供独立的电池,一旦服务器关闭或者掉电,它就要基于自己的电池,把缓存数据写入到磁盘,保证数据不丢失。 3)引入电池带来的问题 电池存在性能衰减,需要定时充电放电,大概每隔30-90天,不同厂商的不一样,就需要自动的充放电一次,来延长电池的寿命和校准电池的容量。

如果不这么做的话,一旦服务器掉电,电池启用,可能电池容量不够,没办法把缓存数据一次性写入磁盘,就会导致数据丢失。 4)电池充放电会有什么问题? 电池充放电,RAID缓存的级别就从write back变成write through,这样就不会写缓存了,就变成了写磁盘,如果是写缓存的话,性能是0.1ms,写磁盘可能就变成了1ms,性能可能会出现10倍衰减。 那么这样就导致了,RAID存储性能存在定期的性能抖动,间接导致数据库定期的性能抖动。 5)RAID锂电池充放电导致性能抖动的优化 raid0,就是多块磁盘组成一个磁盘阵列,数据是分散写入到不同的磁盘的,磁盘的整体容量很大,同时可以写入多块磁盘,磁盘读写的并发能力很强。

但是raid0的问题是磁盘一旦坏了一块,数据就丢失了一部分。

raid1,两块磁盘互为镜像关系,数据会冗余一份,一块磁盘坏了,另外一块磁盘还有数据。一块磁盘压力太大,还可以让读请求路由到另外一块磁盘上去分担压力。

raid10,就是raid0+raid1组合起来,两块硬盘组成raid1,多个raid1再组成raid0,每一组两块硬盘互为冗余。

解决方案: A.给raid卡的锂电池换成电容 电容不用频繁重放电,支持透明充放电,自动检查电量,自动进行充电,缓存可以一直使用,io不用直接走磁盘。但是电容更换非常麻烦,而且容易老化,所以一般不常用。

B.手动充放电 比较常用,关闭raid自动重放电,写一个脚本,每隔一段时间自动在晚上凌晨的业务低峰期,脚本触发充放电,这样避免在业务高峰期raid自动充放电,引起性能抖动。

充放电的时候不关闭write back缓存级别(写缓冲),做好UPS电源的支持 21.Too many connections故障 linux的文件句柄限制,导致了MySQL的最大连接数被限制。

linux为什么要有文件句柄的限制,主要是防止单进程消耗过多的机器上的资源。如果被限制了,就没办法创建大量的网络连接。

一般,在生产环境部署的系统,比如数据库、mq、存储系统、缓存系统,都需要调整linux系统的一些内核参数,这个文件句柄的数量一定要调整的,通常调整为65535。

linux文件句柄是1024的时候,mysql的最大连接数是214,mysql的最大连接数有个公式,源码里面写死了。

其实核心就是一行命令: ulimit -HSn 65535

然后就可以用如下命令检查最大文件句柄数是否被修改了 设置之后,我们要确保变更落地到/etc/security/limits.conf文件里,永久性的设置进程的资源限制 cat /etc/security/limits.conf cat /etc/rc.local

同时,修改mysql的my.cnf里面的max_connections参数,然后重启服务器,重启mysql 这样mysql最大连接数和linux最大文件句柄数就会生效。 22.redo log 1)redo log机制出现的原因 mysql buffer pool内存修改的脏数据,如果要同步刷盘的话,这个是随机写,而且是16kb的页大小,性能非常低。

redolog的好处,一是尺寸小,只记录表空间号,数据页号,磁盘文件偏移量,更新值这些信息,写入快;而是redolog是顺序写,一条一条记录往一个日志文件里面追加,速度快。

这样事务提交之后,即使内存脏数据丢失了,redolog里面还保存了这个数据更新后的信息,恢复也很方便。

2)redo log的存储格式 日志类型(就是类似MLOG_1BYTE之类的),表空间ID,数据页号,数据页中的偏移量,具体修改的数据

类型就告诉了你他这次增删改操作修改了多少字节的数据;MLOG_1BYTE、2、4、8 哪个表空间; 哪个数据页号; 在数据页的哪个偏移量开始执行,具体修改的数据是什么

有了上述信息,就可以还原一次数据增删改操作的遍历。

如果是MLOG_WRITE_STRING类型的日志,因为不知道具体修改了多少字节的数据,会多一个修改数据长度,就告诉你他这次修改了多少字节的数据,如下所示他的格式:

日志类型(就是类似MLOG_1BYTE之类的),表空间ID,数据页号,数据页中的偏移量,修改数据长度,具体修改的数据

3)为什么需要redo log buffer 大量的事务执行的时候,都去写磁盘,性能肯定是比较低了,因此需要一个redo log buffer在内存里面存储先存储redo log日志。

redo log buffer 默认innodb_log_buffer_size=16MB

redo log buffer里面分成了一个一个小的redo log block,每个512kb,以块为单位向磁盘写入。

块里面,同一个事务的多条redo log先暂存起来,然后一组数据写入redo log block里面。 4)redo log buffer什么时间刷盘 写入的日志达到总量的一半,也就是超过了8MB(高并发场景) 事务提交的时候,必须把它所在的redo log block刷盘 后台线程每秒刷盘一次 mysql关闭的时候,刷盘 5)redo log 参数设置 通过show variables like 'datadir’来查看

通过innodb_log_group_home_dir参数来设置日志存储的目录 通过innodb_log_file_size可以指定每个redo log文件的大小,默认是48MB 通过innodb_log_files_in_group可以指定日志文件的数量,默认就2个。

默认情况下,目录里就两个日志文件,分别为ib_logfile0和ib_logfile1,每个48MB,最多就这2个日志文件,就是先写第一个,写满了写第二个。那么如果第二个也写满了呢?继续写第一个,覆盖第一个日志文件里原来的redo log就可以了。循环使用。 23.undo log的格式 undo log主要是用来做事务回滚的。

比如你执行了INSERT语句,那么你的undo log必须告诉你插入数据的主键ID,让你在回滚的时候可以从缓存页里把这条数据给删除了;

如果你执行了DELETE语句,那么你的undo log必须记录下来被删除的数据,回滚的时候就得重新插入一条数据;

如果你执行了UPDATE语句,那么你必须记录下来修改之前的数据,回滚的时候就得把数据给更新回去

1)insert语句的undo log 这条日志的开始位置 主键的各列长度和值:单一主键或联合主键的id 表id:undo log属于哪个表 undo log日志编号:事务里面多个undo log,id从0递增 undo log日志类型:增删改 这条日志的结束位置

2)undo log内容 里面的内容有 事务id,以及该事务id下,被修改字段的原始值

恢复到该事务id的时候,就把这些字段的原始值,覆盖回去就行了

24.事务 1)事务并发执行会产生什么问题? 业务系统是很可能多并发去执行事务的,会产生一些问题 事务并发执行对同一行数据更新,冲突怎么处理? 事务在更新一条数据,别的事务要查询这条数据,冲突怎么处理?

事务并执行的时候是通过 MySQL事务的隔离级别、MVCC多版本隔离、锁机制 2)脏写、脏读、不可重复读、幻读 A.脏写 事务A对一条数据更新,事务B也对这条数据更新,事务A回滚,导致事务B的数据丢失

脏写就是两个事务没提交的状况下,都修改同一条数据,结果一个事务回滚了,把另外一个事务修改的值也给撤销了,所谓脏写就是两个事务没提交状态下修改同一个值。

B.脏读 事务A对一条数据更新,还没有提交,事务B也查询了这条数据,读到了事务A还没提交的数据,这就是脏读。

无论是脏写还是脏读,都是因为一个事务去更新或者查询了另外一个还没提交的事务更新过的数据。

因为另外一个事务还没提交,所以他随时可能会反悔会回滚,那么必然导致你更新的数据就没了,或者你之前查询到的数据就没了,这就是脏写和脏读两种坑爹场景。

C.不可重复读 一个事务多次查询一条数据读到的都是不同的值

一条数据,其他事务在一直修改,都提交了,事务A,在事务里面多次读取这条数据,每次读取的数据都是不一样的。

不可重复读,就是一条数据的值没法满足多次重复读值都一样,别的事务修改了值提交之后,就不可重复读了

D.幻读 一个事务用一样的SQL多次查询,结果每次查询都会发现查到了一些之前没看到过的数据,出现了幻觉

E.本质 都是数据库的多事务并发问题,那么为了解决多事务并发问题,数据库才设计了事务隔离机制、MVCC多版本隔离机制、锁机制,用一整套机制来解决多事务并发问题 3)SQL标准中的四个隔离级别 这4种级别包括了:read uncommitted(读未提交),read committed(读已提交),repeatable read(可重复读),serializable(串行化)

A.RU 解决脏写,不允许脏写,也就是不允许两个事务同事更新一条数据。 但是还存在脏读、不可重复读、幻读的问题。

B.RC 解决脏写和脏读,其他事务未提交的数据,看不到,提交了可以看到。 但是还存在 不可重复读、幻读的问题。

C.RR 解决脏写、脏读、不可重复读 每次读 读到的数据都是一样的,但是还可能是幻读的。

D.serializable 事务串行起来一个一个排队执行,一旦串行,数据库的并发可能就只有几十了,一般不会设置。 4)MySQL对四个隔离级别的支持 A.mysql对隔离级别的支持

mysql四种隔离级别都支持 默认事务隔离级别,RR 同时RR级别是可以避免幻读发生的

也就是说,MySQL里执行的事务,默认情况下不会发生脏写、脏读、不可重复读和幻读的问题,事务的执行都是并行的,大家互相不会影响,我不会读到你没提交事务修改的值,即使你修改了值还提交了,我也不会读到的,即使你插入了一行值还提交了,我也不会读到的,总之,事务之间互相都完全不影响!

实现如此多的效果,主要靠mvcc多版本并发控制隔离机制

mysql里面设置隔离级别 REPEATABLE READ,READ COMMITTED,READ UNCOMMITTED,SERIALIZABLE SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level;

B.spring事务对隔离级别的支持

@Transactional(isolation=Isolation.DEFAULT),然后默认的就是DEFAULT值,这个就是MySQL默认支持什么隔离级别就是什么隔离级别。

那MySQL默认是RR级别,自然你开发的业务系统的事务也都是RR级别的了。 5)mvcc多版本并发控制隔离机制 A.undo log版本链

每条数据都有两个隐藏字段

一个是trx_id事务id,操作这条数据的事务id

一个是roll_pointer 指向上一个版本数据的undo log

事务操作数据之前,会创建undolog

里面的内容有事务id,以及该事务id下,被修改字段的原始值

然后操作数据,然后更新回滚指针

undo log还指向了上一个undo log

所有的undolog就形成了一个undo log版本链。

B.ReadView

事务在开始执行的时候,会生成一个 ReadView

里面比较关键的东西有4个

一个是m_ids,未提交的事务id

一个是min_trx_id,最小事务id

一个是max_trx_id,最大事务id,还没有生成(<的关系)

一个是creator_trx_id,当前事务id

C.ReadView+UndoLog实现MVCC

举个例子: 假设数据库有一行记录,事务id=32,原始值=1

接着有两个事务来并发操作这条记录。

事务A(id=45),事务A来读取数据 事务B(id=59),事务B来修改数据

事务A开启ReadView,里面有两个活跃事务id(45、59),最小事务id(45),最大事务id(60),当前事务id(45) 此时事务A,查询到的数据是 事务id=32,原始值=1的那条记录

事务B接着开启ReadView,把值修改为2,数据行的事务id设置成自己,回滚指针指向了对应的undo log,之后提交数据。

此时事务A继续查询,此时数据行的事务id=59,小于最大事务id60,大于最小事务id45,同时活跃列表里面有45和59这两个事务id, 因此确认是并发执行的,事务A只能查询比自己视图最小事务id之前的事务,因此顺着undolog版本链继续去查询,找到事务id=32的数据,查询出来。

如果此时事务A修改数据,数据

标签: 如何放掉电容器一半电快速插拔电源电池连接器rpm电容25v1f电容ld电容封装电容锂电池代18650

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台