1. 事务的特性和隔离级别 牛客458611235号
特性:ACID。原子性(Atomic)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)
原子性:事务中进行的操作是一个不可分割的单元,要么全部执行,要么全不执行。
一致性:事务完成时,所有数据保持一致。完整性约束,比如外键约束、程序员设定的外界约束(银行转账金额不能为负)等。
隔离性:一个事务的执行不能被其他事务干扰。
持久性:事务一旦提交,对数据库中的数据改变是永久性的。
并发问题:
脏读:事务A修改了一个数据,但未提交,事务B读到了事务A未提交的更新结果,如果事务A失败回滚,事务B读到的就是脏数据。
不可重复读:在同一个事务中,对于同一份数据读取到的结果不一致。比如,事务B在事务A提交前读到的结果,和提交后读到的结果可能不同。
幻读:在同一个事务中,同一个查询多次返回的结果不一致。事务A新增了一条记录,事务B在事务A提交前后各执行了一次查询操作,发现后一次比前一次多了一条记录。
隔离级别:
读未提交:一个事务可以读到另一个事务未提交的结果。
读已提交:只有在事务提交后,其更新结果才会被其他事务看见。
可重复读:在一个事务中,对于同一份数据的读取结果总是相同的,无论是否有其他事务对这份数据进行操作,以及这个事务是否提交。
可串行化:事务串行化执行,隔离级别最高,牺牲了系统的并发性。可以解决并发事务的所有问题。
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read-uncommitted) | 是 | 是 | 是 |
读已提交(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-read) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
2. 数据库索引 牛客269000345号 - 美团实习一面
索引类型
普通索引:最基本的索引,没有任何限制。
唯一索引:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。
主键索引:是一种特殊的唯一索引,一个表只能有一个主键,不允许有空值。一般是在建表的时候同时创建主键索引。
组合索引:指多个字段上创建的索引,只有在查询条件中使用了创建索引时的第一个字段,索引才会被使用。使用组合索引时遵循最左前缀集合。
全文索引:主要用来查找文本中的关键字,而不是直接与索引中的值相比较。fulltext索引跟其它索引大不相同,它更像是一个搜索引擎,而不是简单的where语句的参数匹配。
索引好处
- 提高数据检索速度,降低数据库IO成本,使用索引的意义就是通过缩小表中需要查询的记录的数目加快搜索的速度。
- 降低数据排序的成本,降低CPU消耗。索引之所以查的快,是因为现将数据排好序,若该字段正好需要排序,则正好降低排序成本。
索引坏处
- 占用存储空间,索引实际上也是一张表,记录了主键和索引字段,一般以索引文件的形式存储在磁盘上。
- 降低更新表的速度:表数据发生变化时,对应的索引也需要一起变更,从而降低了更新速度。
3. MySQL的B+Tree和B-Tree windwj000 - 阿里AE部门春招一面
B-树
定义:对于M阶的B-树
- 每个节点最多有m - 1个关键字。
- 根节点最少只有一个关键字。
- 非根节点至少 m / 2 个关键字。
- 每个节点中的关键字按照从小到大的顺序排列,每个关键字左子树中的关键字都小于它,右子树所有关键字都大于它。
- 所有叶子结点都位于同一层,根节点到每个叶子结点的长度都相同。
- 每个节点都存有索引和数据。
B+树
定义:M阶的B+树
相同点:
- 根节点至少一个元素。
- 非根节点元素范围:m / 2 <= key <= m - 1。
不同点:
- B+树的所有数据存在于叶子结点中,内部节点只存储索引,不存储数据。
- 叶结点中将关键字按大小顺序排列,相邻结点按大小顺序连接起来。
- 内部结点中仅包含各个子节点中索引的最大值及指向其子节点的指针。
- 在B+树中查找时,无论查找成功还是失败一定查找到叶节点。
B 树VS B+ 树
- B树每一个结点包含索引和数据,因此经常访问的元素离根节点越近,访问越快。而B+树非叶子结点只存储索引,因此每次查找都要查找到叶子结点。
- B+树所有叶子结点使用链表相连,便于区间查找和遍历。相比B树需要进行遍历,且相邻的元素可能在内存中并不相邻,缓存命中性没有B+树好。
- B+树结点不保存数据,能容纳更多结点元素。
4. MySQL中B+树相对于红黑树在查找上为什么更占优势?树高和磁盘两个角度 windwj000 - 腾讯看点春招一面
从B树和B+树特点下手。
磁盘IO性能相对于内存是非常慢的。数据库索引存储在磁盘上,当数据量非常大时,就不能把索引全部加载到内存中,只能注意加载每一个磁盘页。
相比红黑树而言,B+树非叶子结点只用来存储索引,而不存储数据,因此每个结点容纳的元素更多,使得整体的树高更低。假定每个结点都需要访问一次磁盘,那么查找过程中,树高就决定了访问磁盘的次数,树高越低,访问磁盘的次数越少,效率越高。
5. 用户密码如何保存在数据库中,如何在用户登录时验证身份信息,如何防止登录请求报文被窃取 windwj000 - 腾讯看点春招二面
待补充。
6. MySQL在哪些场景下适合分库分表,分表后id冲突怎么解决 windwj000 - 美团广告部春招一面
分表:系统的绝对并发量没有上来,但是单表的数据量太大,影响了SQL效率,加重了CPU负担,以至于成为瓶颈。这时候需要分表,表的数据量少了,单词SQL执行效率高,自燃减轻了CPU的负担。
分库:系统的绝对并发量上来了,数据库的并发能力成为了瓶颈。将不同功能的表放在不同的库中,降低单库的并发量,提升整体性能。
分表后id冲突:
- 数据库自增id,先获取到一个数据库自增的id,然后拿着这个id往对应的表中写入。适用于数据量太大导致的分库分表,如果是单库并发过高导致的分库,这种方法不适合。
- 设置数据库sequence或者表自增步长。依据不同表编号设置不同的初始id的值,每次步长设置为表的总数。实现起来简单,也能达到性能要求,但是扩充表时会很麻烦。
- uuid。本地生成,脱离数据库。但是uuid太长,占用空间太大,作为主键性能太差。
- snowflake算法。snowflake是twitter开源的分布式id生成算法,将一个64位的long型的id,1bit不用,41bit作为毫秒数,10bit作为工作机器id,12bit作为序列号。适用于高并发。
7. 分表后怎么解决数据热度不均衡的问题 windwj000 - 美团广告部春招一面
哈希取模。hash的方案就是对指定的路由key(如:id)对分表总数进行取模,比如假如现在数据量过大,将全部数据分到四个表中存放。id=12的订单,对4进行取模,也就是会得到0,那此订单会放到0表中。id=13的订单,取模得到为1,就会放到1表中。
这样数据可以均匀地放在4个表中,避免热点问题。
存在的问题
增加将来数据迁移和扩容的难度。比如随着业务越来越好,数据库的数据增大,分表数从原来的4增加到8,那么取模的基数就变成了8,以前id=12的订单会被分到4表中,但是之前此订单在0表中,这就导致数据查不到。
8. 数据库的索引种类,hash索引和B+树索引优劣 DateBro - 美团金融后端二面
FULLTEXT、HASH、B+树
-
FULLTEXT,全文索引。这是一种特殊类型的索引,它查找的是文本中的关键词,而不是直接比较索引中的值,全文索引更类似于搜索引擎做的事情。
-
HASH,哈希索引。哈希索引基于hash表实现,类似于Java中的HashMap,通过计算key的hash值映射对应的value,在不发生hash冲突的情况下时间复杂度为常数级别。
-
B+树索引,这是InnoDB的默认索引类型,我们常听人说MySQL的B-TREE索引,其实MySQL的B树索引就是B+树。
B+树和hash索引对比
-
如果是等值查询,那么哈希索引明显有绝对优势。因为只需要经过极少次算法即可找到相应的键值。前提是存储的数据重复度极低。
-
如果是范围查询检索,这时候哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;
-
哈希索引也没办法利用索引完成排序,以及like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);
-
哈希索引也不支持多列联合索引的最左匹配规则;
-
B+树索引的关键字检索效率比较平均,在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在所谓的哈希碰撞问题。
9. 在什么样的列上建索引,联合索引 DateBro - 美团金融后端二面
- 经常需要排序、分组和联合操作的列建立索引。
- 常作为查询条件(where后的列)的列建立索引。
创建索引的原则
-
选择唯一性索引。唯一性索引的值是唯一的,可以更快速的通过该索引来确定某条记录。
-
为经常排序、分组和联合操作的字段建立索引。经常需要ORDER BY、GROUP BY、DISTINCT和UNION等操作的字段,排序操作会浪费很多时间。如果为其建立索引,可以有效地避免排序操作。
-
常作为查询条件的字段建立索引。如果某个字段经常用来做查询条件,那么该字段的查询速度会影响整个表的查询速度。
-
限制索引的数目。索引的数目不是越多越好。每个索引都需要占用磁盘空间,索引越多,需要的磁盘空间就越大。且修改表时,需要一并修改索引,代价高。
-
尽量使用数据量少的索引。如果索引的值很长,那么查询的速度会受到影响。
-
尽量使用前缀来索引。如果索引字段的值很长,最好使用值的前缀来索引。即截取数据库字段的前边几位作为索引。
-
删除不再使用或者很少使用的索引。表中的数据被大量更新,或者数据的使用方式被改变后,原有的一些索引可能不再需要。数据库管理员应当定期找出这些索引,将它们删除,从而减少索引对更新操作的影响。
-
最左前缀匹配原则,非常重要的原则。建立联合索引时,条件中按照联合索引的顺序使用。见12题,最左前缀原则。
-
尽量选择区分度高的列作为索引。区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少。像性别字段,就不适合作为索引,因为取值只有男、女两种,区分度不大。
-
尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。
-
当单个索引字段查询数据很多,区分度都不是很大时,则需要考虑建立联合索引来提高查询效率。
10. 数据库的悲观锁和乐观锁 DateBro - 美团金融后端二面
悲观锁:具有强烈的独占和排他特性。指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
悲观锁主要是 共享锁
或 排他锁
。共享锁
又称为读锁,简称S锁,共享锁就是多个事务对于同一数据可以共享一把锁,都能访问到数据,但是只能读不能修改。排他锁
又称为写锁,简称X锁,顾名思义,排他锁就是不能与其他所并存,如一个事务获取了一个数据行的排他锁,其他事务就不能再获取该行的其他锁,包括共享锁和排他锁,但是获取排他锁的事务是可以对数据就行读取和修改。
乐观锁:乐观锁机制采取了更加宽松的加锁机制。乐观锁是相对悲观锁而言,也是为了避免数据库幻读、业务处理时间过长等原因引起数据处理错误的一种机制,但乐观锁不会刻意使用数据库本身的锁机制,而是依据数据本身来保证数据的正确性。
乐观锁的两种实现机制
- 使用版本号 使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。什么是版本号?即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。
- 使用时间戳 乐观锁定的第二种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp), 和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。
使用场景
悲观锁:比较适合写入操作比较频繁的场景,如果出现大量的读取操作,每次读取的时候都会进行加锁,这样会增加大量的锁的开销,降低系统的吞吐量。
乐观锁:比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发生冲突的可能性就会增大,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量的查询操作,降低系统吞吐量。
原文链接 面试必备的数据库悲观锁和乐观锁
11. MySQL的存储引擎,区别,默认哪一个 猿兄 - 美团后端春招一面
MySQL支持很多存储引擎,包括MyISAM、InnoDB、BDB、MEMORY、MERGE、EXAMPLE、NDB Cluster、ARCHIVE等。MySQL5.5之前默认使用MyISAM,5.5之后默认使用InnoDB。
MyISAM和InnoDB比较
MyISAM | InnoDB | |
---|---|---|
构成上 | 每个MyISAM在磁盘上存储成三个文件。第一个文件的名字以表的名字开始,扩展名支出文件类型。.frm文件存储表定义。.MYD存储数据。.MYI存储索引文件。 | 基于磁盘的资源时InnoDB表空间数据文件和它的日志文件,InnoDB表的大小只受限于操作系统文件的大小,一般为2GB |
事务处理上 | MyISAM强调性能,执行速度比InnoDB类型快,但是不支持事务。 | InnoDB提供事务支持事务,外部键等高级数据库功能。 |
CRUD操作上 | 执行大量SELECT,MyISAM是更好的选择。 | 执行大量INSERT和UPDATE,选择InnoDB。清空整个表时,InnoDB一行一行的删除,效率慢,MyISAM重建表。 |
count(*) | MyISAM只要简单的读出保存好的行数。当count(*)语句包含where时,两种表的操作时一样的。 | InnoDB不保存表的具体行数。InnoDB要扫描一遍整个表计算行数。 |
锁 | 表锁 | 提供行锁,提供不加锁读取。InnoDB表的行锁也不是绝对的,如果执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB同样会锁定全表。 |
12. 最左前缀原则,为什么 猿兄 - 美团后端春招一面
最左匹配原则是指在联合索引中,如果你的SQL语句中用到了联合索引的最左边的索引,那么这条SQL语句就可以利用这个联合索引进行匹配。假如某个数据库表有联合索引(a,b,c)。
select * from t where a = 1 and b = 1 and c = 1; # 使用索引(a,b,c)
select * from t where a = 1 and b = 1; # 使用索引(a,b,c)
select * from t where a = 1; # 使用索引(a,b,c)
select * from t where a = 1 and c = 1; # 不使用索引
通过最左匹配原则可以定义一个联合索引,在多种查询条件中用到该索引。但是,当遇到范围查询(>、<、between、like)就会停止匹配。
select * from t where a=1 and b>1 and c =1; #这样a,b可以用到索引(a,b,c),c用不到索引
原理
索引底层是一颗B+树,联合索引也是一颗B+树,不过联合索引的键值数量不是一个,而是多个。构建一颗B+树只能依据一个值来构建,因此数据库依据联合索引最左的字段来构建B+树。
例如,构建一个(a,b,c)的联合索引,它的索引树是这样的。
非叶子结点存储的是第一个关键字的索引a,叶子结点存储的是三个关键字的数据。a是有序的,而b、c都是无序的。但是当a相同时,b是有序的,b相同时,c又是有序的。当使用 select * from t where a=1 and b>1 and c =1;
语句时,查询到b之后,查到的是一个范围值,c是无序的,因此不能使用联合索引来确定取哪一行。
原文链接 MySQL最左匹配原则