面试之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+树机制,MySQL的InnoDB引擎的存储方式也是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-1,version= version+1 where id=100 and
version=#{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 对结果集进行排序。