高性能MySQL:一个诊断案例(3)

高性能MySQL:一个诊断案例(3),第1张

一个诊断案例( )

我们看到了两种可能性 要么是数据库导致了I/O(如果能找到源头的话 那么可能就找到了问题的原因) 要么不是数据库导致了所有的I/O 而是其他什么导致的 而系统因为缺少I/O 资源影响了数据库性能 我们也很小心地尽力避免引入另外一个隐式的假设 磁盘很忙并不一定意味着MySQL 会有问题 要记住 这个服务器主要的压力是内存读取 所以也很可能出现磁盘长时间无法响应但没有造成严重问题的现象

如果你一直跟随我们的推理逻辑 就可以发现还需要回头检查一下另外一个假设 我们已经知道磁盘设备很忙 因为其等待时间很高 对于固态硬盘来说 其I/O 平均等待时间一般不会超过 / 秒 实际上 从iostat 的输出结果也可以发现磁盘本身的响应还是很快的 但请求在块设备队列中等待很长的时间才能进入到磁盘设备 但要记住 这只是iostat 的输出结果 也可能是错误的信息

究竟是什么导致了性能低下?

当一个资源变得效率低下时 应该了解一下为什么会这样 有如下可能的原因

资源被过度使用 余量已经不足以正常工作

资源没有被正确配置

资源已经损坏或者失灵

回到上面的例子中 iostat 的输出显示可能是磁盘的工作负载太大 也可能是配置不正确(在磁盘响应很快的情况下 为什么I/O 请求需要排队这么长时间才能进入到磁盘?) 然而 比较系统的需求和现有容量对于确定问题在哪里是很重要的一部分 大量的基准测试证明这个客户使用的这种SSD 是无法支撑几百MB/s 的写 *** 作的 所以 尽管iostat 的结果表明磁盘的响应是正常的 也不一定是完全正确的 在这个案例中 我们没有办法证明磁盘的响应比iostat 的结果中所说的要慢 但这种情况还是有可能的 所以这不能改变我们的看法 可能是磁盘被滥用注 或者是错误的配置 或者两者兼而有之 是性能低下的罪魁祸首

在检查过所有诊断数据之后 接下来的任务就很明显了 测量出什么导致了I/O 消耗 不幸的是 客户当前使用的GNU/Linux 版本对此的支持不力 通过一些工作我们可以做一些相对准确的猜测 但首先还是需要探索一下其他的可能性 我们可以测量有多少I/O来自MySQL 但客户使用的MySQL 版本较低以致缺乏一些诊断功能 所以也无法提供确切有利的支持

作为替代 基于我们已经知道MySQL 如何使用磁盘 我们来观察MySQL 的I/O 情况 通常来说 MySQL 只会写数据 日志 排序文件和临时表到磁盘 从前面的状态计数器和其他信息来看 首先可以排除数据和日志的写入问题 那么 只能假设MySQL 突然写入大量数据到临时表或者排序文件 如何来观察这种情况呢?有两个简单的方法 一是观察磁盘的可用空间 二是通过lsof 命令观察服务器打开的文件句柄 这两个方法我们都采用了 结果也足以满足我们的需求 下面是问题期间每秒运行df–h 的结果

下面则是lsof 的数据 因为某些原因我们每五秒才收集一次 我们简单地将mysqld 在/tmp 中打开的文件大小做了加总 并且把总大小和采样时的时间戳一起输出到结果文件中

$ awk

/mysqld *tmp/ {

total += $

}

/^Sun Mar / &&total {

printf %s % f MB\n $ total/ /

total =

} lsof txt

: : MB

: : MB

: : MB

: : MB

: : MB

从这个数据可以看出 在问题之初MySQL 大约写了 GB 的数据到临时表 这和之前在SHOW PROCESSLIST 中有大量的 Copying to tmp table 相吻合 这个证据表明可能是某些效率低下的查询风暴耗尽了磁盘资源 根据我们的工作直觉 出现这种情况比较普遍的一个原因是缓存失效 当memcached 中所有缓存的条目同时失效 而又有很多应用需要同时访问的时候 就会出现这种情况 我们给开发人员出示了部分采样到的查询 并讨论这些查询的作用 实际情况是 缓存同时失效就是罪魁祸首(这验证了我们的直觉) 一方面开发人员在应用层面解决缓存失效的问题 另一方面我们也修改了查询 避免使用磁盘临时表 这两个方法的任何一个都可以解决问题 当然最好是两个都实施

       返回目录 高性能MySQL

       编辑推荐

       ASP NET开发培训视频教程

数据仓库与数据挖掘培训视频教程

lishixinzhi/Article/program/MySQL/201311/29695

前言

案例取自极客时间《mysql45讲》

案例

模拟执行器分析查询语句

场景复现

奇了怪了,此时没用索引,进行了全表扫描

虽然使用了索引,但是还是扫描了37116行,不妨结合之前的知识分析一下:

1.另一个事务未提交,需要保存之前的数据的数据版本,因此delete10万行数据实际是标记数据,这样每一行数据就有两个数据版本,旧的是delete之前的,新的是标记为delete的,索引a上的数据有两份

2.那还多出来的1万7呢,之前介绍过索引树的叶子节点存的是主键,select * 还要进行回表查询,这里将回表的扫描行数一并算上

为什么会选错索引

选择索引是优化器的工作,优化器要找到最优的执行方案并选择最小的代价去执行,扫描行数是影响执行代价之一(扫描越小,访问磁盘次数越少,消耗CPU资源越少)

mysql执行语句之前需要通过根据信息来统计记录数

这个统计信息就是索引的区分度,即索引上不同的值越多,区分度越高越好(show index t 的 cardinality字段查看),索引的区分度是利用采样统计得到的即取小部分统计信息再乘以整体。

除了使用统计信息,还会计算回表代价(主键不需要回表)

如果是统计信息不对那就修正

另一种场景复现

按理说这是个空集,利用索引a只扫描1000行,利用索引b要扫描50000行,这里优化器竟然选择了索引b!!

mysql又选错了索引

解决办法

2.引导使用a索引

我们知道索引树上的数据是有序的,优化器使用b索引,一方面是认为索引b可以避免排序 ,order by a,b强制按照a,b排序意味着两个都需要排序,因此扫描行数成了影响决策的主要条件

3.删掉索引b

解决mysql选错索引主要有两大方向

1.强制指定索引

2.干涉优化器选择(比如增大limit数量,增加order by ,写成子查询)

MySQL选错索引导致的线上慢查询事故

mysql中走与不走索引的情况汇集(待全量实验)

参考: https://www.jianshu.com/p/5e0062f6cf62

图中是两组分片,红色我们称为shard1,蓝色我们称为shard2

51 52是服务器

两个3307互为主从(双主),3309是本地3307的从库

说明:没有明确说明是只在某一个节点上做的,就是两个节点都做

两台虚拟机 db01 db02

每台创建四个mysql实例:3307 3308 3309 3310

mysql软件我们之前已完成二进制安装,直接初始化即可

我们server-id规划为:db01上是7/8/9/10,db02上是17/18/19/20

"箭头指向谁是主库"

10.0.0.51:3307<-----> 10.0.0.52:3307

10.0.0.51:3309------> 10.0.0.51:3307

10.0.0.52:3309------> 10.0.0.52:3307

两个分片,每个分片四个mysql节点

shard1:

Master:10.0.0.51:3307

slave1:10.0.0.51:3309

Standby Master:10.0.0.52:3307

slave2:10.0.0.52:3309

shard2:

Master:10.0.0.52:3308

slave1:10.0.0.52:3310

Standby Master:10.0.0.51:3308

slave2:10.0.0.51:3310

shard1

10.0.0.51:3307 <----->10.0.0.52:3307

db02

db01

db02

10.0.0.51:3309 ------>10.0.0.51:3307

db01

10.0.0.52:3309 ------>10.0.0.52:3307

db02

shard2

10.0.0.52:3308 <----->10.0.0.51:3308

db01

db02

db01

10.0.0.52:3310 ----->10.0.0.52:3308

db02

10.0.0.51:3310 ----->10.0.0.51:3308

db01

这个复制用户在谁上建都行

注:如果中间出现错误,在每个节点进行执行以下命令

常见方案:

360 Atlas-Sharding 360

Alibaba cobar 阿里

Mycat 开源

TDDL 淘宝

Heisenberg 百度

Oceanus 58同城

Vitess 谷歌

OneProxy

DRDS 阿里云

我们装的是openjdk,不是官方的那个

Mycat-server-xxxxx.linux.tar.gz

http://dl.mycat.io/

配置环境变量

我们mycat的命令也是在bin目录下

启动

8066就是对外提供服务的端口,9066是管理端口

连接mycat:

默认123456

db01:

我们一般先把原schema.xml备份,然后自己新写一个:

xml和html看起来差不多,xml是从下往上调用的

前三行我们不用看,直接从第四行schema开始看起:

定义了schema,然后以/schema结尾

为什么要用逻辑库?

业务透明化

此配置文件就是实现读写分离的配置

重启mycat

读写分离测试

总结:

以上案例实现了1主1从的读写分离功能,写 *** 作落到主库,读 *** 作落到从库.如果主库宕机,从库不能在继续提供服务了。

我们推荐这种架构

一写三读,

不设置双写的原因是:性能没提升多少,反而引起主键冲突的情况

配置文件:

之后重启:mycat restart

真正的 writehost:负责写 *** 作的writehost

standby writeHost :和readhost一样,只提供读服务

我们此处写了两个writehost,默认使用第一个

当写节点宕机后,后面跟的readhost也不提供服务,这时候standby的writehost就提供写服务,

后面跟的readhost提供读服务

测试:

读写分离测试

对db01 3307节点进行关闭和启动,测试读写 *** 作

结果应为另一台(52)的3307(17)是写,3309(19)是读

一旦7号节点恢复,此时因为7落后了,写节点仍是17

balance属性

负载均衡类型,目前的取值有3种:

writeType属性

负载均衡类型,目前的取值有2种:

switchType属性

-1 表示不自动切换

1 默认值,自动切换

2 基于MySQL主从同步的状态决定是否切换 ,心跳语句为 show slave status

datahost其他配置

<dataHost name="localhost1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1">

maxCon="1000":最大的并发连接数

minCon="10" :mycat在启动之后,会在后端节点上自动开启的连接线程,长连接,好处是连接速度快,弊端是占内存

tempReadHostAvailable="1"

这个一主一从时(1个writehost,1个readhost时),可以开启这个参数,如果2个writehost,2个readhost时

<heartbeat>select user()</heartbeat> 监测心跳

其他参数sqlMaxLimit自动分页,必须在启用分表的情况下才生效

创建测试库和表:

我们重启mycat后连接到8066

发现跟一个库一样,实际上已经分到不同的物理硬件上了

分片:对一个"bigtable",比如说t3表

热点数据表 核心表

(1)行数非常多,800w下坡

(2)访问非常频繁

分片的目的:

(1)将大数据量进行分布存储

(2)提供均衡的访问路由

分片策略:

范围 range 800w 1-400w 400w01-800w 不适用于业务访问不均匀的情况

取模 mod(取余数) 和节点的数量进行取模

枚举 按枚举的种类分,如移动项目按省份分

哈希 hash

时间 流水

优化关联查询(否则join的表在不同分片上,效率会比单库还要低)

全局表

ER分片

案例:移动统一:先拆出边缘业务,再按地域分片,但对应用来说是统一的

vim rule.xml

<tableRule name="auto-sharding-long">

<rule>

<columns>id</columns>

<algorithm>rang-long</algorithm>

</rule>

<function name="rang-long"

class="io.mycat.route.function.AutoPartitionByLong">

<property name="mapFile">autopartition-long.txt</property>

</function>

===================================

vim autopartition-long.txt

0-10=0

11-20=1

创建测试表:

mysql -S /data/3307/mysql.sock -e "use taobaocreate table t3 (id int not null primary key auto_increment,name varchar(20) not null)"

mysql -S /data/3308/mysql.sock -e "use taobaocreate table t3 (id int not null primary key auto_increment,name varchar(20) not null)"

测试:

重启mycat

mycat restart

mysql -uroot -p123456 -h 127.0.0.1 -P 8066

insert into t3(id,name) values(1,'a')

insert into t3(id,name) values(2,'b')

insert into t3(id,name) values(3,'c')

insert into t3(id,name) values(4,'d')

insert into t3(id,name) values(11,'aa')

insert into t3(id,name) values(12,'bb')

insert into t3(id,name) values(13,'cc')

insert into t3(id,name) values(14,'dd')

取余分片方式:分片键(一个列)与节点数量进行取余,得到余数,将数据写入对应节点

vim schema.xml

<table name="t4" dataNode="sh1,sh2" rule="mod-long" />

vim rule.xml

<property name="count">2</property>

准备测试环境

创建测试表:

mysql -S /data/3307/mysql.sock -e "use taobaocreate table t4 (id int not null primary key auto_increment,name varchar(20) not null)"

mysql -S /data/3308/mysql.sock -e "use taobaocreate table t4 (id int not null primary key auto_increment,name varchar(20) not null)"

重启mycat

mycat restart

测试:

mysql -uroot -p123456 -h10.0.0.52 -P8066

use TESTDB

insert into t4(id,name) values(1,'a')

insert into t4(id,name) values(2,'b')

insert into t4(id,name) values(3,'c')

insert into t4(id,name) values(4,'d')

分别登录后端节点查询数据

mysql -S /data/3307/mysql.sock

use taobao

select * from t4

mysql -S /data/3308/mysql.sock

use taobao

select * from t4

t5 表

id name telnum

1 bj 1212

2 sh 22222

3 bj 3333

4 sh 44444

5 bj 5555

sharding-by-intfile

vim schema.xml

<table name="t5" dataNode="sh1,sh2" rule="sharding-by-intfile" />

vim rule.xml

<tableRule name="sharding-by-intfile">

<rule><columns>name</columns>

<algorithm>hash-int</algorithm>

</rule>

</tableRule>

<function name="hash-int" class="org.opencloudb.route.function.PartitionByFileMap">

<property name="mapFile">partition-hash-int.txt</property>

<property name="type">1</property>

<property name="defaultNode">0</property>

</function>

partition-hash-int.txt 配置:

bj=0

sh=1

DEFAULT_NODE=1

columns 标识将要分片的表字段,algorithm 分片函数, 其中分片函数配置中,mapFile标识配置文件名称

准备测试环境

mysql -S /data/3307/mysql.sock -e "use taobaocreate table t5 (id int not null primary key auto_increment,name varchar(20) not null)"

mysql -S /data/3308/mysql.sock -e "use taobaocreate table t5 (id int not null primary key auto_increment,name varchar(20) not null)"

重启mycat

mycat restart

mysql -uroot -p123456 -h10.0.0.51 -P8066

use TESTDB

insert into t5(id,name) values(1,'bj')

insert into t5(id,name) values(2,'sh')

insert into t5(id,name) values(3,'bj')

insert into t5(id,name) values(4,'sh')

insert into t5(id,name) values(5,'tj')

a b c d

join

t

select t1.name ,t.x from t1

join t

select t2.name ,t.x from t2

join t

select t3.name ,t.x from t3

join t

使用场景:

如果你的业务中有些数据类似于数据字典,比如配置文件的配置,

常用业务的配置或者数据量不大很少变动的表,这些表往往不是特别大,

而且大部分的业务场景都会用到,那么这种表适合于Mycat全局表,无须对数据进行切分,

要在所有的分片上保存一份数据即可,Mycat 在Join *** 作中,业务表与全局表进行Join聚合会优先选择相同分片内的全局表join,

避免跨库Join,在进行数据插入 *** 作时,mycat将把数据分发到全局表对应的所有分片执行,在进行数据读取时候将会随机获取一个节点读取数据。

vim schema.xml

<table name="t_area" primaryKey="id" type="global" dataNode="sh1,sh2" />

后端数据准备

mysql -S /data/3307/mysql.sock

use taobao

create table t_area (id int not null primary key auto_increment,name varchar(20) not null)

mysql -S /data/3308/mysql.sock

use taobao

create table t_area (id int not null primary key auto_increment,name varchar(20) not null)

重启mycat

mycat restart

测试:

mysql -uroot -p123456 -h10.0.0.52 -P8066

use TESTDB

insert into t_area(id,name) values(1,'a')

insert into t_area(id,name) values(2,'b')

insert into t_area(id,name) values(3,'c')

insert into t_area(id,name) values(4,'d')

A

join

B

为了防止跨分片join,可以使用E-R模式

A join B

on a.xx=b.yy

join C

on A.id=C.id

<table name="A" dataNode="sh1,sh2" rule="mod-long">

<childTable name="B" joinKey="yy" parentKey="xx" />

</table>


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/8628088.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-04-19
下一篇 2023-04-19

发表评论

登录后才能评论

评论列表(0条)

保存