MySQ数据库
1.说说MySQL的基础架构
Mysql的逻辑架构主要分为三层:
(1)第一层负责连接处理,授权认证,安全等
(2)第二层负责编译并优化SQL
(3)第三层是存储引擎
2.说说一条SQL语句的执行流程
Mysql大致分为 Server层 和 存储引擎层 两部分
Server层:
连接器:TCP握手后服务器来验证登陆用户身份,A用户创建连接后,管理员对A用户权限修改了也不会影响到已经创建的连接权限,必须重新登陆。
查询缓存:查询后结果存储在缓存,8.0版本后已经取消,因为查询缓存失效太频繁,得不偿失。
分析器:根据语法规则,判断输入的这个SQL语句是否满足 Mysql 语法。
优化器:多种执行策略可实现目标,系统自动选择最优进行执行。
执行器:判断是否有权限,将最终任务提交到存储引擎。
存储引擎层:
负责数据的存储和提取。其架构模式是插件式的,支持 InnonDB、MyISAM、Memory等多个存储引擎。现在最常用的存储引擎是 InnonDB。5.5.5 版本开始成为默认存储引擎。
3.一条SQL查询语句在MYSQL中如何执行?
先检查语句 是否有权限,如果没有权限,直接返回错误信息,如果有权限会先查询缓存。
如果没有缓存,分析器进行 词法分析,提取sql语句中的关键元素,判断sql语句是否有语法错误,比如关键词是否正确等等。
最后优化器确定执行方案进行权限校验,如果没有权限就直接返回错误信息,如果有权限就会 调用数据库引擎接口,返回执行结果。
存储引擎
概念
数据库存储引擎是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建、查询、更新和删除数据。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎,还可以 获得特定的功能。现在许多不同的数据库管理系统都支持多种不同的数据引擎。存储引擎主要有: 1. MyIsam , 2. InnoDB, 3. Memory, 4. Archive, 5. Federated 。
InnoDB(B+树)
InnoDB 底层存储结构为B+树, B树的每个节点对应innodb的一个page,page大小是固定的,一般设为16k。其中非叶子节点只有键值,叶子节点包含完成数据。
适用场景:
1)经常更新的表,适合处理多重并发的更新请求。
2)支持事务。
3)可以从灾难中恢复(通过bin-log日志等)。
4)外键约束。只有他支持外键。
5)支持自动增加列属性auto_increment。
TokuDB(Fractal Tree-节点带数据)
TokuDB 底层存储结构为Fractal Tree,Fractal Tree的结构与B+树有些类似, 在Fractal Tree中,每一个child指针除了需要指向一个child节点外,还会带有一个Message Buffer ,这个Message Buffer 是一个FIFO的队列,用来缓存更新操作。
例如,一次插入操作只需要落在某节点的Message Buffer就可以马上返回了,并不需要搜索到叶子节点。这些缓存的更新会在查询时或后台异步合并应用到对应的节点中。
TokuDB在线添加索引,不影响读写操作, 非常快的写入性能, Fractal-tree在事务实现上有优势。 他主要适用于访问频率不高的数据或历史数据归档。
MyIASM
MyIASM是MySQL默认的引擎,但是它没有提供对数据库事务的支持,也不支持行级锁和外键,因此当INSERT(插入)或UPDATE(更新)数据时即写操作需要锁定整个表,效率便会低一些。
ISAM执行读取操作的速度很快,而且不占用大量的内存和存储资源。在设计之初就预想数据组织成有固定长度的记录,按顺序存储的。---ISAM是一种静态索引结构。
缺点是它不 支持事务处理。
Memory
Memory(也叫HEAP)堆内存:使用存在内存中的内容来创建表。每个MEMORY表只实际对应一个磁盘文件。MEMORY类型的表访问非常得快,因为它的数据是放在内存中的,并且默认使用HASH索引。但是一旦服务关闭,表中的数据就会丢失掉。 Memory同时支持散列索引和B树索引,B树索引可以使用部分查询和通配查询,也可以使用<,>和>=等操作符方便数据挖掘,散列索引相等的比较快但是对于范围的比较慢很多。
事务
1.什么是事务
事务(TRANSACTION)是作为单个逻辑工作单元执行的一系列操作,这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行 。事务是一个不可分割的工作逻辑单元
事务必须具备以下四个属性,简称ACID 属性,原子性、一致性、隔离性、持久性。
2.事务的四大特性及其实现原理(ACID)
原子性:
1. 事务是一个完整的操作。事务的各步操作是不可分的(原子的);要么都执行,要么都不执行。
一致性:
2. 当事务完成时,数据必须处于一致状态。
隔离性:
3. 对数据进行修改的所有并发事务是彼此隔离的,这表明事务必须是独立的,它不应以任何方式依赖于或影响其他事务。
持久性:
4. 事务完成后,它对数据库的修改被永久保持,事务日志能够保持事务的永久性。
3.事务的四种隔离级别?MySQL的默认隔离级别是什么?
未提交读(READ UNCOMMITTED) :别人改数据的事务尚未提交,我在我的事务中也能读到。
提交读(READ COMMITTED):别人改数据的事务已经提交,我在我的事务中才能读到
可重复读 (REPEATABLE READ):别人改数据的事务已经提交,我在我的事务中也不去读,以此保证重复读一致性
可串行化(SERIALIZABLE):我的事务尚未提交,别人就别想改数据
可重复读是 MYSQL的默认事务隔离级别
4.事务操作可能会出现的数据问题(即脏读、不可重复读、幻读、可重复读)
脏读:事务A、B交替执行,B事务更改数据还未提交,A事务已经看到并且用了。B事务如果回滚,则A事务用错了数据,这就是脏读。
不可重复读:在一个事务范围内,两个相同的查询,读取同一条记录,却返回了不同的数据,这就是不可重复读。
幻读:事务A先修改了某个表的所有记录的状态字段为 已处理,未提交;事务B也在此时新增了一条未处理的记录,并提交了;事务A随后查询记录,却发现有一条记录是未处理的造成幻读现象,幻读仅 专指新插入的行。幻读会造成语义上的问题跟数据一致性问题。
可重复读:在可重复读 RR隔离级别下,普通查询是 快照读,是不会看到别的事务插入的数据的。因此,幻读在当前读下才会出现。要用 间隙锁 解决此问题。
隔离级别越严实,效率就会越低。
MySQL InnDB 默认为 RR级别,但是不会出现幻读。因为当事务A更新了的所有记录的某个字段,此时事务A会获得对这个表的表锁,因为事务A还没有提交,所以事务A获得的锁没有释放,此时事务B在该表插入新记录,会因为无法获得该表的锁,则导致插入操作被阻塞。只有事务 A提交了事务后,释放了锁,事务B才能进行接下去的操作。所以可以说 MYSQL 的RR级别的隔离是已经实现解决了脏读、不可重复读和幻读的。
存储过程(特定功能的SQL语句集)
一组为了完成特定功能的SQL 语句集,存储在数据库中,经过第一次编译后再次调用不需要再次编译,用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象。
存储过程优化思路:
1. 尽量利用一些sql语句来替代一些小循环,例如聚合函数,求平均函数等。
2. 中间结果存放于临时表,加索引。
3. 少使用游标。sql是个集合语言,对于集合运算具有较高性能。而cursors是过程运算。比如对一个100万行的数据进行查询。游标需要读表100万次,而不使用游标则只需要少量几次读取。
4. 事务越短越好。sqlserver支持并发操作。如果事务过多过长,或者隔离级别过高,都会造成并发操作的阻塞,死锁。导致查询极慢,cpu占用率极地。
5. 使用try-catch处理错误异常。
6. 查找语句尽量不要放在循环内。
触发器(一段能自动执行的程序)
触发器是一段能自动执行的程序,是一种特殊的存储过程,触发器和普通的存储过程的区别是:触发器是当对某一个表进行操作时触发。诸如:update、insert、delete这些操作的时候,系统会自动调用执行该表上对应的触发器。
SQL Server 2005中触发器可以分为两类:DML触发器和DDL触发器,其中DDL触发器它们会影响多种数据定义语言语句而激发,这些语句有create、alter、drop语句。
数据库锁
1.什么是死锁,死锁是怎么产生的,解决方案,死锁竞争的是什么资源
死锁是指两个或者多个事务在同一资源上互相占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。
当多个事务试图以不同的顺序锁定资源时,就可能会产生死锁。多个事务同时锁定同一个资源时,也会产生死锁。
为解决死锁问题,数据库系统实现了各种死锁检测和死锁超时机制。InnoDB 存储引擎,能检测到死锁的循环依赖,并立即返回一个错误。这种解决方式很有效,斗否则死锁会产生非常慢的查询。
另一种解决方式,就是当查询的时间达到锁等待超时的设定后放弃锁请求,这种方式通常来说不太好。InnoDB 目前处理死锁的方法是,将持有最少行级排他锁的事务进行回滚。
2.锁的分类:
表级锁
表示对当前操作的整张表加锁,它实现简单,资源消耗较少,被大部分MySQL引擎支持。最常使用的MYISAM与INNODB都支持表级锁定。
表级锁定分为表共享读锁(共享锁)与表独占写锁(排他锁)。表级锁是Mysql中锁定力度最大的一种锁。
页级锁
页级锁是MySQL中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。所以取了折中的页级,一次锁定相邻的一组记录。
行级锁
MySQL中锁定粒度最细的一种锁,表示只针对当前操作的行进行加锁。行级锁能最大减少数据库操作的冲突。其加锁粒度最小,但加锁的开销也最大。
行级锁比一定比表级锁要好:锁的粒度越细,代价越高,相比表级锁在表的头部直接加锁,行级锁还要扫描找到对应的行对其上锁,这样的代价其实是比较高的。
行级锁只在存储引擎层实现,服务器层完全不了解存储引擎中的锁实现。
数据库并发策略
并发控制一般采用三种方法,分别是乐观锁和悲观锁以及时间戳。
乐观锁
乐观锁认为一个用户读数据的时候,别人不会去写自己所读的数据;
悲观锁就刚好相反,觉得自己读数据库的时候,别人可能刚好在写自己刚读的数据,其实就是持一种比较保守的态度;
时间戳就是不加锁,通过时间戳来控制并发出现的问题。
悲观锁
悲观锁就是在读取数据的时候,为了不让别人修改自己读取的数据,就会先对自己读取的数据加锁,只有自己把数据读完了,才允许别人修改那部分数据,或者反过来说,就是自己修改某条数据的时候,不允许别人读取该数据,只有等自己的整个事务提交了,才释放自己加上的锁,才允许其他用户访问那部分数据。
时间戳
时间戳就是在数据库表中单独加一列时间戳,比如“TimeStamp”,每次读出来的时候,把该字段也读出来,当写回去的时候,把该字段加1,提交之前 ,跟数据库的该字段比较一次,如果比数据库的值大的话,就允许保存,否则不允许保存,这种处理方法虽然不使用数据库系统提供的锁机制,但是这种方法可以大大提高数据库处理的并发量,
以上悲观锁所说的加“锁”,其实分为几种锁,分别是:排它锁(写锁)和共享锁(读锁)。
基于Redis分布式锁
1. 获取锁的时候,使用setnx(SETNX key val:当且仅当key不存在时,set一个key为val的字符串,返回1;若key存在,则什么都不做,返回0)加锁,锁的value值为一个随机生成的UUID,在释放锁的时候进行判断。并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁。
2. 获取锁的时候调用setnx,如果返回0,则该锁正在被别人使用,返回1则成功获取锁。 还设置一个获取的超时时间,若超过这个时间则放弃获取锁。
3. 释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。
索引
1.MySQL的索引?索引的优缺点?索引的数据结构?
索引(Index)是帮助MySQL高效获取数据的数据结构。索引常见模型有 哈希表、有序数组 和 搜索数
哈希表:一种以KV存储数据的结构,只适合等值查询,不适合范围查询。
有序数组:只适用于静态存储引擎,设计到插入的时候比较麻烦。可以参考Java中的 ArrayList。
搜索树:按照数据结构中的二叉树来存储数据,不过此时是N叉树(B+树)。广泛应该在存储引擎层。
索引在存储引擎层实现。
2.常见索引原则有:
1.选择唯一性索引,唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。
2.为经常需要排序order by、分组group by和联合操作的字段建立索引,可以大幅度提高分组和排序效率。
3.重复率小的列建议生成索引。因为重复数据少,索引树查询更有效率,等价基数越大越好。
4.经常用于查询条件的字段建议生成索引。用过索引查询,速度更快。
5.尽量使用数据量少的索引,如果索引的值很长,那么查询的速度会受到影响。
6.尽量使用前缀来索引,如果索引字段的值很长,最好使用值的前缀来索引。
7.删除不再使用或者很少使用的索引
不应该:
1.索引不是越多越好。索引太多,维护索引需要时间跟空间。
2.频繁更新的数据,不宜建索引。
3.数据量小的表没必要建立索引。
3.索引的优点:
1.唯一索引可以保证每一行数据的唯一性
2.索引大大减少了服务器需要扫描的数据量,提高查询速度。
3.加速表与表的连接
4.显著减少查询中分组和排序的时间
5.通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能
4.索引的缺点:
1.创建和维护都需要耗时
2.创建索引时,需要对表加锁,在锁表的同时,可能会影响到其他的数据操作
3.索引需要磁盘的空间进行存储,磁盘占用也很快
4.当对表中的数据进行CRUD时,也会触发索引的维护,而维护索引需要时间,可能会降低数据操作性能
5.索引失效的场景
①模糊搜索:左模糊或全模糊都会导致索引失效,比如 '%a' 和 '%a%'。但是右模糊是可以利用索引的,比如 'a%'。
②隐式类型转换:比如 select * from t where name=xxx,name是字符串类型,但是没有加引号,所以是由 MySQL隐式转换的,所以会让索引失效。
③当语句中带有 or 的时候
④不符合联合索引的最左前缀匹配:(A,B,C)的联合索引,只 where 了C或B或只有 B,C
数据库的三范式
范式是具有最小冗余的表结构。3范式具体如下:
第一范式(1st NF -列都是不可再分)
第一范式的目标是确保每列的原子性:如果每列都是不可再分的最小数据单元(也称为最小的原子单元),则满足第一范式(1NF)
第二范式(2nd NF-每个表只描述一件事情)
首先满足第一范式,并且表中非主键列不存在对主键的部分依赖。 第二范式要求每个表只描述一件事情。
第三范式(3rd NF- 不存在对非主键列的传递依赖)
第三范式定义是,满足第二范式,并且表中的列不存在对非主键列的传递依赖。除了主键订单编号外,顾客姓名依赖于非主键顾客编号。
MySQL优化
SQL优化主要分4个方向:SQL语句跟索引、表结构、系统配置、硬件。
总优化思路就是最大化利用索引、尽可能避免全表扫描、减少无效数据的查询:
1、减少数据访问:设置合理的字段类型,启用压缩,通过索引访问等减少磁盘 IO。
2、返回更少的数据:只返回需要的字段和数据分页处理,减少磁盘 IO 及网络 IO。
3、减少交互次数:批量 DML 操作,函数存储等减少数据连接次数。
4、减少服务器 CPU 开销:尽量减少数据库排序操作以及全表查询,减少 CPU 内存占用 。
5、分表分区:使用表分区,可以增加并行操作,更大限度利用 CPU 资源。
SQL语句优化大致举例:
1、合理建立覆盖索引:可以有效减少回表。
2、union,or,in都能命中索引,建议使用in
3、负向条件(!=、<>、not in、not exists、not like 等) 索引不会使用索引,建议用in。
4、在列上进行运算或使用函数会使索引失效,从而进行全表扫描
5、小心隐式类型转换,原字符串用整型会触发CAST函数导致索引失效。原int用字符串则会走索引。
6、不建议使用%前缀模糊查询。
7、多表关联查询时,小表在前,大表在后。在 MySQL 中,执行 from 后的表关联查询是从左往右执行的(Oracle 相反),第一张表会涉及到全表扫描。
8、调整 Where 字句中的连接顺序,MySQL 采用从左往右,自上而下的顺序解析 where 子句。根据这个原理,应将过滤数据多的条件往前放,最快速度缩小结果集。
SQL调优大致思路:
1、先用慢查询日志定位具体需要优化的sql
2、使用 explain 执行计划查看索引使用情况
3、重点关注(一般情况下根据这4列就能找到索引问题):
1、key(查看有没有使用索引)
2、key_len(查看索引使用是否充分)
3、type(查看索引类型)
4、Extra(查看附加信息:排序、临时表、where条件为false等)
4、根据上1步找出的索引问题优化sql 5、再回到第2步
表结构优化:
1、尽量使用TINYINT、SMALLINT、MEDIUM_INT作为整数类型而非INT,如果非负则加上UNSIGNED 。
2、VARCHAR的长度只分配真正需要的空间 。
3、尽量使用TIMESTAMP而非DATETIME 。
4、单表不要有太多字段,建议在20以内。
5、避免使用NULL字段,很难查询优化且占用额外索引空间。字符串默认为''。
读写分离:
只在主服务器上写,只在从服务器上读。对应到数据库集群一般都是一主一从、一主多从。业务服务器把需要写的操作都写到主数据库中,读的操作都去从库查询。主库会同步数据到从库保证数据的一致性。一般 读写分离 的实现方式有两种:代码封装跟数据库中间件。
分库分表:
分库分表分为垂直和水平两个方式,一般是先垂直后水平。
1、垂直分库:将应用分为若干模块,比如订单模块、用户模块、商品模块、支付模块等等。其实就是微服务的理念。
2、垂直分表:一般将不常用字段跟数据较大的字段做拆分。
3、水平分表:根据场景选择什么字段作分表字段,比如淘宝日订单1000万,用userId作分表字段,数据查询支持到最近6个月的订单,超过6个月的做归档处理,那么6个月的数据量就是18亿,分1024张表,每个表存200W数据,hash(userId)%100找到对应表格。
4、ID生成器:分布式ID 需要跨库全局唯一方便查询存储-检索数据,确保唯一性跟数字递增性。
目前主要流行的分库分表工具 就是Mycat和sharding-sphere。