Mysql 杂记(一)

回表:回到主键索引树搜索的过程,称为回表  

覆盖索引:某索引已经覆盖了查询需求,称为覆盖索引,例如:select ID from T where k be  tween 3 and 5  在引擎内部使用覆盖索引在索引K上其实读了三个记录,R3~R5(对应的索引k上的记录项),但  对于MySQL的Server层来说,它就是找引擎拿到了两条记录,因此MySQL认为扫描行数是2  

最左前缀原则:B+Tree这种索引结构,可以利用索引的"最左前缀"来定位记录  只要满足最左前缀,就可以利用索引来加速检索。  最左前缀可以是联合索引的最左N个字段,也可以是字符串索引的最左M个字符  第一原则是:如果通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑  采用的。  

索引下推:在MySQL5.6之前,只能从根据最左前缀查询到ID开始一个个回表。到主键索引上  找出数据行,再对比字段值。  MySQL5.6引入的索引下推优化,可以在索引遍历过程中,对索引中包含的字段先做判断,直  接过滤掉不满足条件的记录,减少回表次数。

二级索引(普通索引)比主键索引快:前提是普通索引不需要回表

总结一

1. 表的逻辑结构 ,表 —> 段 —> 段中存在数据段(leaf node segment) ,索引段( Non-leaf  node segment),请问数据段就是主键索引的数据, 索引段就是二级索引的数据么

 2. 建立的每个索引都有要维护一个数据段么 ?? 那么新插入一行值 , 岂不是每个索引段都  会维护这个值  

3. 索引的n阶表示n个数据页么。那是不是插入第一行数据树高1 ,就是一个数据页, 插入二  三行,树高是二,那就是两个数据页,而且B+树只有leaf node存数据,所以父节点实际上有  没有数据,但是占一个页 ,好浪费 , 是我理解有误么  

4. 树高取决于数据页的大小么 , 这个不是很能理解 ,数据页为16k 。 那么树高不是一个定  值了么,难道还和里面存数据的大小有关么  

5. 查询数据的时候,大致的流程细化来说 ,我这么理解对么 。 通过优化器到表里的数据段/  索引段取数据 ,数据是按照段->区->页维度去取 , 取完后先放到数据缓冲池中, 分法查询叶结点的有序链表数组找到行数据返回给用户 。 当数据量大的时候,会存在不同的  区,取范围值的时候会到不同的区取页的数据返回用户。

回答:

1. 这样理解也算对,不过要记得 主键也是索引的一种哈

2. 是的,所以说索引越多,“维护成本”越大

3. 如果是几百个儿子节点共用一个父节点,是不是就不会看上去那么浪费啦

4. 树高其实取决于叶子树(数据行数)和“N叉树”的N。 而N是由页大小和索引大小决定的。

5. 基本是你说的流程。不过不是“优化器”去取的,是执行器调用引擎,引擎内部才管理了你 说的 段、页这些数据

总结二 TRUNCATE、Drop、Delete区别


1.drop和delete只是删除表的数据(定义),drop语句将删除表的结构、被依赖的约束(constrain)、触发器 (trigger)、索引(index);依赖于该表的存储过程/函数将保留,但是变为invalid状态。

2.delete语句是DML语言,这个操作会放在rollback segement中,事物提交后才生效;如果有相应的触发器(trigger),执行的时候将被触发。truncate、drop是DDL语言,操作后即 生效,原数据不会放到rollback中,不能回滚,操作不会触发trigger。

3.delete语句不影响表所占用的extent、高水线(high watermark)保持原位置不动。drop语句将表所占用的空间全部释放。truncate语句缺省情况下将空间释放到minextents的 extent,除非使用reuse storage。truncate会将高水线复位(回到最初)。

4.效率方面:drop > truncate > delete

5.安全性:小心使用drop与truncate,尤其是在 没有备份的时候,想删除部分数据可使用delete需要带上where子句,回滚段要足够大,想删除表可以用drop,想保留表只是想删除表的所有数据、 如果跟事物无关可以使用truncate,如果和事物有关、又或者想触发 trigger,还是用delete,如果是整理表内部的碎片,可以用truncate跟上reuse stroage,再重新导入、插入数据。

6.delete是DML语句,不会自动提交。drop/truncate都是DDL语句,执行后会自动提交。

7、drop一般用于删除整体性数据 如表,模式,索引,视图,完整性限制等;delete用于删除局部性数据 如表中的某一元组

8、DROP把表结构都删了;DELETE只是把数据清掉

9、当你不再需要该表时, 用 drop;当你仍要保留该表,但要删除所有记录时, 用 truncate;当你要删除部分记录时(always with a WHERE clause), 用 delete.

 

总结三

根据加锁范围:MySQL里面的锁可以分为:全局锁、表级锁、行级锁

一、全局锁: 对整个数据库实例加锁。

MySQL提供加全局读锁的方法:Flush tables with read lock(FTWRL) 这个命令可以使整个库处于只读状态。

使用该命令之后,数据更新语句、数据定义语句和更新 类事务的提交语句等操作都会被阻塞。

使用场景:全库逻辑备份。

风险:

1.如果在主库备份,在备份期间不能更新,业务停摆

2.如果在从库备份,备份期间不能执行主库同步的binlog,导致主从延迟 官方自带的逻辑备份工具mysqldump,当mysqldump使用参数--single-transaction的时 候,会启动一个事务,确保拿到一致性视图。而由于MVCC的支持,这个过程中数据是可以正 常更新的。

一致性读是好,但是前提是引擎要支持这个隔离级别。 如果要全库只读,为什么不使用set global readonly=true的方式?

1.在有些系统中,readonly的值会被用来做其他逻辑,比如判断主备库。所以修改global变量 的方式影响太大。

2.在异常处理机制上有差异。如果执行FTWRL命令之后由于客户端发生异常断开,那么MySQ L会自动释放这个全局锁,整个库回到可以正常更新的状态。

而将整个库设置为readonly之 后,如果客户端发生异常,则数据库就会一直保持readonly状态,这样会导致整个库长时间处 于不可写状态,风险较高。

二、表级锁 MySQL里面表级锁有两种

一种是表锁,一种是元数据所(meta data lock,MDL) 表锁的语法是:lock tables ... read/write 可以用unlock tables主动释放锁,也可以在客户端断开的时候自动释放。lock tables语法除 了会限制别的线程的读写外,也限定了本线程接下来的操作对象。

对于InnoDB这种支持行锁的引擎,一般不使用lock tables命令来控制并发,毕竟锁住整个表 的影响面还是太大。

MDL:不需要显式使用,在访问一个表的时候会被自动加上。 MDL的作用:保证读写的正确性。 在对一个表做增删改查操作的时候,加MDL读锁;当要对表做结构变更操作的时候,加MDL 写锁。 读锁之间不互斥。读写锁之间,写锁之间是互斥的,用来保证变更表结构操作的安全性。 MDL 会直到事务提交才会释放,在做表结构变更的时候,一定要小心不要导致锁住线上查询 和更新。

总结四

如果mysqldump 备份的是整个schema,某个小表t1只是该schema上其中有一张表  

情况1:  master上对小表t1的DDL传输到slave去应用的时刻,mysqldump已经备份完了t1表的数据,  此时slave 同步正常,不会有问题。  

情况2:  master上对小表t1的DDL传输到slave去应用的时刻,mysqldump正在备份t1表的数据,此时会  发生MDL 锁,从库上t1表的所有操作都会Hang 住。  

情况3:  master 上对小表t1的DDL传输到slave去应用的时刻,mysqldump 还没对t1表进行备份,该D  DL会在slave的t1表应用成功,但是当导出到t1表的时候会报“ERROR 1412 (HY000): Table d  efinition has changed, please retry transaction” 错误,导致导出失败

总结五 update 一个没有索引的列会怎样

innodb行级锁是通过锁索引记录实现的。如果update的列没建索引,即使只update一条记录  也会锁定整张表吗?比如update t set t.name='abc' where t.name='cde'; name字段无索  引。为何innodb不优化一下,只锁定name='cde'的列?

第一个问题是好问题,我加到答疑文章中。简单的回答:是的。但是你可以再往前考虑一下,  如果是 你的update 语句后面加个limit 1, 会怎么锁?  Innodb支持行锁,没有支持“列锁” 哈

 

总结六  怎么减少行锁对性能的影响?

两阶段锁:在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释 放, 而是要等到事务结束时才释放。  建议:如果你的事务中需要锁多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往 后放。  

死锁:当并发系统中不同线程出现循环资源依赖,涉及的线程都在等待别的线程释放资源时,  就会导致这几个线程都进入无限等待的状态。  

     解决方案:  

          1、通过参数 innodb_lock_wait_timeout 根据实际业务场景来设置超时时间,InnoDB引擎默  认值是50s。  

          2、发起死锁检测,发现死锁后,主动回滚死锁链条中的某一个事务,让其他事务得以继续执  行。将参数 innodb_deadlock_detect 设置为 on,表示开启这个逻辑(默认是开启状态)。  

如何解决热点行更新导致的性能问题?  

      1、如果你能确保这个业务一定不会出现死锁,可以临时把死锁检测关闭掉。一般不建议采用

      2、控制并发度,对应相同行的更新,在进入引擎之前排队。这样在InnoDB内部就不会有大量  的死锁检测工作了。

     3、将热更新的行数据拆分成逻辑上的多行来减少锁冲突,但是业务复杂度可能会大大提高。  innodb行级锁是通过锁索引记录实现的,如果更新的列没建索引是会锁住整个表的。

 

总结七 普通索引和唯一索引应该怎么选择

 

这两类索引在查询能力上  是没差别的,主要考虑的是对更新性能的影响。所以,尽量选择普通索引

 

对于唯一索引来说,所有的更新操作都要先判断这个操作是否违反唯一性约束。

比如,要插入  (4,400) 这个记录,就要先判断现在表中是否已经存在 k=4 的记录,而这必须要将数据页读入内 存才能判断。如果都已经读入到内存了,那直接更新内存会更快,就没必要使用 change buffer  了。  因此,唯一索引的更新就不能使用 change buffer,实际上也只有普通索引可以使用。  

change buffer 用的是 buffer pool 里的内存,因此不能无限增大。change buffer 的大小,可以通过参数innodb_change_buffer_max_size 来动态设置。这个参数设置为 50 的时候,表示  change buffer 的大小最多只能占用 buffer pool 的 50%。  

现在,你已经理解了 change buffer 的机制,那么我们再一起来看看如果要在这张表中插入一个  新记录 (4,400) 的话,InnoDB 的处理流程是怎样的。  

第一种情况是,这个记录要更新的目标页在内存中。

这时,InnoDB 的处理流程如下:  对于唯一索引来说,找到 3 和 5 之间的位置,判断到没有冲突,插入这个值,语句执行结  束;  对于普通索引来说,找到 3 和 5 之间的位置,插入这个值,语句执行结束。  这样看来,普通索引和唯一索引对更新语句性能影响的差别,只是一个判断,只会耗费微小的  CPU 时间。  但,这不是我们关注的重点。

第二种情况是,这个记录要更新的目标页不在内存中。

这时,InnoDB 的处理流程如下:  对于唯一索引来说,需要将数据页读入内存,判断到没有冲突,插入这个值,语句执行结  束;  对于普通索引来说,则是将更新记录在 change buffer,语句执行就结束了。 将数据从磁盘读入内存涉及随机 IO 的访问,是数据库里面成本最高的操作之一。change buffer  因为减少了随机磁盘访问,所以对更新性能的提升是会很明显的。  之前我就碰到过一件事儿,某个业务的库内存命中率突(使用hit rate 查看命中率)  然从 99% 降低到了 75%,整个系统处于阻塞状态,更新语句全部堵住。而探究其原因后,发现这个业务有大量插入数据的操作,是在前一天把其中的某个普通索引改成了唯一索引。

如果某次写入使用了 change buffer 机制,之后主机异常  重启,是否会丢失 change buffer 和数据。  这个问题的答案是不会丢失,留言区的很多同学都回答对了。虽然是只更新内存,但是在事务提  交的时候,我们把 change buffer 的操作也记录到 redo log 里了,所以崩溃恢复的时候,  change buffer 也能找回来。

总结8  mysql为什么有时候会选择错索引

一个表有两个索引  index(a),index(b) 十万条数据

是优化器的索引选择,从而牵引出索引统计的  更新机制,以及通过Analyze tabe来进行分析索引。当然有时避免不了可以选择简单粗暴的用  Force index来进行索引选择。 

索引选择方案,

一、通过Force index强行选择一  个索引。

二、修改语 句,引导 MySQL 使用我们期望的索引。

现在 order by b,a 这种写法,要求按照 b,a 排序,就意味着使用这两个索引都需要排序。因  此,扫描行数成了影响决策的主要条件,于是此时优化器选了只需要扫描 1000 行的索引 a。 

三、通过提供一个更合适的索引来给优化器做出  选择。

创建一个新的索引或者是删除索引b。

 

总结9 怎么给字符串字段加索引

 

要嘛直接加 index(String)  但是这样索引所占的空间就会很大,如果使用前缀索引 index2(String(length))  索引占的空间就会更小,但是相应的扫描行数就会变多,而且不能使用覆盖索引

使用这个sql来确定length的长度,length合适时查出来的数据越少就越好这样执行sql时扫描行数就更少

> select count(distinct String(length)) as L from SUser;

但是:

select id,name,email from SUser where email='[email protected]';

就算是index(email(length) ,name) 覆盖了 name email id的信息,也会进行回表查询,并不能使用覆盖索引

 还有就是如果前缀索引不行,区分度不高,可以把 字段(如 身份证 身份证前几位号码区分度不高)搞成一个hash值存储为一个新字段,缺点是不支持范围查询

总结10 为什么mysql会抖一下

redo-log内存满了,不停的要刷脏页回磁盘。现象就会是发现机器io不高,但是mysql明显的  卡顿。

 redo-log(保障了 ACID  中的D  也就是持久性)

 

你可能感兴趣的:(Mysql)