MySQL 知识

事务

事务特点:ACID

原子性(atomicity)
一个事务必须被视为一个不可分割的最小工作单元,整个事务要么全部提交成功,要么全部失败回滚
一致性(consistency)
数据库总是从一个一致性的状态转换到另外一个一致性的状态。也就是事务没有进行提交不会将数据保存到数据库中
隔离性(isolation)
一个事务所做的修改在最终提交以前, 对其他事务不可见
持久性(durability)
一旦事务提交,则其所做的修改就会永久保存导数据中

事务隔离

MySQL 的事务隔离是在 MySQL. ini 配置文件里添加的,在文件的最后添加:transaction-isolation = REPEATABLE-READ

可用的配置值:READ-UNCOMMITTED、READ-COMMITTED、REPEATABLE-READ、SERIALIZABLE。

隔离级别

隔离级别 说明 脏读可能性 不可重复可读性 幻读可能性 加锁读
read uncommitted 未提交读 Yes Yes Yes NO
read committed 提交读 NO Yes Yes NO
repeatable read 可重复读 NO NO Yes NO
serializable 可串行化 NO NO NO Yes
  • 脏读: 事务可以读取未提交的数据
  • 不可重复读: 一个事务从开始直到提交之前,所做的任何修改,其他事务不可见
  • 幻读:当某个事务在读取某个范围内的纪录时,另一个事务又在该范围内插入了新的纪录,当之前的事务再次读取该范围的纪录行,会产生幻行
  • 加锁读:在读取每一行数据都加上锁

多版本并发控制协议:MVCC

MySQL InnoDB存储引擎,实现的是基于多版本的并发控制协议——MVCC (Multi-Version Concurrency Control)
可以认为MVCC是行级锁的一个变种

InnoDB的MVCC:

  • 原理:
    是通过在每行纪录后面保存两个隐藏的列来实现的。这两个列,一个保存了行的创建时间,一个保存行的过期时间(或删除时间)。当然存储的并不是实际的时间值,而是系统版本号
  • 实现过程:
    • select:
      InnoDB会根据以下两个条件检查每行纪录:

      • InnoDB值查找版本早于当前事务版本的数据行,这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的
      • 行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读取到的行,在事务开始之前未被删除

      只有符合上述两个条件的纪录,才能返回作为查询的结果

    • insert:
      InnoDB为新插入的每一行保存当前系统版本号作为行版本号

    • delete:
      InnoDB为删除的每一行保存当前版本号作为行的删除标识

    • update:
      InnoDB为插入一行新纪录,保存当前版本号作为行版本号,同时保存当前系统版本号到原来的行作为行的删除标识

事务使用

START TRANSACTION | BEGIN [WORK] 
COMMIT [WORK] [AND [NO] CHAIN] [[NO] RELEASE] 
ROLLBACK [WORK] [AND [NO] CHAIN] [[NO] RELEASE] 
SET AUTOCOMMIT = {0 | 1}
  • START TRANSACTION 或 BEGIN 语句:开始一项新的事务。
  • COMMIT 和 ROLLBACK:用来提交或者回滚事务。
  • CHAIN 和 RELEASE 子句:分别用来定义在事务提交或者回滚之后的操作,CHAIN 会立即启动一个新事物,并且和刚才的事务具有相同的隔离级别,RELEASE 则会断开和客户端的连接。
  • SET AUTOCOMMIT 可以修改当前连接的提交方式, 如果设置了 SET AUTOCOMMIT=0,则设置之后的所有事务都需要通过明确的命令进行提交或者回滚

行级锁、表锁、页锁,MyISAM 只支持表锁,InnoDB 支持表锁和行锁,默认为行锁。

  • 表级锁:开销小,加锁快,不会出现死锁。锁定粒度大,发生锁冲突的概率最高,并发量最低。

  • 行级锁:开销大,加锁慢,会出现死锁。锁力度小,发生锁冲突的概率小,并发度最高。

说一下乐观锁和悲观锁?

  • 乐观锁:每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在提交更新的时候会判断一下在此期间别人有没有去更新这个数据。

  • 悲观锁:每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻止,直到这个锁被释放。

存储引擎

常用的引擎有InnoDB、MyISAM、Memory,当然经过MySQL的不断迭代和升级,目前最常用的是InnoDB,除非使用一些InnoDB没有特性,其余的情况下,建议使用InnoDB,因为在mysql5.5版本开始InnoDB已经成为MySQL的默认引擎,说明其优势有目共睹的,同时,InnoDB支持事务,在读写方面也有了很大的提升

区别

item MyISAM InnoDB
事务 不支持 支持
外键 不支持 支持
表级锁 行级锁
索引 支持全文索引 5.6.4版本后支持全文索引
索引 非聚簇索引 聚簇索引
count(*) 快,因为内部维护计数器,但存在where时,不会变快
MVCC 不支持 支持
系统奔溃 困难
存储文件 三个,表结构,索引,数据 两个,表结构,索引和数据

注意

  1. 聚簇索引:数据文件和索引文件绑定在一起的,必须要有主键,通过主键索引效率很高,但是辅助索引需要两次查询,先查询到主键,然后再通过主键查询到数据。因此主键不应该过大,因为主键太大,其他索引也都会很大。
  2. 非聚簇索引: 数据文件是分离的,索引保存的是数据文件的指针。主键索引和辅助索引是独立的。

索引

索引类型

  • 普通索引
  • 唯一索引
  • 主键索引
  • 全文索引
  • 组合索引

索引的优点

  • 索引大大减少了服务器需要扫描的数据量
  • 所以可以帮助服务器避免排序和临时表
  • 索引可以将随机I/O变为顺序I/O

索引方法

  • B-Tree
  • HASH

B-Tree

  • 原理
    • 多叉平衡查找树
    • B-Tree
      MySQL 知识_第1张图片
    • B+Tree
      MySQL 知识_第2张图片

Hash

  • 原理

    • 采用一定的哈希算法,把键值换算成新的哈希值
      MySQL 知识_第3张图片
  • 优点

    • 在没有大量重复键值时,等值查询,Hash索引查询速度非常快
  • 缺点

    • Hash索引只能满足 “=” ,“IN”,“<=>”查询,不能进行范围查询
    • 在联合索引中, Hash要么全部使用索引,要么全部不使用索引,无法像BTree那样使用左前缀规则
    • Hash在遇见大量重复值时,效率不一定比BTree高

B树和B+树的区别

1、B树,每个节点都存储key和data,所有节点组成这棵树,并且叶子节点指针为nul,叶子结点不包含任何关键字信息。
MySQL 知识_第4张图片

2、B+树,所有的叶子结点中包含了全部关键字的信息,及指向含有这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大的顺序链接
MySQL 知识_第5张图片

所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字。 (而B 树的非终节点也包含需要查找的有效信息)

MySQL联合索引

1、联合索引是两个或更多个列上的索引。

对于联合索引:Mysql从左到右的使用索引中的字段,一个查询可以只使用索引中的一部份,但只能是最左侧部分。

索引:	最左原则
	a  b  c   
	a  b
	a
	a  c  // 只使用 a 的索引
	b  c  // 无法锁定索引
匹配:
	全值匹配
	a = 2 and b = 2 and c =2

	匹配最左前缀
	a = 2
	匹配列前缀
	a like '2%'
	匹配范围值
	a BETWEEN 2 and 10

	精确匹配某一列并范围匹配另一列
	a = 2 and b like '2%'
	只访问索引的查询
注意: 范围查询后的所有索引无法使用

什么情况下应不建或少建索引

1、表记录太少

2、经常插入、删除、修改的表

3、数据重复且分布平均的表字段,假如一个表有10万行记录,有一个字段A只有T和F两种值,且每个值的分布概率大约为50%,那么对这种表A字段建索引一般不会提高数据库的查询速度。

4、经常和主字段一块查询但主字段索引值比较多的表字段

分区

什么是表分区?

分区表是一个独立的逻辑表,但底层由多个物理子表组成。从底层文件系统来看,每一个分区表都有一个使用 # 分隔命名的表文件

表分区与分表的区别

分表:指的是通过一定规则,将一张表分解成多张不同的表。比如将用户订单记录根据时间成多个表。

分表与分区的区别在于:分区从逻辑上来讲只有一张表,而分表则是将一张表分解成多张表。

表分区有什么好处?

  • 表非常大以至于无法全部都放在内存中,或者只在表的最后部分有热点数据,其他均是历史数据
  • 分区表的数据更容易维护,例如:想批量删除大量数据可以清除整个分区
  • 分区表的数据可以分布在不同的物理设备上,从而高效的利用多个硬件设备
  • 可以使用分区表来避免某个特殊的瓶颈,例如InnoDB的单个索引的互斥访问
  • 优化查询。在where语句中包含分区条件时,可以只扫描一个或多个分区表来提高查询效率

分区表的限制因素

  • 一个表最多只能有1024个分区
  • MySQL5.1中,分区表达式必须是整数,或者返回整数的表达式。在MySQL5.5中提供了非整数表达式分区的支持。
  • 如果分区字段中有主键或者唯一索引的列,那么多有主键列和唯一索引列都必须包含进来。即:分区字段要么不包含主键或者索引列,要么包含全部主键和索引列。
  • 分区表中无法使用外键约束

如何判断当前MySQL是否支持分区?

命令:show variables like '%partition%' 运行结果:

have_partintioning 的值为YES,表示支持分区。

MySQL支持的分区类型有哪些?

  • RANGE分区: 这种模式允许将数据划分不同范围。例如可以将一个表通过年份划分成若干个分区
  • LIST分区: 这种模式允许系统通过预定义的列表的值来对数据进行分割。按照List中的值分区,与RANGE的区别是,range分区的区间范围值是连续的。
  • HASH分区 :这中模式允许通过对表的一个或多个列的Hash Key进行计算,最后通过这个Hash码不同数值对应的数据区域进行分区。例如可以建立一个对表主键进行分区的表。
  • KEY分区 :上面Hash模式的一种延伸,这里的Hash Key是MySQL系统产生的。

SQL 优化

表结构及字段

  • 表结构
    • 将字段很多的表分解成多个表
    • 增加相应的冗余字段
    • 增加中间表,提高查询效率
  • 字段
    • 遵循小而简单,选择简单的类型,并设置合适的大小
    • 尽可能使用not null,在某个情况下,可以使用其他值存储代替Null
    • 字段长度固定的表查询会更快

索引

  • 建立合适的索引,对联合索引中的字段进行分析,合理的创建索引以及顺序
    • 索引使用最左原则,比如: a b c , a b , a, 这中才能命中索引, a c中c字段是无法命中索引的
    • 依据最左原则, 当where中遇见范围查询时,范围查询后的所有索引无法使用
    • 尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,
  • 索引列不能参与计算
  • 尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。
  • 禁止给where中条件都单独设置一个索引,可以使用联合索引

SQL语句

  • 不要在where中 对索引列使用表达式或函数,将运算放在等号后面
  • 对于count(*)、count(列)、count(常量),在SQL92中count(*)是标准统计行数的语法,并且在效率上面与count(常量) 差不太多
  • like查询,不要以通配符开头,如果需要可以使用sphinx
  • 禁止使用select(*) ,因为会增加很多不必要的消耗(cpu、io、内存、带宽等),并且减少了覆盖索引的可能性
  • 对于大数据,进行分段操作
  • 对于联合索引,如果存在范围查询,比如between,<,>等条件时,会造成后面索引失效,如果范围值比较小,可以采取in进行替代
  • 尽量使用inner join,避免left join,因为如果连接方式是inner join,在没有其他过滤条件的情况下MySQL会自动选择小表作为驱动表
  • 避免使用JOIN关联太多的表
  • 尽量使用join代替子查询
  • 多表连接时,尽量小表驱动大表,即小表 join 大表
  • 在千万级分页时使用limit
  • 对于经常使用的查询,可以开启缓存
  • 尽量少用 or , 可以使用union all 来代替
  • 避免隐式类型转换

注意

写完sql , 尽量使用explain进行分析下,sql是否命中索引,以及是否有优化的空间

主从复制

  • 工作

    • 在主库上把数据更改纪录到二进制日志中
    • 备库将主库上的日志复制到自己的中继日志中
    • 备库读取中继日志中的事件,将其重放到备库数据之上
  • 具体步骤(此步骤适用于主库和从库都是刚搭建)

    • 在主库的my.cnf文件中增加或修改如下内容,并重启服务器:
    log_bin = mysql-bin
    # 介于 1 ~ 2^32 -1 之间
    server_id = 10 
    
    • 备库上也需要在my.cnf 中增加类似的配置,并重启服务器:
    log_bin = mysql-bin
    server_id  = 2
    # 指定中继日志的位置
    relay_log  = /var/lib/mysql/mysql-relay-bin  
    # 允许备库将其重放的时间也纪录到自身的二进制日志中
    log_slave_updates = 1           
    # 阻止任何没有特权权限的线程修改数据                   
    read_only  =1 
    
    • 启动复制
    mysql> change master to MASTER_HOST = ‘server1’,
           -> MASTER_USER = ‘repl’,
           -> MASTER_PASSWORD =‘password’,
           -> MASTER_LOG_FILE = ‘mysql-bin.000001’,
           -> MASTER_LOG_POS =0;
     注意:
       1、MASTER_LOG_POS 参数被设置为0,因为要从日志的开头读起。
       2、可以使用 SHOW SLAVE STATUS 语句检查复制是否正确执行
    
    • 开始复制
    mysql> START SLAVE
    

你可能感兴趣的:(MySQL)