View Post


数据库

  1. 数据库设计准则

  • 第一范式:列的原子性,列不可拆分。
  • 第二范式1)表必须有一个主键;2)没有包含在主键的列必须完全依赖于主键,而不是部分依赖
  • 第三范式:非主键列必须直接依赖于主键,不能存在传递依赖。

 

  1. MySQL数据库引擎

https://www.jianshu.com/p/4bb9f78b4f6d

InnoDB主要面向在线事务处理(OLTP)的应用。MyISAM主要面向一些联机分析处理(OLAP)的应用。

  • InnoDB存储引擎:InnoDB是默认的MySQL引擎,提供了具有提交、回滚和崩溃恢复能力的事务安全(ACID兼容)存储引擎,支持行锁定外键,默认创建的是B+tree索引,。
  • MyISAM存储引擎:拥有较高的插入、查询速度,但不支持事务。在 MyISAM 中,B+ 树索引的叶子节点并不存储数据,而是存储数据的文件地址
  • MEMORY存储引擎:MEMORY存储引擎将表中的数据存储到内存中,为查询和引用其他表数据提供快速访问。也可以为B+tree索引(或者Hash索引)
  • Archive存储引擎:基本上用于数据归档;它的压缩比非常的高,它不支持事务,其设计目标只是提供高速的插入和压缩功能。

①InnoDB存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会更高一些(多个锁,一个锁),但是在整体并发处理能力方面要远远优于MyISAM的表级锁定当系统处于高并发量的时候,InnoDB的整体性能和MyISAM相比就会有比较明显的优势了

②InnoDB的行锁定同样有其脆弱的一面(间隙锁危害),当使用不当时可能会让InnoDB的整体性能表现不仅不能比MyISAM高,甚至可能更差。

 

  1. 为什么MyISAM查询比Innodb快?

INNODB在做SELECT的时候,要维护的东西比MYISAM引擎多很多:

  • INNODB缓存数据块,MYISAM只缓存索引块,这中间还有换进换出的时间;
  • INNODB寻址要先映射到块,再到行;MYISAM记录的直接是文件的OFFSET定位比INNODB要快(注:MYISAM 更新频率低,所以索引变更少,所以允许每次更新,即更新主索引,也更新副索引,更新offset)
  • INNODB还需要维护MVCC一致;虽然你的场景没有,但他还是需要去检查和维护,MYISAM 表锁牺牲了写性能,提高了读性能

 

  1. 数据库保证并发性(MVCC)

https://www.jianshu.com/p/8845ddca3b23

MVCC,全称Multi-Version Concurrency Control,即多版本并发控制。MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。MVCC在MySQL InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读

  • MVCC带来的好处是?

多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照 所以MVCC可以为数据库解决以下问题:

  • 在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能
  • 同时还可以解决脏读,不可重复读,幻读等事务隔离问题,但不能解决更新丢失问题
  • 小结

总之,MVCC就是为了解决数据库仅仅采用悲观锁这样性能不佳的形式去解决读-写冲突问题,所以在数据库中我们可以形成两个组合:

  • MVCC + 悲观锁:MVCC解决读写冲突,悲观锁解决写写冲突
  • MVCC + 乐观锁:MVCC解决读写冲突,乐观锁解决写写冲突

这种组合的方式就可以最大程度的提高数据库并发性能,并解决读写冲突,和写写冲突导致的问题

 

  1. mysql的主从复制

MySQL主从复制是其最重要的功能之一。主从复制是指一台服务器充当主数据库服务器,另一台或多台服务器充当从数据库服务器,主服务器中的数据自动复制到从服务器之中。对于多级复制,数据库服务器即可充当主机,也可充当从机。MySQL主从复制的基础是主服务器对数据库修改记录二进制日志从服务器通过主服务器的二进制日志自动执行更新。MySQL主从复制的两种情况:同步复制和异步复制,实际复制架构中大部分为异步复制。

 

  1. 索引

  1. 数据库索引其实就是为了使查询数据效率快:通过不断的缩小想要获得数据的范围来筛选出最终想要的结果,同时把随机的事件变成顺序的事件,也就是我们总是通过同一种查找方式来锁定数据。
  2. 创建唯一性索引,保证数据库表中每一行数据的唯一性;
  3. 加速表和表之间的连接
  • 缺点:
  1. 索引需要占用数据表以外的物理存储空间
  2. 创建索引和维护索引要花费一定的时间
  3. 当对表进行更新操作时,索引需要被重建,这样降低了数据的维护速度。
  • 最左匹配原则:以最左边的为起点任何连续的索引都能匹配上。同时遇到范围查询(><betweenlike)就会停止匹配,例如abc使用索引的为aababcac
  • 类型:
  1. 聚集索引(主键索引):以主键作为 B+ 树索引的键值而构建的 B+ 树索引一个表只能包含一个聚集索引。在聚集索引中,表中行的物理顺序与键值的逻辑(索引)顺序相同在数据库里,所有行数都会按照主键索引进行排序。表在磁盘上的存储结构就由整齐排列的结构转变成了树状结构,类似整个表就变成了一个索引,也就是所谓的「聚集索引」。
  2. 非聚集索引:以主键以外的列值作为键值构建的 B+ 树索引,通过聚集索引可以查到需要查找的数据,而通过非聚集索引可以查到记录对应的主键值,再使用主键的值通过聚集索引查找到需要的数据。非聚集索引与聚集索引的区别在于非聚集索引的叶子节点不存储表中的数据,而是存储该列对应的主键,想要查找数据我们还需要根据主键再去聚集索引中进行查找,这个再根据聚集索引查找数据的过程,我们称为回表。
  3. 联合索引:就是多个字段组成的索引,称为联合索引。联合索引(abc),用到索引的有aababcac
    因为优化器会自动调整and前后的顺序,所以bacbabcaca都会用到索引,其他的都不会用到该索引。ac这一组仅仅是a用到索引。
  • 底层实现方式:B+

聚簇索引:索引页+数据页组成的B+树,是 MySQL 基于主键索引结构创建的

  • 索引在 MySQL 数据库中分四类:B+ 树索引、Hash 索引、全文索引、R-Tree索引(GIS数据)

哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快。

 

  1. Mysql索引分类

  • 从逻辑角度:主键索引、唯一索引、普通索引(单列)、联合索引(多列)、空间索引
  • 从数据结构角度:B+ 树索引、Hash 索引、全文索引、R-Tree索引(GIS数据)
  • 从物理存储角度:聚集索引和非聚集索引(二级索引)

 

  1. 主键索引和唯一索引的区别

  • 主键是一种约束,唯一索引是一种索引,两者在本质上是不同的。
  • 主键创建后一定包含一个唯一性索引,唯一性索引并不一定就是主键。
  • 唯一性索引列允许空值,而主键列不允许为空值
  • 主键列在创建时,已经默认为空值 + 唯一索引了。
  • 主键可以被其他表引用为外键,而唯一索引不能。
  • 一个表最多只能创建一个主键,但可以创建多个唯一索引。
  • 主键更适合那些不容易更改的唯一标识,如自动递增列、身份证号等。
  • RBO 模式下,主键的执行计划优先级要高于唯一索引。
    两者可以提高查询的速度。

 

  1. Hash索引和B+树索引的区别

  2. 如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;当然了,这个前提是,键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;
  3. 如果是范围查询检索,这时候哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;
  4. 同理,哈希索引也没办法利用索引完成排序,以及like\’xxx%\’这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);
  5. 哈希索引也不支持多列联合索引的最左匹配规则
  6. B+树索引的关键字检索效率比较平均,不像B树那样波动幅度大,在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在所谓的哈希碰撞问题。

 

  1. 回表和覆盖索引

  • 回表定义:数据库根据索引(非主键)找到了指定的记录所在行后还需要根据主键再次到数据块里获取数据
  • 如何避免回表:将需要的字段放在索引中去。查询的时候就能避免回表。
  • 覆盖索引定义:指一个查询语句的执行只用从索引中就能够取得,不必从数据表中读取。当一条查询语句符合覆盖索引条件时,MySQL只需要通过索引就可以返回查询所需要的数据,这样避免了查到索引后再返回表操作,减少I/O、提高效率。
    如表covering_index_sample中有一个普通索引 idx_key1_key2(key1key2)。当我们通过SQL语句:select key2 from covering_index_sample where key1 = \’keytest\’;的时候,就可以通过覆盖索引查询,无需回表

 

  1. 索引创建原则

  • 一个表中只能有一个主键,可以有多个unique key
  • 经常被用来过滤记录的字段
  1. primary key 字段,系统自动创建主键的索引;
  2. unique key 字段,系统自动创建对应的索引;
  3. foreign key 约束所定义的作为外键的字段;
  4. 在查询中用来连接表的字段;
  5. 经常用来作为排序(order by 的字段)基准的字段;
  • 内容变动多的字段谨慎创建,索引维护消耗性能
  • 避免选择大型数据类型的列作为索引
  • 应该避免对具有较少值的字段进行索引
  • 索引会额外占用磁盘空间,不是越多越好

 

  1. 索引失效的情况

  • or必全有索引:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引
  • 联合索引未用左列字段;
  • like%开头;“%” 可用于定义通配符(模式中缺少的字母)
  • 需要类型转换
  • where中索引列有运算;
  • where中索引列使用了函数;
  • 如果mysql觉得全表扫描更快时(数据少);
  • 最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(><betweenlike)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4 如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。

 

  1. B+树

  • MySQL 中最常用的索引的数据结构是 B+ 树,有以下特点:
  1. B+ 树中,所有数据记录节点都是按照键值的大小存放在同一层的叶子节点上,而非叶子结点只存储key的信息,这样可以大大减少每个节点的存储的key的数量,降低B+ 树的高度
  2. B+ 树叶子节点的关键字从小到大有序排列,左边结尾数据都会保存右边节点开始数据的指针。
  • B+树和B树的区别:
  1. B+ 树的层级更少:相较于 B B+ 树每个非叶子节点存储的关键字数更多,树的层级更少所以查询数据更快
  2. B+ 树查询速度更稳定:B+ 所有关键字数据地址都存在叶子节点上,所以每次查找的次数都相同所以查询速度要比B树更稳定;
  3. B+ 树天然具备排序功能B+ 树所有的叶子节点数据构成了一个有序链表,在查询大小区间的数据时候更方便,数据紧密性很高,缓存的命中率也会比B树高。
  4. B+ 树全节点遍历更快:B+ 树遍历整棵树只需要遍历所有的叶子节点即可,,而不需要像 B 树一样需要对每一层进行遍历,这有利于数据库做全表扫描。

 

  1. B树、B-树、B+树、B*树

https://www.jianshu.com/p/92d15df75027

  • B树:每个结点只存储一个关键字,等于则命中,小于走左结点,大于走右结点;
  • B-树:多路搜索树,每个结点存储M/2M个关键字,非叶子结点存储指向关键字范围的子结点;所有关键字在整棵树中出现,且只出现一次,非叶子结点可以命中
  • B+树:在B-树基础上,为叶子结点增加链表指针,所有关键字都在叶子结点中出现,非叶子结点作为叶子结点的索引;B+树总是到叶子结点才命中;
  • B*树:在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率从1/2提高到2/3

 

  1. 为什么用B+树而不是B树?

  • http://www.liuzk.com/410.html
  • B树中,你可以将键和值存放在内部节点和叶子节点,但在B+树中,内部节点都是键,没有值。
  • B+树的叶子节点有一条链相连,而B树的叶子节点各自独立。
  • B树和B+树区别:
    • 关键字数量不同B+树分支结点M个关键字,叶子节点也有M个;B树分支结点则存在 k-1 个关键码
    • 数据存储位置不同B+树数据存储在叶子结点上,而且数据是按照顺序排列的;B树存储在每个结点上;
    • 查询不同B+树是从根节点到叶子节点的路径;B树是只需要找到数据就可以
    • 分支节点存储信息不同B+树存索引信息;B树存的是数据关键字

 

  1. 数据库索引为什么用B+树,不用红黑树?

文件系统的基本存储单位是B+树一个节点有多少分支就是根据一个簇能容纳多少分支来决定的,这使得B+树相比红黑树更显得”矮胖”,矮胖即节点更大,树的深度更低,深度低使得io的次数能够有效地减少

 

  1. 乐观锁和悲观锁

  • 悲观锁:假定会发生并发冲突,屏蔽一切违反数据完整性的操作。每次取数据的时候都会上锁,想要拿数据就会阻塞直到拿到锁。如Synchornized就是悲观锁的一种实现,适用于写多读少场景。行锁、页锁、表锁
  • 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。只在提交更新时判断别人有没有更新数据。适用于读多写少的场景。乐观锁的实现方式一般包括使用版本号和时间戳。
    • version字段
    • CAS
  1. 数据版本:需要乐观锁控制的table中增加一个字段version
  2. 时间戳:需要乐观锁控制的table中增加一个字段timestamp
  • 乐观锁具体实现过程
  1. 表中添加字段,作为乐观锁的版本号
  2. 对应实体类添加版本号属性及@Version注解

@Version

private
Integer version;//
版本号

  1.  配置乐观锁插件(添加一个配置类)

//加注解这是一个配置类

@Configuration

@MapperScan(“com.ljq01.mpdemo01.mapper”)//扫描接口,加上mapper包的地址

//可以将其它配置一起放在这个文件中

public
class
MpConfig
{


/**

* 乐观锁插件

*/


@Bean


public
OptimisticLockerInterceptor optimisticLockerInterceptor(){


return
new
OptimisticLockerInterceptor();


}

}

  1. 乐观锁测试

//测试乐观锁

@Test

public
void testOptimistLocker(){


//
根据id查询数据


User user = userMapper.selectById(202069);


//
进行修改

user.setUsername(“maomao”);

userMapper.updateById(user);

}

 

  1. 共享锁和独占锁

  • 共享锁:如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。获准共享锁的事务只能读数据,不能修改数据。在查询语句后面加上lock in share mode
  • 排他锁:如果事务T对数据A加上排他锁后,则其他事务不能再对A加任何类型的锁(即其他事务也不能对数据A再加共享锁进行读操作)。获取排他锁的事务既能读数据,又能修改数据。Sql语句后加for update

 

  1. 事务

  • 定义:访问并可能更新数据库中各种数据项的一个程序执行单元(unit)
  • 事务的ACID特性:
  1. 原子性Atomicity:一个事务时不可分割的单位,事务中的操作要么都成功,要么都失败。
  2. 一致性Consistency:事务操作前后数据库完整性一致。
  3. 隔离性Isolation:一个事务的执行不能被其他事务干扰。
  4. 永久性Durability:事务完成后对数据库的改变是永久性的。
  • 事务隔离级别

  1. 未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据。
    1. 事务在读数据的时候并未对数据加锁。
    2. 事务在修改数据的时候只对数据增加行级共享锁
  2. 已提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别(不重复读)
    1. 事务对当前被读取的数据加行级共享锁(当读到时才加锁),一旦读完该行,立即释放该行级共享锁;
    2. 事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加行级排他锁,直到事务结束才释放。
  3. 可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻读,即后一次查询看到了前一次查询没有看到的行
    1. 事务在读取某数据的瞬间(就是开始读取的瞬间),必须先对其加行级共享锁,直到事务结束才释放;
    2. 事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加行级排他锁,直到事务结束才释放。
  4. 串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
    1. 事务在读取数据时,必须先对其加表级共享锁,直到事务结束才释放;
    2. 事务在更新数据时,必须先对其加表级排他锁,直到事务结束才释放。

 

  1. 事务的实现原理

  • 原子性:使用undo.log,从而达到回滚。
  • 持久性:使用redo.log,从而达到故障后恢复。
  • 隔离性:使用锁以及MVCC,运用的优化思想有读写分离,读读并行,读写并行。
  • 一致性:通过回滚,以及恢复,和在并发环境下的隔离做到一致性。

 

  1. 脏读、不可重复读、幻读

  • 脏读:脏读又称无效数据读出(读出了脏数据)。一个事务读取另外一个事务还没有提交的数据叫脏读。
  • 不可重复读:不可重复读是指在同一个事务内两次相同的查询返回了不同的结果。例如:事务T1会读取两次数据,在第一次读取某一条数据后,事务T2修改了该数据并提交了事务,T1此时再次读取该数据,两次读取便得到了不同的结果。不可重复读的重点是修改
  • 幻读:幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务向表中插入一行新数据。那么,操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。幻读的重点在于新增或者删除

 

  1. 为什么实际开发中使用已提交读更多?

  • 可重复读隔离级别下,存在间隙锁,导致出现死锁的几率比已提交读大的多
  • 可重复读隔离级别下,条件列未命中索引会锁表!而在已提交读隔离级别下,只锁行
  • 已提交读隔离级别下,半一致性读(semi-consistent)特性增加了update操作的并发性

 

  1. 间隙锁(Gap)

  • 定义:一个在索引记录之间的间隙上的锁。
  • 作用:保证某个间隙内的数据在锁定情况下不会发生任何变化。比如mysql默认隔离级别下的可重复读(RR)。
  • Gap锁会用在非唯一索引或不走索引的当前读
  • 对主键索引或者唯一索引会使用间隙锁吗?
    • 如果where条件全部命中,则不会使用gap锁,只会加记录锁
    • 如果where条件部分命中或全不命中,则会加gap

 

  1. 快照读和当前读

  • 快照读:读取的是记录数据的可见版本(可能是过期的数据),不用加锁,第一次执行事务中select时生成快照
  • 当前读:读取的是记录数据的最新版本,并且当前读返回的记录都会加上锁,保证其他事务不会再并发的修改这条记录,当你执行insertdeleteupdate这几个操作的时候默认会执行当前读

 

  1. 对象属性与数据库字段不一致

  2. Sql语句起别名
  3. Mapper.xml中的resultMap自定义映射,<result column=”” property=””>
  4. 配置文件中开启驼峰命名规则(对象属性为驼峰命名)

 

  1. 数据库优化方法

(1)选取最适用的字段属性

  1. MySQL可以很好的支持大数据量的存取,但是一般说来,数据库中的表越小,在它上面执行的查询也就会越快。因此,在创建表的时候,为了获得更好的性能,我们可以将表中字段的宽度设得尽可能小。例如,在定义邮政编码这个字段时,如果将其设置为CHAR(255),显然给数据库增加了不必要的空间,甚至使用VARCHAR这种类型也是多余的,因为CHAR(6)就可以很好的完成任务了。同样,我们应该使用MEDIUMINT而不是BIGINT来定义整型字段。
  2. 在可能的情况下,应该尽量把字段设置为NOT NULL,这样在将来执行查询的时候,数据库不用去比较NULL值。
  3. 对于某些文本字段,例如”省份”或者”性别”,我们可以将它们定义为ENUM类型。因为在MySQL中,ENUM类型被当作数值型数据来处理,而数值型数据被处理起来的速度要比文本类型快得多

(2)使用连接(JOIN)来代替子查询(Sub-Queries)

MySQL从4.1开始支持SQL的子查询。这个技术可以使用SELECT语句来创建一个单列的查询结果,然后把这个结果作为过滤条件用在另一个查询中。例如,我们要将客户基本信息表中没有任何订单的客户删除掉,就可以利用子查询先从销售信息表中将所有发出订单的客户ID取出来,然后将结果传递给主查询

(3)使用联合(UNION)来代替手动创建的临时表

MySQL从4.0的版本开始支持union查询,它可以把需要使用临时表的两条或更多的select查询合并的一个查询中。在客户端的查询会话结束的时候,临时表会被自动删除,从而保证数据库整齐、高效。使用union来创建查询的时候,我们只需要用UNION作为关键字把多个select语句连接起来就可以了,要注意的是所有select语句中的字段数目要相同

(4)使用事务

尽管我们可以使用子查询(Sub-Queries)、连接(JOIN)和联合(UNION)来创建各种各样的查询,但不是所有的数据库操作都可以只用一条或少数几条SQL语句就可以完成的。更多的时候是需要用到一系列的语句来完成某种工作。但是在这种情况下,当这个语句块中的某一条语句运行出错的时候,整个语句块的操作就会变得不确定起来。设想一下,要把某个数据同时插入两个相关联的表中,可能会出现这样的情况:第一个表中成功更新后,数据库突然出现意外状况,造成第二个表中的操作没有完成,这样,就会造成数据的不完整,甚至会破坏数据库中的数据。要避免这种情况,就应该使用事务,它的作用是:要么语句块中每条语句都操作成功,要么都失败。换句话说,就是可以保持数据库中数据的一致性和完整性。事物以BEGIN关键字开始,COMMIT关键字结束。在这之间的一条SQL操作失败,那么,ROLLBACK命令就可以把数据库恢复到BEGIN开始之前的状态。

 

  1. Explain+sql分析

  • 查看索引

  • Seq_in_index索引中的列序列号,从1开始。(联合索引最左原则)
  • Column_name被索引的列名
  • Cardinality索引中唯一值的数目的估计值,尽可能和表行数接近1:1(特殊YN,Status列状态除外)。它会估计索引中不重复记录,如果这个相对值很小,可能就要评估索引是否有意义
  • Explain

  • id: 表示查询中执行select子句或操作表的顺序
    • id相同,执行顺序由上至下
    • id不同,如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
    • id相同,同时存在,id如果相同可以认为是一组,从上往下顺序执行。
  • select_type8种): 表示查询中每个select子句的类型

  • table: 显示这一步所访问数据库中表名称(显示这一行的数据是关于哪张表的),有时不是真实的表名,可能是简称T1,T2,T3
  • type: 对表访问方式,表示MySQL在表中找到所需行的方式,又称”访问类型”。常用的类型有:ALLindexrangerefeq_refconstsystemNULL(从左到右,性能从差到好)
    • ALL:全表扫描,MySQL将遍历全表以找到匹配的行
    • index:索引树扫描,indexALL区别为index类型只遍历索引树
    • range:范围扫描,只检索给定范围的行,使用一个索引来选择行,key列显示使用了哪个索引,一般就是在你的where语句中出现between< >in等的查询,这种范围扫描索引比全表扫描要好,因为它只需要开始于索引的某一点,而结束于另一点,不用扫描全部索引。
    • ref:非主键非唯一索引等值扫描,显示索引的那一列被使用了,如果可能的话,最好是一个常数。哪些列或常量被用于查找索引列上的值。
    • eq_ref:主键索引或者非空唯一索引等值扫描,类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者unique key作为关联条件
    • const:常量连接,当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量
    • system:系统表,少量数据,往往不需要进行磁盘IOsystemconst类型的特例,当查询的表只有一行的情况下,使用system
    • NULLMySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。
  • possible_keys:指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用(该查询可以利用的索引,如果没有任何索引显示null)。如果该列是NULL,则没有相关的索引,可以通过检查WHERE子句看是否它引用某些列或适合索引的列来提高你的查询性能。如果是这样,创造一个适当的索引并且再次用EXPLAIN检查查询
  • key:显示MySQL实际决定使用的键(索引),必然包含在possible_keys中。如果没有选择索引,则是NULL。要想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用FORCE INDEXUSE INDEX或者IGNORE INDEX
  • key_len:表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)不损失精确性的情况下,长度越短越好
  • charvarchar类型key_len计算公式:

    varchar(N)变长字段且允许NULL = N * (utf8=3, gbk=2, latin1=1) + 1(NULL) + 2(变长字段)

    varchar(N)变长字段且不允许NULL = N * (utf8=3, gbk=2, latin1=1) + 2(变长字段)

     

    char(N)固定字段且允许NULL = N * (utf8=3, gbk=2, latin1=1) + 1(NULL)

    char(N)固定字段且允许NULL = N * (utf8=3, gbk=2, latin1=1)

  • 数值数据的key_len计算公式:

    TINYINT允许NULL = 1 + 1(NULL)

    TINYINT不允许NULL = 1

     

    SMALLINT允许为NULL = 2 + 1(NULL)

    SMALLINT不允许为NULL = 2

     

    INT允许为NULL = 4 + 1(NULL)

    INT不允许为NULL = 4

  • 日期时间型的key_len计算:(针对mysql5.5及之前版本)

    DATETIME允许为NULL = 8 + 1(NULL)

    DATETIME不允许为NULL = 8

     

    TIMESTAMP允许为NULL = 4 + 1(NULL)

    TIMESTAMP不允许为NULL = 4

  • ref:显示索引的那一列被使用了,如果可能的话,最好是一个常数。哪些列或常量被用于查找索引列上的值。
    • 如果是使用的常数等值查询,这里会显示const
    • 如果是连接查询,被驱动表的执行计划这里会显示驱动表的关联字段
    • 如果是条件使用了表达式或者函数,或者条件列发生了内部隐式转换,这里可能显示为func
  • rows:估算出结果集行数,表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数
  • Extra:包含MySQL解决查询的详细信息,有以下几种情况:
    • Using index(性能好):表示相应的select操作中使用了覆盖索引(Covering Index),避免访问了表的数据行,效率不错。如果同时出现using where,表明索引被用来执行索引键值的查找;如果没有同时出现using where,表明索引用来读取数据而非执行查找动作
    • Using where(性能较好):不用读取表中所有信息,仅通过索引就可以获取所需数据,这发生在对表的全部的请求列都是同一个索引的部分的时候,表示mysql服务器将在存储引擎检索行后再进行过滤
    • Using filesort(性能较差) :说明mysql会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取。MySQL中无法利用索引完成的排序操作称为”文件排序”。
    • Using temporary(性能差):表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询,常见group by order by
    • Using join buffer(性能差) :该值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。
    • Impossible where:强调了where语句会导致没有符合条件的行(通过收集统计信息不可能存在结果)。
    • Select tables optimized away:这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行
    • No tables usedQuery语句中使用from dual 或不含任何from子句

 

  1. 联合索引

联合索引a b 其实是根据ab进行排序,因此下面语句可以直接使用联合索引得到结果:

SELECT * FROM TABLE WHERE A=XXX ORDER BY B

然而对于联合索引ABC(三列组成) 来说下列语句也可以得到结果:

SELECT * FROM TABLE WHERE A=XXX ORDER BY B

SELECT * FROM TABLE WHERE A=XXX AND B=XXX ORDER BY C

但是以下语句则不能直接得到结果,需要执行一次FILESORT 排序操作,因为索引AC 没有排序

SELECT * FROM TABLE WHERE A=XXX ORDER BY C

 

  1. 隐式转换

  • EXPLAIN SELECT GOODS_NO FROM bs_itembasewhere GOODS_NO = \’100000112570\’;(索引查找)
  • EXPLAIN SELECT GOODS_NO FROM bs_itembasewhere GOODS_NO = 100000112570; (索引扫描)

 

  1. 慢查询优化方式

https://blog.csdn.net/qq_35571554/article/details/82800463

  1. 数据库中设置SQL慢查询
  • 方式一:修改配置文件,在 my.ini 文件中增加几行: 主要是慢查询的定义时间(超过2秒就是慢查询),以及慢查询log日志记录 slow_query_log
  • 方法二:通过MySQL数据库开启慢查询:
  1. 分析慢查询日志

直接分析mysql慢查询日志 ,利用explain关键字可以模拟优化器执行SQL查询语句,来分析sql慢查询语句,具体记录了:哪条语句导致慢查询(sql_text),该慢查询语句的查询时间(query_time),锁表时间(Lock_time),查询返回的行数(Rows_sent),以及扫描过的行数(rows_examined)等信息。

# Time: 2018-08-16T00:00:04.821003+08:00

# User@Host: zwfwroot[zwfwroot] @ 19.15.0.30 [19.15.0.30] Id: 67508

# Schema: gdqlk Last_errno: 0 Killed: 0

# Query_time: 14.344185 Lock_time: 0.000028 Rows_sent: 237064 Rows_examined: 237064 Rows_affected: 0

# Bytes_sent: 96078006

SET timestamp=1534348804;

SELECT * FROM `audit_catalog_lobby`; 

  1. 常见的慢查询优化
    1. 索引没起作用的情况
    2. 优化数据库结构
  • 将字段很多的表分解成多个表
  • 增加中间表
  1. 分解关联查询
  2. 优化LIMIT分页
  • 筛选字段加索引
  • 先查出主键ID
  • 关延迟联
  • 建立复合索引
  1. 分析具体的SQL语句
  1. 具体步骤
  • 第一步:根据慢日志定位慢查询SQL
    • 首先检查SQL中是否使用函数,隐式类型转换(字符串转数字),隐式函数或者传入的值超过索引长度
    • SQL中字段的字符集是否一致
    • 如果使用count计数,尽量使用count(*)或者count(1)count(字段)会涉及到回表操作count(id)会全表扫描,并且count(字段)count(id)都需要判空操作,也可以按照具体的业务选择MyISAM引擎,直接取值
    • 查看当前语句的状态,是否是在等MDL(元数据)锁,数据页flush,其他线程占用了行锁
    • 如果是热点数据,需要控制访问资源的并发事务量,可以将一行数据改成逻辑上的多行数据
  • 第二步:使用explain等工具分析 SQL
    • 查看SQL是否按照理想的状态检索最少的数据行,如果没有则查看是否走了指定索引
    • 判断优化器是否选错索引,可以通过强行选择索引或者重新统计索引信息
  • 第三步:修改SQL或者尽量让SQL走索引
    • 在数据库空闲的时候,定期进行索引统计,防止优化器选错索引,造成索引失效
    • 优化手段:聚簇索引,覆盖索引,索引下推优化,联合索引
    • 字符串索引:前缀索引,倒序存储,hash索引
    • 如果需要业务字段做索引,必须确保是唯一索引,符合K-V结构,不需要考虑其他索引叶子节点的大小
    • 尽量使用自增主键索引,每次插入新的数据都是追加操作,可以防止数据页黑洞出现,保证索引的紧凑,不涉及挪动其他数据,也不会触动叶子节点的页分裂
    • 主键索引的长度不可以过长,造成其他索引树的叶子节点较大
    • 在建立联合索引的时候,如果可以通过顺序少建立一个索引,则调整顺序,同时需要考虑空间占用
    • 数据写多读少的时候选择普通索引,利用change buffer可以提高效率,合理设置change buffer大小,防止频繁merge
    • 如果sql语句出现锁操作,尽量让锁操作最后执行,防止影响其他SQL的执行
    • 如果使用长连接,在进行较大查询之后,需要重置链接,防止占用较大内存,造成数据库异常重启
    • 不要删除索引,删除索引可能会造成页分裂,导致数据页出现黑洞
    • 使用order by查询的时候,如果单行数据过大,会造成回表操作,可以使用联合索引,让字段本身有序

 

  1. Mysql分页查询

mysql分页查询使用的是limit关键字。关键字之后需要两个参数m,n,最终mysql语句的样式:

–m的含义表示从数据的第m条开始查询(mysql中第一条数据m=0

–n的含义是从第m条数据开始往后查询n条数据

 

  1. 数据库去重

  • 第一种:两条记录或者多条记录的每一个字段值完全相同,这种情况去重复最简单,用关键字distinct就可以去掉。例:

SELECT DISTINCT * FROM TABLE

  • 第二种:两条记录之间之后只有部分字段的值是有重复的,但是表存在主键或者唯一性ID。如果是这种情况的话用DISTINCT是过滤不了的,这就要用到主键id的唯一性特点及group by分组。例:

SELECT * FROM TABLE WHERE ID IN (SELECT MAX(ID) FROM TABLE GROUP BY [去除重复的字段名列表,….])

  • 第三种:两条记录之间只有部分字段的值是有重复的,但是表不存在主键或者唯一性ID。这种情况可以使用临时表,将数据复制到临时表并添加一个自增长的ID,在删除重复数据之后再删除临时表。例:

//创建临时表,并将数据写入到临时表

SELECT IDENTITY(INT1,1) AS ID,* INTO NEWTABLE(时表) FROM TABLE

//查询不重复的数据

SELECT * FROM NEWTABLE WHERE ID IN (SELECT MAX(ID) FROM NEWTABLE GROUP BY [去除重复的字段名列表,….])

//删除临时表

DROP TABLE NEWTABLE

在进行去重时,数据库无法进行边查询边删除,所以删除的时候必须是第三张临时表作为匹配删除,这样的操作可以省略创建临时表直接删除原表中数据,推荐这种方式进行去重操作。

delete
from user where name in
(select name from
(select name from user group
by name having count(name)
>
1)
as s1)
and id not
in
(select id from
(select min(id)
as id from user group
by name having count(name)>1)
as s2);

 

  1. 数据库水平切分与垂直切分

垂直拆分就是要把表按模块划分到不同数据库表中(当然原则还是不破坏第三范式),这种拆分在大型网站的演变过程中是很常见的。当一个网站还在很小的时候,只有小量的人来开发和维护,各模块和表都在一起,当网站不断丰富和壮大的时候,也会变成多个子系统来支撑,这时就有按模块和功能把表划分出来的需求。其实,相对于垂直切分更进一步的是服务化改造,说得简单就是要把原来强耦合的系统拆分成多个弱耦合的服务,通过服务间的调用来满足业务需求看,因此表拆出来后要通过服务的形式暴露出去,而不是直接调用不同模块的表,淘宝在架构不断演变过程,最重要的一环就是服务化改造,把用户、交易、店铺、宝贝这些核心的概念抽取成独立的服务,也非常有利于进行局部的优化和治理,保障核心模块的稳定性。垂直拆分依然存在单表大数据量性能瓶颈

水平拆分,就是要把一个表按照某种规则把数据划分到不同表或数据库里。例如像计费系统,通过按时间来划分表就比较合适,因为系统都是处理某一时间段的数据。而像SaaS应用,通过按用户维度来划分数据比较合适,因为用户与用户之间的隔离的,一般不存在处理多个用户数据的情况,简单的按user_id范围来水平切分。

通俗理解:水平拆分行,行数据拆分到不同表中,垂直拆分列,表数据拆分到不同表中。

 

  1. Statement 和 PreparedStatement 的区别

PreparedStatement接口代表预编译的语句,与Statement相比,它主要的优势在于:

  • 可以减少SQL的编译错误并增加SQL的安全性(减少SQL注射攻击的可能性);
  • PreparedStatement中的SQL语句是可以带参数的,避免了用字符串连接拼接SQL语句的麻烦和不安全;
  • 当批量处理SQL或频繁执行相同的查询时,PreparedStatement有明显的性能上的优势,由于数据库可以将编译优化后的SQL语句缓存起来,下次执行相同结构的语句时就会很快(不用再次编译和生成执行计划)。

 

  1. select *缺点

  • 返回多余的列给客户端,造成网络压力过大,增加服务器压力。
  • 如果涉及多表查询,可能会使相同表名的列返回数据混乱。
  • select * from table where [] …,当我们对[]添加索引后,返回数据时,都需要再次进行RID查找,获取列的全部信息,使索引效果减弱
  • 由于需返回全部列,数据库返回数据时,都会从系统基础表中获取相关列信息,增加服务器系统表查询次数。

 

  1. Mysql窗口函数

  • 专用窗口函数:rank()dense_rank()row_number()

  • 汇总函数:max()min()count()sum()avg()

 

  1. left join 、right join 、inner join之间的区别

  • left join(左联接) 返回包括左表中的所有记录和右表中联结字段相等的记录
  • right join(右联接) 返回包括右表中的所有记录和左表中联结字段相等的记录
  • inner join(等值连接) 只返回两个表中联结字段相等的行
  • 笛卡尔积原本是代数的概念,他的意思是对于两个不同的集合AB。对于A中的每一个元素,都有对于在B中的所有元素做连接运算。可以见得对于两个元组分别为mn的表。笛卡尔积后得到的元组个数为m * n个元组。而对于mysql来说,默认的连接就是笛卡尔积连接。

 

  1. LSMT

Log-structed Merge-tree,LSM树使用了一种独特的机制牺牲了一些读操作的性能,保证了写操作的能力,它能够让所有的操作顺序化几乎完全避免了随机读写。

SSTable的全称是Sorted String Table,本质就是一个KV结构顺序排列的文件。最基础的SSTable就是key和value的键值对按照key值的大小排序,并存储在文件当中。当我们需要查找某个key值对应的数据的时候,我们会将整个文件读入内存进行查找。同样,写入也是如此,我们会将插入的操作在内存中进行,得到结果之后,直接覆盖原本的文件,而不会在文件当中修改,因为这会牵扯到移动大量的数据。

如果文件中的数据量过大,我们需要另外建立一个索引文件,存储不同的key值对应的offset,方便我们在读取文件的时候快速查找到我们想要查找的文件。需要注意,SSTable是不可修改的,我们只会用新的SSTable去覆盖旧的,而不会再原本的基础上修改。因为修改会涉及随机读写,这是我们不希望的。

  • 原理:最基本的LSM就是在SSTable的基础上增加了一个MemtableMemtable顾名思义就是存放在内存当中的表结构。当然也不一定是表结构,也可以是树结构,这并不影响,总之是一个可以快速增删改查的数据结构,比如红黑树、SkipList都行。其次我们还需要一个log文件,和数据库当中的log一样,记录数据发生的变化。方便系统故障或者是数据丢失的时候进行找回,所以整体的架构如下:

  • 查找:当我们需要查找一个元素的时候,我们会先查找Memtable,因为它就在内存当中,不需要额外读取文件,如果Memtable当中没有找到,我们再一个一个查找SSTable,由于SSTable当中的数据也是顺序存储的,所以我们可以使用二分查找,整个查找的速度也会非常快。但是SSTable的数量可能会很多,而且我们必须要顺序查找,所以当SSTable数量很大的时候,也会影响查找的速度。为了解决这个问题,我们可以引入布隆过滤器进行优化。我们对每一个SSTable建立一个布隆过滤器,可以快速地判断元素是否在某一个SSTable当中。
  • 增加:当我们要增加一个元素的时候,我们直接增加在Memtable,而不是写入文件。这也保证了增加的速度可以做到非常快。把增删改这三个操作都看成是添加,但是这就带来了一个问题,这么操作会导致很快Memtable当中就积累了大量数据,而我们的内存资源也是有限的,显然不能无限拓张。为了解决这个问题,我们需要定期将Memtable当中的内容存储到磁盘,存储成一个SSTable。这也是SSTable的来源,SSTable当中的数据不是凭空出现的,而是LSM落盘产生的。

  • 同样,由于我们不断地落盘同样也会导致SSTable的数量增加,前面我们也已经分析过了,SSTable的数量增加会影响我们查找的效率。再加上我们还存储了许多修改和删除的信息,我们需要把这些信息落实。为了达成这点,我们需要定期将所有的SSTable合并在合并的过程当中我们完成数据的删除以及修改工作。换句话说,之前的删除、修改操作只是被记录了下来,直到合并的时候才真正执行。

  • B+树当中,我们为了能够快速读取而使用了多路平衡树,这样可以迅速找到对应key的节点。我们只需要读入节点当中的内容即可,但也正因为平衡树的结构,导致了我们在写入数据的时候会引起树结构的变动,也就涉及到多次文件的随机读写。当我们数据的吞吐量很大的时候,会带来巨大的开销。LSMT读取的时候效率比B+树要低,但是对于大数据的写入支持得更好。在大数据场景当中,许多对于数据的吞吐量有着很高的要求,比如消息系统、分布式存储等。这个时候B+树就有些无能为力了,但是同样,如果我们需要保证查找的效率,那LSMT也不太合适,因此两者其实并没有谁比谁更优,而是针对的场景不同。关于LSMT,其实也有很多个变种,其中比较有名的是Jeff Dean写的Leveldb,它在LSMT的基础上做了一些改动,进一步提升了性能。

 

  1. SQL语句

————————TOP————————

// 用于规定要返回的记录的数目

// 从 “Persons” 表中选取头两条记录

SELECT TOP 2 * FROM Persons

// 从”Persons” 表中选取 50% 的记录

SELECT TOP 50 PERCENT * FROM Persons

 

————————%————————

// 代表零个或多个字符

// 从 “Persons” 表中选取居住在以 “N” 开始的城市里的人

SELECT * FROM Persons WHERE City LIKE \’N%\’

// 从 “Persons” 表中选取居住在以 “g” 结尾的城市里的人

SELECT * FROM Persons WHERE City LIKE \’%g\’

// 从 “Persons” 表中选取居住在包含 “lon” 的城市里的人

SELECT * FROM Persons WHERE City LIKE \’%lon%\’

 

————————_————————

// 仅替代一个字符

// 从”Persons” 表中选取名字的第一个字符之后是 “eorge” 的人

SELECT * FROM Persons WHERE FirstName LIKE \’_eorge\’

// 从 “Persons” 表中选取的这条记录的姓氏以 “C” 开头,然后是一个任意字符,然后是 “r”,然后是一个任意字符,然后是 “er”

SELECT * FROM Persons WHERE LastName LIKE \’C_r_er\’

 

————————[charlist]————————

// 字符列中的任何单一字符

// 从”Persons” 表中选取居住的城市以 “A” 或 “L” 或 “N” 开头的人

SELECT * FROM Persons WHERE City LIKE \'[ALN]%\’

 

————————[!charlist]/[^charlist] ———————

// 不在字符列中的任何单一字符

// 从上面的 “Persons” 表中选取居住的城市不以 “A” 或 “L” 或 “N” 开头的人

SELECT * FROM Persons WHERE City LIKE \'[!ALN]%\’

 

————————IN————————

// 允许我们在 WHERE 子句中规定多个值

// 从上表中选取姓氏为 Adams 和 Carter 的人

SELECT * FROM Persons WHERE LastName IN (\’Adams\’,\’Carter\’)

 

————————BETWEEN————————

// 选取介于两个值之间的数据范

// 以字母顺序显示介于 “Adams”(包括)和 “Carter”(不包括)之间的人

SELECT * FROM Persons WHERE LastName BETWEEN \’Adams\’ AND \’Carter\’

// 显示范围之外的人

SELECT * FROM Persons WHERE LastName NOT BETWEEN \’Adams\’ AND \’Carter\’

 

———————— UNION/UNION ALL—————-

// 用于合并两个或多个 SELECT 语句的结果集

// 列出所有在中国和美国的不同的雇员名,这个命令无法列出在中国和美国的所有雇员。在上面的例子中,我们有两个名字相同的雇员,他们当中只有一个人被列出来了。UNION 命令只会选取不同的值。

SELECT E_Name FROM Employees_China

UNION

SELECT E_Name FROM Employees_USA

// UNION ALL 命令会列出所有的值

SELECT E_Name FROM Employees_China

UNION ALL

SELECT E_Name FROM Employees_USA

 

———————— SELECT INTO————————

// 可用于创建表的备份复件

// SELECT INTO 语句从一个表中选取数据,然后把数据插入另一个表中。

// SELECT INTO 语句常用于创建表的备份复件或者用于对记录进行存档。

SELECT column_name(s)

INTO new_table_name [IN externaldatabase]

FROM old_tablename

 

————————AVG()————————

// 计算 “OrderPrice” 字段的平均值

SELECT AVG(OrderPrice) AS OrderAverage FROM Orders

 

————————COUNT()————————

SELECT COUNT(column_name) FROM table_name

SELECT COUNT(*) FROM table_name

SELECT COUNT(DISTINCT column_name) FROM table_name

 

———————— FIRST()/LAST()————————

// 函数返回指定的字段中第一个记录的值

// 可使用 ORDER BY 语句对记录进行排序

SELECT FIRST(OrderPrice) AS FirstOrderPrice FROM Orders

SELECT LAST(OrderPrice) AS LastOrderPrice FROM Orders

 

———————— GROUP BY————————

// 用于结合合计函数,根据一个或多个列对结果集进行分组

SELECT Customer,SUM(OrderPrice) FROM Orders GROUP BY Customer

 

————————HAVING———————

// WHERE 关键字无法与合计函数一起使用

// 查找订单总金额少于 2000 的客户

SELECT Customer,SUM(OrderPrice) FROM Orders

GROUP BY Customer

HAVING SUM(OrderPrice)<2000

// 查找客户 “Bush” 或 “Adams” 拥有超过 1500 的订单总金额

SELECT Customer,SUM(OrderPrice) FROM Orders

WHERE Customer=\’Bush\’ OR Customer=\’Adams\’

GROUP BY Customer

HAVING SUM(OrderPrice)>1500

 

————————UCASE()/LCASE()————————

// UCASE 函数把字段的值转换为大写

SELECT UCASE(LastName) as LastName,FirstName FROM Persons

// LCASE 函数把字段的值转换为小写

SELECT LCASE(LastName) as LastName,FirstName FROM Persons

 

————————MID()————————

// MID 函数用于从文本字段中提取字符

// start必需。规定开始位置(起始值是 1)。

// length可选。要返回的字符数。如果省略,则 MID() 函数返回剩余文本。

SELECT MID(column_name,start[,length]) FROM table_name

// 从 “City” 列中提取前 3 个字符

SELECT MID(City,1,3) as SmallCity FROM Persons

 

————————LEN()————————

// LEN 函数返回文本字段中值的长度

SELECT LEN(City) as LengthOfCity FROM Persons

 

————————ROUND()————————

// ROUND 函数用于把数值字段舍入为指定的小数位数

// 规定要返回的小数位数

SELECT ROUND(column_name,decimals) FROM table_name

 

————————NOW()————————

// NOW 函数返回当前的日期和时间

SELECT NOW() FROM table_name

 

————————FORMAT()————————

// FORMAT 函数用于对字段的显示进行格式化

SELECT ProductName, UnitPrice, FORMAT(Now(),\’YYYY-MM-DD\’) as PerDate FROM Products

  • MySQL日期函数

  • MySQL数据类型
  1. TEXT类型

  1. Number类型

  1. Date 类型

 

  1. 持久层

持久化就是将内存中的数据保存到关系型数据库、文件系统、消息队列等提供持久化支持的设备中。持久层就是系统中专注于实现数据持久化的相对独立的层面。持久层设计的目标包括:

  1. 数据存储逻辑的分离,提供抽象化的数据访问接口。
  2. 数据访问底层实现的分离,可以在不修改代码的情况下切换底层实现。
  3. 资源管理和调度的分离,在数据访问层实现统一的资源调度(如缓存机制)。
  4. 数据抽象,提供更面向对象的数据操作。

 

  1. MyBatis 中 #{} 和 ${} 的区别

  • #{} 占位符,${} 拼接符
  • #{}:动态解析 -> 预编译 -> 执行,#{} 能防止sql 注入;${}:动态解析 -> 编译 -> 执行
  • 变量替换后,#{} 对应的变量自动加上单引号 \’\’;变量替换后,${} 对应的变量不会加上单引号 \’\’
  • 表名作参数时,必须用 ${}。如:select * from ${tableName}
  • order by
    时,必须用 ${}
    。如:select * from t_user order by ${columnName}

 

  1. Mybatis动态SQL

https://www.cnblogs.com/ysocean/p/7289529.html

  • if 语句
  • if+where 语句
  • if+set 语句
  • choose(whenotherwise) 语句
  • trim 语句
  • SQL 片段
  • foreach 语句

 

  1. mybatis 一级缓存和二级缓存区别

  • 一级缓存基于sqlSession默认开启,在操作数据库时需要构造SqlSession对象,在对象中有一个HashMap用于存储缓存数据。不同的SqlSession之间的缓存数据区域是互相不影响的。一级缓存的作用域是SqlSession范围的,当在同一个sqlSession中执行两次相同的sql语句时,第一次执行完毕会将数据库中查询的数据写到缓存(内存),第二次查询时会从缓存中获取数据,不再去底层数据库查询,从而提高查询效率。如果SqlSession执行了DML增删改)操作,并且提交到数据库,MyBatis则会清空SqlSession中的一级缓存,这样做的目的是为了保证缓存中存储的是最新的信息,避免出现脏读现象。当一个SqlSession结束后该SqlSession中的一级缓存也就不存在了。(必须在同一事务内)
  • 二级缓存是mapper级别的缓存。使用二级缓存时,多个SqlSession使用同一个Mappersql语句去操作数据库,得到的数据会存在二级缓存区域,它同样是使用HashMap进行数据存储。相比一级缓存SqlSession二级缓存的范围更大,多个Sqlsession可以共用二级缓存二级缓存是跨SqlSession。二级缓存的作用域是mapper同一个namespace不同的sqlSession两次执行相同的namespace下的sql语句,且向sql中传递的参数也相同,即最终执行相同的sql语句,则第一次执行完毕会将数据库中查询的数据写到缓存,第二次查询会从缓存中获取数据,不再去底层数据库查询,从而提高效率。二级缓存必须在前面的sqlSession提交事务之后,才能够支持,并且使用具有局限性,每一个sqlSession执行完之后,必须进行提交操作,其他sqlSession才能感应到变化,对于多表操作容易拿到脏数据

 

  1. Mybatis中的Dao接口和XML文件里的SQL是如何建立关系的

首先,Mybatis在初始化SqlSessionFactoryBean的时候,找到mapperLocations路径去解析里面所有的XML文件。

1. 根据mapper中的每句SQL生成对应的SqlSource,Mybatis会把每个SQL标签封装成SqlSource对象。然后根据SQL语句的不同,又分为动态SQL和静态SQL。其中,静态SQL包含一段String类型的sql语句;而动态SQL则是由一个个SqlNode组成。

2. 创建MappedStatement,XML文件中的每一个SQL标签就对应一个MappedStatement对象,这里面有两个属性很重要:id和sqlSource。

3. 缓存到Configuration,所有xml解析完后,configuration对象具有所有sql信息

  • 总的来说,就是通过statement全限定类型+方法名拿到MappedStatement对象,然后通过执行器Executor去执行具体SQL并返回

 

  1. Mybatis和Hibernate的区别

  1. hibernate完全可以通过对象关系模型实现对数据库的操作,拥有完整的JavaBean对象与数据库的映射结构来自动生成sql。而mybatis仅有基本的字段映射对象数据以及对象实际关系仍然需要通过手写sql来实现和管理。

  2. hibernate通过它强大的映射结构和hql语言,大大降低了对象与数据库的耦合性,而mybatis由于需要手写sql,因此与数据库的耦合性直接取决于程序员写sql的方法,如果sql不具通用性而用了很多某数据库特性的sql语句的话,移植性也会随之降低很多,成本很高。

  3. hibernate日志系统非常健全,涉及广泛,包括:sql记录、关系异常、优化警告、缓存提示、脏数据警告等;而mybatis则除了基本记录功能外,功能薄弱很多。

  4. 由于mybatis的sql都是写在xml里,因此优化sql比hibernate方便很多。而hibernate的sql很多都是自动生成的,无法直接维护sql;虽有hql,但功能还是不及sql强大,见到报表等需求时,hql也不行,也就是说hql是有局限的;hibernate虽然也支持原生sql,但开发模式上却与orm不同,需要转换思维,因此使用上不是非常方便。总之写sql的灵活度上hibernate不及mybatis。

  5. MyBatis的二级缓存配置都是在每个具体的表-对象映射中进行详细配置,这样针对不同的表可以自定义不同的缓存机制。并且Mybatis可以在命名空间中共享相同的缓存配置和实例,通过Cache-ref来实现。

  6. hibernate配置要比mybatis复杂的多,学习成本也比mybatis高。但也正因为mybatis使用简单,才导致它要比hibernate关心很多技术细节。mybatis由于不用考虑很多细节,开发模式上与传统jdbc区别很小,因此很容易上手并开发项目,但忽略细节会导致项目前期bug较多,因而开发出相对稳定的软件很慢,而开发出软件却很快。hibernate则正好与之相反。但是如果使用hibernate很熟练的话,实际上开发效率丝毫不差于甚至超越mybatis。


 

 

版权声明:本文为ttyq原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/ttyq/p/15182490.html