qiuyadong's Homepage

掌握mysql

2019-07-03

Mysql!

概览

掌握mysql大致分三块:存储、事务以及优化;

存储

索引

为了从数据集中最快检索到有效数据,一般都会建立索引,所以正确建立合适的索引成为关键!

mysql中使用B+树建立索引,它的优势:

1 减少存储引擎需要扫描的数据量

2 将随机IO转换成顺序IO

3 帮助我们在进行分组、排序的时候使用临时表

  • 为什么B+树可以有这些优势呢?

B+树只有叶子节点存储数据,单个节点存储的关键字多,有效的减少了IO操作的次数

叶子节点存储数据天然有序,使用B+树更加稳定,查询效率可算

  • 使用B+树对程序员建立索引的启示

有效的建立索引,不要建立冗余索引

索引定义最小宽度

语句最左匹配会使用到索引

索引要建立在离散度高的列上

不要使用select * ,表明字段在查找的时候,会覆盖索引,提高查询效率

使用查看执行计划检验索引使用的好坏

存储引擎

存储引擎可定义在表上,5.7版本之后默认使用innodb存储引擎,之前默认使用myisam,都是采用可插拔方式使用。

CSV存储引擎使用在数据快速导入导出,表格直接转换成CSV

Archive存储引擎占用空间在最少,100万数据占用3M存储,而innodb占大约27M。

Memory存储引擎,主要存储在内存中大小默认16M,缓存存储引擎,也可用于临时表,热数据

Myisam:文件MYD、MYI分别存储数据和索引,Frm表定义文件,表级锁、不支持事务

Innodb:支持事务ACID,行级锁,以聚集索引存储数据,支持外键保证数据完整性(不建议使用)

事务机制

事务

ACID:原子性、一致性、隔离性、持久性;

由于并发带来了事务性,那事务带来的实际问题:

脏读、可重复读、幻读三个问题;

为了解决或者容忍它带来的问题,mysql定义了四种隔离级别:

RU:未提交读(啥也没解决)

RC:提交读(解决脏读)

RR:可重复读(解决重复读)(innodb中解决了幻读问题,所以nb)

serializable串行化(都解决了)

InnoDB的行锁是通过加在了索引项上来实现的,只有通过索引条件进行的检索才使用行级锁,否则也是表锁,锁住表中所有记录;

根据锁住的类型分为:

  • 共享锁(行锁):s锁,又称读锁

只能读不能修改,加锁方式,在语句后+ LOCK IN SHARE MODE;

  • 排它锁(行级):X锁,又称写锁

只有获取到了才能读取或者修改,加锁方式,增删改默认就加了,读语句+FOR UPDATE;

  • 意向共享锁IS(表锁):其实就是表的标志位,提供给其他的调用者查看是否该表已经有s锁了

即一个数据行加共享锁前必须先取得该表的IS锁,意向共享锁之间是可以相互兼容的

  • 向排他锁IX表锁):其实就是表的标志位,提供给其他的调用者查看是否该表已经有x锁了

即一个数据行加排他锁前必须先取得该表的IX锁,意向排它锁之间是可以相互兼容的

意向锁(IS、 IX)是InnoDB数据操作之前自动加的, 不需要用户干预,意义:当事务想去进行锁表时, 可以先判断意向锁是否存在, 存在时则可快速返回该表不能启用表锁

  • 自增锁:提示就是自增id不连续的问题,给自增id的具体一个数据加了锁

根据锁的算法又划分:

  • 记录锁

根据索引项查找数据项,锁住具体某一条数据项

  • 间隙锁

根据索引项查找数据项,没有命中数据时候,根据左开右开建立区间,将假设命中数据区间锁住,GAP锁仅仅存在在RR事务级别中

  • 临界锁

当索引查找的是范围时,根据左开右闭建立区间,将命中数据区间和下个区间锁住,这个也是RR为啥能解决幻读的问题,因为这些区间在叶子节点存储数据是有序的,锁住后,不能修改了。

通过锁很大程度上解决了事务的一部分问题,但并没有真正解决所有问题。

MVCC(并发多版本控制)

在并发事务中,为了提高效率引入了MVCC,避免写的时候,无法读引起的并发问题;

MVCC中默认的每个表有DB_TRX_ID数据行的版本号列和DB_ROLL_PT删除版本号列

  • 具体如何操作

插入数据,将本事务的版本号赋值给【数据行的版本号】;

如果删除以索引为查找的某行时,将该删除行复制一条数据放在表中,该事务的操作的版本号赋值给复制的这条数据的【删除版本号列】

那么,(本事务未提交)在另一个事务中查找时,会查找出来,数据版本小于当前版本的数据行(确保读取的是以前存在的,要么是自己修改或插入过得),查找删除版本大于当前版本或者null的行(确保查找出来的记录是在事务开启之前没有被删除的)。

  • 问题

及时加了MVCC,依然没有解决脏读问题,因为只解决了先查后改的问题,没有解决先改后查

  • 为了真正解决问题,提出了Undo log+ Redo log

  • Undo log:

为了实现事务的原子性而出现的产物,指事务开始之前, 在操作任何数据之前,首先将需操作的数据备份到一个地方 (Undo Log),以撤销操作为目的, 返回指定某个状态的操作,Undo 中的数据可作为数据旧版本快照供其他并发事务进行快照读

解释一下:快照读和当前读

快照读:SQL读取的数据是快照版本, 也就是历史版本, 普通的SELECT就是快照读

innodb快照读, 数据的读取将由 cache(原本数据) + undo(事务修改过的数据) 两部分组成

当前读:SQL读取的数据是最新版本。 通过锁机制来保证读取的数据无法通过其他事务进行修改UPDATE、 DELETE、 INSERT、 SELECT … LOCK IN SHARE MODE、 SELECT … FOR UPDATE都是当前读

-Redo log:

不是随着事务的提交才写入的, 而是在事务的执行过程中, 便开始写入redo 中。 具体的落盘策略可以进行配置,指事务中操作的任何数据,将最新的数据备份到一个地方 (Redo Log), 以恢复操作为目的, 重现操作;为了实现事务的持久性而出现的产物

Redo Log实现事务持久性:防止在发生故障的时间点, 尚有脏页未写入磁盘, 在重启mysql服务的时候, 根据redolog进行重做, 从而达到事务的未入磁盘数据进行持久化这一特性。

Redo log的位置:可通过innodb_log_group_home_dir 配置指定目录存储

Redo buffer 持久化Redo log的策略选择0,1,2

大概的意思是:

优化

Mysql使用半双工进行通信-没必要将消息切成块传输,而是增量传输,提高效应效率

msyql一般会先对sql语句通过查询优化器进行优化

如:等价变换规则,基于联合索引,调整条件位置等

优化count/min/max函数,min只需找叶子节点的最左侧就行,max只需找最右侧就行,myisam天然就有count支持

覆盖索引

子查询优化

提前终止查询:如limit

in优化,使用二分查找,所以别用or用in

执行计划

使用explain可以查看某条查询语句的执行计划

  • 如果有多条执行计划,根据id的序列号,执行顺序是:

id相同,由上向下执行

id不同,id越大约先执行

id很多,根据id越大组执行,同组由上至下

  • 阅读执行计划

    • select_type(查询类型,包括普通、联合、子查询)

    SIMPLE: 简单的select查询, 查询中不包含子查询或者union

    PRIMARY: 查询中包含子部分, 最外层查询则被标记为primary

    SUBQUERY/MATERIALIZED: SUBQUERY表示在select 或 where列表中包含了子查询

    MATERIALIZED表示where 后面in条件的子查询

    UNION: 若第二个select出现在union之后, 则被标记为union;

    UNION RESULT: 从union表获取结果的select

    • table(所及到的表)

    一般是表名或者别名

    <unionM,N> 由id为m,n查询union产生的结果

    由Id为N查询生成的结果 - type(最重要的) system:表只有一行记录,就是系统表,基本不会出现 const:通过索引一次就查找到了,const用于比较primary key 或者unique索引 eq_ref:唯一索引扫描,对于每个索引键,表中只有一条记录与之匹配,常见主键或者唯一索引 ref:非唯一性索引,返回匹配某个单独值得所有行,本质也是一种索引访问 range:只检索给定范围的行,使用一个索引来选择行 index:Full Index Scan ,索引全表扫描,把索引从头到尾部扫一遍 All:遍历全表以找到所有匹配行 - possible_keys (查询可能会用到的索引) - key(实际使用的索引,如果为NULL,则没有使用索引) - rows(大致为了找到行而扫描的行数) - filtered(返回结果行与读取行数百分比,越大越好) -Extra Using filesort :使用外部文件排序,而不是使用表内索引排序读取 Using temporary:使用临时表保存中间结果,常见order by或者group by Using index: 表示使用了覆盖索引 Using where:使用了where过滤条件 select tables optimized away:基于索引优化MIN/MAX操作或者MyISAM存储引擎优化COUNT()操作, 不必等到执行阶段在进行计算, 查询执行计划生成的阶段即可完成优化

优化配置

全局配置文件配置

  • max_connections:最大连接数的配置

取决于系统句柄数的配置:如/etc/security/limits.conf,使用ulimit -a 可以查看

除了系统限制外,还受限于mysql句柄限制:如/usr/lib/systemd/system/mysqld.service

  • sort_buffer_size 每一个conntion排序缓存区大小,建议256K(默认值)-> 2M之内

  • join_buffer_size connection关联查询缓冲区大小,建议256K(默认值)-> 2M之内

上述配置4000连接占用内存:

4000*(0.256M+0.256M) = 2G

  • Innodb_buffer_pool_size(innodb buffer/cache的大小默认128M)

数据缓存、索引缓存、缓冲数据、内部结构

大的缓冲池可以减小多次磁盘I/O访问相同的表数据以提高性能

参考计算公式:Innodb_buffer_pool_size = (总物理内存 - 系统运行所用 - connection 所用) * 90%

  • wait_timeout 服务器关闭非交互连接之前等待活动的秒数

  • innodb_open_files 限制Innodb能打开的表的个数

  • innodb_lock_wait_timeout InnoDB事务在被回滚之前可以等待一个锁定的超时秒数

数据库设计的优化

参见:58同城30条

常用语句

show create table bas_file;//查看创建表的语句,发现 ENGINE=InnoDB DEFAULT

show variables like ‘datadir’;//查看数据存储位置

show full processlist / show processlist;//查看连接及状态

kill [id]; //杀死某连接

show variables like ‘query_cache%’;//查询缓存情况

explain select * … ;//查看执行计划

begin / start transaction ;开始事务

commit / rollback ;//事务提交或回滚

set global autocommit = ON/OFF;//是否执行事务

mysql –help ;//寻找配置文件的位置和加载顺序

注意:

全局参数的设定对于已经存在的会话无法生效

会话参数的设定随着会话的销毁而失效

全局类的统一配置建议配置在默认配置文件中, 否则重启服务会导致配置失效



Comments