Monthly Archives: December 2014

mysql: my.cnf在哪里?

先找到mysqld的执行文件

引用
#ps aux|grep mysqld

如:  /usr/libexec/mysqld

运行一下这个程序的help命令,根据输出中的"Default options"关键字找到my.cnf的位置

引用
#sudo /usr/libexec/mysqld –verbose –help |grep -A1 "Default options"

如:

引用

Default options are read from the following files in the given order:

/etc/mysql/my.cnf /etc/my.cnf ~/.my.cnf

注意Prepared Statement执行时会比普通statement多一次连接

使用Prepared Statement时,你要先输入那种带问号的语句,让数据库服务端生成一个Prepared Statement句柄传给你;你再拿着这个句柄,输入参数让数据库去执行n次sql。

也就是说,这里客户端与服务端的交互(round trip)发生了1 + n次。1 = 生成prep stmt, n = 执行sql.

典型的OLTP应用在大部分情况下,都不会循环执行sql,也就是说 n=1.也就是说,如果不使用prep stmt, 一次数据库交互需要1个round trip; 如果用了,那就是 n +1 = 1 + 1 =2. 使用prep stmt时发生的round trip数增加了1倍!

这对性能还是有一定影响的。不过,出于安全原因,没人不敢不用prep stmt. 所以,讨论性能问题似乎也没有多大意义。

MySQL触发器的适用场景和约束

最适合的场景可能是:冗余设计中的数据联动,以及summary table这种。 这两类逻辑用触发器来搞定会比用上层代码搞定要简洁的多。 性能方面没有问题,在InnoDB中,它还可以保证原子性。

不过,

1. 这类逻辑比较隐蔽,容易被忽略,埋坑。

2. 调试难,如果没有全局梳理,一不小心可能导致死锁。

3. 一旦要分表分库,可能处理起来会比在上层应用中处理更棘手。

MySQL query cache出现负面作用的情形

1. 写语句特别频繁的场景不适合使用query cache,  因为query cache会被不停地失效,导致cache miss, 使用query cache纯粹是浪费性能。因为query cache本身也有使用代价,执行语句前它要先查一下cache中有没有,如果没有,从表中查到值之后还要更新一下cache

2. query cache累积过多可能出问题。当表中数据修改时,所有相关的cache都要失效;在失效过程中,要访问cache的查询都会被阻塞(即使跟这张表没关系);如果cache很多,最终给人的感觉是数据库长时间对查询无响应。

3. innodb中的长事务可能会使query cache失效。只要事务中修改了这张表,yi 会导致这个表关联的query cache在事务结束前全局失效:不仅本事务中会cache miss, 其他事务中也会cache miss.

MySQL query cache怎么个缓存法

1. query cache的key是sql字符串,并且要求精确匹配:大小写不一样或者多了一个空格,都会导致cache miss

2. query cache直接把用户输入的原始sql作为key, 不是把解析后或优化后的sql作为key,也不会把其中的子查询独立缓存

2. 存储过程中的sql结果不会被缓存

3. 如果一个sql会产生不确定的结果(即使表中数据没有变化也会不确定),比如select now(), MySQL就不会缓存它的结果

4. 只要表中数据有任何变化,就会导致本表相关的所有query cache统统失效

MySQL使用limit条件时要注意offset不能太大

MySQL使用limit条件时要注意offset不能太大。

这样还可以:

select * from user order by user_name limit 100, 20

这样就不好了:

select * from user order by user_name limit 10000, 20

这条语句实际上是把 1-10020行都取出来,然后再丢掉前10000条记录,最后再返回剩下的20条记录。所以代价会非常高。

如果数据库是innodb且user_name上有索引的话,可以把sql改写成这样来优化一下:

select * from user 
       join
         (select id from user order by user_name limit 10000,20) as lim
       on user.id = lim.id

这里仍然会扫描1万多次,但扫描的不是data rows, 而是user_name上的index(并且是covering index) ; 扫完索引之后,再根据扫出的id把20行数据取出来。也就是说,磁盘读会小很多,速度也会快很多。

注意:limit条件不会影响MySQL Explain Plan中的rows结果

limit条件不会影响MySQL Explain Plan中的rows结果。比如,

explain select * from t limit 10

输出的rows值可能是1百万(如果t中有1百万数据)。 但实际上,MySQL不会为这十行数据扫描整张表的。

Peter Zaitsev说:

LIMIT is not taken into account while estimating number of rows Even if you have LIMIT which restricts how many rows will be examined MySQL will still print full number

如果不知道这个潜规则,会被explain plan迷惑死。这是典型的MySQL制造者的懒惰给使用者造成的麻烦。

MySQL: count()的坑

count(*), count(1), count(someColumn) 倒底有没有分别? 使用count(someColumn)只取一列,会不会性能好点? 如果是这样的话,那count(1)是不是性能最佳?

答案:

1. count(*)和count(1)完全是一码事:都是用来数“行数”的。MySQL遇到"*", 并不会去展开它。在性能方面,这类查询会走covering index. 

2. count(someColumn)跟其他完全不同,它数的是someColumn列中非NULL值的个数。在性能方面,系统会根据someColumn有没有索引,来决定走不走covering index.

例:

drop table if exists play_count;  
create table play_count(
	id bigint auto_increment not null, 
	indexed_column varchar(50),      --有索引列
	non_indexed_column varchar(50),  --无索引列
	primary key(id),
	key idx(indexed_column) 
);

--插入三行数据,存在空值
insert into play_count(indexed_column,non_indexed_column) value('a',null);
insert into play_count(indexed_column,non_indexed_column) value(null,'b');
insert into play_count(indexed_column,non_indexed_column) value('c','c');


--count查询
select count(*) from play_count;     --返回3
select count(1) from play_count;     --返回3   
select count(id) from play_count;     --返回3 
select count(indexed_column) from play_count;     --返回2
select count(non_indexed_column) from play_count;     --返回2 



explain select count(*) from play_count; --Extra: Using Index
explain select count(1) from play_count; --Extra: Using Index
explain select count(id) from play_count; --Extra: Using Index
explain select count(indexed_column) from play_count; --Extra: Using Index
explain select count(non_indexed_column) from play_count; --Type: All