10-07 31—35

31.什么是覆盖索引?

如果一个索引包含(或覆盖)所有需要查询的字段的值,称为‘覆盖索引’。即只需扫描索引而无须回表。
只扫描索引而无需回表的优点:
    1.索引条目通常远小于数据行大小,只需要读取索引,则mysql会极大地减少数据访问量。
    2.因为索引是按照列值顺序存储的,所以对于IO密集的范围查找会比随机从磁盘读取每一行数据的IO少很多。
    3.一些存储引擎如myisam在内存中只缓存索引,数据则依赖于操作系统来缓存,因此要访问数据需要一次系统调用
    4.innodb的聚簇索引,覆盖索引对innodb表特别有用。(innodb的二级索引在叶子节点中保存了行的主键值,所以如果二级主键能够覆盖查询,则可以避免对主键索引的二次查询)

覆盖索引必须要存储索引列的值,而哈希索引、空间索引和全文索引不存储索引列的值,所以mysql只能用B-tree索引做覆盖索引。

当发起一个索引覆盖查询时,在explain的extra列可以看到using index的信息

32.简述数据库读写分离?

MySQL Proxy最强大的一项功能是实现“读写分离(Read/Write Splitting)”。基本的原理是让主数据库处理事务性查询,而从数据库处理SELECT查询。数据库复制被用来把事务性查询导致的变更同步到集群中的从数据库。 当然,主服务器也可以提供查询服务。使用读写分离最大的作用无非是环境服务器压力。

读写分离的好处
1、增加冗余
2、增加了机器的处理能力
3、对于读操作为主的应用,使用读写分离是最好的场景,因为可以确保写的服务器压力更小,而读又可以接受点时间上的延迟。

读写分离提高性能之原因
1、物理服务器增加,负荷增加
2、主从只负责各自的写和读,极大程度的缓解X锁和S锁争用
3、从库可配置myisam引擎,提升查询性能以及节约系统开销
4、从库同步主库的数据和主库直接写还是有区别的,通过主库发送来的binlog恢复数据,但是,最重要区别在于主库向从库发送binlog是异步的,从库恢复数据也是异步的
5、读写分离适用与读远大于写的场景,如果只有一台服务器,当select很多时,update和delete会被这些select访问中的数据堵塞,等待select结束,并发性能不高。 对于写和读比例相近的应用,应该部署双主相互复制
6、可以在从库启动是增加一些参数来提高其读的性能,例如--skip-innodb、--skip-bdb、--low-priority-updates以及--delay-key-write=ALL。当然这些设置也是需要根据具体业务需求来定得,不一定能用上
7、分摊读取。假如我们有1主3从,不考虑上述1中提到的从库单方面设置,假设现在1分钟内有10条写入,150条读取。那么,1主3从相当于共计40条写入,而读取总数没变,因此平均下来每台服务器承担了10条写入和50条读取(主库不承担读取操作)。因此,虽然写入没变,但是读取大大分摊了,提高了系统性能。另外,当读取被分摊后,又间接提高了写入的性能。所以,总体性能提高了,说白了就是拿机器和带宽换性能。MySQL官方文档中有相关演算公式:官方文档 见6.9FAQ之“MySQL复制能够何时和多大程度提高系统性能”
8、MySQL复制另外一大功能是增加冗余,提高可用性,当一台数据库服务器宕机后能通过调整另外一台从库来以最快的速度恢复服务,因此不能光看性能,也就是说1主1从也是可以的。

33.简述数据库分库分表?(水平、垂直)

# 水平分库
1、概念:以字段为依据,按照一定策略(hash、range等),将一个库中的数据拆分到多个库中。
2、结果:
  每个库的结构都一样;
  每个库的数据都不一样,没有交集;
  所有库的并集是全量数据;
3、场景:系统绝对并发量上来了,分表难以根本上解决问题,并且还没有明显的业务归属来垂直分库。
4、分析:库多了,io和cpu的压力自然可以成倍缓解。

# 水平分表
1、概念:以字段为依据,按照一定策略(hash、range等),将一个表中的数据拆分到多个表中。
2、结果:
  每个表的结构都一样;
  每个表的数据都不一样,没有交集;
  所有表的并集是全量数据;
3、场景:系统绝对并发量并没有上来,只是单表的数据量太多,影响了SQL效率,加重了CPU负担,以至于成为瓶颈。
4、分析:表的数据量少了,单次SQL执行效率高,自然减轻了CPU的负担。

# 垂直分库
1、概念:以表为依据,按照业务归属不同,将不同的表拆分到不同的库中。
2、结果:
  每个库的结构都不一样;
  每个库的数据也不一样,没有交集;
  所有库的并集是全量数据;
3、场景:系统绝对并发量上来了,并且可以抽象出单独的业务模块。
4、分析:到这一步,基本上就可以服务化了。例如,随着业务的发展一些公用的配置表、字典表等越来越多,这时可以将这些表拆到单独的库中,甚至可以服务化。再有,随着业务的发展孵化出了一套业务模式,这时可以将相关的表拆到单独的库中,甚至可以服务化。

# 垂直分表
1、概念:以字段为依据,按照字段的活跃性,将表中字段拆到不同的表(主表和扩展表)中。
2、结果:
  2.1、每个表的结构都不一样;
  2.2、每个表的数据也不一样,一般来说,每个表的字段至少有一列交集,一般是主键,用于关联数据;
  2.3、所有表的并集是全量数据;
3、场景:系统绝对并发量并没有上来,表的记录并不多,但是字段多,并且热点数据和非热点数据在一起,单行数据所需的存储空间较大。以至于数据库缓存的数据行减少,查询时会去读磁盘数据产生大量的随机读IO,产生IO瓶颈。
4、分析:可以用列表页和详情页来帮助理解。垂直分表的拆分原则是将热点数据(可能会冗余经常一起查询的数据)放在一起作为主表,非热点数据放在一起作为扩展表。这样更多的热点数据就能被缓存下来,进而减少了随机读IO。拆了之后,要想获得全部数据就需要关联两个表来取数据。
但记住,千万别用join,因为join不仅会增加CPU负担并且会讲两个表耦合在一起(必须在一个数据库实例上)。关联数据,应该在业务Service层做文章,分别获取主表和扩展表数据然后用关联字段关联得到全部数据。

34.数据库锁的作用?

根据不同的锁的作用域我们可以把数据库的锁分为三种,分别为:
  行锁:对表中的某一行进行加锁。
  页锁:对表中一组连续的行进行加锁。
  表锁:对整张表进行加锁
不同的作用域对并发性能是有很大影响的,比如说如果数据库的插入都是使用表锁,那在大量用户对某张表进行插入读取操作的话,同时只能有一个用户可以访问该表,那并发量肯定就是惨不忍睹了。

乐观锁
在乐观锁中,我们有3种常用的做法来实现:

第一种就是在数据取得的时候把整个数据都copy到应用中,在进行提交的时候比对当前数据库中的数据和开始的时候更新前取得的数据。当发现两个数据一模一样以后,就表示没有冲突可以提交,否则则是并发冲突,需要去用业务逻辑进行解决。

第二种乐观锁的做法就是采用版本戳,这个在Hibernate中得到了使用。采用版本戳的话,首先需要在你有乐观锁的数据库table上建立一个新的column,比如为number型,当你数据每更新一次的时候,版本数就会往上增加1。比如同样有2个session同样对某条数据进行操作。两者都取到当前的数据的版本号为1,当第一个session进行数据更新后,在提交的时候查看到当前数据的版本还为1,和自己一开始取到的版本相同。就正式提交,然后把版本号增加1,这个时候当前数据的版本为2。当第二个session也更新了数据提交的时候,发现数据库中版本为2,和一开始这个session取到的版本号不一致,就知道别人更新过此条数据,这个时候再进行业务处理,比如整个Transaction都Rollback等等操作。在用版本戳的时候,可以在应用程序侧使用版本戳的验证,也可以在数据库侧采用Trigger(触发器)来进行验证。不过数据库的Trigger的性能开销还是比较的大,所以能在应用侧进行验证的话还是推荐不用Trigger。

第三种做法和第二种做法有点类似,就是也新增一个Table的Column,不过这次这个column是采用timestamp型,存储数据最后更新的时间。在Oracle9i以后可以采用新的数据类型,也就是timestamp with time zone类型来做时间戳。这种Timestamp的数据精度在Oracle的时间类型中是最高的,精确到微秒(还没与到纳秒的级别),一般来说,加上数据库处理时间和人的思考动作时间,微秒级别是非常非常够了,其实只要精确到毫秒甚至秒都应该没有什么问题。和刚才的版本戳类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。如果不想把代码写在程序中或者由于别的原因无法把代码写在现有的程序中,也可以把这个时间戳乐观锁逻辑写在Trigger或者存储过程中

悲观锁(排他锁)
悲观锁也称之为互斥锁,可以写为X锁,指的是同时只能有一个事务可以对某个资源进行访问操作。如果有两个事务同时要操作某张表,我们称之为事务A和事务B,如果事务A获得了这张表的表锁,那事务B只能等待事务A释放了这个锁之后才能对该表进行操作。

数据库的insert,update操作默认是采用互斥锁进行加锁,读取select则不是,如果要对select操作使用互斥锁,可以这样写

select * from table where id = 1 for update
共享锁
共享锁是一种乐观锁,可以写为S锁,在数据库中共享锁的作用主要是针对读取操作的。如果读取操作使用X锁的话,并发量会非常低,所以数据库提供了共享锁S锁,提高读取操作的并发性能,多个事务可以同时持有一个资源的S锁,不像X锁,同时只能有一个事务持有。

举个例子:

事务A和事务B对表TABLE进行访问,事务A想查看id = 1的行信息

select * from TABLE where id = 1 lock in share mode
如果当前id = 1的行对应的X锁没有被其他事务获取,那事务A就顺利的获得了该行的S锁。

现在事务B也想查看id = 1 的行信息,会怎么样?

select * from TABLE where id = 1 lock in share mode
现在持有该行锁的只有事务A,持有的是S锁,所以事务B也可以获取该行的S锁,两个事务可以并发的读取id = 1的行。

这个和之前所说的乐观锁实现是有区别的,最大的不同就是读取的时候共享锁是要真的去持有锁,但是乐观锁只是实现了一种CAS模式,但是并读取的时候没有真的持有锁。

35.where 子句中有 a,b,c 三个查询条件, 创建一个组合索引 abc(a,b,c),以下哪 种会命中索引

(a)   *****
(b)
(c)
(a,b)    *****
(b,c)
(a,c)
(a,b,c)   *****
posted @ 2019-10-22 22:11  小猿取经-林海峰老师  阅读(600)  评论(1编辑  收藏  举报