MySQL 基础
MySQL 基础知识
视图:有查询结果生成的一张虚拟表。
create view view_name as select ...
存储过程:一组为了完成特定功能的 SQL 语句集。
create procedure 存储过程名(参数,参数,…) begin //代码 end
触发器:有事件引发的操作。
create trigger 触发器名 before|after 触发事件 on 表名 for each row 执行语句;
MySQL 的常用引擎
MySQL 的引擎包括:ISAM ,MyIASM ,Innodb ,HEAP,MERGE 等。
ISAM 执行读取操作的速度很快,且不占用大量的内存和存储资源,但不支持事务处理,并且不能够容错。
MyIASM 强调了快速读取数据的操作,但不能在表损坏之后恢复数据。
Innodb 适合处理多重并发,并且支持事务操作,且具有自动恢复数据,同时又支持外键。默认的搜索引擎。如果更新和查询都比较频繁,会存在锁表的可能。
查看 MySQL 提供了什么存储引擎:show engines;
查看当前表使用了什么引擎:show create table tb_content;
MyIASM 使用的索引是 B+ 树,但数据结构中存储的内容实际上是数据的地址,索引和数据是分开的。
Innodb 使用的索引也是 B+ 树,数据结构中存储的是实际的数据。
MySQL 中的事务
事务:一个最小的不可再分的工作单元。
事务具有四大特性:
- 原子性:最小单元,不可再分。
- 一致性:同时成功或者同时失败。
- 隔离性:两个事务之间必须隔离。
- 持久性:对数据的修改是永久的。
MySql 事务隔离级别
读取未提交。事务中发生了修改,即使没有提交,其它事务也是可见的。—— > 会引起脏读。
例如:A 原来的值是 50。修改成了 100。但还没提交,另外一个事务 B 看到了这个修改,但此刻 A 发生了回滚还是 50 。但 B 却读到了 100。
读提交。对于一个事务从开始直到提交之前,所做的任务修改其它事务都不可见。—— > 会引起不可重复读。
例如:A 原来是 50。提交修改 100 。另外一个事务 B 在这之前读取到 A 是 50。刚读完 A 就被修改成了 100。这个时候另外一个事务 C 在读 A 就是 100了。
可重读。(MySql 默认隔离级别)不管事务有没有提交,每次读取的结果集都相同。—— > 会引起幻读。
序列化。隔离级别最严格,代价最高。但能避免脏读,不可重复读,幻读。
MySQL 如何定位并优化查询慢的 SQL
- 根据日志定位查询慢的 SQL。
- 使用 Explain 等工具分析。
- 修改 SQL 或者让 SQL 走索引以优化查询效率。
MySql 数据库优化
如何判断对应的字段就是索引?
- Where 使用比较频繁的字段。
- Order By 涉及到的列。
MySQL 索引是建立越多越好吗?
- 数据量小的表不需要建立索引,建立索引会增加额外的索引开销。
- 数据变更需要维护索引,因此更多的索引意味着更多的维护成本。
- 更多的索引也意味着更多的空间。
MySQL 索引失效的条件
查看索引失效的语句:explain 执行该语句 key=null 。
- 查询条件包含 or。
- like 以 % 开头。
- 组合索引,但不使用第一列索引。
- 列类型是字符串,查询条件一定要用括号括起来,否则索引失效。
- Where 查询条件里使用了不等号。
- Where 查询条件里使用了函数。
MySQL 索引优化思路
- Where 条件顺序,Oracle 采用自上而下的顺序解析,涉及到最大数量记录的条件写在末尾。
- 先查小表,再联合大表,当使用一行数据的时候,使用 Limit。
- 能用 Inner Join 就用 Inner Join (等值连接)。Left Join 左边结果集尽量少,有条件放左边。
- 避免使用子查询,使用 Join 代替。
- 避免使用 Having 。Having 是检索出所有记录后才对结果集进行过滤。On 是先把不符合的记录过滤后才统计。
- 避免使用 Select * ,而用具体的列。
- 使用关键字 Top,Distinct,关键字去重。
- 使用临时表暂存中间结果。
- 尽量避免在 Where 中使用 != 或 <>。
- 尽量避免在 Where 中对索引进行 Is Null,Is Not Null。(可以将 Null 字段设置成默认值 0 ,改变查询条件 ——> 原因:索引不索引空值)
MySQL 最左匹配原则
mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配了。
- 建立 a,b,c,d 的索引,a=3 and b=4 and c>5 and d=6 。(d 就用不到索引)但是如果 a=3 and b=4 and d=6 and c>5 。则 a,b,c,d 都会用到索引。
- 建立 a,b,c,d 的索引,c>5 and b=4 and d=6 and a=3 也会全部用到 a,b,c,d 索引 。(查询优化器会重新编排)
- 建立 a,b,c 索引 那么只要查询条件有 a 即可用到 a,b,c 索引(如 a,b,c a,b a,c a), 没有 a 就用不到。
- 索引 a 的字段类型是 int 。查询的时候 where a = ‘123’。会使用到索引。
- 索引 a 的字段类型是 varchar 。查询的时候 where a = 123。不会使用到索引。
MySQL 如何保证原子性
想要保证事务的原子性,就需要在异常发生时,对已经执行的操作进行回滚,在 MySQL 中,恢复机制是通过 回滚日志(undo log) 实现的,所有事务进行的修改都会先先记录到这个回滚日志中,然后再执行相关的操作。如果执行过程中遇到异常的话,我们直接利用回滚日志中的信息将数据回滚到修改之前的样子即可!并且,回滚日志会先于数据持久化到磁盘上。这样就保证了即使遇到数据库突然宕机等情况,当用户再次启动数据库的时候,数据库还能够通过查询回滚日志来回滚将之前未完成的事务。
MySQL 中的锁
- select * from … where … for update;
- update … set …;
MySQL 中的事务
事务:一个最小的不可再分的工作单元。
事务具有四大特性:
- 原子性:最小单元,不可再分。
- 一致性:同时成功或者同时失败。
- 隔离性:两个事务之间必须隔离。
- 持久性:对数据的修改是永久的。
MySql 事务隔离级别
读取未提交。事务中发生了修改,即使没有提交,其它事务也是可见的。—— > 会引起脏读。
例如:A 原来的值是 50。修改成了 100。但还没提交,另外一个事务 B 看到了这个修改,但此刻 A 发生了回滚还是 50 。但 B 却读到了 100。
读提交。对于一个事务从开始直到提交之前,所做的任务修改其它事务都不可见。—— > 会引起不可重复读。
例如:A 原来是 50。提交修改 100 。另外一个事务 B 在这之前读取到 A 是 50。刚读完 A 就被修改成了 100。这个时候另外一个事务 C 在读 A 就是 100了。
可重读。(MySql 默认隔离级别)不管事务有没有提交,每次读取的结果集都相同。—— > 会引起幻读。
序列化。隔离级别最严格,代价最高。但能避免脏读,不可重复读,幻读。
MySQL 如何定位并优化查询慢的 SQL
- 根据日志定位查询慢的 SQL。
- 使用 Explain 等工具分析。
- 修改 SQL 或者让 SQL 走索引以优化查询效率。
MySql 设计的时候需要注意的事项
设计表
- 表字段避免null值出现,null值很难查询优化且占用额外的索引空间,推荐默认数字0代替null。
- 尽量使用INT而非BIGINT,如果非负则加上UNSIGNED(这样数值容量会扩大一倍),当然能使用TINYINT、SMALLINT、MEDIUM_INT更好。
- 使用枚举或整数代替字符串类型。
- 尽量使用TIMESTAMP而非DATETIME。
- 单表不要有太多字段,建议在20以内。
- 用整型来存IP。
索引
- 索引并不是越多越好,要根据查询有针对性的创建,考虑在WHERE和ORDER BY命令上涉及的列建立索引,可根据EXPLAIN来查看是否用了索引还是全表扫描。
- 应尽量避免在WHERE子句中对字段进行NULL值判断,否则将导致引擎放弃使用索引而进行全表扫描。
- 值分布很***的字段不适合建索引,例如”性别”这种只有两三个值的字段。
- 字符字段只建前缀索引。
- 字符字段***不要做主键。
- 不用外键,由程序保证约束。
- 尽量不用UNIQUE,由程序保证约束。
- 使用多列索引时主意顺序和查询条件保持一致,同时删除不必要的单列索引。
SQL 的编写需要注意优化
- 使用limit对查询结果的记录进行限定。
- 避免select *,将需要查找的字段列出来。
- 使用连接(join)来代替子查询。
- 拆分大的delete或insert语句。
- 可通过开启慢查询日志来找出较慢的SQL。
- 不做列运算:SELECT id WHERE age + 1 = 10,任何对列的操作都将导致表扫描,它包括数据库教程函数、计算表达式等等,查询时要尽可能将操作移至等号右边。
- sql语句尽可能简单:一条sql只能在一个cpu运算;大语句拆小语句,减少锁时间;一条大sql可以堵死整个库。
- OR改写成IN:OR的效率是n级别,IN的效率是log(n)级别,in的个数建议控制在200以内。
- 不用函数和触发器,在应用程序实现。
- 避免%xxx式查询。
- 少用JOIN。
- 使用同类型进行比较,比如用’123’和’123’比,123和123比。
- 尽量避免在WHERE子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描。
- 对于连续数值,使用BETWEEN不用IN:SELECT id FROM t WHERE num BETWEEN 1 AND 5。
- 列表数据不要拿全表,要使用LIMIT来分页,每页数量也不要太大。
分区
分区的好处是:
- 可以让单表存储更多的数据
- 分区表的数据更容易维护,可以通过清楚整个分区批量删除大量数据,也可以增加新的分区来支持新插入的数据。另外,还可以对一个独立分区进行优化、检查、修复等操作
- 部分查询能够从查询条件确定只落在少数分区上,速度会很快
- 分区表的数据还可以分布在不同的物理设备上,从而搞笑利用多个硬件设备
- 可以使用分区表赖避免某些特殊瓶颈,例如InnoDB单个索引的互斥访问、ext3文件系统的inode锁竞争
- 可以备份和恢复单个分区
分区的限制和缺点:
- 一个表最多只能有1024个分区
- 如果分区字段中有主键或者唯一索引的列,那么所有主键列和唯一索引列都必须包含进来
- 分区表无法使用外键约束
- NULL值会使分区过滤无效
- 所有分区必须使用相同的存储引擎
分区的类型:
- RANGE分区:基于属于一个给定连续区间的列值,把多行分配给分区
- LIST分区:类似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择
- HASH分区:基于用户定义的表达式的返回值来进行选择的分区,该表达式使用将要插入到表中的这些行的列值进行计算。这个函数可以包含MySQL中有效的、产生非负整数值的任何表达式
- KEY分区:类似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL服务器提供其自身的哈希函数。必须有一列或多列包含整数值
- 具体关于mysql分区的概念请自行google或查询官方文档,我这里只是抛砖引玉了。