数据库相关知识点
-
-
聚簇索引:将数据存储与索引放到了一块,找到索引也就找到了数据。具有唯一性,聚簇索引默认是主键,如果表中没有定义主键,InnoDB 会选择一个唯一的非空索引代替。如果没有这样的索引,InnoDB 会隐式定义一个主键来作为聚簇索引。
-
非聚簇索引:将数据存储于索引分开结构,索引结构的叶子节点指向了数据的对应行,myisam通过key_buffer把索引先缓存到内存中,通过索引访问数据,在内存中直接搜索索引,然后通过索引找到磁盘相应数据,这也就是为什么索引不在key buffer命中时,速度慢的原因。
-
innodb中,在聚簇索引之上创建的索引称之为辅助索引,辅助索引访问数据总是需要二次查找,非聚簇索引都是辅助索引,像复合索引、前缀索引、唯一索引,辅助索引叶子节点存储的不再是行的物理位置,而是主键值
-
为什么主键通常建议使用自增id?
聚簇索引的数据的物理存放顺序与索引顺序是一致的,即只要索引是相邻的,那么对应的数据一定也是相邻地存放在磁盘上。如果不是自增id,那么会不断地调整数据的物理地址、分页,而自增id只需要一页一页地写,索引结构相对紧凑,磁盘碎片少,效率高。
MyISAM的主索引为非聚簇索引,因此其物理地址必然凌乱,拿到这些物理地址,按照合适的算法进行I/O读取,而聚簇索引只需要一次I/O
但是涉及大数据量的排序、全表扫描、count之类的操作的话,还是MyISAM占优势些,因为索引所占空间小,这些操作是需要在内存中完成的。
-
适用场景
-
聚簇索引适用在排序的场合
-
取出一定范围数据的适合,使用聚簇索引
-
维护索引非常昂贵,特别是插入新行或者主键被更新导致到分页时
-
当使用UUID(随机ID)作为主键,使数据存储稀疏,这就会出现聚簇索引有可能比全表扫描更慢
-
-
-
InnoDB索引底层数据结构
-
InnoDB是默认的表存储引擎,其特点是行锁设计、支持MVCC、支持外键、提供一致性非锁定读、同时被设计用来最有效的利用以及使用内存和CPU。
-
体系结构:
-
-
后台线程主要负责刷新内存池中的数据、将已修改的数据刷新到磁盘等。
使用缓冲池技术来提高数据库的整体性能,缓冲池就是一块内存区域,在数据库中进行取页的操作。缓冲池中缓存的数据页类型有:索引页、数据页、undo页、插入缓冲、自适应哈希索引、innoDB的锁信息、数据字典信息等。
-
关键特性:插入缓冲(insert buffer,为了提高写性能)、两次写(提高可靠性)、自适应哈希索引(通过缓冲池的B+树页构造而来,建立速度非常快,不需要对整张表构建哈希索引。innoDB存储引擎会自动根据访问的频率和模式来自动地为某些热点页建立哈希索引)、异步IO(AIO,提高磁盘操作性能,用户可以在发出一个IO请求后立即再发出另外一个IO请求,当全部IO请求发送完毕后,等待所有IO操作完成。相对的是Sync IO,即每进行一次IO操作,都需要等待此次操作结束才能继续接下来的操作)、刷新邻接页(InnoDB存储引擎在刷新一个脏页时,会检测该页所在区(extent)的所有页,如果是脏页,那么一起刷新。通过AIO可以将多个IO写操作合并为一个IO操作)
-
-
B+树
-
我们为了设计索引的存储结构,之前是建立平衡二叉树,从而避免遍历整张表,最坏情况的时间复杂度是O(logN)->为了当数据量很大时,降低时间复杂度,我们需要建立其他高度更低的树->那么通过在一个树节点中包含多条数据,和包含多个指针域来实现。->进一步优化,B+树是B树的一种变体,查询性能更好。B+树是一种树数据结构,n叉树,每个节点有多个孩子,通常用于数据库和操作系统的文件系统中。
-
特点是能够保持数据稳定有序,其插入和修改拥有较稳定的对数时间复杂度。B+树元素自底向上插入。
-
一个m阶的B树具有以下特征:(即每个节点最多包含m个孩子,m值取决于磁盘页的大小)
如5阶B树中,结点最多有4个key,最少有2个key。
插入删除时需要始终满足结点key的个数的限制条件。
-
m阶的B+树的特征?
-
B+树和B树(balance tree)的区别?
1.有k个子结点的结点必然有k个关键码
2.B+树非叶结点仅具有索引作用,跟记录有关的信息均存放在叶结点中,因此磁盘页能容纳更多节点元素,更“矮胖”
3.B+树的所有叶结点构成一个有序链表,可以按照关键码排序的次序遍历全部记录
4.B+树的查询必须查找到叶子节点,而B树只要匹配到即可不关注元素位置,因此B+树查找更稳定
5.B+树所有叶子节点本身根据关键字的大小进行连接,从而快速实现范围查询。
https://segmentfault.com/a/1190000019927682?utm_source=tag-newest
https://blog.csdn.net/Fmuma/article/details/80287924
-
-
什么是事务
事务是逻辑上的一组逻辑,要么都执行,要么都不执行。(经典例子:银行转账)
四大特性(ACID):
* 原子性:事务是最小的执行单位,不允许分割
* 一致性:执行事务前后,数据保持一致,多个事务对同一数据读取的结果是相同的。
* 隔离性:并发访问数据库时,一个用户的事务不被其他事务干扰,各并发事务之间的数据库是独立的。
* 持久性:*一个事务被提交后*,它对数据库中数据的改变是持久的。即使数据库发生故障也不应该对其有任何影响。 -
并发事务带来哪些问题
多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对同一数据进行操作)
-
脏读:当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。此时用到的数据是还没有提交的数据。
-
丢失修改:在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失。
-
不可重复读:指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
-
幻读:它发生在一个事务读取了几行数据,接着另一个并发事务插入/删除了一些数据时。在随后的查询中,第一个事务就会发现多了/少了一些原本不存在的记录,就好像发生了幻觉一样。
-
-
事务隔离级别有哪些?MySQL的默认隔离级别是?
-
READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
-
READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
-
REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
-
SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
-
-
MySQL锁机制与InnoDB锁算法
-
为了解决并发、数据安全的问题,采用锁机制。
-
按照锁粒度将数据库锁分为表级锁和行级锁
-
表级锁:对当前操作的整张表加锁,实现简单 ,资源消耗也比较少,加锁快,不会出现死锁 。其锁定粒度最大,触发锁冲突的概率最高,并发度最低,MyISAM和 InnoDB引擎都支持表级锁。当事务更新大表中大部分数据时直接使用表级锁效率更高&事务复杂时,使用行级锁可能会引起死锁导致回滚。
-
行级锁:只针对当前操作的行进行加锁。 行级锁能大大减少数据库操作的冲突。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会出现死锁。InnoDB支持行级锁
-
页级锁:粒度介于两者之间,一次锁定相邻的一组记录。BDB支持页级锁。开销和加锁时间界于表锁和行锁之间,会出现死锁。锁定粒度界于表锁和行锁之间,并发度一般。
-
-
按照是否可写分为共享锁和排他锁:
-
共享锁S:读锁,其他用户可以并发读取数据,但任何事务都不能获取数据上的排他锁,直到已释放所有共享锁。如果事务T对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排他锁。
-
排它锁X:写锁,防止任何其它事务获取资源上的锁,直到在事务的末尾将资源上的原始锁释放为止。如果事务T对数据A加上排他锁后,则其他事务不能再对A加任任何类型的封锁。
-
-
InnoDB另外的两个表级锁:(意向锁是InnoDB自动加的,不需要用户干预)
-
意向共享锁IS:事务准备给数据行记入共享锁,事务在一个数据行加共享锁前必须先取得该表的IS锁。
-
意向排它锁IX:事务准备给数据行加入排他锁,事务在一个数据行加排他锁前必须先取得该表的IX锁。
-
-
死锁和避免死锁
-
InnoDB的行级锁是基于索引实现的,如果查询语句为命中任何索引,那么InnoDB会使用表级锁.
-
MyISAM总是一次性获得所需的全部锁(采用表级锁),InnoDB的锁是逐步获得的(默认行级锁)。当两个事务都需要获得对方持有的锁,导致双方都在等待,从而产生死锁。
-
InnoDB的行级锁是基于索引实现的,如果查询语句为命中任何索引,那么InnoDB会使用表级锁。
-
避免死锁的方式:使用表级锁;多个程序尽量约定以相同的顺序访问表;同一个事务尽可能做到一次锁定所需要的所有资源。
-
-
-
大表优化
-
限定数据的范围:禁止不带任何限制数据范围条件的查询语句。
-
读/写分离:数据库拆分,主库负责写,从库负责读。
-
垂直分区:根据数据库里面数据表的相关性进行拆分,把一张列比较多的表拆分为多张表。可以使列数据变小,减少I/O次数,简化表的结构,易于维护;但是主键会出现冗余,需要管理冗余列,使事务变得更加复杂。
-
水平分区:保持数据表结构不变,通过某种策略存储数据分片。使每一片数据分散到不同的表/库中,实现分布式。水平拆分最好分库(数据库分片的方案有客户端代理:分片逻辑在应用端,封装在jar包里,通过修改或封装JDBC层实现;中间件代理:在应用和数据中间加一个代理层,分片逻辑统一维护在中间件服务中)。支持非常大的数据量存储,应用端改造也少,但是分片事务难以解决,带来逻辑、部署、运维的各种复杂度。
-
-
池化设计思想?数据库连接池?
-
初始预设资源,解决的问题就是抵消每次获取资源的消耗,如创建线程的开销,获取远程连接的开销等。还包括池子的初始值、池子的活跃值、池子的最大值等。
-
最大的作用是支持复用,避免重复的创建销毁带来的开销。
-
代表实现有java线程池、jdbc连接池、redis连接池等。
-
“单线程的方式:大家排队一个一个取水,为了不浪费水,每个人接完水后,关掉水龙头,下一个人接的时候再打开水龙头,开水龙和关水龙头可以看成创建和销毁一个线程用于一个人的取水任务。这种方式适合人少的场景。
多线程的方式:多提供几个水龙头,这种方式适合人较多的场景,例如学生宿舍的公共水房。
线程池的方式:提供一个水池,先将水放到水池,然后由多个人同时在水池取水,水龙头可以不用频繁开关,可以支持多人并发取水,但是水池需要专人监管,如监控水池溢出,水池没水,水池取水人员达到上限等。这种方式适合高并发的情况。”
-
数据库连接本质就是一个socket的连接。在连接池中,创建连接后,将其放置在池中,并再次使用它,因此不必建立新的连接。如果使用了所有连接,则会建立一个新连接并将其添加到池中。连接池还减少了用户必须等待建立与数据库的连接的时间。
-
JDK内置的四种线程池拒绝策略:调用者运行策略;中止策略;丢弃策略;dubbo/Netty/ActiveMq/pinpoint中的线程拒绝策略(第三方实现);
-
-
分库分表
分成多个表之后,每个表都是从1开始累加,但是我们应该是需要一个全局唯一的id来支持。生成全局id的方式有:
-
UUID:不适合作为主键,无序不可读,查询效率低。
-
数据库自增id:两台数据库分别设置不同步长,生成不重复ID的策略来实现高可用。这种方式生成的id有序,但是需要独立部署数据库实例,成本高,且有性能瓶颈。
-
利用redis生成id:性能好,且灵活方便。但是引入新的组件造成系统更加复杂,可用性降低,编码更加复杂,系统成本增加。
-
-
一条SQL语句在MySQL中如何执行?
MySQL分为Server层(所有跨存储引擎的功能在此实现)和存储引擎层(主要负责数据的存储和读取,采用可以替换的插件式架构,支持InnoDB、MyISAM、Memory等)。
0.客户端和server之间通过连接器连接,登录MySQL时进行身份认证和权限相关。
1.应用程序把查询SQL语句发送给服务器端执行
2.查询缓存:接收到查询请求后,并不会直接去数据库查询,而是在数据库的查询缓存中找是否有相应的查询数据。如果语句不在查询缓存中,就会继续后面的执行阶段,执行完成后,执行结果会被存入查询缓存中。
3.查询优化处理,生成执行计划(如果没有命中缓存)/将一个SQL转换为一个执行计划
* 解析SQL:MySQL解析SQL语句,生成一棵对应的解析树。
* 预处理:根据一些MySQL规则进一步检查解析树是否合法,如数据表和列是否存在,解析列名和别名,是否有歧义。
* 优化SQL执行计划:优化器是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联的时候,决定各个表的连接顺序,将SQL语句转化成执行计划,一条查询可以有很多种执行方式,最后都返回相同的结果,最后找到其中最好的执行计划。4.MySQL根据相应的执行计划完成整个查询:执行计划是一个数据结构,其中有大量的操作需要通过调用存储引擎实现的接口完成。
5.将查询结果返回客户端
-
一条SQL语句执行得很慢的原因有哪些?
-
大多数情况下很正常,偶尔很慢
1.数据库在刷新脏页:redolog写满了;内存不够用了;MySQL认为系统“空闲”的时候;MySQL正常关闭的时候
2.执行时遇到锁,如表锁、行锁。通过show processlist查看当前状态
-
一直执行得很慢
1.没用到索引,只能走全表扫描:字段没有索引;字段有索引但没有用上索引(如果字段左边做了运算,那么查询时就不会用上索引。需要把运算放在字段右边);函数操作导致没有用上索引。
2.数据库选错索引:主键索引存放的值是整行字段的数据,非主键索引上存放的值不是整行字段的数据,而是存放主键字段的值。系统通过预测而选择走全表扫描或者走索引,但是由于统计的失误,导致系统没有走索引,而是走了全表扫描。
-