mysql 问题与优化

作者: Tim在路上 | 来源:发表于2019-06-20 21:50 被阅读1次

    存储引擎

    InnoDB: 其数据文件本身就是索引文件。相比MyISAM,索引文件和数据文件是分离的,其表数据文件本身就是按B+Tree组织的一个索引结构,树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引。这被称为“聚簇索引(或聚集索引)”。而其余的索引都作为辅助索引,辅助索引的data域存储相应记录主键的值而不是地址,这也是和MyISAM不同的地方。在根据主索引搜索时,直接找到key所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,再走一遍主索引。 因此,在设计表的时候,不建议使用过长的字段作为主键,也不建议使用非单调的字段作为主键,这样会造成主索引频繁分裂。

    并发事务的问题?

    脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。

    丢失修改(Lost to modify): 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。 例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。

    不可重复读(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。

    幻读(Phantom read): 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。

    不可重复度和幻读区别:

    不可重复读的重点是修改比如多次读取一条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除比如多次读取一条记录发现记录增多或减少了。

    SQL 标准定义了四个隔离级别:

    READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。

    READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。

    REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。

    SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

    与 SQL 标准不同的地方在于 InnoDB 存储引擎在 REPEATABLE-READ(可重读)事务隔离级别下使用的是Next-Key Lock 锁算法,因此可以避免幻读的产生,这与其他数据库系统(如 SQL Server)是不同的。所以说InnoDB 存储引擎的默认支持的隔离级别是 REPEATABLE-READ(可重读) 已经可以完全保证事务的隔离性要求,即达到了 SQL标准的SERIALIZABLE(可串行化)隔离级别。

    存储引擎

    InnoDB支持行级锁(row-level locking)和表级锁,默认为行级锁

    InnoDB存储引擎的锁的算法有三种:

    Record lock:单个行记录上的锁

    Gap lock:间隙锁,锁定一个范围,不包括记录本身

    Next-key lock:record+gap 锁定一个范围,包含记录本身

    innodb对于行的查询使用next-key lock

    Next-locking keying为了解决Phantom Problem幻读问题

    当查询的索引含有唯一属性时,将next-key lock降级为record key

    Gap锁设计的目的是为了阻止多个事务将记录插入到同一范围内,而这会导致幻读问题的产生

    有两种方式显式关闭gap锁:(除了外键约束和唯一性检查外,其余情况仅使用record lock) A. 将事务隔离级别设置为RC B. 将参数innodb_locks_unsafe_for_binlog设置为1

    范式

    第一范式: 无重复的列

    第二范式: 实体的属性完全依赖于关键字,不能存在仅仅依赖于主键的部分属性

    第三范式: 表属性不包含其他表的非主属性

    mysql 细节

    • 优先使用 UNION ALL

    在明显不会有重复值时使用 UNION ALL 而不是 UNION
    •UNION 会把两个结果集的所有数据放到临时表中后再进行去重操作
    •UNION ALL 不会再对结果集进行去重操作

    • WHERE 从句中禁止对列进行函数转换和计算
      对列进行函数转换或计算时会导致无法使用索引

    where date(create_time)='20190101'

    在等式的左边禁止使用任何 函数操作

    • 对应同一列进行 or 判断时,使用 in 代替 or

    in 的值不要超过 500 个,in 操作可以更有效的利用索引,or 大多数情况下很少能利用到索引。

    • 充分利用表上已经存在的索引

    避免使用双%号的查询条件。如:a like '%123%',(如果无前置%,只有后置%,是可以用到列上的索引的)

    在定义联合索引时,如果 a 列要用到范围查找的话,就要把 a 列放到联合索引的右侧,使用 left join 或 not exists 来优化 not in 操作,因为 not in 也通常会使用索引失效。

    • 避免潜在的数据类型转换

    在MySQL跟Oracle中,如果存在隐式的数据类型转换,可能导致无法命中索引,从而进行全表扫描的危险。

    例如:

    错误范例(col1为字符串类型):

    select col1,col2 from tab1 where col1 > 10

    改善写法:

    select col1,col2 from tab1 where col1 > '10'

    • 尽量控制单表数据量的大小,建议控制在 500 万以内。
    • 尽可能把所有列定义为 NOT NULL

    原因:

    索引 NULL 列需要额外的空间来保存,所以要占用更多的空间

    进行比较和计算时要对 NULL 值做特别的处理

    • 如何选择索引列的顺序

    建立索引的目的是:希望通过索引进行数据查找,减少随机 IO,增加查询性能 ,索引能过滤出越少的数据,则从磁盘中读入的数据也就越少。

    区分度最高的放在联合索引的最左侧(区分度=列中不同值的数量/列的总行数)

    尽量把字段长度小的列放在联合索引的最左侧(因为字段长度越小,一页能存储的数据量越大,IO 性能也就越好)

    使用最频繁的列放到联合索引的左侧(这样可以比较少的建立一些索引)

    innodb的事务与日志的实现方式

    1、有多少种日志

    redo和undo

    2、日志的存放形式

    redo:在页修改的时候,先写到 redo log buffer 里面, 然后写到 redo log 的文件系统缓存里面(fwrite),然后再同步到磁盘文件( fsync)。
    Undo:在 MySQL5.5 之前, undo 只能存放在 ibdata文件里面, 5.6 之后,可以通过设置 innodb_undo_tablespaces 参数把 undo log 存放在 ibdata之外。

    3、事务是如何通过日志来实现的,说得越深入越好

    基本流程如下:
    因为事务在修改页时,要先记 undo,在记 undo 之前要记 undo 的 redo, 然后修改数据页,再记数据页修改的 redo。 Redo(里面包括 undo 的修改) 一定要比数据页先持久化到磁盘。 当事务需要回滚时,因为有 undo,可以把数据页回滚到前镜像的
    状态,崩溃恢复时,如果 redo log 中事务没有对应的 commit 记录,那么需要用 undo把该事务的修改回滚到事务开始之前。 如果有 commit 记录,就用 redo 前滚到该事务完成时并提交掉。

    相关文章

      网友评论

        本文标题:mysql 问题与优化

        本文链接:https://www.haomeiwen.com/subject/ajysqctx.html