vlambda博客
学习文章列表

面试之mysql数据库篇(1)

1:mysql五种索引

mysql目前主要有以下几种数据类型:普通索引,唯一索引,主键索引,组合索引,全文索引。

2:mysql事务性(ACID)

原子性、一致性、隔离性、持久性

3:mysql事务隔离级别

读未提交(read-uncommitted)

不可重复读(read-committed)----oracle默认

可重复读(repeatable-read)----mysql默认

串行化(serializable)


4:mysql数据库中MyISAM存储引擎和InnoDB存储引擎的区别

Mysql 在5.5之前默认使用 MyISAM 存储引擎,之后使用 InnoDB 。查看当前存储引擎:

show variables like '%storage_engine%';

MyISAM 操作数据都是使用的表锁,你更新一条记录就要锁整个表,导致性能较低,并发不高。当然同时它也不会存在死锁问题。

而 InnoDB 与 MyISAM 的最大不同有两点:一是 InnoDB 支持事务;二是 InnoDB 采用了行级锁。也就是你需要修改哪行,就可以只锁定哪行。


5:BTree和B+Tree比较

首先,B+树的查找和B树一样,起始于根节点,自顶向下遍历树。

不同的是,B+树中间节点不存储数据,只有键值和指针,而B树每个结点要存储键值和实际数据,这就意味着同样的大小的磁盘块B+树可以容纳更多节点元素,在相同的数据量下,B+树更加“矮胖”,IO操作更少 。

现代操作系统中,磁盘的存储结构使用的是B+树机制,MySQLInnoDB引擎的存储方式也是B+树机制。


6.索引什么时候会失效 
(1) 如果MySQL估计使用索引比全表扫描更慢,则不使用索引。例如,如果列key均匀分布在1和100之间,下面的查询使用索引就不是很好:select

* from table_name where key>1 and key<90;

(2)如果like是以‘%’开始的,则该列上的索引不会被使用。例如select * from table_name where key1 like'%a';该查询即使key1上存在索引,也不会被使用。这种模糊查询再加个%,谁知道你要查啥啊。

(3)用or分隔开的条件,如果or前的条件中的列有索引,而后面的列没有索引,那么涉及到的索引都不会被用到,例如:select * from table_name where key1='a' or key2='b';如果在key1上有索引而在key2上没有索引,则该查询也不会走索引。要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引。

(4)复合索引,如果索引列不是复合索引的第一部分,则不使用索引(即不符合最左前缀),例如,复合索引为(key1,key2),则查询select * from table_name where key2='b';将不会使用索引

(5)如果列为字符串,则where条件中必须将字符常量值加引号,否则即使该列上存在索引,也不会被使用。例如,select * from table_name where key1=1;如果key1列保存的是字符串,即使key1上有索引,也不会被使用


7:什么是死锁?怎么解决?

官方定义如下:两个事务都持有对方需要的锁,并且在等待对方释放,并且双方都不会释放自己的锁。

Mysql有两种死锁的处理方式:

(1)等待,直到超时(innodb_lock_wait_timeout=50s)

  (2)   发起死锁检测,主动回滚一条事务,让其他事务继续执行。

         (innodb_deadlock_detect=on)

由于性能原因,一般都是使用死锁检测来进行处理死锁。

(3)加更新锁(mysql不存在更新锁,有的数据库引入了,如Mssql。

死锁检测

死锁检测的原理是构建一个以事务为顶点、锁为边的有向图,判断有向图是否存在环,存在即有死锁。

回滚

检测到死锁之后,选择插入更新或者删除的行数最少的事务回滚,基于 INFORMATION_SCHEMA.INNODB_TRX 表中的 trx_weight 字段来判断。

减少死锁:

  • 使用事务,不使用 lock tables 。

  • 保证没有长事务。

  • 操作完之后立即提交事务,特别是在交互式命令行中。

  • 如果在用 (SELECT ... FOR UPDATE or SELECT ... LOCK IN SHARE MODE),尝试降低隔离级别。

  • 修改多个表或者多个行的时候,将修改的顺序保持一致。

  • 创建索引,可以使创建的锁更少。

  • 最好不要用 (SELECT ... FOR UPDATE or SELECT ... LOCK IN SHARE MODE)。

如果上述都无法解决问题,那么尝试使用 lock tables t1, t2, t3 锁多张表

具体答案参考:https://m.jb51.net/article/159737.htm

锁类型介绍:


MySQL有三种锁的级别:页级、表级、行级。


表级锁(锁定整个表):开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。

行级锁(锁定一行):开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

页面锁(锁定一页):开销和加锁时间界于表锁和行锁之间;会出现死锁;锁定粒度界于表锁和行锁之间,并发度一般//mysql一页16kb

还有一些相关名词:

共享锁(S锁,MyISAM 叫做读锁)

排他锁(X锁,MyISAM 叫做写锁)

悲观锁(抽象性,不真实存在这个锁)

乐观锁(抽象性,不真实存在这个锁)


8:悲观锁和乐观锁怎么实现?

1.使用悲观锁(其实说白了也就是排他锁)

例如程序A在查询库存数时使用排他锁

select * from table where id=10 for update

必须等本次事务提交之后,程序B,C...才能执行,这样保证了数据不会被其他事务修改。Mysql有个问题是select...for updata语句执行中所有扫描过的行都会 被锁上,因此在Mysql中用悲观锁必须确定走了索引,而不是全表扫描,否则将会把整个数据表锁住。

2.使用乐观锁(版本号机制)

一般是在该商品表添加version版本字段或者timestamp时间戳字段或者CAS

程序A查询后,执行更新变成了:

update table set num=num-1version= version+1 where id=100 andversion=#{version};

一般是说在数据表中加上一个数据库版本号version字段,在表述数据被修改的次数当数据被修改时,它的version 值会加1。

如:

当然线程A需要更新数据值时,在读取数据的同时也会读取 version 值,在提交更新时,若刚才读取到的 version 值为当前数据库中的 version 值相等时才更新,否则重试更新操作,直到更新成功。


9:CAS

前言:

CAS,即 Compare And Swap(比较与交换),是一种无锁算法,基于硬件原语实现,能够在不使用锁的情况下实现多线程之间的变量同步。jdk中的java.util.concurrent.atomic包中的原子类就是通过CAS来实现了乐观锁。

CAS的原理CAS,Conmpare And Swap,英文翻译过来就是“比较和交换”。它的过程是3步,第一步是读值,第二步比较值,看值和自己刚刚读的一不一样,第3步是修改,如果值跟自己读的一样,就修改。CAS最经典的实现类就是AtomicInteger。比如2个线程同时要对AtomicInteger加1,A线程读旧值,是0,B线程也读旧值,是0,A这时执行CAS操作,比较值,发现值和刚刚自己读的一样,都是0,然后它修改值为1;B线程执行CAS操作,比较值,发现值和刚刚自己读的不一样,变成1了,它相当于又读了一遍旧值,将自己内存中的旧值改为1,然后继续执行CAS操作。CAS在底层的硬件级别给你保证一定是原子的,同一时间只有一个线程可以执行CAS,先比较再设置,其他的线程的CAS同时间去执行此时会失败。CAS的bug是会出现的问题 ABA 空循环。如果要解决ABA 问题,可以使用AtomicStampedReference类, 它内部用类似创建版本号的方式来解决 ABA 问题。


10:SQL的select语句完整的执行顺序

1、from 子句组装来自不同数据源的数据;

2、where 子句基于指定的条件对记录行进行筛选;

3、group by 子句将数据划分为多个分组;

4、使用聚集函数进行计算;

5、使用 having 子句筛选分组;

6、计算所有的表达式;

7、select 的字段;

8、使用 order by 对结果集进行排序。