其是建立在关系模型基础上的一种数据库,这种关系分为:一对一,一对多,多对多。
我们的数据存放在表中,在表中会有一至多个字段,一行就是一条数据。
数值类型: tinyInt,smallint,mediumInt,int, bigInt。
浮点类型:float,double。
字符类型:char varchar, tinyText, mediumText, Text, longText, tinyBlob, mediumBlob, Blob, longBlob。
时间类型:date,dateTime, year, time, timeStamp。
unsigned属性的作用就是不允许整数值为负数的情况,在勾选该属性后,正整数的上线就是原来的两倍。(就比如 tinyInt的范围 -128~127,如果勾选这个属性后,其范围就是0~255)
当我们的id从0开始做自增操作时就适合使用此属性,这样id就可以取到更大的值。
char和varchar的区别
char字符串的长度是固定的,而varchar字符串的长度是可变的。
char字符串在写入的字符串长度没有达到设置的长度时,会从右边填满空格直达达到设置的长度,在检索的是会去掉空格。
varchar字符串会使用一至两个字符来记录字符串的长度,在检索的时候无需其他的处理。
char和varchar的使用场景
char适合使用在字符串的长度较小的情况,就比如:被MD5加密后的秘密,身份证号。
varchar适合使用在那些字符串长度变化幅度较大的情况,就比如:文章的标题。
varchar(100)和varchar(10)都是可变长字符串,但在字符串的储存上varchar(100)可以储存更长的字符串。
在存入的字符串长度相同的时候:
1.varchar(100)和varchar(10)在磁盘上的存储大小是相同的。
2.varchar(100)和varchar(10)在内存上,varchar会消耗更多的内存,因为在内存操作时,通过会分配固定大小的内存来进行操作,而这个分配的大小就是根据设置的长度确定的,是varchar(100)会更消耗内存。
DECIMAL和浮点数的区别
DECIMAL重要是用来记录存储的小数值,而浮点数这是存储近似的小数值(在浮点数中,就比如: 0.3可能会被用0.3000001表示,只能是个近似值),在一些数据中为了防止这种精度的损失就使用DECIMAL,就比如在我们处理金额信息的时候会使用DECIMAL。
java中操作DECIMAL
在java中我们主要是通过使用类Java.math.BigDecimal来操作的。
Text和char/varchar的作用类似,但是它存储更长的字符串,就比如可以储存博客内容。
Blob主要储存二进制的大对象,就比如:图片,音频。
但在数据库规范中不推荐我们使用Text和Blob,在字符串长度范围知道的情况下,推荐使用varchar。Text和Blob存在大量的缺点。
缺点:
1.不能设置默认值。
2.检索效率较低。
3.不能直接设置索引,需要先设置前缀长度。
4.可能会消耗大量的网络和IO宽带。
5.可能会导致增删改查效率变低。
DATETIME没有时区信息,而TIMESTAMP则与时区相关。
在储存空间上,TIMESTAMP使用4个字节储存数据,而DATETIME使用8个字节储存数据。因此TIMESTAMP所能表示的时间范围更小。
DateTime的表示范围:1000-01-01 00:00:00 ~ 9999-12-31 23:59:59。
TimeStamp的表示范围:1970-01-01 00:00:01 ~ 2037-12-31 23:59:59。
总结:与时区是否相关,储存的大小,可表示的时间范围。
1.NULL代表的是一个不确定的值,在select NULL = NULL的值为false,但是在使用distinct(去重),order By(排序),group By(分组)的时候NULL是相等的。
2.‘ ’的长度为0,不会占用空间,而NULL会占用空间。
3.NULL会影响聚合函数的结果,如使用sum,avg,min,max等函数时,会忽略NULL的值。
在使用count函数时,如果是count(*)则会统计符合条件的数据(即使数据中的值为NULL),而如果是count(某个字段),则当改字段为NULL的时候就不会被统计进去。
4.在NULL做逻辑判断的时候不能使用比较远算符号(<,=,>),而是要使用IS NULL,IS NOT NULL,' '做判断的时候则可以直接使用比较运算符号。
在数据库中没有Boolean类型,所以一般使用Tinyint(1)表示,当数据为0的时候表示为false,当数据为1的时候表示为true。
连接器:身份认证和权限校验。
缓存:缓存对应的数据,执行语句的时候会先查询缓存。(在MySQL8.0后移除了该缓存,因为该功能不太实用)
分析器:在缓存中没有查到数据后就会进入分析器中,分析器主要就是判断语句的作用和判断语句是否存在语法错误。
优化器:按照Mysql认为的最优方案来执行。
执行器:执行语句然后从储存引擎中返回数据。在执行语句前会判断权限,如果权限不够就会放回错误信息。
插件式储存引擎:用于储存和读取数据的引擎。就比如:InnoDB,MyISAM,Memory等储存引擎。
执行流程
连接器进行连接 -> 进入缓存查询数据->如果没有数据就进入分析器,分析语句的作用和判断语法的正确性->进入优化器,按mysql认为的最优方法执行->进入执行器,判断权限,并执行语句->加入储存引擎读取数据。
mysql支持的储存引擎
我们可以通过以下指令查询mysql支持的储存引擎。
select Engines
我目前知道的储存引擎就是: InnoDB,MyISAM,Memory。
InnoDB是mysql支持的储存引擎中唯一支持事务的引擎。
默认的储存引擎
在Mysql 5.5.5之前,默认的储存引擎是MyISAM。
在Mysql5.5.5之后,默认的储存引擎就是InnoDB。(因为innoDB支持事务)
mysql的储存引擎采用的是插件式架构,mysql支持多种储存引擎,我们甚至可以为不同的表设置不同的储存引擎,储存引擎是基于表的,而非基于库的。储存引擎可以理解为一个一个的插件。
我们还可以使用mysql定义的储存引擎实现的标准接口,自定义储存引擎。
在最开始的时候,默认的储存引擎就是MyISAM,但是其不支持事务,最终默认的储存引擎就被换为InnoDB。
1.InnoDB支持行级锁,而MyISAM只支持表级锁,在并发大量写操作时,MyISAM锁住整张表,就会导致大量的写无法完成。
2.InnoDB支持事务且实现了事务的四大特性,而MyISAM不支持事务。
3.InnoDB和MyIAM的索引都是基于b+树结构的,但是其实现方式是不相同的,InnoDB的数据文件就是索引文件,而MyISAM的索引文件和数据文件则是分离的。
4.InnoDB异常崩溃后的自动恢复,但数据发生崩溃后,重启数据裤后数据就会恢复到崩溃之前,而MyISAM是不支持的。
5.InnoDB的性能比MyISAM好。
6.InnoDB支持MVCC(多版本并发控制,解决事务的隔离性,防止出现脏读),而MyISAM不支持。MVCC就是基于行级锁实现的。
在现在基本上是不会考虑使用MyISAM,如果你不在意并发能力,也不需要事务的支持,并且不在意崩溃异常后的自动恢复的话,我们就可以使用MyISAM。
mysql索引面试难点
日志类型为下:
1.错误日志(err log): 对于mysql的启动,运行,关闭过程进行记录。
2.二进制日志(binary log):主要是记录更改数据库的SQL语句。
3.一般查询日志(general log):主要是记录客户端发送到数据库中的所有SQL语句。
4.慢查询日志(slow query log):记录执行时间超过long_query_time的所有查询语句,在后续解决慢查询的时候会使用到。
5.事务日志(rado log和undo log):rado log是做重日志,undo log是做回滚日志。
6.中断日志(relay log):就是复制过程中产生的日志,跟二进制日志差不多,relay log针对的是主从复制中的从库。
7.DDL日志(metadata log): DDL语句就是对库的增删改操作,记录DDL操作的日志。
事务日志
rado log 是Innodb特有的日志,他让mysql拥有异常奔溃后的恢复能力,当mysql宕机后,会通过rado log文件中的语句完成数据的恢复操作。
Mysql中的数据是以页的为单位的,你查询一条记录的时,从磁盘中读取一页的数据出来, 加载的数据叫做数据页,会将该数据页存放到Buffer pool(类似缓存)中。
后续查询都会先到Buffer pool中查询,没有命中的话再去磁盘中查询,更新也是一样,如果在Buffer pool中有要更新的数据,就直接在Buffer pool中更新,如何将修改对应的语句存入重做日志缓存中(redo log buffer),在通过刷盘存入rado log中。
binlog
rado log是物理日志,记录的内容就是“在某个数据页上做了什么修改”,而binlog就是逻辑日志,记录的内容就是原来的逻辑,类似“给Id=2的行某个字段+1"。
不管什么存储引擎,只要表的数据发生修改,就会产生bigLog。
在mysql数据库中的数据备份,主主,主从都不会离开binlog,需要依靠bigLog,保证数据的一致性。
bigLog会记录所有涉及更新数据的逻辑操作,并按顺序写。
undoLog
为了保证事务的原子性,当数据发生异常的时候,我们需要对应数据进行回滚的操作。恢复机制是通过回滚日志实现的,当我们在进行更新操作的时候会先将语句存入回滚日志中,再进行更新操作,当我们后续发生异常的时候,就通过回滚日志进行恢复操作。
数据库事务可以保证,多个数据库操作构成一个逻辑整体,到达:要么都成功,要么都失败的效果。
数据库事务有四大特性
1.原子性(Atomicity):在事务中的sql语句要么都成功,要么都失败。
2. 一致性(Consistency):在事务中,总值不会发生改变。就比如:转账问题,A有100,B有100,那在sql语句执行完之后,它们的总金额一直就是200,这就是一致性。
3.隔离性(Isolation):事务和事务之间是不会相互影响的结果的。
4.持久性(Durability):在事务完成之后数据会持久化到数据库。
只有保证事务的持久性,原子性,隔离性,才能保证事务的一致性。
脏读
当事务读取到另一个事务中没有被提交的数据时就会可能出现脏读的情况。就比如:当事务1对某个数据进行了修改且事务1还没进行提交,此时事务2读取到事务1未提交的数据,正好事务1进行了回滚操作,事务1就脏读了。
丢失修改
当事务1和事务2都同时修改某个数据,但是可能会出现事务2的修改将事务1的修改给覆盖了的情况。
就比如事务1和事务2都是同时获取某个数据的值(假设值为20),两个事务都是将值-1,正确的值应该是18,但是它们没有获得到实时的数据,导致最终的值为19,导致丢失修改的情况发生。
不可重复读(注重的是记录的信息)
一个事务1在事务中多次读取某个数据,在此期间事务2对该数据进行了修改,导致事务1两次读取某个数据不一致的情况,导致不可重复读的发生。
幻读(注重的时是记录的数量)
和不可重复读类似,事务1多次读取某个范围的数据,但事务2往这个范围插入了新的数据,当事务1下次读取数据就发现多出了几条数据,就像读到不存在的数据似的,导致幻读的发生。
不可重复读的重点是在多次查询某些记录,发现记录被修改。
而幻读的重点是在多次查询某些记录,但发现记录的数量发生改变。
可以说幻读是不可重复读的一种情况。
那为为什么要将幻读和不可重复读单独分开呢?
幻读和不可重复读在解决方案上是不一样的,不可重复读使用行级锁来解决的,我们就是对应的记录行上锁,防止被修改。而幻读则是使用间隙锁解决的,我们无法对还没创建的记录加锁(不能使用行级锁的原因)。
READ-UNCOMMITED(读取未提交):最低的隔离级别,允许读取没有提交的数据,但是会导致脏读,不可重复读,幻读。
READ-COMMITED(读取已提交):允许读取并发事务已提交的数据,可以防止脏读,但是不能防止可重复读和幻读。
REPEATABLE-READ(可重复读):对同一个脏读字段读取都是一致的,除非是被当前事务修改的。
可以解决可重复读和在脏读的问题,但是还是解决不了幻读的问题。
SERIALIZABLE(可串行化):最高的隔离级别,完全服从事务的四大特性,所有的事务都是逐个执行的,这样事务之间就不会相互刚干扰了,可以解决脏读,不可重复读,幻读的问题。
MySQL的隔离级别是基于锁和MVCC实现的。
可串行化是基于锁实现的,读取已提交和可重复读都是基于MVCC实现的,除可串行化以外的其他隔离级别也可能也需要使用锁,可重复读需要加锁才能解决幻读的问题。
InnoDB存储引擎默认的隔离级别是REPEATABLE-READ(可重复读),可以通过以下指令查询隔离级别。
select @@tx_isolation
MyISAM仅仅支持表级锁,但是表级锁会对操作记录对应的表上锁,在处理高并发的场景下效率十分的低。InnoDB支持表级锁和行级锁,默认是行级锁。
行级锁的粒度更小,仅对应某一条记录的行就行上锁,在高并发的场景下性能更高。
二者的区别
表级锁:锁定粒度最大的一种锁(除全局锁以外),是针对非索引字段加的锁,对整个记录表加锁,实现简单,加锁速度快,资源消耗少,不会出现死锁的现象,但有可能出现锁冲突的情况,在高并发的场景下效率极低,表级锁和存储引擎无关,MyISAM和InnoDB都支持表级锁。
行级锁:锁定粒度最小的一种锁,是针对索引字段加的锁,对记录的行进行加锁,行级锁大大降低了锁冲突的问题,在高并发的场景下效率较高,但加锁开销大,加锁速度慢,可能会存在死锁的情况,表级锁和存储引擎相关,是基于存储引擎层实现的。
InnoDB的行级锁是针对索引字段加的锁,而表级锁是针对非索引字段加的锁,当我们执行update,delete操作时,如果where语句没有命中唯一索引或者索引失效的话,就会导致扫描全表中的所有行,并对每个行加行级锁,导致效率低下,所以需要注意。
记录锁(Record Lock):单个行记录上的锁。
间隙锁(Gap Lock):锁定某个记录范围的锁,但是不包括记录本身。
临键锁(Next-key Lock):就是一种特殊的间隙锁,它使用到记录锁和间隙锁,其效果就是锁定某个范围并且包括记录本身。拥有解决幻读的问题。
InnoDB默认的隔离级别是可重复读,行锁默认的就是使用临键锁,如果操作的索引是唯一索引或是主键的话,InnoDB会对临键锁进行优化,将临键锁降级为记录锁,只锁住索引本身,而不是一个范围。
共享锁:在事务在读取记录的时候会获取共享锁,允许多个事务同时获取共享锁。(兼容)
排他锁:也叫独占锁,在事务修改记录的时候获取排他锁,不允许多个事务同时获取排他锁,如果某个记录已经被加上了排他锁,不允许其他事务为这个记录添加其他的锁。(不兼容)
排他锁和任何的锁都不兼容,而共享锁只能兼容共享锁。
如果需要使用表级锁的话,我们需要判断表中的记录是否存在行级锁,如果使用遍历一条条查询的话,效率十分的低下,所以需要使用意向锁,通过意向锁判断是否可以使用表级锁。
意向共享锁(intention Share Lock IS锁):事务有意向对表中的某些记录添加共享锁,在加共享锁的时候必须先获取该表的IS锁。
意向排他锁(intetion Exclusive Lock IE锁):事务有意向对表中的某些记录添加排他锁,在添加排他锁 的时候必须先获取该表的IE锁。
意向锁是由数据引擎自己维护的,用户无法手动操作意向表,在数据行添加共享锁和排他锁时,先要获取数据行对应的表中的意向锁。
意向锁之间是相互兼容的。
意向锁和排他锁和共享锁是相互排斥的。(这里的排他锁和共享锁是表级锁,而排他锁和共享锁的行级锁和意向锁不会互斥)
当前读(普通读):就是单纯的select语句。
快照读:就是给行记录添加X锁和S锁。