mysql优化大的思路:
查看状态:
mysql> show status;
Queries | 3
Threads_connected | 1
Threads_running | 1
[root@localhost ~]# mysql -S /tmp/mysql3306.sock -e “show processlist\G”|grep State:|sort|uniq -c|sort -rn
以下几种状态要注意:
converting HEAP to Myisam 查询结构太大,把结果放在磁盘
create tmp table 创建临时表(如group时储存中间结果)
Copying to tmp table on disk 把内存临时表复制到磁盘
locked 被其他查询锁住
logging slow query 记录慢查询
查看size大小
mysql> show variables like ‘%size%’;
其中
tmp_table_size | 268435456 为
设置为1024
mysql> set global tmp_table_size=1024;
设置本次会话生效:
mysql> set session tmp_table_size=1024;
打开mysql自带性能分析
mysql> show variables like ‘%profiling%’;
开始跑测试数据:
mysql> select * from customer where c_id<10000;
查看结果:
mysql> show profiles;
±---------±-----------±----------------------------------------+
| Query_ID | Duration | Query |
±---------±-----------±----------------------------------------+
| 1 | 0.00296500 | show variables like ‘%profiling%’ |
| 2 | 0.00039850 | show tables |
| 3 | 0.00053700 | select * from customer limit 10 |
| 4 | 3.23267150 | select * from customer where c_id<10000 |
±---------±-----------±----------------------------------------+
4 rows in set, 1 warning (0.00 sec)
mysql> show profile for query 4;
±---------------------±---------+
| Status | Duration |
±---------------------±---------+
| starting | 0.000514 |
| checking permissions | 0.000016 |
| Opening tables | 0.000016 |
| init | 0.000080 |
| System lock | 0.000014 |
| optimizing | 0.000057 |
| statistics | 0.000020 |
| preparing | 0.000013 |
| executing | 0.000003 |
| Sending data | 3.231762 |
| end | 0.000013 |
| query end | 0.000007 |
| closing tables | 0.000008 |
| freeing items | 0.000103 |
| logging slow query | 0.000036 |
| cleaning up | 0.000011 |
±---------------------±---------+
16 rows in set, 1 warning (0.00 sec)
索引优化策略:
B-tree索引
注:名叫b-tree索引,大的方面看,都用的平衡树,但具体的实现上,各引擎稍有不同,比如,严格的说,NBD引擎,使用的是T-tree。
myisam,innodb默认使用B-tree索引,B-tree可理解为“排好序的快速查找结构”
hash索引:
在memory表里,默认是hash索引,hash的理论查询时间复杂度为O(1)
疑问:既然hash的查找如此高效,为什么不都用hash索引?
1.hash函数计算后的结果,是随机的,如果是在磁盘上放置数据,比如主键ID为例,那么随着id的增长,id对应的行,在磁盘上随机放置,
2.无法对范围查询进行优化
3.无法利用前缀索引,比如在b-tree中,field列的值 helloworld,并加索引查询xx=helloworld 自然可以利用索引,xx=hello,也可以利用索引,(左前缀索引)因为hash(‘helloworld’)和hash(‘hello’),两者的关系仍为随机
4.排序也无法优化
5.必须回行,就是说 通过索引拿到数据位置,必须回到表中取数据。
一、B-tree索引的常见误区
1、在where条件常用的列上都加上列索引
例:where cat_id=3 and price>100; 查询第三个栏目,100元以上的商品
误:cat_id上和price 上都加上索引
错:只能用上cat_id或price索引,因为是独立的索引,同时只能用上1个
二、在多列上建立索引后,查询哪个列,索引都将发挥作用
误:多列索引上,索引发挥作用,需要满足左前缀要求。
以index(a,b,c)为例
语句 索引是否发挥作用
where a=3 是
where a=3 and b=5 是
where a=3 and b=5 and c=4 是
where b=3 或 where c=4 否
where a=3 and c=4 a列能发挥索引,c不能
where a=3 and b>10 and c=7 a能利用索引,b能利用,c不能利用
同上 where a=3 and b like ‘xxxx%’ and c=7 a能利用索引,b能利用索引,c不能用
索引问题:
假设某个表有一个联合索引(c1,c2,c3,c4)
where c1=x and c2=x and c4>x and c3=x
答案:全部用上了,虽然看起来规则颠倒,但是mysql自身会进行sql语句优化。不改变本意的情况下
where c1=x and c2=x and c4=x order by c3
where c1=x and c4=x order by c3,c2
where c1=?and c5=? order by c2,c3
where c1=? and c2=? and c5=? order by c2,c3
接下来我建表来帮你解答一下哈定义表t,有c1到c5 5个字段,特别说明一下 字段类型都是定长char(1)类型,并且非空,字符集是utf8(与计算索引使用字节数有关)
QQ截图20131109233034.png
创建联合索引
QQ截图20131109234601.png
插入2条测试数据
QQ截图20131109234733.png
好,我们先来看A选项
QQ截图20131109234954.png
我们看解析A这条sql的结果,与索引有关的主要是possible_keys,key,key_len这三项,
possible_keys是指可能会用到的索引,key是当前sql使用到的索引,key_len是索引的使用字节数
key的值是c1234表示联合索引用上了,那是不是c1,c2,c3,c4全用上了咧,我们得从key_len分析一下
因为字段类型是char(1),字符集是utf8,所以每个字段的key_len 是 1*3=3,key_len现在等于12表示c1,c2,c3,c4这四个字段都用上了索引,(如果字段类型是null,那单个字段的索引字节数需要 +1,如果字段类型为非定长类型,比如varchar,那字节数需要再 +2,这里方便理解,统一定义成了定长char)
再接着看B这条sql语句
QQ截图20131110000049.png
我们看到key=c1234,表示B使用了联合索引,key_len=6表示有两个字段使用了索引,这两个字段就是C1和c2,这个sql里面有一个order by c3,order by不能使用索引,但是却利用了索引,为什么这么说咧,先看C这条sql
QQ截图20131110000731.png
key=c1234,表示B使用了联合索引,key_len=3表示有1个字段使用了索引,这个字段就是C1,与B语句不一样的是 Extra的值
C语句里面使用了临时表(Using temporary) 和 排序(filesort),
因为组合索引是需要按顺序执行的,比如c1234组合索引,要想在c2上使用索引,必须先在c1上使用索引,要想在c3上使用索引,必须先在c2上使用索引,依此。。
回到B语句中,因为c2字段已经使用了索引,所以在order by c3的时候 c3其实在索引表里面已经是排好序的了,不需要建临时表,不需要再排序,所以说其实他利用上了索引
而C语句中,group by 的顺序是先c3,再c2,在对c3进行group by的时候,c2字段上的索引并没用使用,所以索引在这里就断了,只用上了c1一个字段的索引
D语句c1字段使用了索引,c2,c3字段在order by中是顺序执行 所以也利用了索引
QQ截图20131110002133.png
E语句c1和c2使用了索引,c3在order by中利用了索引
QQ截图20131110002358.png
计算平均值sql:
select cat_id,avg(shop_price) from ecs_goods group by cat_id;
清理缓存sql:
mysql> reset query cache;
索引类型:
聚簇索引
索引覆盖
一、聚簇索引区别
innodb与myisam区别
innodb的次索引指向对主键的引用(数据聚簇在一起)
myisam的次索引和主索引都指向磁盘物理行(数据文件)
innodb的主索引文件上 直接存放该行数据,称为聚簇索引,次索引指向对主键的引用
myisam中,主索引和次索引,都指向对物理行
注意:innodb来说:
1:主键索引,即存储索引值,又在叶子中存储行的数据
2:如果没有主键,则会unique key做主键
3:如果没有unique,则系统生成一个内部的rowid做主键
4:像innodb中,主键的索引结构中,即存储了主键值,又存储了行数据,这种结构称为”聚簇索引“
二、覆盖索引:
永远记住查索引是快的,回行是慢的(从磁盘上返回数据)
索引覆盖是指 如果查询的列恰好是索引的一部分,那么查询只需要在索引文件上进行,不需要回行到磁盘再找数据。
这种查询速度非常快,
explain xxxx
如果最后又 using index 说明使用了索引覆盖,速度是非常快的
问题案例:
create table A (
id varchar(64) primary key,
ver int,
)
在id、ver上有联合索引
100000条数据
为什么select id from A order by id 特别慢?
而select id from A order by id,ver非常快
我的表有几个很长的字段varbinary(3000)
分析:
1.是innodb引擎
2.有多个比较长的列
3.是聚簇索引,导致沿id排序时,要跨好多小文件块
4.有比较长的列,导入块比较多
1.如果是myisam,将不存在这个问题
高性能索引策略:
对于innodb而言,因为节点下有数据文件,因此节点的分裂将会比较慢,
对于innodb的主键,尽量用整型,而且是递增的整型,如果是无规律的数据,将会产生的页的分裂,影响速度。
好的索引:
1.查询频繁 2.区分度高 3.长度小 4.尽量能覆盖常用查询字段
取出最长字节的数据:
mysql> select dept_no,dept_name from departments order by length(dept_name) desc limit 2;
±--------±-------------------+
| dept_no | dept_name |
±--------±-------------------+
| d006 | Quality Management |
| d009 | Customer Service |
±--------±-------------------+
索引优化总结:
1.首先分析表是聚簇索引还是非聚簇索引,
2.是innodb还是myisam,
3.能不能索引覆盖,
4.常查询的列是不是经常在一起查的,可以建联合索引
5.索引的长度要建多少?怎么跟区分度保持平衡。
6.伪hash索引降低索引长度
对于左前缀不易区分的列,建立索引技巧
如URL列:
http://www.baidu.com
http://www.zixue.it
列的前11字符都是一样的,不易区分,可以用如下2个办法来解决,
1.把列的内容倒过来存储,并建立索引
moc.udiab.www//:ptth
ti.euxiz.www://ptth
这样左前缀区分度大
2.伪hash索引效果
同时存url_hash列
实验:
创建测试表:
mysql> create table t10 (
id int primary key auto_increment,
url varchar(30) not null default ''
)
;
Query OK, 0 rows affected (0.03 sec)
插入url数据:
mysql> insert into t10 (url) values (‘www.baidu.com’),(‘www,zixue.it’);
Query OK, 2 rows affected (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 0
查看是否支持crc模式:
mysql> select crc32(‘a’);
±-----------+
| crc32(‘a’) |
±-----------+
| 3904355907 |
±-----------+
1 row in set (0.00 sec)
增加一个列,并且指定crcurl是无符号
mysql> alter table t10 add crcurl int unsigned not null default 0;
Query OK, 0 rows affected (0.05 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> select * from t10;
±—±--------------±-------+
| id | url | crcurl |
±—±--------------±-------+
| 1 | www.baidu.com | 0 |
| 2 | www,zixue.it | 0 |
±—±--------------±-------+
设定crcurl列数据为url列数据转化
mysql> update t10 set crcurl=crc32(url);
Query OK, 2 rows affected (0.00 sec)
Rows matched: 2 Changed: 2 Warnings: 0
添加索引
mysql> alter table t10 add index url(url(16));
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table t10 add index crcurl(crcurl);
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
按照生产业务应该先转化为crc32:
mysql> select crc32(‘www.baidu.com’);
±-----------------------+
| crc32(‘www.baidu.com’) |
±-----------------------+
| 387695885 |
±-----------------------+
1 row in set (0.01 sec)
然后进行查询:效果对比
mysql> explain select * from t10 where url=‘www.baidu.com’\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t10
partitions: NULL
type: ref
possible_keys: url
key: url
key_len: 66
ref: const
rows: 1
filtered: 100.00
Extra: Using where
1 row in set, 1 warning (0.00 sec)
ERROR:
No query specified
mysql> explain select * from t10 where crcurl=‘387695885’\G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t10
partitions: NULL
type: ref
possible_keys: crcurl
key: crcurl
key_len: 4
ref: const
rows: 1
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.00 sec)
————————————————————————————
大数据量下的分页效果:
select field from table limit 30,10;
比如 每页显示perpage条,当前是第N页
前面翻过了(N-1)*perpage
limit (N-1)*perpage,N
问题:面试官提问,当翻到了9999页,查询速度非常慢,效率非常低,怎么办?
1.从业务上去优化
办法:不允许翻过100页
以百度为例,一般翻页到70页左右,谷歌40页左右
问题:
mysql> select emp_no,birth_date from employees limit 100000,10 ;
为什么limit offset 的效率这么低
在offset 比较大时
答:以为是自上而下逐行查找,用limit offset 时,并不是先跳过,再查询,而是先查询,后跳过。
案例:
limit 100W/10
就要把100w取出来,然后跳过前100W行
可以改进:
mysql> select emp_no,birth_date from employees limit 300000,3 ;
±-------±-----------+
| emp_no | birth_date |
±-------±-----------+
| 499976 | 1963-08-20 |
| 499977 | 1956-06-05 |
| 499978 | 1960-03-29 |
±-------±-----------+
3 rows in set (0.06 sec)
mysql> select emp_no,birth_date from employees where emp_no>300000 limit 3;
±-------±-----------+
| emp_no | birth_date |
±-------±-----------+
| 400000 | 1963-11-29 |
| 400001 | 1962-06-02 |
| 400002 | 1964-08-16 |
±-------±-----------+
3 rows in set (0.00 sec)
1.为什么下面比上面快很多,
答:因为emp_no是建了主键索引,索引发挥了作用
缺点:要求数据完整性,没有被删除过,因为一旦删除过数据,那么根据ID顺序来取出的数据就不正常。
2.为什么两次数据不一致?
答:原因:数据被物理删除过,有空洞
解决:数据不进行物理删除(可以逻辑删除),最终在页面上显示数据时,逻辑删除的条目不显示即可
缺点:要求数据完整性,没有被删除过,因为一旦删除过数据,那么根据ID顺序来取出的数据就不正常。
(一般来说,大网站的数据都是不物理删除,只做逻辑删除(逻辑标记,比如is_delete=1))
3.非要物理删除,还要用offset精确查询,还不限制用户分页,怎么办?
分析:优化思路是 不查,少查,查索引,少取
我们现在必须要查,则只查索引,不查数据,得到id
再用id去查具体条目,这种技巧就是延迟索引
select lx_com.id,name from lx_com inner join (select id from lx_com limit 5000000,10) as tmp on lx_com.id=tmp.id;
mysql> select id,name from lx_com inner join (select id from lx_com limit 500000,10) as tmp using(id);
索引与排序:
排序可能发生2种情况:
1.对于覆盖索引,直接在索引上查询时,就是有顺序的,using index
2.先取出数据,形成临时表做filesort(文件排序,但文件可能在磁盘上,也可能在内存中)
我们的争取目标-----取出来的数据本身就是有序的,利用索引来排序
比如:goods商品表,(cat_id,shop_price)组成联合索引,
where cat_id=N order by shop_price,可以利用索引来排序
可以:
select goods_id,cat_id,shop_price from goods order by shop_price;
//using where,按照shop_price索引取出的结果,本身就是有序的。
不可以:
select goods_id,cat_id,shop_price from goods order by click_count;
//using filesort 用到了文件排序,即取出的结果再次排序。
重复索引与冗余索引
重复索引:是指 在同一个列(如age),或者 顺序相同的几个列(age,school),建立多个索引
称为重复索引,重复索引没有任何帮助,只会增大索引文件,拖慢更新速度,去掉
冗余索引;
冗余索引是指2个索引所覆盖的列有重叠,称为冗余索引
比如x,m列,加索引 index x(x),index xm(x,m)
x,m索引,两者的x列重叠了,这种情况下,称为冗余索引
甚至可以把index mx(m,x)索引也建立,mx,xm也不是重复的,因为列的顺序不一样
比如,冗余索引:
alter table test add index click1 (click_count);
alter table test add index click2 (click_count);
还有:
x,y ,index xy(x,y) ,index yx(y,x) 就是冗余索引
索引碎片与维护:
在长期的数据更改过程中,索引文件和数据文件,都将产生空洞,形成碎片
我们可以通过一个nop操作(不产生对数据实质影响的操作),来修改表
比如:表的引擎为innodb,可以alter table xxx engine innodb
注意:虽然没有对任何数据进行更改,但是会重新规整数据和索引文件,必须在凌晨半夜执行,非常耗时
另一种方法:
optimize table goods;
两种方法效果一样
注意:修复表的数据及索引碎片,就会把所有的数据文件重新整理一遍,使之对齐。
这个过程,如果表的行数比较大,也是非常耗费资源的操作,
所以,不能频繁的修复,
如果表的upfate操作很频繁,可以按周/月,来修复
如果不频繁,可以更长的周期来做修复
SQL语句优化:
mysql> explain select 1 \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: NULL
partitions: NULL
type: NULL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
filtered: NULL
Extra: No tables used
type:
有可能是
实际的表名: 如 select * from t1;
表的别名: 如select * from t2 as tmp;
derived :如from型子查询
null : 直接计算得结果,不用走表
possible_key : 可能用到的索引
注意:系统估计可能用的几个索引,但最终,只能用1个
key:最终用的索引
key_len:使用的索引的最大长度,越短越好,速度越快
type列:是指查询的方式,非常重要,是分析“查数据过程”的重要依据,可能的值
all:意味着从表的第1行,往后,逐行做全表扫描,运气不好扫描到最后一行
ref:连接查询时,前表与后表的引用关系,引用意思
rows:估计扫描多少行
extra:using index ,using where , using temp,using filesort ,等等。。。。。
mysql> explain select to_date from dept_emp \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: dept_emp
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 331143
filtered: 100.00
Extra: NULL
1 row in set, 1 warning (0.01 sec)
mysql> explain select emp_no from dept_emp \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: dept_emp
partitions: NULL
type: index
possible_keys: NULL
key: emp_no
key_len: 4
ref: NULL
rows: 331143
filtered: 100.00
Extra: Using index
1 row in set, 1 warning (0.00 sec)
index: 比all性能好一点
通俗的说:all 扫描所有的数据行,相当于data_all index扫描所有的索引节点,相当于index_all
2种情况可能出现:
1.索引覆盖的查询情况下,能利用上索引,但是又必须全索引扫描
mysql> explain select emp_no from dept_emp where emp_no=100001 or emp_no+1=10159 \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: dept_emp
partitions: NULL
type: index
possible_keys: PRIMARY,emp_no
key: emp_no
key_len: 4
ref: NULL
rows: 331143
filtered: 100.00
Extra: Using where; Using index
1 row in set, 1 warning (0.00 sec)
2.是利用索引来进行排序,但取出所有的节点
mysql> explain select emp_no from dept_emp order by emp_no desc \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: dept_emp
partitions: NULL
type: index
possible_keys: NULL
key: emp_no
key_len: 4
ref: NULL
rows: 331143
filtered: 100.00
Extra: Using index
1 row in set, 1 warning (0.00 sec)
分析:没有加where条件,就得取所有索引节点,同时,又没有回行,只取索引节点,再排序,经过所有索引节点。
range:意思是查询时,能根据索引做范围扫描
mysql> EXPLAIN select emp_no from dept_emp where emp_no>10159 \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: dept_emp
partitions: NULL
type: range
possible_keys: PRIMARY,emp_no
key: PRIMARY
key_len: 4
ref: NULL
rows: 165571
filtered: 100.00
Extra: Using where; Using index
1 row in set, 1 warning (0.01 sec)
ref 意思是指 通过索引列,可以直接引用到某些数据行
mysql> EXPLAIN select emp_no from dept_emp where emp_no=10159 \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: dept_emp
partitions: NULL
type: ref
possible_keys: PRIMARY,emp_no
key: PRIMARY
key_len: 4
ref: const
rows: 1
filtered: 100.00
Extra: Using index
1 row in set, 1 warning (0.01 sec)
eq_ref 是指,通过索引列,直接饮用某1行数据,常见于连接查询中
mysql> explain select employees.emp_no,employees.first_name,employees.last_name,dept_emp.dept_no from employees inner join dept_emp on employees.emp_no = dept_emp.emp_no where employees.emp_no>499997 \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: dept_emp
partitions: NULL
type: range
possible_keys: PRIMARY,emp_no
key: emp_no
key_len: 4
ref: NULL
rows: 2
filtered: 100.00
Extra: Using where; Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: employees
partitions: NULL
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: employees.dept_emp.emp_no
rows: 1
filtered: 100.00
Extra: NULL
2 rows in set, 1 warning (0.00 sec)
const , system , null 这3个分别指查询优化到常量级别,甚至不需要查找时间
一般按照主键来查询时,易出现const,system
或者直接查询某个表达式,不经过表时,出现null
mysql> explain select emp_no from employees where emp_no=499997 \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: employees
partitions: NULL
type: const
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: const
rows: 1
filtered: 100.00
Extra: Using index
1 row in set, 1 warning (0.00 sec)
几个误区:
explain 不产生查询 这是错误的,在一些复杂或者大的查询中,explain也是会产生查询。
对limit的解释 查询比较复杂
explain 即执行过程 只是优化即分析结果
mysql 5.6 确实不产生查询,只产生查询计划。还可以分析非select 语句
in型子查询陷阱
mysql> select emp_no,first_name,last_name from employees where emp_no in (select emp_no from salaries where salary=107118);
±-------±-----------±----------+
| emp_no | first_name | last_name |
±-------±-----------±----------+
| 11486 | Itzchak | Ramaiah |
| 91781 | Emran | Broomell |
| 220903 | Marlo | Domenig |
| 296691 | Yuping | Welham |
| 443206 | Marco | Oppitz |
±-------±-----------±----------+
5 rows in set (7.89 sec)
实例:
说明:并没有使用emp_no索引,type为all
mysql> explain select emp_no,first_name,last_name from employees where emp_no in (select emp_no from salaries where salary=107118) \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: employees
partitions: NULL
type: ALL
possible_keys: PRIMARY
key: NULL
key_len: NULL
ref: NULL
rows: 299866
filtered: 100.00
Extra: NULL
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: salaries
partitions: p01,p02,p03,p04,p05,p06,p07,p08,p09,p10,p11,p12,p13,p14,p15,p16,p17,p18,p19
type: ref
possible_keys: PRIMARY,emp_no
key: PRIMARY
key_len: 4
ref: employees.employees.emp_no
rows: 1
filtered: 10.00
Extra: Using where; FirstMatch(employees)
2 rows in set, 1 warning (0.00 sec)
手动添加效果
mysql> explain select emp_no,first_name,last_name from employees where emp_no in (11486,91781,220903,296691,443206) \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: employees
partitions: NULL
type: range
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: NULL
rows: 5
filtered: 100.00
Extra: Using where
1 row in set, 1 warning (0.01 sec)
使用下面方法:
mysql> select employees.emp_no,first_name,last_name from employees inner join ids on employees.emp_no=ids.id;
±-------±-----------±----------+
| emp_no | first_name | last_name |
±-------±-----------±----------+
| 11486 | Itzchak | Ramaiah |
| 91781 | Emran | Broomell |
| 220903 | Marlo | Domenig |
| 296691 | Yuping | Welham |
| 443206 | Marco | Oppitz |
±-------±-----------±----------+
mysql> explain select employees.emp_no,first_name,last_name from employees inner join ids on employees.emp_no=ids.id \G;
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: ids
partitions: NULL
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 5
filtered: 100.00
Extra: NULL
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: employees
partitions: NULL
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: employees.ids.id
rows: 1
filtered: 100.00
Extra: NULL
2 rows in set, 1 warning (0.00 sec)
exists 一定比子查询慢吗?
select c.cat_id,cat_name from category as c inner join goods as g on c.cat_id=g.cat_id group by cat_name
优化后:
select c.cat_id,cat_name from category as c inner join goods as g on c.cat_id=g.cat_id group by cat_id
count优化:
误区:
1.myisam的count()非常快
答:是比较快,但仅限于查询表的”所有行“比较快,因为myisam对行数进行了存储,一旦有条件的查询,速度就不再快了,尤其是where条件的列上没有索引
2:假如,id<100 的商家是我们内部的测试,我们想查查真是的商家有多少?
select count() from lx_com where id>=100;(1000多万行用了6.X秒)
小技巧:
select count() from lx_com; 快
select count() from lx_com where id<100;快
select count() from lx_com -select count() from lx_com where id<100; 快
select (select count() from lx_com) -(select count(*) from lx_com where id<100);
group by
注意:
1.分组用于统计,而不是用于筛选数据
比如:统计平均分,最高分,适合,但用于筛选重复数据,则不适合
以及用索引来避免临时表和文件排序
2.以A,B表连接为例,主要查询A表的列。
那么group by,order by 的列尽量相同,而且列应该显示声明为A 的列
union优化
注意:union all不过滤,效率提高,如非必须,请用union all
因为union去重的代价非常高,放在程序里去重
巧用变量减少查询
首先建表
mysql> create table t10 (
-> name char(10) not null default '',
-> score smallint not null default 0,
-> );
插入数据:
mysql> insert into t10 values(‘zhang’,100),(‘wang’,95),(‘li’,95),(‘zhao’,90),(‘luo’,100);
排序看一下
mysql> select * from t10 order by score desc;
±------±------+
| name | score |
±------±------+
| zhang | 100 |
| luo | 100 |
| wang | 95 |
| li | 95 |
| zhao | 90 |
±------±------+
mysq变量使用:
mysql> set @age:=20;
Query OK, 0 rows affected (0.02 sec)
mysql> select @age;
±-----+
| @age |
±-----+
| 20 |
±-----+
1 row in set (0.01 sec)
mysql> set @pres:=0,@currs:=0,@rank:=0; Query OK, 0 rows affected (0.00 sec)
mysql> select name,(@currs:=score) as score,@rank:=if(@currs<>@pres,@rank:=@rank+1,@rank) as rank,@pres:=score as prev from t10 order by score desc;
±------±------±-----±-----+
| name | score | rank | prev |
±------±------±-----±-----+
| zhang | 100 | 1 | 100 |
| luo | 100 | 1 | 100 |
| wang | 95 | 2 | 95 |
| li | 95 | 2 | 95 |
| zhao | 90 | 3 | 90 |
±------±------±-----±-----+
5 rows in set (0.00 sec)
在name列添加唯一约束索引:
mysql> alter table t10 add unique index name(name);
添加报错:
mysql> insert into t10 (name,score) values(‘li’,80);
ERROR 1062 (23000): Duplicate entry ‘li’ for key ‘name’
mysql> insert into t10 (name,score) values (‘li’,80) on duplicate key update score=score+1;
Query OK, 2 rows affected (0.01 sec)
mysql> select * from t10;
±------±------+
| name | score |
±------±------+
| li | 96 |
| luo | 100 |
| wang | 95 |
| zhang | 100 |
| zhao | 90 |
±------±------+
5 rows in set (0.00 sec)
主服务器的日志格式用哪种比较好
有statement,row,mixed 这3种,其中mixed是指前2种的混合
以insert into xxtable values(x,y,x,z) 为例
影响:1行,且为新增1行,对于其他行没有影响
这个情况,用row格式,直接复制磁盘上1行的新增变化
以update xxtable set age=21 where name=‘sss’;
这个情况一般也只是影响1行,用row也比较合适
以过年发红包,全公司的人,都涨薪100元
update xxtable set salary=salary+100;
这个语句带来的影响是针对每一行的,因此磁盘上很多row都发生了变化
此处适合statement格式日志
2种日志,各有各的高效的地方,mysql提供了mixed类型,可以根据语句的不同,而自动选择适合的日志格式
主主复制
同步冲突:
例:create table stu(
id int primary key auto_increment
)…
2台mysql地位相等,假如2个请求同时到达2台服务器
请求a节点 stu的id为1
请求b节点stu的id为1
同步----冲突
如何解决?
让1台服务器1,3,5,7,9来增长
另一台服务器2,4,6,8,来增长
一台服务器:
set global auto_increment_increment = 2
set global auto_increment_offset=1;
set session auto
./bin/mysql-proxy -P 192.168.0.199:4040 --proxy-backend-addresses=192.168.0.199:3306 --proxy-backend-addresses=192.168.0.200:3306
./bin/mysql-proxy -b 192.168.0.199:3306 -r 192.168.0.200:3306 -s /usr/local/mysql-proxy/share/doc/mysql-proxy/rw-splitting.lua
MyCat又是在Cobar基础上发展的版本,两个显著点是:
后端由BIO改为NIO,并发量有大幅提高
增加了对Order By、Group By、limit等聚合功能的支持(,虽然Cobar也可以支持Order By、Group By、limit语法,但是结果没有进行聚合,只是简单返回给前端,聚合功能还是需要业务系统自己完成)。
partition分区
create table topic (
tid int primary key auto_increment,
title varchar(20) not null default ''
)
partition by range(tid) (
partition t0 values less than(10),
partition t1 values less than(20),
partition t2 values less than(MAXVALUE)
);
事务:
四个特征:
原子性
一致性
隔离性
持久性
以银行汇款为例,张三给李四转款300元
原子性:是指某几句sql的影响,要么都发生,要么都不发生
即:张三减300,李四加300,insert银行流水,这3个操作必须都完成,或都不产生效果
一致性:事务前后的数据,保持业务上的合理一致
(汇款前)张三的余额+李四的余额 =====(汇款后)张三的余额+李四的余额
比如:张三只有280元,280-300=-20,储蓄卡不是信用卡,不能为负,因此张三0元将导致,汇款后,2者余额,汇款前差了20元
隔离性:在事务进行过程中,其他事务,看不到此事务的任何效果
持久性:事务一旦发生,不能取消,只能通过补偿性事务,来抵消效果。