MySQL笔记

MySQL使用原则:

  1. 自己扛住的自己扛,扛不住的使用中间件,比如:Redis;
  2. 最佳的单表存储量是千万级别,超过这个级别采取分表分布的方式来存储表。

MySQL体系架构

MySQL语句在数据库的执行过程:

  1. 经过parser进行词法解析和语法解析;
  2. 解析之后经过优化器生成执行计划,MySQL不缓存执行计划(也叫做硬解析);
  3. 执行计划进入执行器执行,到存储引擎中找数据;
  4. 返回数据。

MySQL包括核心层(mysqld)和存储引擎层,前者在内存和CPU中执行,后者在硬盘中,指的是不同的存储方法,最主要的两种是InnoDB和MyISAM

MySQL一次读取数据为16K,每个16K叫做page(页),

InnoDB
包括主键索引和辅助索引(又叫K值索引,二级索引),主键索引的叶子节点存放的就是记录,辅助键索引叶子结点存放的是索引字段和主键,通过索引字段查找对应的记录,根据该记录中的主键去主键索引找对应的记录。

建表时,如果没有声明主键,InnoDB会通过第一个非空且唯一的字段建立主索引,没有的话,会建立隐藏列(6字节)。

索引是一颗B+树,所有的叶子节点是一个双向链表,在插入数据维护索引时,可能会出现页分裂的情况。

根据辅助索引查询数据时,如果需要查询除主键索引外的数据,那么需要回表(辅助索引到主键索引的过程),但是如果只查找主键,就不需要回表。

联合索引
多个字段联合建立索引。

最左前缀原则
当你定义了联合索引时,在执行查找时,只有where字句出现的字段与联合索引出现的顺序一致,才能使用上索引。

支持事务。

表在存储引擎中被存放到两个文件,.idb文件存放的实际数据,.frm文件存放的是表的定义,由于系统中文件大小有限制,因此表存储的数据量有限制。

innodb buffer pool - 数据和索引都能缓存,MyISAM只能缓存索引。
MySQL查询数据时取表中16K的数据放到Innodb buffer pool做查询,满足查询条件的数据留下,继续扫描直到扫描了全表,同时,也存储了索引。
相关参数:
innodb_buffer_pool_size - innodb buffer pool在内存里的大小;
innodb_buffer_pool_instance - innodb buffer pool被分为几块,一般设置成16或者32。
innodb buffer pool使用LRU(Least Recent Used,最近最少使用)算法存储缓存在内存中的数据,默认页的大小是16K。
查询机制

  1. 如果要查询到数据所在页不在Buffer Pool中,把该页从磁盘加载到Buffer Pool中的缓存页时,将该缓存页包装成节点塞到链表的头部;
  2. 如果该页在Buffer pool中,则直接把该页对应的LRU链表节点移动到链表的头部。
  3. LRU链的前半部分称为热端,后半部分称为冷端。
  4. 缓存页直接放在热端带来的问题是,访问极少使用的表后,对应的页会放置在热端,造成原本频繁访问的数据被淘汰,后期再访问时存在大量磁盘IO,因此为了优化这一问题,在LRU中设置了midpoint,默认情况下新缓存页第一次插入到LRU列表的5/8处,再次查询时再放到LRU链表头部。

update机制
事务:对数据做修改,以commit或者rollback结束的过程。
假设当前有个用户对某条数据进行更新,执行如下语句:update emp set name=JACK where empno=7369;,随后又来一个用户读取同一条数据,select * form emp where empno=7369;,数据库默认不执行commit,其流程如下:

  1. 读线程io_read_thread从存储引擎的*.idb文件中找到该行数据,并读取到innodb buffer pool中,并将修改前的数据写到UNDO中;
  2. 写线程io_write_thread对innodb buffer pool中的数据进行修改,此时,事务未进行commit时,该条数据在innodb buffer pool和存储引擎中的数据会不一致,在innodb buffer pool中的是否在事务中字段会存储是,UNDO字段中会存放该条数据前镜像的地址**;
  3. 后一个用户此时读数据时,读线程在innodb buffer pool中找到该条数据后,根据事务字段判断该条数据处于事务未提交状态,然后根据UNDO字段找到数据存储在ibdata1文件(存储被修改前的数据)中的地址,并去读取。

UNDO文件(保证一致性)的作用:

  1. 事务未提交时作一致性读取;
  2. 事务回滚后从UNDO中找回更新前的数据。

数据库软件设计的原则

  1. 内存读优于磁盘读;
  2. 日志先行(先记再改)。

log buffer

  1. log buffer在内存中,记录下对数据库的update、delete和insert操作,由log thread(只有1个)执行;
  2. commit后,log buffer会写到磁盘中,存在ib_logfile0和ib_logfile1文件中,这两个文件称作redo log,当两个文件都写满之后,接下来数据库只能进行读,无法执行其他操作,这时的数据库状态叫做hung住。
  3. 写到磁盘的条件:1)达到1/2满;2)超过1M大小;3)commit(相关的参数:inndb_flush_log_at_trx_commit,这个参数=0时,表示log buffer每秒落一次盘;=1时表示每次commit就落盘,默认是1;);满足上述三个条件中的任意一个log buffer都会写入到磁盘中;
    解决系统因为redo日志无法覆盖造成hung住的解决方案:
  4. 调整redo日志大小;
  5. 调整redo日志文件数量。
    MySQL启动时会进行实例恢复,即重做redo日志。
    redo日志不能归档,即ib_logfile0和ib_logfile1文件写满后无法复制到其他文件中。

change buffer

  1. 在innodb buffer pool中,用来缓存用户在innodb buffer pool对数据进行修改且未提交的数据;
  2. 目的是减少落盘时磁头在不同盘块间的移动次数(即随机访问的次数);
  3. 不适合马上写马上读的操作;
  4. 唯一索引不能使用change buffer。

表连接

  1. 驱动表:表连接时,需要全表扫描的表,让非驱动表走索引。栗子:select * from t1 join t2 on(t1.a=t2.1); //其中t1是驱动表
  2. Index Nested-Loop Join(Index NLJ,嵌套循环连接):非驱动表走索引,优化的方向;
  3. Simple Nested-Loop Join(Simple NLJ):非驱动表不走索引,select * from t1 straight_join t2 on(t1.a=t2.b);
  4. Block Nested-Loop Join(BNLJ):即当连接的表都不存在索引时,在作连接时,会将驱动表整块表或者分块放进join buffer中,由于驱动表存放在内存中,因此在作全表扫描时,能够加快表的连接;
  5. Batched Key Access:Index Nested-Loop Join 和 MRR的结合;
    表连接的方式使用选择:Index NLJ -> BNLJ -> Simple NLJ,除了Simple NLJ,其他的表连接方式都会用到join buffer;
  6. 辅助索引范围扫描的优化方式:Multi rang read(MRR),存储在分配给用户的buffer中,即在对辅助索引进行范围查询时,每查到一个数据不直接进行回表,而是将所有满足条件的数据放到MRR buffer中,按照主键索引进行排序,然后再根据排序的结果返回到主键索引中进行查找。

SQL语句 -- 考题

  1. select length("开心生活") - 返回存储字符串的字节数;
  2. select char_length("开心生活") - 返回字符串的字符数,与本地客户端的字节编码方式(character_set_client = utf8 or latin1)有关;
  3. 每个用户都会有自己的join、sort、multi range的buffer;
  4. select concat和select concat_ws的区别:前者将所有字符串连接在一块,后者第一个参数是其它参数的分隔符,分隔符的位置放在要连接的两个字符串之间,如果分隔符为 NULL,则结果为 NULL。函数会忽略任何分隔符参数后的 NULL 值,但是CONCAT_WS()不会忽略任何空字符串;
  5. user()和current_user()的区别:
  6. 获取当前的日期:CURDATE(),获取当前时间:CURTIME(),获取当前日期时间:NOW()。
  7. where字句中!=、is null、is not null在MySQL5.7.21版本中不走索引;
  8. utf8编码用3个字节存储字符。
  9. 常见字符串函数:
  • instr(string, substring): 返回substring第一次出现在string的起始位置,如果不存在substring返回0。
  • strcmp(string1,string2): 两个字符串相同时,返回0;string1在字典中比string2靠后,返回1;否则返回-1。
  • left(string, numstr): 返回字符串左边的numstr个字符;
  • right(string,numstr): 返回字符串右边的numstr个字符;
  • lpad(string, len, padstring): 如果string的字符数量大于len,那就从string截取len个字符返回,如果string的字符数量小于len,那就在string的前边添加padstring直到string有len个字符返回;
  • rpad(string, len, padstring): 与lpad作用相反,在右边补充字符。
  • substring(string, pos, length): 返回string中pos位置开始的len个字符,MySQL从1开始数。
  • substring_index:
  • trim
  1. 常见的DATE and TIME运算:
  • 类型:DATE、TIME、DATETIME;
  • 函数:NOW()、CURDATE()、CURTIME();
    DATE_ADD()、DATE_SUB()。
  1. 常见的Numeric函数:
  • ROUND(): 四舍五入;
  • TRUNCATE():
  • ceiling(x):对x进行向上取整。
  1. 系统参数:
  • 所有创建的用户都在mysql库中的user表中;
  • select user();: 获取当前真正连接数据库的用户;
  • select current_user();
  • 创建用户时,需要指定用户名、密码和客户端IP地址create user user_name@ip_address identified by pwd;
  1. 聚合函数:
  • GROUP BY:
  • HAVING:
  • LIMIT:
  • GROUP_CONCAT()
  1. Union 与 union all
  2. temporary buffer: group by,union,distinct
  3. 唯一索引和普通索引的区别:只有普通索引能使用change buffer,普通索引适合于频繁写入,但不常常读取。
  4. MySQL没有函数索引和索引跳跃扫描;
  5. 执行计划中通过type判断查询走不走索引。
  6. key_len的算法:索引列类型if:1)int不能为空时=4;能为空时=5;2)varchar(20),由于加上变长,key_len=22;3)datetime=8;4)date=3。
  7. sync_binlog、innodb_flush_log_at_trx_commit:两个值都设为1时,称作双1模式。

用户访问数据库,如果需要sort,在内存中会生成一块私人的sort buffer,从innodb buffer pool中取数据,放到sort buffer中排序。
索引

  1. 索引是什么?
    索引的出现是为了提高数据查询的效率,就像书的目录一样。
  2. 有哪些索引,适用于对哪一列进行索引?
  • 常见的索引模型

    • 哈希表:1)适合于单个数据进行精确查询,以及插入速度快;2)不适合范围查询,以及不适合做更新。
    • 有序数组:根据索引对数据进行排序,1)范围查询速度快;2)插入和更新满,因为数组是连续存储的,因此在插入和更新时需要移动元素。适用于历史表。
    • 二叉搜索树:由于分支数量为2,导致树较高,增加了查询数据时磁盘IO的次数,在数据库中不常使用。
    • N叉搜索树(MySQL使用,也叫B+树):树根的数据块总是放在内存中,每一个索引在InnoDB中对应一棵B+树。
  • 常见的索引

    • 主键索引
    • 唯一索引:由于索引定义了唯一性,查找到第一个满足条件的记录后,就会停止继续检索。
    • 普通索引:查找到满足条件的第一个记录后,需继续查找下一个记录,直到碰到第一个不满足条件的记录。
    • 前缀索引:即当要建立索引的字段长度太长时,从左往右截取一定长度的字符串建立索引,但是需要满足截取下来的子串区分度较大,栗子:如果对订单号建立前缀索引时,由于订单前面几位通常以日期开头,此时区分度较小,可以将订单号反转。
  1. 索引设计原则
  • 选择度越大,越适合建索引,选择度是指:对于一个表,随机抽出N行,在这N行中,对于索引列而言如果存在n个不同的值,我们称n/N为该列的选择度。MySQL通过收集统计信息来计算选择度,统计信息存储在.frm文件中,手动收集统计信息语句:analyze table 表名;,查看统计信息语句:show index from 表名;

执行计划的阅读

  • 使用EXPLAIN命令查看SQL语句的执行计划,但是语句不会被执行;
  • 执行计划的关键字段:
    type: 值是range表示索引范围扫描(在sql语句的where字句表示范围查找),值是ALL表示全表扫描,值是ref表示等值索引扫描(等号查找);
    possible_keys:所有查询时可能用到的索引;
    key: 查询实际使用的索引;
    key_len: 所使用索引的长度;
    ref:
    rows: 要查找的行数,从统计信息而来。
    filtered:
    Extra: 值是Using index表示当前查询用到了索引覆盖优化。
  • 复制表中的所有数据到新表中:create table t_new as select * from t;
  • where字句中如果是数学表达式带函数的列!、<>和not in(都表示不等于)is null就走不了索引。
  • null和任何值做运算结果必然是null。

MySQL中的锁
undo保证事务的一致性,commit和rollback保证事务的原子性,锁保证隔离性,redo日志保证持久性。

  1. 全局锁
    整个数据库只能读,不能写。Flush tables with read lock(FTWRL),适用于数据库进行备份时使用全局锁。
  2. 表级锁
    包括表锁和元数据锁。
  • 表锁
    lock table t read;: 给表t加读锁,此时表t只允许读,不允许写;
    unlock tables;: 释放锁;
    lock table t write;: 给表t加写锁,此时表t不允许读和写。
  • 元数据锁(生产常用)
    所有的SQL语句对表进行操作时,会对表加MDL锁(Metadata lock,元数据锁),此时无法对表的结构进行修改(比如增加或者减少列),因为修改表结构需要。
  1. 行级锁
  • MySQL加锁的默认隔离级别是可重复读(tx_isolation=repeated read),即某个会话开启事务后,读取的数据结果不受其他客户端对数据进行修改的影响。

排序

  1. 在对数据进行sort时,innodb buffer pool中的数据会存放到分配给用户个人的sortbuffer(默认大小是256K,生产过程中一般分配64M)中进行排序,加入待排序的数据量小于sortbuffer size,此时直接在sortbuffer进行快速排序,如果大于sortbuffer size,将待排序数据分块放入sortbuffer中,排序之后写到磁盘中产生临时文件,所有块都排好后,在使用堆排序,直到整个数据都变成有序(执行计划中的Extra项显示using filesort);
  2. 在对数据进行sort时,如果sort列是索引,且select返回的结果就是该列数据,那么sort时直接从二级索引的叶子结点中取数据(执行计划中的extra项显示using index),不需要放入sortbuffer中排序。

高可用

  1. 在线热备;
  2. 读写分离:主库负责写,从库用来读。
  3. 一主三从库的用法:从库1分担读的压力,从库2用于备份,从库3用于主库宕机后的切换。金融科技需要保证数据一致性,可以牺牲一定的可用性。
  4. binlog cache: 记录用户的所有修改操作,commit之后,这些操作落盘保存到binlog文件中,特点:1)记录完整的事务,从begin:到commit;2)以commit的时间顺序记录;3)只记录提交的事务。从库进行备份时,主库只需将binlog文件传给从库,从库保存为Relaylog文件,从库执行即可。
  5. binlog cache相关的参数:sync_binlog,该值设为1时,表示一次commit就落盘一次;值设为0时,每秒落一次盘;设为>2的任意值N,即没N次落一次盘。
  6. 如果要求写入即读,只能采用单实例模式(主库读主库写)。
  7. 半同步可以防止主从复制数据丢失。
  8. MHA:主从架构,主库宕机后,MHA可从剩下的从库中自动选取与原先主库最接近的从库作为新的主库。
    LVS+Keepalived:
    PXC:Galery(支持多点写入),分布式数据库无法即时读写。
    MGR:
  9. 分表分库
  10. 中间件:mycat(开源)、DELE,不跨分片进行排序和查询,用分布式数据库时,事务不能跨分片做,保证不了事务的一致性。

数据库开发规范

  1. 创建表时,建表语句里一定要有自增主键id int NOT NULL PRIMARY KEY AUTO_INCREMENT,根据自增主键列建立索引时,每次插入数据都在索引最后面插入,不改变已有索引的结构。
  2. 主键长度越小,普通索引的叶子结点就越小,占用的空间也就越小,自增主键选用int类型时,占4个字节。
  3. 日期选用DATE类型,时间选用TIME类型,日期时间选用DATETIME类型(用1个字节存储),表示成YYYY-MM-DD HH:MM:SS。
    索引树越高,磁盘的IO次数越多,索引效果越差。

MyISAM
包括主键索引(又叫聚簇索引)和辅助键索引,两种索引的叶子结点除了存放索引字段外,还包括指向记录的指针。

不支持事务。

MySQL是单进程多线程的服务器。
用户连接数据库,一个连接叫做一个session,空闲的session叫做idle,要对数据库操作的叫做active。

你可能感兴趣的:(MySQL笔记)