《我想进大厂》之 MYSQL 夺命连环13问
来源 | 科技缪缪
能说下 myisam 和 innodb 的区别吗?
说下mysql的索引有哪些吧,聚簇和非聚簇索引又是什么?
create table user(
id int(11) not null,
age int(11) not null,
primary key(id),
key(age)
);
这是主键聚簇索引存储的结构,那么非聚簇索引的结构是什么样子呢?非聚簇索引(二级索引)保存的是主键id值,这一点和myisam保存的是数据地址是不同的。
最终,我们一张图看看InnoDB和Myisam聚簇和非聚簇索引的区别。
那你知道什么是覆盖索引和回表吗?
explain select id,age from user where age=1; //可以直接从索引获取
你能说下事务的基本特性和隔离级别吗?
read commit 读已提交,两次读取结果不一致,叫做不可重复读。
repeatable read 可重复复读,这是mysql的默认级别,就是每次读取结果都一样,但是有可能产生幻读。
那ACID靠什么保证的呢?
那你说说什么是幻读,什么是MVCC?
这时候假设小明去执行查询,此时current_version=3:
select * from user where id<=3;
update user set name='张三三' where id=1;
如果这时候还有小黑在删除id=2的数据,current_version=5,执行后结果是这样的。
由于MVCC的原理是查找创建版本小于或等于当前事务版本,删除版本为空或者大于当前事务版本,小明的真实的查询应该是这样:
select * from user where id<=3 and create_version<=3 and (delete_version>3 or delete_version is null);
1、小明开启事务current_version=6查询名字为'王五'的记录,发现不存在。
2、小红开启事务current_version=7插入一条数据,结果是这样:
那你知道什么是间隙锁吗?
begin;
select * from user where age=20 for update;
begin;
insert into user(age) values(10); #成功
insert into user(age) values(11); #失败
insert into user(age) values(20); #失败
insert into user(age) values(21); #失败
insert into user(age) values(30); #失败
(negative infinity,10],(10,20],(20,30],(30,positive infinity)
你们数据量级多大?分库分表怎么做的?
垂直分表
水平分表
那分表后的ID怎么保证唯一性的呢?
设定步长,比如1-1024张表我们分别设定1-1024的基础步长,这样主键落到不同的表就不会冲突了。 分布式ID,自己实现一套分布式ID生成算法或者使用开源的比如雪花算法这种。 分表后不使用主键作为查询依据,而是每张表单独新增一个字段作为唯一主键使用,比如订单表订单号是唯一的,不管最终落在哪张表都基于订单号作为查询依据,更新也一样。
分表后非sharding_key的查询怎么处理呢?
可以做一个mapping表,比如这时候商家要查询订单列表怎么办呢?不带user_id查询的话你总不能扫全表吧?所以我们可以做一个映射关系表,保存商家和用户的关系,查询的时候先通过商家查询到用户列表,再通过user_id去查询。
打宽表,一般而言,商户端对数据实时性要求并不是很高,比如查询订单列表,可以把订单表同步到离线(实时)数仓,再基于数仓去做成一张宽表,再基于其他如es提供查询服务。
数据量不是很大的话,比如后台的一些查询之类的,也可以通过多线程扫表,然后再聚合结果的方式来做。或者异步的形式也是可以的。
for (int shardingIndex = 0; shardingIndex < 1024; shardingIndex++) {
taskList.add(() -> (userMapper.getProcessingAccountList(shardingIndex)));
}
List<ThirdAccountInfo> list = null;
try {
list = taskExecutor.executeTask(taskList);
} catch (Exception e) {
//do something
}
public class TaskExecutor {
public <T> List<T> executeTask(Collection<? extends Callable<T>> tasks) throws Exception {
List<T> result = Lists.newArrayList();
List<Future<T>> futures = ExecutorUtil.invokeAll(tasks);
for (Future<T> future : futures) {
result.add(future.get());
}
return result;
}
}
说说mysql主从同步怎么做的吧?
master提交完事务后,写入binlog。 slave连接到master,获取binlog。 master创建dump线程,推送binglog到slave。 slave启动一个IO线程读取同步过来的master的binlog,记录到relay log中继日志中。 slave再开启一个sql线程读取relay log事件并在slave执行,完成同步。 slave记录自己的binglog。
由于mysql默认的复制方式是异步的,主库把日志发送给从库后不关心从库是否已经处理,这样会产生一个问题就是假设主库挂了,从库处理失败了,这时候从库升为主库后,日志就丢失了。由此产生两个概念。
那主从的延迟怎么解决呢?
更多阅读推荐