mysql高性能优化方案
2016-12-09 21:47
579 查看
Showstatus;
Threads_connected:当前的客户端已经连接的数量,这个值会少于预设值,
Threads_running:记录了并发执行stmt/command的数量。正在运行。
Awk:
1.读取下一行,并把下一行赋给$0,各列赋给$1,$2,
2.用指定命令处理改行。
3.awk'/l/{printf("%s\n",$1)}'test.txt
mysqladmin-urootext|
注意:有的服务器可能需要输入密码,需要加-p参数。
mysqladmin-urootext|awk'/Queries/{printf("%d",$4)}';
mysqladmin-urootext|awk'/Threads_connected/{printf("%d",$4)}';
mysqladmin-urootext|awk'/Threads_running/{printf("%d",$4)}';
mysqladmin-urootext|awk'/Queries/{printf("%d",$4)}/Threads_connected/{printf("%,$4)}/Threads_running/{printf("%d",$4)}';
创建测试表:
createtablet_user(
idsmallint(11)auto_increment,
usernamechar(100)notnullcomment'name',
addresschar(100)notnullcomment'address',
primarykey(id)
)engine=myisamdefaultcharset=utf8;
PHP写测试数据插入100000条数据
<?php
$t=time();
set_time_limit(1000);
$myFile="/var/www/insert.sql";
$fhandler=fopen($myFile,'wb');
if($fhandler){
$sql="'zhangsan'\t'zhongguancunnandajie'";
$i=0;
while($i<1000000)//1,000,000
{
$i++;
fwrite($fhandler,$sql."\r\n");
}
echo"写脠鲁晒娄,潞脛卤拢潞",mktime()-$t;
}
loaddatalocalinfile'/var/www/insert.sql'intotablet_user(username,address);
获取状态的脚本:
whiletrue
do
./mysqladmin-urootext|awk'/Queries/{q=$4}/Threads_connected/{c=$4}/Threads_running/{r=$4}END{printf("%d%d%d\n",q,c,r)}'>>status.txt
sleep1
done
./ab-c50-n200000http://10.18.93.124/callback.php
Telnet10.18.93.12211211
^]
请不要按ctrl+]
处理status.txt文件
每秒的请求数
awk'{q=last;last=$1}{printf("%d%d%d\n",$1,$2,$3)}'status.txt
MySQL>showprocesslist
./mysql-uroot-e'showprocesslist'
./mysql-uroot-e'showprocesslist\G'|grepState
./mysql-uroot-e'showprocesslist\G'|grepState|uniq|sort-rn
//查询processliststate的脚本。
#!/bin/bash
whiletrue
do
./mysql-uroot-e'showprocesslist\G'|grepState|uniq|sort-rn>>proce.txt
usleep100000
done
Sysbench对硬盘io性能的测试:
测量CPU和io能力。
./sysbench--test
./configure--prefix=/usr/local/sysbench--with-mysql-includes=/usr/local/mysql/include--with-mysql-libs=/usr/local/mysql/lib&&make&&makeinstall
./sysbench:errorwhileloadingsharedlibraries:libmysqlclient.so.18:cannotopensharedobjectfile:Nosuchfileordirectory
ln-s/usr/local/mysql/lib/libmysqlclient.so.18/lib64/libmysqlclient.so.18
Cpu性能测试:cpu测试使用64的整数,测试计算素数直到摸个最大值所需要的时间。
./sysbench--test=cpu--cpu-max-prime=20000run
Testexecutionsummary:
totaltime:58.8928s
totalnumberofevents:10000
totaltimetakenbyeventexecution:58.8342
per-requeststatistics:
min:2.02ms
avg:5.88ms
max:32.30ms
approx.95percentile:7.33ms
Threadsfairness:
events(avg/stddev):10000.0000/0.00
executiontime(avg/stddev):58.8342/0.00
我们只关心totaltime即可。
测试thread测试:
sysbench--test=threads--num-threads=64run
Testexecutionsummary:
totaltime:15.8065s
totalnumberofevents:10000
totaltimetakenbyeventexecution:1008.8293
per-requeststatistics:
min:0.66ms
avg:100.88ms
max:385.39ms
approx.95percentile:198.66ms
Threadsfairness:
events(avg/stddev):156.2500/4.99
executiontime(avg/stddev):15.7630/0.03
主要看totaltime2.4值越低说明线程调度的性能越高。
内存测试:
sysbench--test=memory--memory-block-size=8K--memory-total-size=2G--num-threads=16run
上面的语句的含义为:传输2G数据,每个block的大小为8k,我们关心的是吞吐量:
2048.00MBtransferred(8030.45MB/sec)
重要参数:
每秒请求数是88.33Requests/sec,吞吐量是1.3802Mb/sec
文件io的测试:
1.生成测试数据,生成的测试数据文件要比内存大,如果文件的数据能够完全放倒内存中则操作系统缓存了大部分的数据,导致测试结果无法体现io密集型的工作负载。
首先先创建一个数据集:
sysbench--test=fileio--file-total-size=40Gprepare
Otlp测试:
对mysql的oltp的测试,需要经历prepare,run,cleanup三个阶段,prepare阶段在数据库中产生一张指定行数的表,默认表在sbtest下,表名为sbtest
./sysbench--test=oltp--mysql-table-engine=innodb--mysql-user=root--db-driver=mysql--mysql-db=test--oltp-table-name=t1--oltp-table-size=3000--mysql-socket=/tmp/mysql.sock
下面来看最重要也是最复杂的测试————oltp。oltp基准测试模拟了一个简单的事物处理系统的工作负载。下面的例子使用的是一张超过百万行记录的表,第一步是先生成这张表:
生成数据只需要上面这条简单的命令即可。这条命令在test数据库中新建了一个表(sbtest),并在表中插入了1000000条记录。
对于非默认安装的mysql,需要指定连接到msyql服务器的socket(my.cnf中的socket值),如下所示:
数据加载完成以后就可以开始测试了,这个例子采用了16个线程,测试时长为720秒:
与插入记录时一样,如果mysql是非默认安装,还需要指定--mysql-socket的值。
如上所示,结果中包含了相当多的信息。其中最有价值的信息如下;
·总的事务数
·每秒事务数
·时间统计信息(最小,平均,最大响应时间,以及95%百分比响应时间)
·线程公平性统计信息
最最重要的当然是每秒事务数(2601.71persec.)。
oltp测试注意事项:
1.
--max-requests--max-requests默认值为10000,如果设置了--max-requests或者使用默认值,分析结果的时候主要查看运行时间(totaltime),一般情况下,都将--max-requests赋值为0,即不限制请求数量,通过--max-time来指定测试时长,然后查看系统的每秒事务数。
2.
3.
--oltp-test-mode
--oltp-test-mode用以指定测试模式,取值有(simeple,complex,nontrx),默认是complex。不同模式会执行不同的语句。具体执行语句如下所示:
Simple这种模式只是简单的执行selec语句。
SELECTcFROMsbtestWHEREid=N
complex(Advancedtransactional)在事务中,可能包含下列语句。
Pointqueries:
SELECTcFROMsbtestWHEREid=N
Rangequeries:
SELECTcFROMsbtestWHEREidBETWEENNANDM
RangeSUM()queries:
SELECTSUM(K)FROMsbtestWHEREidBETWEENNandM
RangeORDERBYqueries:
SELECTcFROMsbtestWHEREidbetweenNandMORDERBYc
RangeDISTINCTqueries:
SELECTDISTINCTcFROMsbtestWHEREidBETWEENNandMORDERBYc
UPDATEsonindexcolumn:
UPDATEsbtestSETk=k+1WHEREid=N
UPDATEsonnon-indexcolumn:
UPDATEsbtestSETc=NWHEREid=M
DELETEqueries:
DELETEFROMsbtestWHEREid=N
INSERTqueries:
INSERTINTOsbtestVALUES(...)
nontrx(Non-transactional)这种模式包含下列SQL语句。
Pointqueries:
SELECTpadFROMsbtestWHEREid=N
UPDATEsonindexcolumn:
UPDATEsbtestSETk=k+1WHEREid=N
UPDATEsonnon-indexcolumn:
UPDATEsbtestSETc=NWHEREid=M
DELETEqueries:
DELETEFROMsbtestWHEREid=N
INSERTqueries:
INSERTINTOsbtest(k,c,pad)VALUES(N,M,S)
§
Run:对上面产生的表进行oltp测试:
./sysbench--test=oltp--mysql-table-engine=innodb--mysql-user=root--db-driver=mysql--mysql-db=test--oltp-table-name=t1--oltp-table-size=3000--mysql-socket=/tmp/mysql.sockrun;
State:
Null
Sendingdata:返回大数据时。
排序之后去掉重复的:
morepro.txt|sort|uniq-c|sort-rn
Null,不用管,statistics正在进行语法的分析。
Copyingtotmptable
Sendingdata
Sortingresult
Convertingheaptomyisam:查询结果太大,内存不够大,只好往磁盘上放
Showvariableslike‘%size%’;
tmp_table_size:16777216
setglobaltmp_table_size=1024;
setsessiontmp_table_size=1024;
查看慢查询
showvariableslike'long%';
设置慢查询
setlong_query_time=5;
首先要开启profilling,
Showvariableslike‘%pro%’
setprofiling=1;
showprofiles;
setnamesutf-8;
select*fromt_userlimit10000;//查询10000条记录
showprofilecpuforquery6;
Openingtalbes//打开表
Tablelock//锁上表
Init初始化
Optimizing//优化语句
Preparing//准备
Sendindata
select*fromt_usergroupbyid%20;
慢日志查询可以知道哪些sql语句执行效率低下,通过explain可以知道sql语句的具体执行情况,索引使用等,还可以结合show命令查看执行状态。
如果explain的信息不够详细,可以使用profiling命令得到更准确的sql执行消耗资源的信息
使用连接(join)来代替子查询(sub-queries)
使用join之所以更有效率一些,是因为mysql不需要在内存中创建临时表来完成这个逻辑上需要两个步骤的查询工作。
使用enum,char,而不是varchar,使用合理的字段属性长度。
尽可能的使用notnull
固定长度的表会更快
拆分大的delete或insert
查询的列越小越好
showprocesslist详解:
-----+-------------+--------------------+-------+---------+-------+----------------------------------+----------
|Id|User|Host|db|Command|Time|State|Info
+-----+-------------+--------------------+-------+---------+-------+----------------------------------+----------
|207|root|192.168.0.20:51718|mytest|Sleep|5||NULL
|208|root|192.168.0.20:51719|mytest|Sleep|5||NULL
|220|root|192.168.0.20:51731|mytest|Query|84|Locked|
selectbookname,culture,value,typefrombookwhereid=001
Command列,显示当前连接执行的命令:一般为sleep,query,connect,time,
Time是这个状态持续的时间。State,只是语句执行的某一个状态,一个sql语句,以查询为例可能需要经过copingtotmptable,sortingresultsendingdata等状态才可以完成,info,显示这个sql语句
表的优化,和列类型的选择:
列选择优先级:整型>date,time>charvarchar>blob
2.够用就行,不要慷慨,(smalllint,varchar(n))
大的字段浪费内存影响速度。
以varchar(10),varchar(300)存储的内容相同,但是表联查时,varchar(300)要花很多的内存。
3.尽量避免用null
Null,不利于索引,要用特殊的字节来标注。
要用多余的字符来存储null。
在磁盘的占据的空间更大。
select*fromt5wherenameisnull;才能查出来。
如果允许为null,keylen会多一个字节。
每一行多一个字节,如果上亿行就更大了。
Enum是枚举类型,内部是整形。
Enum的列在内部是用整形存储的
createtablet5(
sexenum('male','female')notnulldefault'male'
)enginemyisamcharsetutf8;
Enum比varchar省空间。
性别,学历要用enum,省空间。
其实内部用整形来存的。
多列索引生效
Btree索引:
Mysiam,innodb用b-tree.
Ndb用t-tree
B-tree系统,可理解为排好序的快速查找结构。
Hash索引
在memory表里,默认的是hash索引,hash的理论的时间复杂度为
O(1):一次查询就能查到。
Hash函数计算的结果是随机的,随机放在磁盘中,
找数据的速度很快,但是随机拿数据很慢。
不能对范围进行优化。有利于范围查询。
Btree索引常见的误区:
1.where条件常用的列上都加上索引
错,例如:wherecat_id=3andprice>100;
Cat_id上,和price上都加上索引只能用上一个,因为是独立索引。
在多列建立索引,如index(a,b,c);
多列索引,必须用到第一个,否则不生效,
在多列索引上,索引需要发挥作用需要满足左前缀要求。
要从到右使用。
Index(a,b,c)
Explain:
Type:range指的是范围,
Key_len:12一个字符等于3个字节,4个全用上了12;
Key_len:6说明两个索引起了作用。
Usingfilesort;c5本身就是无序的所以要排一下序。
一般而言,分组统计先按分组字段有序排列。
用到临时表排列,在临时表中聚合运算。
与myisam重要的区别是:虽然都是b+tree,但是innodb表数据本身就是按照b
+tree组织的索引结构,这棵树的叶节点data保存了完整记录,这个索引的
Key就是主键。
myisam和innodb索引实现的不同
2012-11-300 个评论
作者:wangjj20
收藏我要投稿
MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址。下图是MyISAM索引的原理图:
这里设表一共有三列,假设我们以Col1为主键,则上图是一个MyISAM表的主索引(Primarykey)示意。可以看出MyISAM的索引文件仅仅保存数据记录的地址。在MyISAM中,主索引和辅助索引(Secondarykey)在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复。如果我们在Col2上建立一个辅助索引,则此索引的结构如下图所示:
同样也是一颗B+Tree,data域保存数据记录的地址。因此,MyISAM中索引检索的算法为首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其data域的值,然后以data域的值为地址,读取相应数据记录。
MyISAM的索引方式也叫做“非聚集”的,之所以这么称呼是为了与InnoDB的聚集索引区分。
InnoDB索引实现
虽然InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同。
第一个重大区别是InnoDB的数据文件本身就是索引文件。从上文知道,MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。
上图是InnoDB主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录。这种索引叫做聚集索引。因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL系统会自动选择一个可以唯一标识数据记录的列作为主键,如果不存在这种列,则MySQL自动为InnoDB表生成一个隐含字段作为主键,这个字段长度为6个字节,类型为长整形。
第二个与MyISAM索引的不同是InnoDB的辅助索引data域存储相应记录主键的值而不是地址。换句话说,InnoDB的所有辅助索引都引用主键作为data域。例如,下图为定义在Col3上的一个辅助索引:
这里以英文字符的ASCII码作为比较准则。聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。
了解不同存储引擎的索引实现方式对于正确使用和优化索引都非常有帮助,例如知道了InnoDB的索引实现后,就很容易明白为什么不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。再例如,用非单调的字段作为主键在InnoDB中不是个好主意,因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。
辅助索引需要检测两遍索引,辅助索引的叶子节点存放了数据,和主键的值,首先要
找到数据,然后根据数据的主键,查找主键索引,找到记录。
Myisam的辅助索引还是地址。
Myisam检索索引算法,按照b+tree算法搜索索引,如果指定的key存在,则取出data域的值(地址),读取相应的记录。
Myisam和innodb索引的区别。
Innodb的(辅助索引)指向对主键的应用,
Myisam的次索引和主索引都指向物理行。
聚簇索引:
直接
聚簇索引分块特点:
节点会分裂。
对聚簇索引比较严重。
对于myisam,节点存储的对物理行的地址,又缓存在
内存中,分裂较快。
对于innodb,因为节点下存储了行数据,因此在分裂的时候,还需要移动行数据。
对于innodb而言,因为节点有数据文件,节点分裂比较慢,对于innodb主键,尽量
是整型,而且是递增的,如果是无规律的的数据,将会产生页的分裂。降低效率。
可以查看innodb_page_written1471
这个值在随机插入要比顺序插入要搞得的多。
Showstatus;
Innodb_insert_block;
索引覆盖:
由索引-》磁盘取数据
回行
索引的速度非常快,因为索引是一个特别快的数据结构,而且
要缓存在内存中。
查索引是快的,回行是慢的。
回行的时候,需要到磁盘上去。
//username索引
Selectusername,agefromtablewhereusername=’li’;
Selectusernamefromtablewhereusername=’li’//不需要回行,查询速度非常快
这种叫做索引覆盖。
索引覆盖是指,如果查询的列恰好是索引的一部分,那么查询只需要在
索引文件上进行,不需要回行到磁盘上在找数据。
这种查询速度非常快。
清除查询缓存:resetquerycache;
Useindex出现了索引覆盖。
实例:
CreatetableA{
idvarchar(64)primarykey.
Verint;
}
在id,ver上有联合索引
10000条数据
SelectidfromAorderbyid特别慢
SelectidfromAorderbyid,ver就非常快。
Innodb中,数据记录一定,innodb块大小1024,
如果叶子的数据大,1024/1024=1条数据,10000条数据,
就需要10000个块,查找时要横跨很多块。
是innodb引擎,
有多个比较长的列
1.聚簇索引,导致id排序时,要跨过很多块。
2.有比较长的列,导致块比较多。
1.如果是myisam不存在这个问题。
Id.ver联合索引
Id.ver
31
$conn=mysql_connect('localhost','root');
mysql_query('usetest',$conn);
mysql_query('setnamesutf8',$conn);
$str=str_repeat('m',3000);
for($i=0;$i<=10000;$i++)
{
$id=dechex($i);
$sql=sprintf("insertintot10values('%s',%d,'%s','%s','%s')",$i,$i,$str,$str,$str);
mysql_query($sql,$conn);
}
Showindexfromt10\G;//查看索引
altertablet10addindexidver(id,ver);//添加复合索引。
Usingindex是表示索引覆盖。
Mysql清除表的数据:
Truncatetableteacher;
Primarykey主键索引必须是int型,如果是char型的
explainSelectnamefromt12whereid=3\G
用不到索引。
改成int型,就可以使用主键索引。
高效的索引:
1.频繁度
2.索引覆盖
3.区分度高
4.长度小
长度小,建立的块就少。
区分度,长度必须达到一种平衡。
//查到最长的记录
select*fromt12orderbylength(name)desclimit1;
区分度:
SelectdistinctnamefromA
返回A中不同的值
selectleft(name,2)fromt12;
返回从左截取2个字符的值。
selectcount(distinctleft(name,2))/count(*)fromt12;
算出摸个字段重复的概率。
Varchar类型会有变化,所以key_len14//3*4+2//一个utf8字符占3个字节。
altertablepersonaddindexname(name(15));
altertablepersonmodifynamechar(300);//改变列的长度。
对于前缀不易区分的列,
http://www.bai.du.com
http://www.zixue.it
1.可以倒过来查
2.违哈希。
createtablep1(
idintprimarykeyauto_incrementnotnull,
namechar(100)notnulldefault''
)engineinnodbcharsetutf8;
insertintourl(url)values("www.baidu.com");//单独给摸一个列赋值。
altertableppaddcrcurlintunsignednotnulldefualt0;
Selectcrc32(‘a’);//将a字符转化为2的32次方的整数。
altertableurladdcrcurlintunsignednotnull;//增加一个列
updateurlsetcrcurl=crc32(url);
selectcrc32('www.baidu.com');
explainselect*fromurlwherecrcurl=387695885\G;//用crc32构造哈希列
把字符串的列,构成整型,来降低索引的长度。
对于一般系统而言区别度到达0.1,索引的性能就可以接受。
所以建索引中指定索引长度。
altertablet12addindexname(name(4));//指定建立索引长度。
大数据量的分页效果
Selectfileldfromtablelimit30,10;
比如每页显示perpage条,当前是n页
Limit(n-1)*perpage,N;
翻过了几十万条,数据很慢,
怎么优化:
1.从业务上去优化:
Selectid,namefromt12limit10000,10;
0.02sec
Selectid,namefromt12limit5000000,10
4.4sec
为什么limitm,n,m越大时查询越慢?
要逐行查找,用limitoffset时不是先跳过在查询,
而是先查询在跳过。
就是要把100w行取出来,让后再跳过前100w行。
2个办法用索引查询:
Selectid,namefromt12whereid>100000limit10;
这次为什么快,因为id主键索引,迅速找到10000,让后去10
有个问题,要求前提数据没删过。
一般来说,大网站数据部物理删除,只做逻辑删除,
比如is_delete=1做标记,显示是不要显示。
延迟关联:
1.让索引覆盖先快一些:
explainSelectidfromt12limit5000000,10\G.
如果有name字段要回行,要在表中查。
Selectid,namefromt12innerjoin(selectidfromt12limit50000,10)astmpont12.id=tmp.id;
1.34sec
selectt12.id,namefromt12innerjoin(selectidfromt12limit10000,10)astmpont12.id=tmp.id;
Resetquerycache;
索引与排序:
1.对于覆盖索引,直接在索引上查询时,就是有顺序的。
2.先取出数据形成临时表,叫做filesort,(文件排序,但文件可能在磁盘上吗,也可能在内存中)。
我们争取目标--取出来数据就是有序的,利用索引来排序。
索引本身就是排序的。
Filesort取出结果在排序,即取出结果在排序。
比如good表中(cat_id,shop_price)组成联合索引。
Wherecat_id=Norderbyshop_price可以利用索引排上序。
Selectgood_id,cat_idshop_pricefromgoodsorderbyshop_price;
Usingwhere安装shop_price索引取出结果,本身就是有序的
重复索引
一个列上建了两个索引。(毫无意义,拖慢你的增删改的速度)
冗余索引:是可以的。
索引碎片的修复:
optimizetableinfo;
Truncatetableinfo;
resetquerycache;
表大的时候非常耗资源。
多列索引。
复合索引:在两个以上的列建索引,就是复合索引。
多列索引可以理解成合并多列的值创建的有序的数组,当查询条件包含lastname和fisetname
Select*fromtestwherelast_name=’ss’andfirst_name=’li’
Sql先会过滤lastname复合条件的记录,在基础上在过滤firstname符合条件的记录。
如果分别在lastname和firstname创建两个索引,mysql处理方式就不一样了。他只会选择一检索能力最强的检索,另外一个用不上。
主要因为,分别创建时创建了两个索引,不知道用那个
复合索引还是一个索引,
以下都能用上多列索引:
以下用不上复合索引
慢,等待时间:表被锁住了,看看连接数啊,看
执行时间:1.取了多少数据
2扫描了多少行
Explain:
Simple(不含子查询)primary(含子查询)
Table:表名,有可能是别名,
Derived如from子查询时
Null
如select1+2;
key_len:
Type:是查询方式,
All,全表扫描,
Index扫描索引节点。扫描的行数还是过多。
All实在磁盘上扫描所有行,index沿着索引文件扫描所有行
explainselectidfrominfowhereidlike'%'\G;
Range:范围
explainselectidfrominfowhereid>3\G;
Range是摸个范围的扫描。
Ref:引用,索引确定摸个位置。
eq_ref;
Const:效率更高。
explainselectidfrominfowhereid=3\G;
System,null最快。
explainselectnamefrominfowhereidin(1,2)\G;
误区:explainselectnamefrominfowhereidin(select)
explainselectnamefrominfowhereidin(selectidfromgood_id)\G;
Info表中的每一行取出id,去在good_id表对比,info表需要全表扫描。
explainselectnamefrominfowhereidin(1,2)\G;
不一样。
explainselectinfo.id,namefrominfoinnerjoin(selectidfromgood_id)astmpontmp.id=info.id\G;
Exsits应用:
selectid,namefrominfoascwhereexists(select*fromgood_idasgwherec.id=g.id);
explainselecti.id,i.namefrominfoasiinnerjoingood_idasgoni.id=g.idgroupbyid\G;
explainSelectcat_id,cat_namefromcategroryascwhereexists(select*fromgoodsasgwhereg.cat_id=c.cat_id)\G;
不查,
少查,
快查。
1.关于Max,min优化。
explainselectmax(id)frominfo\G;
***************************1.row***************************
id:1
select_type:SIMPLE
table:NULL
type:NULL
possible_keys:NULL
key:NULL
key_len:NULL
ref:NULL
rows:NULL
Extra:Selecttablesoptimizedaway
1rowinset(0.00sec)
Selectidfromit_areause(primary)wherepid=1087limit1;
Selectmin(id)fromit_areawherepid=1087;
它的工作是先把整张表都找出来,在找到where1087的行,都找到最小的。
加了where就不快了,
给pid加索引。
Count优化。
Selectcount(*)frominfo;
Selectcount(*)frominfoid<=100;
Select(selectcount(*)frominfo)-(selectcount(*)frominfowhereid<100)ascnt;
3.groupby用于统计,而不用筛选数据
以及用索引来避免临时表和文件排序
Union优化,
Unionall效率高。
数据库主从集群配置。
数据库的复制技术。
大致原理:
主服务器建立binlog,授权复制账号
从服务器建立relaylog利用复制账号来监听服务器的日志。
#binarylog
log-bin=mysql-bin
#binlogformat
binlog_format=mixed
Slaveserver:
relay-log=mysql-relay
建立mysql服务器的id
#server-id
server-id=200
建立授权账号:
Mysql-uroot
showgrants;
Grantreplicationclient,replicationslaveon*.*to‘repl’@’192.168.1.100’identifiedby‘repl’
grantreplicationclient,replicationslaveon*.*to'repl'@'10.18.93.120'identifiedby'repl';
问题:
TheMySQLserverisrunningwiththe--skip-grant-tablesoptionsoitcannotexecutethisstatement?
解决:
flushprivileges;
Slave:
给你一个账号,去你的master
Showmasterstatus;
/*
Changemasterto
master_host=’192.168.1.199’
Master_user=’repl’
Master_password=’repl’
Master_log_file=’mysql-bin.00001’
Master_log_pos=268;
*/
changemasterto
master_host='10.18.93.122',
master_log_file='mysql-bin.000029',
master_log_pos=7275,
master_user='repl',
master_password='repl';
Showslavestatus;
Resetslave;
Startslave;
问题:
Couldnotinitializemasterinfostructure;moreerrormessagescanbefoundintheMySQLerrorlog?
解决:
slavestop;
Resetslave;
changemasterto
master_host='10.18.93.121',
master_log_file='mysql-bin.000029',
master_log_pos=107,
master_user='repl',
master_password='repl';
Showslavestatus;
createtablemsss(
->idintprimarykeyauto_incrementnotnull,
->namechar(10)notnulldefault''
->)enginemyisamcharsetutf8;
常用语句:
Showmasterstatus//查看master状态
Showslavestatus
Resetslave//重置slave
Startslave//启动slave一旦开启就监听master状态
Stopslave
日志格式:
Statement语句级的
Row
Mixed
以insertintotablevalues()为例,
影响1行,对于其他行没有影响,就用row格式
的比较合理。
直接复制上1行的新增变化。
以updatetableage=21wherename=sss
这个情况一般只是影响1行,用row也比较合适
Updatetablesetsalary=salary+100
这个语句带来的影响是针对没一行的,因此磁盘上很多发生变化,
适用于statement格式日志。
2种日志,各有各的不同,mysql提供了mixed类型。
主主复制:
相当于分片加复制。
Master:
server-id=121
log-bin=mysql-bin
binlog_format=mixed
#添加relaylog
relay-log=mysql-relay
grantreplicationclient,replicationslaveon*.*to'repl'@'10.18.93.120'identifiedby'repl';
changemasterto
master_host='10.18.93.124',
master_log_file='mysql-bin.000001',
master_log_pos=361,
master_user='repl',
master_password='repl';
Startslave
Slave:
server-id=120
log-bin=mysql-bin
binlog-format=mixed
relay-log=mysql-relay
grantreplicationclient,replicationslaveon*.*to'repl'@'10.18.93.121'identifiedby'repl';
changemasterto
master_host='10.18.93.121',
master_log_file='mysql-bin.000032',
master_log_pos=3720,
master_user='repl',
master_password='repl';
Startslave
主主复制时,主键冲突问题解决?
让一台服务器1,3,5,7
另一台服务器2,4,6,8
Setsessionauto_increment_increment=2//每步增长2
Setsessionauto_increment_offset=1//从1开始增长
Setglobalauto_increment_increment=2//每步增长2
Setglobalauto_increment_offset=1
Setsessionauto_increment_increment=2;
Setsessionauto_increment_offset=2;
Setglobalauto_increment_increment=2;
Setglobalauto_increment_offset=2;
如果后期加服务器,这个办法就有限制了,
我们可以再业务上来解决。
比如:
设计一个Oracle有sequnce序列。
序列每次访问生成递增或第减。
以redes为例,我们构建一个global:userid
每次php插入mysql前,先incr-》global:userid得到一个不重复的userid。
被动主主复制:
/etc/my.cnf
read-only=1
如果有问题可以可以快速切换。
路由sql语句:
Mysql-proxy
原理:
能读写分离,
还能做负载均衡
./bin/mysql-proxy--help-all
Proxy在那个端口,//默认4040
Proxy代理了那几台服务器:--proxy-backend-address
./bin/mysql-proxy-P10.18.93.121:4040--proxy-backend-addresses=10.18.93.121:3306--proxy-backend-addresses=10.18.93.120:3306
mysql-h10.18.93.120-uroot-p-P4040
4条语句,没有给我们做均衡:
均衡不是语句带来的均衡,容易带来不一致。
Update
Delete
以连接为单位的均衡。
所以多建几个连接:
均衡是以连接为单位的。
读写分离:
A:mysql服务器:10.18.93.121
Bmysql服务器:10.18.93.120
C:mysql-proxy服务器:10.18.93.124
分别在A,B服务器上建个账号:
grantallon*.*to'root'@'10.18.93.120'identifiedby'';
建立10.18.93.124访问服务器的权限。
在客户端中:mysql-h10.18.93.124-uroot-p-P4040
Proxy服务器会直接拿10.18.93.121服务器写,
去那10.18.93.120去读
Mysql-proxy下载:
http://mirrors.sohu.com/mysql/MySQL-Proxy/
./bin/mysql-proxy--proxy-backend-addresses=10.18.93.120:3306--
Proxy-read-only-backend-addresses=10.18.93.121:3306--proxy-lua-script=/usr/local/mysql-proxy/share/doc/mysql-proxy/rw-spliting.lua
rw-spliting.lua这个脚本专门用来分析你的sql语句。分成不同的类型做判断。
简写:
./bin/mysql-proxy-b10.18.93.121:3306-r10.18.93.120:3306-s/usr/local/mysql-proxy/share/doc/mysql-proxy/rw-splitting.lua
在mysql-proxy/share/doc/mysql-proxy/rw-splitting.lua下找到:
ifnotproxy.global.config.rwsplitthen
proxy.global.config.rwsplit={
min_idle_connections=1,
max_idle_connections=2,
is_debug=false
}
End
表分区的概念:
createtabletop(
->tidintprimarykeyauto_increment,
->titlechar(20)notnulldefault''
->)enginemyisamcharsetutf8
->partitionbyrange(tid)(
->partitiont0valueslessthan(10),
->partitiont1valueslessthan(20),
->partitiont2valueslessthan(MAXVALUE)
->);
createtabletop(tidintprimarykeyauto_increment,titlechar(20)notnulldefault'')enginemyisamcharsetutf8partitionbyrange(tid)(partitiont0valueslessthan(10),partitiont1valueslessthan(20),partitiont2valueslessthan(MAXVALUE));
insertintotop(tid,title)values(11,'a');
好处是:打开的线程数多,文件独立,不会死锁。
Createtableuser(
aidint,
unamechar(6)
)enginemyisamcharsetutf8;
partitionbylist(aid)(
Partitionbjvaluesin(1);
Partitionhbvaluesin(2),
Partitionxsvaluesin(3),
Partitionahvaluesin(4)
);
Insertintouser(uname,aid)values(‘poluy’,1);
Insertintouser(uname,aid)values(‘lily’,2);
原子性:事情要不全做完,要不干脆不做,不可分割的。
即:张三减300李四+300insert银行流水,这3个操作,必须都完成,或者都不做。
一致性:钱的逻辑性是一致的,事物前后的数据保持业务的合理一致。
汇款前张三的余额+李四的余额==汇款后张三的余额+李四的余
如果用户有42亿,给42亿加100就会溢出了,那边减100没事
或者,就有200元,200-300=-100是不行的
隔离性;
持久性:事务一旦发生,不能取消。
事务使用:
Starttranscation
执行语句
Commit/callback
隔离性:
Starttransaction;
Updateaccountsetmoney=money+100whereuname=’li’;
Commit;
Setsessiontransactionisolationlevelreaduncommitted;
Showvariableslike‘%isolation%’
Setsessiontransactionisolationlevelreadcommitted
Session1:starttransactionsession2starttransaction
这是两个事务进行处理:
可重复读:
Setsessiontransactionisolationlevelrepeatableread;
查询系统的隔离级别:
select@@global.tx_isolation,@@tx_isolation;
设置隔离级别:
setglobaltx_isolation='read-uncommitted';
隔离级别的分类:
(readuncommitted)读取没有提交的内容,也叫脏读。
(read-committed)读取提交的内容
(repeatable-read)可重复读,会导致幻读。
Serializable(可串行化)
Mysql锁机制:
Myisam表锁:
可以通过showstatuslike‘table%’;
变量来分析系统上表锁定的争夺。
如果tableLOCKS_WAITED比较高则说明存在着严重的表锁争用情况。
Session1session2
Locktablepersonwrite;
Insertintoperson(name)values(‘guojia’);select*fromperson;
阻塞,
Unlocktables;等待
Session2释放。
加读锁:
Locktablepersonread;
总结:
Session加读锁时,session1不能更新,只能查询,session
2能查询,更新时需要等待session1释放锁。不能查询其他表。(读的时候,其他session能读,但是不能写);
Session加写锁时,session1能读,能写,session1,不能读,也不能写,需要session1,释放才可以。
并发插入:
Session1加上读锁,该线程可以对表进行查询操作,但不能进行更新操作,其他表
虽然不能对表进行删除更新操作,却可以对表进行并发插入操作。
Myisam表调度:
写锁的优先级最高,即使读锁请求在写锁请求队列前。
当时可以降低写锁的优先级:
Setlow_priority_updates=1;使连续发出的更新请求的优先级降低。
可以设置max_write_lock_count一个合适的值,当一个表的读锁达到这个值后,mysql
会暂时将写请求的优先级降低,给读进程机会。
Showvariableslike‘max_write%’;
获得innodb系统来分析系统上行锁的争夺状态:
Showstatuslike‘innodb_row_lock%’
如果current_waits比较高,
Innodb_monitor查看状态:
Createtableinnodb_monitor(aint)engine=innodb;
Showinnodbstatus\G;
Innodb的两种锁:
共享锁:lockinsharemode;
排他锁:forupdate;
共享锁:主要用来确认记录是否存在,并却确保没有人对这个记录update,delete。
排它锁:对于所定记录还需要更新操作应该加排他锁,forupdate;
Setautocommit=0;
共享锁:
Session1:加共享锁,
Session1可以select,
可以update。
Session2可以select,update时,因为session1加锁了,等待
也可以加共享锁。因为session1,加锁了,session2加锁了,如果session1
Update就会死锁。
Myisam读锁是:session1可以读,不能update,session2能读update。需要等待释放锁。
Forupdate锁
Session1加forupdate,session1能够select,能够update,session2能select
,forupdate需要等到session1comit释放锁。
来自网上的总结:
Mysql优化之问题定位
2014-08-040 个评论
来源:星空下的密码
收藏我要投稿
先扯淡下,很久没有来csdn写博客了,最近在学燕18的mysql优化,并且这位老师讲的高达上还接地气,今天刚好有空可以来总结这段时间学到的东西
先上一张流程图(这张图引自燕18的教程)
当遇到一台db服务器有问题的时候,首先不是去看代码哪里有问题,想sql语句是否写,表的结构是否合理之类的问题;而是需要从宏观的角度去看哪些地方有问题
如果一台服务器硬件本身就不好,只能承受100M的io读写,如果你非要它提供的io达到200M,那么就算你怎么优化也搞不定是吧,那么我们首先需要基准测试需要安装sysbench,它提供了cpu,Io,memory,mysql等性能的测试,;
1.cpu测试
sysbench--test=cpu--cpu-max-prime=2000000run
2.io测试
3.OLTP测试
如果硬件问题不大,那么我们就需要观察mysql的状态了,一般这个状态不是一时半会儿能搞定的,都是需要写个脚本在后台记录mysql在某一个周期的压力值记录,比如是一天,一周为一个周期;查看mysql的状态命令是showstatus;这个命令返回好几百行的东西,而我们只需要关注3行1.Queries,当前已经发生过的查询(可以用两个时间段的查询数量相减得到时间段内的查询数)2.Threads_connected,当前有多少个连接连上mysql3.Threads_running,当前有几个线程正在运行通常是Threads_connected>=
Threads_running,因为连上mysql也不一定要工作,可能阻塞,挂起之类的
1.我们写个脚本每隔一秒去读取这三个数追加到mysql.status文件里面2.用ab工具模拟访问,用50个并发,发送20000个请求(这个页面的每一次请求会多次访问mysql),这样就能使上面那个脚本得到结果了ab-c50-n2000http://59.69.128.203/JudgeOnline/nyistoj/index.php/Problem/index我们来查看这个mysql.status文件的内容我们用上一行的第一个值减去下一行的第一个值就可以得到每一秒的访问mysql数量,差不多是1000+,
也可以看出基本上是有50个连接的,平均用两个线程在处理请求;可以再次写一个脚本做一下处理这样就得到每一秒的处理数量,1000多一点儿,貌似不咋好的感觉
1.访问mysql的频率很稳定(如下图),那就从mysql的其它部分优化,比如表的结构,sql语句的优化,mysql的配置,引擎的选择,索引的优化等2.mysql的访问频率呈周期性的变化(如下图),那么就是从峰值上优化;比如memcatch是否都是周期性失效,那么就可以用随机方式让失效地更加均匀,或者是让他在晚上3点左右失效,这个时候的访问量不大,到了第二天时memcatch的缓冲也基本上建立好了;或者是从业务角度优化,比如12306的放票,可以分省分时间段分批放票,这样就避免了全国各地大家集体抢票带来的超高峰值;
也可以在高峰期的时候开启慢查询,processlist等工具分析到具体的sql语句;
如果需要知道mysql这个进程对处理sql语句的整体情况,那么我们需要用到showprocesslist这个工具,这个工具主要是能够记录下来每一条sql执行的过程;我们写一个脚本抓取status,然后整体看看我们的mysql进程花的时间基本上都是在干什么;showprocesslist\G这里的Status状态可能情况比较多,不过我们主要是关注如下几个状态:1.Createtmptable;创建临时表,比如用了右连接就会新建一张临时表2.SendingData;发送数据,比如limit
1,1000;那么这样就会传送大量的数据而花费时间,可以limit小一点儿3.SortIngforGroup;正在为分组排序,这个时候就优化一般是借助复合索引4.Copyingtotmptableondesk;正在将内存的表拷贝的硬盘,主要是表太大,比如join一下就产生很大的表只能放硬盘了,避免join5.Locked;锁住数据,事务性方面优化,能不用事务就不用6.ConvertingHEAPtoMyISAM;查询的结果太大,正在想硬盘存结果;
优化就是尽量一次稍差点儿数据,比如新闻列表的读取一次少读点儿,读者很少一次性读到几百条以后;那么我们写一个脚本抓取这些status:
然后处理下mysql.process;
就能得到如下结果了:
从图中可以看出很多次都是花在了Copyingtotmptable,Sendingdata,Sortresult的次数不少,可以大致知道是业务逻辑导致需要取出的数据比较多,可以变化业务或者做缓冲服务器挡在mysql前面;
看看Copyingtotmptable;首先打开profiles;
打开监控,打开这个开关之后就能为sql的执行的每一个阶段拍快照,这样我们就能清楚得找知道sql的执行过程,具体花时间在哪个阶段了,再有针对性的优化
然后执行sql就会被记录了,
再用showprofiles得到刚才语句的id;
就能知道该语句的id是27,花了6秒多,查看id为26的具体内容:
现在我们知道了这条sql花时间在拷贝到硬盘与排序,因为我们有了三次join,而这些join的同时用了title排序,导致无法索引覆盖,从而需要回行到硬盘中的数据这样就导致了一张非常大的表而无法放入到内存中,只能放到硬盘了;然后再有针对性的优化就行了这条sql;
经过上面的几步,我们已经能逐步能能够定位我们的服务器哪个地方出了问题,是服务器本身不够强,或者是周期性的问题,或者就是自己的代码或者表结构不够好,或者是业务逻辑之类的问题,后面我们主要是针对具体的问题优化,这个是下一篇的内容了
Threads_connected:当前的客户端已经连接的数量,这个值会少于预设值,
Threads_running:记录了并发执行stmt/command的数量。正在运行。
Awk:
1.读取下一行,并把下一行赋给$0,各列赋给$1,$2,
2.用指定命令处理改行。
3.awk'/l/{printf("%s\n",$1)}'test.txt
mysqladmin-urootext|
注意:有的服务器可能需要输入密码,需要加-p参数。
mysqladmin-urootext|awk'/Queries/{printf("%d",$4)}';
mysqladmin-urootext|awk'/Threads_connected/{printf("%d",$4)}';
mysqladmin-urootext|awk'/Threads_running/{printf("%d",$4)}';
mysqladmin-urootext|awk'/Queries/{printf("%d",$4)}/Threads_connected/{printf("%,$4)}/Threads_running/{printf("%d",$4)}';
创建
createtablet_user(
idsmallint(11)auto_increment,
usernamechar(100)notnullcomment'name',
addresschar(100)notnullcomment'address',
primarykey(id)
)engine=myisamdefaultcharset=utf8;
<?php
$t=time();
set_time_limit(1000);
$myFile="/var/www/insert.sql";
$fhandler=fopen($myFile,'wb');
if($fhandler){
$sql="'zhangsan'\t'zhongguancunnandajie'";
$i=0;
while($i<1000000)//1,000,000
{
$i++;
fwrite($fhandler,$sql."\r\n");
}
echo"写脠鲁晒娄,潞脛卤拢潞",mktime()-$t;
}
loaddatalocalinfile'/var/www/insert.sql'intotablet_user(username,address);
Threads_connected当前打开的连接的数量
获取状态的脚本:
whiletrue
do
./mysqladmin-urootext|awk'/Queries/{q=$4}/Threads_connected/{c=$4}/Threads_running/{r=$4}END{printf("%d%d%d\n",q,c,r)}'>>status.txt
sleep1
done
./ab-c50-n200000
Telnet10.18.93.12211211
^]
请不要按ctrl+]
处理status.txt文件
每秒的请求数
awk'{q=last;last=$1}{printf("%d%d%d\n",$1,$2,$3)}'status.txt
./mysql-uroot-e'showprocesslist'
./mysql-uroot-e'showprocesslist\G'|grepState
./mysql-uroot-e'showprocesslist\G'|grepState|uniq|sort-rn
//查询processliststate的脚本。
#!/bin/bash
whiletrue
do
./mysql-uroot-e'showprocesslist\G'|grepState|uniq|sort-rn>>proce.txt
usleep100000
done
Sysbench对硬盘io性能的测试:
测量CPU和io能力。
./sysbench--test
./configure--prefix=/usr/local/sysbench--with-mysql-includes=/usr/local/mysql/include--with-mysql-libs=/usr/local/mysql/lib&&make&&makeinstall
./sysbench:errorwhileloadingsharedlibraries:libmysqlclient.so.18:cannotopensharedobjectfile:Nosuchfileordirectory
ln-s/usr/local/mysql/lib/libmysqlclient.so.18/lib64/libmysqlclient.so.18
Cpu性能测试:cpu测试使用64的整数,测试计算素数直到摸个最大值所需要的时间。
./sysbench--test=cpu--cpu-max-prime=20000run
Testexecutionsummary:
totaltime:58.8928s
totalnumberofevents:10000
totaltimetakenbyeventexecution:58.8342
per-requeststatistics:
min:2.02ms
avg:5.88ms
max:32.30ms
approx.95percentile:7.33ms
Threadsfairness:
events(avg/stddev):10000.0000/0.00
executiontime(avg/stddev):58.8342/0.00
我们只关心totaltime即可。
测试thread测试:
sysbench--test=threads--num-threads=64run
Testexecutionsummary:
totaltime:15.8065s
totalnumberofevents:10000
totaltimetakenbyeventexecution:1008.8293
per-requeststatistics:
min:0.66ms
avg:100.88ms
max:385.39ms
approx.95percentile:198.66ms
Threadsfairness:
events(avg/stddev):156.2500/4.99
executiontime(avg/stddev):15.7630/0.03
主要看totaltime2.4值越低说明线程调度的性能越高。
内存测试:
sysbench--test=memory--memory-block-size=8K--memory-total-size=2G--num-threads=16run
上面的语句的含义为:传输2G数据,每个block的大小为8k,我们关心的是吞吐量:
2048.00MBtransferred(8030.45MB/sec)
Numberofthreads:16
DoingmemoryoperationsspeedtestMemoryblocksize:8K
Memorytransfersize:2048M
Memoryoperationstype:writeMemoryscopetype:globalThreadsstarted!Done.
Operationsperformed:262144(1027897.89ops/sec)
2048.00MBtransferred(8030.45MB/sec)
Testexecutionsummary:
totaltime:0.2550s
totalnumberofevents:262144
totaltimetakenbyeventexecution:3.1911
per-requeststatistics:
min:0.00ms
avg:0.01ms
max:29.55ms
approx.95percentile:0.00ms
Threadsfairness:
events(avg/stddev):16384.0000/926.14
executiontime(avg/stddev):0.1994/0.02
重要参数:
每秒请求数是88.33Requests/sec,吞吐量是1.3802Mb/sec
文件io的测试:
1.生成测试数据,生成的测试数据文件要比内存大,如果文件的数据能够完全放倒内存中则
首先先创建一个数据集:
sysbench--test=fileio--file-total-size=40Gprepare
sysbench--test=fileio--file-total-size=40G--file-test-mode=rndrw\
--init-rng=on--max-time=300--max-requests=0run
Extrafileopenflags:0128files,240Mbeach30GbtotalfilesizeBlocksize16KbNumberofrandomrequestsforrandomIO:0Read/WriteratioforcombinedrandomIOtest:1.50PeriodicFSYNCenabled,callingfsync()each100requests.Callingfsync()attheendoftest,Enabled.UsingsynchronousI/OmodeDoingrandomr/wtestThreadsstarted!Timelimitexceeded,exiting...Done.
Operationsperformed:15900Read,10600Write,33842Other=60342TotalRead248.44MbWritten165.62MbTotaltransferred414.06Mb(1.3802Mb/sec)
88.33Requests/secexecuted
Testexecutionsummary:
totaltime:300.0074s
totalnumberofevents:26500
totaltimetakenbyeventexecution:164.1563
per-requeststatistics:
min:0.01ms
avg:6.19ms
max:315.51ms
approx.95percentile:15.83ms
Threadsfairness:
events(avg/stddev):26500.0000/0.00
executiontime(avg/stddev):164.1563/0.00
Otlp测试:
对mysql的oltp的测试,需要经历prepare,run,cleanup三个阶段,prepare阶段在
./sysbench--test=oltp--mysql-table-engine=innodb--mysql-user=root--db-driver=mysql--mysql-db=test--oltp-table-name=t1--oltp-table-size=3000--mysql-socket=/tmp/mysql.sock
3.6oltp
下面来看最重要也是最复杂的测试————oltp。oltp基准测试模拟了一个简单的事物处理系统的工作负载。下面的例子使用的是一张超过百万行记录的表,第一步是先生成这张表:sysbench--test=oltp--oltp-table-size=1000000--mysql-db=test\
--mysql-user=rootprepare
生成数据只需要上面这条简单的命令即可。这条命令在test数据库中新建了一个表(sbtest),并在表中插入了1000000条记录。
对于非默认安装的mysql,需要指定连接到msyql服务器的socket(my.cnf中的socket值),如下所示:
sysbench--test=oltp--oltp-table-size=1000000--mysql-user=root\--mysql-db=test--mysql-socket=/data/ntse/lmx/sysbench/var/mysqld.sock\
prepare
数据加载完成以后就可以开始测试了,这个例子采用了16个线程,测试时长为720秒:
sysbench--test=oltp--oltp-table-size=1000000--mysql-db=test\
--mysql-user=root--max-time=720--max-requests=0\
--num-threads=16--oltp-test-mode=complexrun
与插入记录时一样,如果mysql是非默认安装,还需要指定--mysql-socket的值。
Numberofthreads:16
DoingOLTPtest.RunningmixedOLTPtestUsingSpecialdistribution(12iterations,1pctofvaluesarereturnedin75pctcases)Using"BEGIN"forstartingtransactionsUsingauto_incontheidcolumnThreadsstarted!Timelimitexceeded,exiting...(lastmessagerepeated15times)Done.
OLTPteststatistics:
queriesperformed:
read:26225724
write:9366330
other:3746532
total:39338586
transactions:1873266(2601.71persec.)
deadlocks:0(0.00persec.)
read/writerequests:35592054(49432.47persec.)
otheroperations:3746532(5203.42persec.)
Testexecutionsummary:
totaltime:720.0136s
totalnumberofevents:1873266
totaltimetakenbyeventexecution:11506.8251
per-requeststatistics:
min:2.37ms
avg:6.14ms
max:400.48ms
approx.95percentile:14.90ms
Threadsfairness:
events(avg/stddev):117079.1250/275.62
executiontime(avg/stddev):719.1766/0.01
如上所示,结果中包含了相当多的信息。其中最有价值的信息如下;
·总的事务数
·每秒事务数
·时间统计信息(最小,平均,最大响应时间,以及95%百分比响应时间)
·线程公平性统计信息
最最重要的当然是每秒事务数(2601.71persec.)。
oltp测试注意事项:
1.
--max-requests--max-requests默认值为10000,如果设置了--max-requests或者使用默认值,分析结果的时候主要查看运行时间(totaltime),一般情况下,都将--max-requests赋值为0,即不限制请求数量,通过--max-time来指定测试时长,然后查看系统的每秒事务数。
2.
3.
--oltp-test-mode
--oltp-test-mode用以指定测试模式,取值有(simeple,complex,nontrx),默认是complex。不同模式会执行不同的语句。具体执行语句如下所示:
Simple这种模式只是简单的执行selec语句。
SELECTcFROMsbtestWHEREid=N
complex(Advancedtransactional)在事务中,可能包含下列语句。
Pointqueries:
SELECTcFROMsbtestWHEREid=N
Rangequeries:
SELECTcFROMsbtestWHEREidBETWEENNANDM
RangeSUM()queries:
SELECTSUM(K)FROMsbtestWHEREidBETWEENNandM
RangeORDERBYqueries:
SELECTcFROMsbtestWHEREidbetweenNandMORDERBYc
RangeDISTINCTqueries:
SELECTDISTINCTcFROMsbtestWHEREidBETWEENNandMORDERBYc
UPDATEsonindexcolumn:
UPDATEsbtestSETk=k+1WHEREid=N
UPDATEsonnon-indexcolumn:
UPDATEsbtestSETc=NWHEREid=M
DELETEqueries:
DELETEFROMsbtestWHEREid=N
INSERTqueries:
INSERTINTOsbtestVALUES(...)
nontrx(Non-transactional)这种模式包含下列SQL语句。
Pointqueries:
SELECTpadFROMsbtestWHEREid=N
UPDATEsonindexcolumn:
UPDATEsbtestSETk=k+1WHEREid=N
UPDATEsonnon-indexcolumn:
UPDATEsbtestSETc=NWHEREid=M
DELETEqueries:
DELETEFROMsbtestWHEREid=N
INSERTqueries:
INSERTINTOsbtest(k,c,pad)VALUES(N,M,S)
§
Run:对上面产生的表进行oltp测试:
./sysbench--test=oltp--mysql-table-engine=innodb--mysql-user=root--db-driver=mysql--mysql-db=test--oltp-table-name=t1--oltp-table-size=3000--mysql-socket=/tmp/mysql.sockrun;
State:
Null
Sendingdata:返回
排序之后去掉重复的:
morepro.txt|sort|uniq-c|sort-rn
Null,不用管,statistics正在进行语法的分析。
Copyingtotmptable
Sendingdata
Sortingresult
Convertingheaptomyisam:查询结果太大,内存不够大,只好往磁盘上放
Showvariableslike‘%size%’;
tmp_table_size:16777216
setglobaltmp_table_size=1024;
setsessiontmp_table_size=1024;
查看慢查询
showvariableslike'long%';
设置慢查询
setlong_query_time=5;
首先要开启profilling,
Showvariableslike‘%pro%’
setprofiling=1;
showprofiles;
setnamesutf-8;
select*fromt_userlimit10000;//查询10000条记录
showprofilecpuforquery6;
Openingtalbes//打开表
Tablelock//锁上表
Init初始化
Optimizing//优化语句
Preparing//准备
Sendindata
select*fromt_usergroupbyid%20;
慢日志查询可以知道哪些sql语句执行效率低下,通过explain可以知道sql语句的具体执行情况,索引使用等,还可以结合show命令查看执行状态。
如果explain的信息不够详细,可以使用profiling命令得到更准确的sql执行消耗资源的信息
使用连接(join)来代替子查询(sub-queries)
使用join之所以更有效率一些,是因为mysql不需要在内存中创建临时表来完成这个逻辑上需要两个步骤的查询工作。
使用enum,char,而不是varchar,使用合理的字段属性长度。
尽可能的使用notnull
固定长度的表会更快
拆分大的delete或insert
查询的列越小越好
showprocesslist详解:
-----+-------------+--------------------+-------+---------+-------+----------------------------------+----------
|Id|User|Host|db|Command|Time|State|Info
+-----+-------------+--------------------+-------+---------+-------+----------------------------------+----------
|207|root|192.168.0.20:51718|mytest|Sleep|5||NULL
|208|root|192.168.0.20:51719|mytest|Sleep|5||NULL
|220|root|192.168.0.20:51731|mytest|Query|84|Locked|
selectbookname,culture,value,typefrombookwhereid=001
Command列,显示当前连接执行的命令:一般为sleep,query,connect,time,
Time是这个状态持续的时间。State,只是语句执行的某一个状态,一个sql语句,以查询为例可能需要经过copingtotmptable,sortingresultsendingdata等状态才可以完成,info,显示这个sql语句
表的优化,和列类型的选择:
列选择优先级:整型>date,time>charvarchar>blob
2.够用就行,不要慷慨,(smalllint,varchar(n))
大的字段浪费内存影响速度。
以varchar(10),varchar(300)存储的内容相同,但是表联查时,varchar(300)要花很多的内存。
3.尽量避免用null
Null,不利于索引,要用特殊的字节来标注。
要用多余的字符来存储null。
在磁盘的占据的空间更大。
select*fromt5wherenameisnull;才能查出来。
如果允许为null,keylen会多一个字节。
每一行多一个字节,如果上亿行就更大了。
Enum是枚举类型,内部是整形。
Enum的列在内部是用整形存储的
createtablet5(
sexenum('male','female')notnulldefault'male'
)enginemyisamcharsetutf8;
Enum比varchar省空间。
性别,学历要用enum,省空间。
其实内部用整形来存的。
多列索引生效
Btree索引:
Mysiam,innodb用b-tree.
Ndb用t-tree
B-tree系统,可理解为排好序的快速查找结构。
Hash索引
在memory表里,默认的是hash索引,hash的理论的时间复杂度为
O(1):一次查询就能查到。
Hash函数计算的结果是随机的,随机放在磁盘中,
找数据的速度很快,但是随机拿数据很慢。
不能对范围进行优化。有利于范围查询。
Btree索引常见的误区:
1.where条件常用的列上都加上索引
错,例如:wherecat_id=3andprice>100;
Cat_id上,和price上都加上索引只能用上一个,因为是独立索引。
在多列建立索引,如index(a,b,c);
多列索引,必须用到第一个,否则不生效,
在多列索引上,索引需要发挥作用需要满足左前缀要求。
要从到右使用。
Index(a,b,c)
Explain:
Type:range指的是范围,
Key_len:12一个字符等于3个字节,4个全用上了12;
Key_len:6说明两个索引起了作用。
Usingfilesort;c5本身就是无序的所以要排一下序。
一般而言,分组统计先按分组字段有序排列。
用到临时表排列,在临时表中聚合运算。
与myisam重要的区别是:虽然都是b+tree,但是innodb表数据本身就是按照b
+tree组织的索引结构,这棵树的叶节点data保存了完整记录,这个索引的
Key就是主键。
myisam和innodb索引实现的不同
2012-11-30
作者:wangjj20
MyISAM引擎使用B+Tree作为索引结构,叶节点的data域存放的是数据记录的地址。下图是MyISAM索引的原理图:
这里设表一共有三列,假设我们以Col1为主键,则上图是一个MyISAM表的主索引(Primarykey)示意。可以看出MyISAM的索引文件仅仅保存数据记录的地址。在MyISAM中,主索引和辅助索引(Secondarykey)在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复。如果我们在Col2上建立一个辅助索引,则此索引的结构如下图所示:
同样也是一颗B+Tree,data域保存数据记录的地址。因此,MyISAM中索引检索的
MyISAM的索引方式也叫做“非聚集”的,之所以这么称呼是为了与InnoDB的聚集索引区分。
InnoDB索引实现
虽然InnoDB也使用B+Tree作为索引结构,但具体实现方式却与MyISAM截然不同。
第一个重大区别是InnoDB的数据文件本身就是索引文件。从上文知道,MyISAM索引文件和数据文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。
上图是InnoDB主索引(同时也是数据文件)的示意图,可以看到叶节点包含了完整的数据记录。这种索引叫做聚集索引。因为InnoDB的数据文件本身要按主键聚集,所以InnoDB要求表必须有主键(MyISAM可以没有),如果没有显式指定,则MySQL
第二个与MyISAM索引的不同是InnoDB的辅助索引data域存储相应记录主键的值而不是地址。换句话说,InnoDB的所有辅助索引都引用主键作为data域。例如,下图为定义在Col3上的一个辅助索引:
这里以英文字符的ASCII码作为比较准则。聚集索引这种实现方式使得按主键的搜索十分高效,但是辅助索引搜索需要检索两遍索引:首先检索辅助索引获得主键,然后用主键到主索引中检索获得记录。
了解不同存储引擎的索引实现方式对于正确使用和优化索引都非常有帮助,例如知道了InnoDB的索引实现后,就很容易明白为什么不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。再例如,用非单调的字段作为主键在InnoDB中不是个好主意,因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。
辅助索引需要检测两遍索引,辅助索引的叶子节点存放了数据,和主键的值,首先要
找到数据,然后根据数据的主键,查找主键索引,找到记录。
Myisam的辅助索引还是地址。
Myisam检索索引算法,按照b+tree算法搜索索引,如果指定的key存在,则取出data域的值(地址),读取相应的记录。
Myisam和innodb索引的区别。
Innodb的(辅助索引)指向对主键的应用,
Myisam的次索引和主索引都指向物理行。
聚簇索引:
直接
聚簇索引分块特点:
节点会分裂。
对聚簇索引比较严重。
对于myisam,节点存储的对物理行的地址,又缓存在
内存中,分裂较快。
对于innodb,因为节点下存储了行数据,因此在分裂的时候,还需要移动行数据。
对于innodb而言,因为节点有数据文件,节点分裂比较慢,对于innodb主键,尽量
是整型,而且是递增的,如果是无规律的的数据,将会产生页的分裂。降低效率。
可以查看innodb_page_written1471
这个值在随机插入要比顺序插入要搞得的多。
Showstatus;
Innodb_insert_block;
索引覆盖:
由索引-》磁盘取数据
回行
索引的速度非常快,因为索引是一个特别快的
要缓存在内存中。
查索引是快的,回行是慢的。
回行的时候,需要到磁盘上去。
//username索引
Selectusername,agefromtablewhereusername=’li’;
Selectusernamefromtablewhereusername=’li’//不需要回行,查询速度非常快
这种叫做索引覆盖。
索引覆盖是指,如果查询的列恰好是索引的一部分,那么查询只需要在
索引文件上进行,不需要回行到磁盘上在找数据。
这种查询速度非常快。
清除查询缓存:resetquerycache;
Useindex出现了索引覆盖。
实例:
CreatetableA{
idvarchar(64)primarykey.
Verint;
}
在id,ver上有联合索引
10000条数据
SelectidfromAorderbyid特别慢
SelectidfromAorderbyid,ver就非常快。
Innodb中,数据记录一定,innodb块大小1024,
如果叶子的数据大,1024/1024=1条数据,10000条数据,
就需要10000个块,查找时要横跨很多块。
是innodb引擎,
有多个比较长的列
1.聚簇索引,导致id排序时,要跨过很多块。
2.有比较长的列,导致块比较多。
1.如果是myisam不存在这个问题。
Id.ver联合索引
Id.ver
31
$conn=mysql_connect('localhost','root');
mysql_query('usetest',$conn);
mysql_query('setnamesutf8',$conn);
$str=str_repeat('m',3000);
for($i=0;$i<=10000;$i++)
{
$id=dechex($i);
$sql=sprintf("insertintot10values('%s',%d,'%s','%s','%s')",$i,$i,$str,$str,$str);
mysql_query($sql,$conn);
}
Showindexfromt10\G;//查看索引
altertablet10addindexidver(id,ver);//添加复合索引。
Usingindex是表示索引覆盖。
Mysql清除表的数据:
Truncatetableteacher;
Primarykey主键索引必须是int型,如果是char型的
explainSelectnamefromt12whereid=3\G
用不到索引。
改成int型,就可以使用主键索引。
高效的索引:
1.频繁度
2.索引覆盖
3.区分度高
4.长度小
长度小,建立的块就少。
区分度,长度必须达到一种平衡。
//查到最长的记录
select*fromt12orderbylength(name)desclimit1;
区分度:
SelectdistinctnamefromA
返回A中不同的值
selectleft(name,2)fromt12;
返回从左截取2个字符的值。
selectcount(distinctleft(name,2))/count(*)fromt12;
算出摸个字段重复的概率。
Varchar类型会有变化,所以key_len14//3*4+2//一个utf8字符占3个字节。
altertablepersonaddindexname(name(15));
altertablepersonmodifynamechar(300);//改变列的长度。
对于前缀不易区分的列,
1.可以倒过来查
2.违哈希。
createtablep1(
idintprimarykeyauto_incrementnotnull,
namechar(100)notnulldefault''
)engineinnodbcharsetutf8;
insertintourl(url)values("www.baidu.com");//单独给摸一个列赋值。
altertableppaddcrcurlintunsignednotnulldefualt0;
Selectcrc32(‘a’);//将a字符转化为2的32次方的整数。
altertableurladdcrcurlintunsignednotnull;//增加一个列
updateurlsetcrcurl=crc32(url);
selectcrc32('www.baidu.com');
explainselect*fromurlwherecrcurl=387695885\G;//用crc32构造哈希列
把字符串的列,构成整型,来降低索引的长度。
对于一般系统而言区别度到达0.1,索引的性能就可以接受。
所以建索引中指定索引长度。
altertablet12addindexname(name(4));//指定建立索引长度。
大数据量的分页效果
Selectfileldfromtablelimit30,10;
比如每页显示perpage条,当前是n页
Limit(n-1)*perpage,N;
翻过了几十万条,数据很慢,
怎么优化:
1.从业务上去优化:
Selectid,namefromt12limit10000,10;
0.02sec
Selectid,namefromt12limit5000000,10
4.4sec
为什么limitm,n,m越大时查询越慢?
要逐行查找,用limitoffset时不是先跳过在查询,
而是先查询在跳过。
就是要把100w行取出来,让后再跳过前100w行。
2个办法用索引查询:
Selectid,namefromt12whereid>100000limit10;
这次为什么快,因为id主键索引,迅速找到10000,让后去10
有个问题,要求前提数据没删过。
一般来说,大网站数据部物理删除,只做逻辑删除,
比如is_delete=1做标记,显示是不要显示。
延迟关联:
1.让索引覆盖先快一些:
explainSelectidfromt12limit5000000,10\G.
如果有name字段要回行,要在表中查。
Selectid,namefromt12innerjoin(selectidfromt12limit50000,10)astmpont12.id=tmp.id;
1.34sec
selectt12.id,namefromt12innerjoin(selectidfromt12limit10000,10)astmpont12.id=tmp.id;
Resetquerycache;
索引与排序:
1.对于覆盖索引,直接在索引上查询时,就是有顺序的。
2.先取出数据形成临时表,叫做filesort,(文件排序,但文件可能在磁盘上吗,也可能在内存中)。
我们争取目标--取出来数据就是有序的,利用索引来排序。
索引本身就是排序的。
Filesort取出结果在排序,即取出结果在排序。
比如good表中(cat_id,shop_price)组成联合索引。
Wherecat_id=Norderbyshop_price可以利用索引排上序。
Selectgood_id,cat_idshop_pricefromgoodsorderbyshop_price;
Usingwhere安装shop_price索引取出结果,本身就是有序的
重复索引
一个列上建了两个索引。(毫无意义,拖慢你的增删改的速度)
冗余索引:是可以的。
索引碎片的修复:
optimizetableinfo;
Truncatetableinfo;
resetquerycache;
表大的时候非常耗资源。
多列索引。
复合索引:在两个以上的列建索引,就是复合索引。
多列索引可以理解成合并多列的值创建的有序的数组,当查询条件包含lastname和fisetname
Select*fromtestwherelast_name=’ss’andfirst_name=’li’
Sql先会过滤lastname复合条件的记录,在基础上在过滤firstname符合条件的记录。
如果分别在lastname和firstname创建两个索引,mysql处理方式就不一样了。他只会选择一检索能力最强的检索,另外一个用不上。
主要因为,分别创建时创建了两个索引,不知道用那个
复合索引还是一个索引,
以下都能用上多列索引:
以下用不上复合索引
慢,等待时间:表被锁住了,看看连接数啊,看
执行时间:1.取了多少数据
2扫描了多少行
Explain:
Simple(不含子查询)primary(含子查询)
Table:表名,有可能是别名,
Derived如from子查询时
Null
如select1+2;
key_len:
Type:是查询方式,
All,全表扫描,
Index扫描索引节点。扫描的行数还是过多。
All实在磁盘上扫描所有行,index沿着索引文件扫描所有行
explainselectidfrominfowhereidlike'%'\G;
Range:范围
explainselectidfrominfowhereid>3\G;
Range是摸个范围的扫描。
Ref:引用,索引确定摸个位置。
eq_ref;
Const:效率更高。
explainselectidfrominfowhereid=3\G;
System,null最快。
explainselectnamefrominfowhereidin(1,2)\G;
误区:explainselectnamefrominfowhereidin(select)
explainselectnamefrominfowhereidin(selectidfromgood_id)\G;
Info表中的每一行取出id,去在good_id表对比,info表需要全表扫描。
explainselectnamefrominfowhereidin(1,2)\G;
不一样。
explainselectinfo.id,namefrominfoinnerjoin(selectidfromgood_id)astmpontmp.id=info.id\G;
Exsits应用:
selectid,namefrominfoascwhereexists(select*fromgood_idasgwherec.id=g.id);
explainselecti.id,i.namefrominfoasiinnerjoingood_idasgoni.id=g.idgroupbyid\G;
explainSelectcat_id,cat_namefromcategroryascwhereexists(select*fromgoodsasgwhereg.cat_id=c.cat_id)\G;
不查,
少查,
快查。
1.关于Max,min优化。
explainselectmax(id)frominfo\G;
***************************1.row***************************
id:1
select_type:SIMPLE
table:NULL
type:NULL
possible_keys:NULL
key:NULL
key_len:NULL
ref:NULL
rows:NULL
Extra:Selecttablesoptimizedaway
1rowinset(0.00sec)
Selectidfromit_areause(primary)wherepid=1087limit1;
Selectmin(id)fromit_areawherepid=1087;
它的工作是先把整张表都找出来,在找到where1087的行,都找到最小的。
加了where就不快了,
给pid加索引。
Count优化。
Selectcount(*)frominfo;
Selectcount(*)frominfoid<=100;
Select(selectcount(*)frominfo)-(selectcount(*)frominfowhereid<100)ascnt;
3.groupby用于统计,而不用筛选数据
以及用索引来避免临时表和文件排序
Union优化,
Unionall效率高。
数据库主从集群配置。
数据库的复制技术。
大致原理:
主服务器建立binlog,授权复制账号
从服务器建立relaylog利用复制账号来监听服务器的日志。
#binarylog
log-bin=mysql-bin
#binlogformat
binlog_format=mixed
Slaveserver:
relay-log=mysql-relay
建立mysql服务器的id
#server-id
server-id=200
建立授权账号:
Mysql-uroot
showgrants;
Grantreplicationclient,replicationslaveon*.*to
grantreplicationclient,replicationslaveon*.*to'repl'@'10.18.93.120'identifiedby'repl';
问题:
TheMySQLserverisrunningwiththe--skip-grant-tablesoptionsoitcannotexecutethisstatement?
解决:
flushprivileges;
Slave:
给你一个账号,去你的master
Showmasterstatus;
/*
Changemasterto
master_host=’192.168.1.199’
Master_user=’repl’
Master_password=’repl’
Master_log_file=’mysql-bin.00001’
Master_log_pos=268;
*/
changemasterto
master_host='10.18.93.122',
master_log_file='mysql-bin.000029',
master_log_pos=7275,
master_user='repl',
master_password='repl';
Showslavestatus;
Resetslave;
Startslave;
问题:
Couldnotinitializemasterinfostructure;moreerrormessagescanbefoundintheMySQLerrorlog?
解决:
slavestop;
Resetslave;
changemasterto
master_host='10.18.93.121',
master_log_file='mysql-bin.000029',
master_log_pos=107,
master_user='repl',
master_password='repl';
Showslavestatus;
createtablemsss(
->idintprimarykeyauto_incrementnotnull,
->namechar(10)notnulldefault''
->)enginemyisamcharsetutf8;
常用语句:
Showmasterstatus//查看master状态
Showslavestatus
Resetslave//重置slave
Startslave//启动slave一旦开启就监听master状态
Stopslave
日志格式:
Statement语句级的
Row
Mixed
以insertintotablevalues()为例,
影响1行,对于其他行没有影响,就用row格式
的比较合理。
直接复制上1行的新增变化。
以updatetableage=21wherename=sss
这个情况一般只是影响1行,用row也比较合适
Updatetablesetsalary=salary+100
这个语句带来的影响是针对没一行的,因此磁盘上很多发生变化,
适用于statement格式日志。
2种日志,各有各的不同,mysql提供了mixed类型。
主主复制:
相当于分片加复制。
Master:
server-id=121
log-bin=mysql-bin
binlog_format=mixed
#添加relaylog
relay-log=mysql-relay
grantreplicationclient,replicationslaveon*.*to'repl'@'10.18.93.120'identifiedby'repl';
changemasterto
master_host='10.18.93.124',
master_log_file='mysql-bin.000001',
master_log_pos=361,
master_user='repl',
master_password='repl';
Startslave
Slave:
server-id=120
log-bin=mysql-bin
binlog-format=mixed
relay-log=mysql-relay
grantreplicationclient,replicationslaveon*.*to'repl'@'10.18.93.121'identifiedby'repl';
changemasterto
master_host='10.18.93.121',
master_log_file='mysql-bin.000032',
master_log_pos=3720,
master_user='repl',
master_password='repl';
Startslave
主主复制时,主键冲突问题解决?
让一台服务器1,3,5,7
另一台服务器2,4,6,8
Setsessionauto_increment_increment=2//每步增长2
Setsessionauto_increment_offset=1//从1开始增长
Setglobalauto_increment_increment=2//每步增长2
Setglobalauto_increment_offset=1
Setsessionauto_increment_increment=2;
Setsessionauto_increment_offset=2;
Setglobalauto_increment_increment=2;
Setglobalauto_increment_offset=2;
如果后期加服务器,这个办法就有限制了,
我们可以再业务上来解决。
比如:
设计一个
序列每次访问生成递增或第减。
以redes为例,我们构建一个global:userid
每次php插入mysql前,先incr-》global:userid得到一个不重复的userid。
被动主主复制:
/etc/my.cnf
read-only=1
如果有问题可以可以快速切换。
路由sql语句:
Mysql-proxy
原理:
能读写分离,
还能做负载均衡
./bin/mysql-proxy--help-all
Proxy在那个端口,//默认4040
Proxy代理了那几台服务器:--proxy-backend-address
./bin/mysql-proxy-P10.18.93.121:4040--proxy-backend-addresses=10.18.93.121:3306--proxy-backend-addresses=10.18.93.120:3306
mysql-h10.18.93.120-uroot-p-P4040
4条语句,没有给我们做均衡:
均衡不是语句带来的均衡,容易带来不一致。
Update
Delete
以连接为单位的均衡。
所以多建几个连接:
均衡是以连接为单位的。
读写分离:
A:mysql服务器:10.18.93.121
Bmysql服务器:10.18.93.120
C:mysql-proxy服务器:10.18.93.124
分别在A,B服务器上建个账号:
grantallon*.*to'root'@'10.18.93.120'identifiedby'';
建立10.18.93.124访问服务器的权限。
在客户端中:mysql-h10.18.93.124-uroot-p-P4040
Proxy服务器会直接拿10.18.93.121服务器写,
去那10.18.93.120去读
Mysql-proxy下载:
./bin/mysql-proxy--proxy-backend-addresses=10.18.93.120:3306--
Proxy-read-only-backend-addresses=10.18.93.121:3306--proxy-lua-script=/usr/local/mysql-proxy/share/doc/mysql-proxy/rw-spliting.lua
rw-spliting.lua这个脚本专门用来分析你的sql语句。分成不同的类型做判断。
简写:
./bin/mysql-proxy-b10.18.93.121:3306-r10.18.93.120:3306-s/usr/local/mysql-proxy/share/doc/mysql-proxy/rw-splitting.lua
在mysql-proxy/share/doc/mysql-proxy/rw-splitting.lua下找到:
ifnotproxy.global.config.rwsplitthen
proxy.global.config.rwsplit={
min_idle_connections=1,
max_idle_connections=2,
is_debug=false
}
End
表分区的概念:
createtabletop(
->tidintprimarykeyauto_increment,
->titlechar(20)notnulldefault''
->)enginemyisamcharsetutf8
->partitionbyrange(tid)(
->partitiont0valueslessthan(10),
->partitiont1valueslessthan(20),
->partitiont2valueslessthan(MAXVALUE)
->);
createtabletop(tidintprimarykeyauto_increment,titlechar(20)notnulldefault'')enginemyisamcharsetutf8partitionbyrange(tid)(partitiont0valueslessthan(10),partitiont1valueslessthan(20),partitiont2valueslessthan(MAXVALUE));
insertintotop(tid,title)values(11,'a');
好处是:打开的线程数多,文件独立,不会死锁。
Createtableuser(
aidint,
unamechar(6)
)enginemyisamcharsetutf8;
partitionbylist(aid)(
Partitionbjvaluesin(1);
Partitionhbvaluesin(2),
Partitionxsvaluesin(3),
Partitionahvaluesin(4)
);
Insertintouser(uname,aid)values(‘poluy’,1);
Insertintouser(uname,aid)values(‘lily’,2);
原子性:事情要不全做完,要不干脆不做,不可分割的。
即:张三减300李四+300insert银行流水,这3个操作,必须都完成,或者都不做。
一致性:钱的逻辑性是一致的,事物前后的数据保持业务的合理一致。
汇款前张三的余额+李四的余额==汇款后张三的余额+李四的余
如果用户有42亿,给42亿加100就会溢出了,那边减100没事
或者,就有200元,200-300=-100是不行的
隔离性;
持久性:事务一旦发生,不能取消。
事务使用:
Starttranscation
执行语句
Commit/callback
隔离性:
Starttransaction;
Updateaccountsetmoney=money+100whereuname=’li’;
Commit;
Setsessiontransactionisolationlevelreaduncommitted;
Showvariableslike‘%isolation%’
Setsessiontransactionisolationlevelreadcommitted
Session1:starttransactionsession2starttransaction
这是两个事务进行处理:
可重复读:
Setsessiontransactionisolationlevelrepeatableread;
查询系统的隔离级别:
select@@global.tx_isolation,@@tx_isolation;
设置隔离级别:
setglobaltx_isolation='read-uncommitted';
隔离级别的分类:
(readuncommitted)读取没有提交的内容,也叫脏读。
(read-committed)读取提交的内容
(repeatable-read)可重复读,会导致幻读。
Serializable(可串行化)
Mysql锁机制:
Myisam表锁:
可以通过showstatuslike‘table%’;
变量来分析系统上表锁定的争夺。
如果tableLOCKS_WAITED比较高则说明存在着严重的表锁争用情况。
Session1session2
Locktablepersonwrite;
Insertintoperson(name)values(‘guojia’);select*fromperson;
阻塞,
Unlocktables;等待
Session2释放。
加读锁:
Locktablepersonread;
总结:
Session加读锁时,session1不能更新,只能查询,session
2能查询,更新时需要等待session1释放锁。不能查询其他表。(读的时候,其他session能读,但是不能写);
Session加写锁时,session1能读,能写,session1,不能读,也不能写,需要session1,释放才可以。
并发插入:
Session1加上读锁,该线程可以对表进行查询操作,但不能进行更新操作,其他表
虽然不能对表进行删除更新操作,却可以对表进行并发插入操作。
Myisam表调度:
写锁的优先级最高,即使读锁请求在写锁请求队列前。
当时可以降低写锁的优先级:
Setlow_priority_updates=1;使连续发出的更新请求的优先级降低。
可以设置max_write_lock_count一个合适的值,当一个表的读锁达到这个值后,mysql
会暂时将写请求的优先级降低,给读进程机会。
Showvariableslike‘max_write%’;
获得innodb系统来分析系统上行锁的争夺状态:
Showstatuslike‘innodb_row_lock%’
如果current_waits比较高,
Innodb_monitor查看状态:
Createtableinnodb_monitor(aint)engine=innodb;
Showinnodbstatus\G;
Innodb的两种锁:
共享锁:lockinsharemode;
排他锁:forupdate;
共享锁:主要用来确认记录是否存在,并却确保没有人对这个记录update,delete。
排它锁:对于所定记录还需要更新操作应该加排他锁,forupdate;
Setautocommit=0;
共享锁:
Session1:加共享锁,
Session1可以select,
可以update。
Session2可以select,update时,因为session1加锁了,等待
也可以加共享锁。因为session1,加锁了,session2加锁了,如果session1
Update就会死锁。
Myisam读锁是:session1可以读,不能update,session2能读update。需要等待释放锁。
Forupdate锁
Session1加forupdate,session1能够select,能够update,session2能select
,forupdate需要等到session1comit释放锁。
来自网上的总结:
Mysql优化之问题定位
2014-08-04
来源:星空下的密码
Mysql优化之问题定位
先扯淡下,很久没有来csdn写博客了,最近在学燕18的先上一张流程图(这张图引自燕18的教程)
当遇到一台db服务器有问题的时候,首先不是去看代码哪里有问题,想sql语句是否写,表的结构是否合理之类的问题;而是需要从宏观的角度去看哪些地方有问题
第一步找出服务器问题所在,是否是硬件有瓶颈
如果一台服务器硬件本身就不好,只能承受100M的io读写,如果你非要它提供的io达到200M,那么就算你怎么优化也搞不定是吧,那么我们首先需要基准测试需要安装sysbench,它提供了cpu,Io,memory,mysql等性能的测试,;1.cpu测试
sysbench--test=cpu--cpu-max-prime=2000000run
2.io测试
sysbench--test=fileio--num-threads=16--file-total-size=3G--file-test-mode=rndrwprepare
sysbench--test=fileio--num-threads=16--file-total-size=3G--file-test-mode=rndrwrun
sysbench--test=fileio--num-threads=16--file-total-size=3G--file-test-mode=rndrwcleanup
3.OLTP测试
sysbench--test=oltp--mysql-table-engine=myisam--oltp-table-size=1000000--mysql-socket=/tmp/mysql.sock--mysql-user=test--mysql-host=localhost--mysql-password=testprepare
通过这些测试之后差不过也能知道自己服务器的能力了,如果发现服务器的性能不错,但是依然不能满足用户的需求,那么就只能是软件方面的问题了,就需要定位到底是哪一块有问题
第二步,观察mysql在某段时间的连接状态,处理状态
如果硬件问题不大,那么我们就需要观察mysql的状态了,一般这个状态不是一时半会儿能搞定的,都是需要写个脚本在后台记录mysql在某一个周期的压力值记录,比如是一天,一周为一个周期;查看mysql的状态命令是showstatus;这个命令返回好几百行的东西,而我们只需要关注3行1.Queries,当前已经发生过的查询(可以用两个时间段的查询数量相减得到时间段内的查询数)2.Threads_connected,当前有多少个连接连上mysql3.Threads_running,当前有几个线程正在运行通常是Threads_connected>=Threads_running,因为连上mysql也不一定要工作,可能阻塞,挂起之类的
获取结果
1.我们写个脚本每隔一秒去读取这三个数追加到mysql.status文件里面2.用ab工具模拟访问,用50个并发,发送20000个请求(这个页面的每一次请求会多次访问mysql),这样就能使上面那个脚本得到结果了ab-c50-n2000http://59.69.128.203/JudgeOnline/nyistoj/index.php/Problem/index我们来查看这个mysql.status文件的内容我们用上一行的第一个值减去下一行的第一个值就可以得到每一秒的访问mysql数量,差不多是1000+,也可以看出基本上是有50个连接的,平均用两个线程在处理请求;可以再次写一个脚本做一下处理这样就得到每一秒的处理数量,1000多一点儿,貌似不咋好的感觉
结果分析
1.访问mysql的频率很稳定(如下图),那就从mysql的其它部分优化,比如表的结构,sql语句的优化,mysql的配置,引擎的选择,索引的优化等2.mysql的访问频率呈周期性的变化(如下图),那么就是从峰值上优化;比如memcatch是否都是周期性失效,那么就可以用随机方式让失效地更加均匀,或者是让他在晚上3点左右失效,这个时候的访问量不大,到了第二天时memcatch的缓冲也基本上建立好了;或者是从业务角度优化,比如12306的放票,可以分省分时间段分批放票,这样就避免了全国各地大家集体抢票带来的超高峰值;也可以在高峰期的时候开启慢查询,processlist等工具分析到具体的sql语句;
三.查看mysql进程的状态
如果需要知道mysql这个进程对处理sql语句的整体情况,那么我们需要用到showprocesslist这个工具,这个工具主要是能够记录下来每一条sql执行的过程;我们写一个脚本抓取status,然后整体看看我们的mysql进程花的时间基本上都是在干什么;showprocesslist\G这里的Status状态可能情况比较多,不过我们主要是关注如下几个状态:1.Createtmptable;创建临时表,比如用了右连接就会新建一张临时表2.SendingData;发送数据,比如limit1,1000;那么这样就会传送大量的数据而花费时间,可以limit小一点儿3.SortIngforGroup;正在为分组排序,这个时候就优化一般是借助复合索引4.Copyingtotmptableondesk;正在将内存的表拷贝的硬盘,主要是表太大,比如join一下就产生很大的表只能放硬盘了,避免join5.Locked;锁住数据,事务性方面优化,能不用事务就不用6.ConvertingHEAPtoMyISAM;查询的结果太大,正在想硬盘存结果;
优化就是尽量一次稍差点儿数据,比如新闻列表的读取一次少读点儿,读者很少一次性读到几百条以后;那么我们写一个脚本抓取这些status:
然后处理下mysql.process;
就能得到如下结果了:
从图中可以看出很多次都是花在了Copyingtotmptable,Sendingdata,Sortresult的次数不少,可以大致知道是业务逻辑导致需要取出的数据比较多,可以变化业务或者做缓冲服务器挡在mysql前面;
看看Copyingtotmptable;首先打开profiles;
打开监控,打开这个开关之后就能为sql的执行的每一个阶段拍快照,这样我们就能清楚得找知道sql的执行过程,具体花时间在哪个阶段了,再有针对性的优化
然后执行sql就会被记录了,
再用showprofiles得到刚才语句的id;
就能知道该语句的id是27,花了6秒多,查看id为26的具体内容:
现在我们知道了这条sql花时间在拷贝到硬盘与排序,因为我们有了三次join,而这些join的同时用了title排序,导致无法索引覆盖,从而需要回行到硬盘中的数据这样就导致了一张非常大的表而无法放入到内存中,只能放到硬盘了;然后再有针对性的优化就行了这条sql;
总结:
经过上面的几步,我们已经能逐步能能够定位我们的服务器哪个地方出了问题,是服务器本身不够强,或者是周期性的问题,或者就是自己的代码或者表结构不够好,或者是业务逻辑之类的问题,后面我们主要是针对具体的问题优化,这个是下一篇的内容了
相关文章推荐
- MySQL远程访问
- (转)MySQL索引原理及慢查询优化
- mysql Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock’
- MySQL时间段查询
- mysql 数据从一个服务器复制到另一个服务器
- ubuntu 下Mysql导入导出文件报错:ERROR 1290 (HY000): --secure-file-priv
- 赶集mysql军规
- MySQL根据根据经纬度查询距离
- MySQL根据根据经纬度查询距离
- 使用Navicat定时备份mysql数据库和创建报表并邮件自动发送 推荐
- mysql查找附近优化思路
- mysql下mysqladmin日常管理命令总结
- mysql 多行数据转换xml
- mysql表名忽略大小写问题记录
- mysql 语句的索引和优化
- mysql存储过程limit入参问题
- 服务器卡顿,mysql卡顿
- MySQL扩容
- mysql获取某个表的所有字段名
- MySQL 用户权限详细汇总