记录一些MySQL面试、性能和线上、测试环境问题总结(2023/10/17)


记录一些mysql的问题

一、MySQL自增ID用完了会怎样?

  • 解答
    情况分两种:得看有主键和没有主键两种情况
    1)表有主键,且吧主键设置为自增,int类型(MySQL中int占用四个字节(32位),范围是【-2147483648,2147483647】),当插入一个ID为最大值2147483647的值后,再次插入数据,会造成主键冲突
    2)没有主键:InnoDB会自动创建一个不可见的,长度为6字节的row_id,默认是无符号的,所以最大长度是2^48-1。(实际上InnoDB维护了一个全局的dictsys.row_id,所以未定义主键的表都共享该row_id,并不是单表共享,每次插入一条数据,都把全局row_id当作主键id,然后全局row_id加1)

  • 实际方法
    1)可以把主键类型改为bigint,也就是8个字节。这样能存储的最大数据量就是2^64 -1,对于所有场景几乎都是够用的
    2)当row_id达到最大值后会从0重新开始算,前面插入的数据就会被后插入的数据覆盖,且不会报错(当达到最大值后,新数据覆盖旧数据),尽量给表设置主键

二、自增主键的好处和自增主键存在哪里?自增主键一定是连续的吗?

1)自增主键好处:自增主键可以让主键索引尽量的保持递增顺序插入,避免了页分裂,是因此索引更加紧凑,查询的时候效率更高
2)不同引擎对于子增值的保存策略不同,M有ISAM引擎的自增值保存在数据文件中。
①在MySQL8.0前,InnoDB引擎的自增值是存在内存中,但是每次重启后内存这个值就丢失了,所以每次重启后第一次打开表,就会找自增值的最大值max(id),然后将最大值加1作为这个表的自增值
②在MySQL8.0版本会将子增值的变更记录在redo log中,重启时依靠redo log恢复
3)自增主键一定是连续的吗?
不一定,有几种情况会导致自增主键不连续。
唯一键冲突导致自增主键不连续。当我们想一个和自增主键的InnoDB表中插入数据的时候,如果违反表中定义的唯一索引的唯一约束,会导致插入数据失败。此时表的自增主键的键值是会想后加1滚动的
事务回滚导致自增主键不连续。当我们向一个自增主键的InnoDB表中插入数据的时候,如果显示开启了事务,此时表的自增值也会发生滚动,而接下来新插的数据也不能使用滚动过的自增值,而是重新申请一个新的自增值
批量插入导致自增值不连续。MySQL有个批量申请自增id的策略:语句执行过程中,第一次申请自增id,分配1个自增id;1个用完后,第二次申请会分配2个自增id;两个用完后,第三次盛情,会分配4个自增id;依次类推。如果下一次事务再次插入数据的时候,则会基于上一个事务申请后自增值基础上再申请。此时就会出现自增值不连续的情况出现。
自增步长不是1,也会导致自增主键不连续

三、mysql有那些锁?

  • 按锁粒度分类,有行、表和页级锁
    1)行级锁粒度最细,只针对当前操作的某一行进行加锁,大大减少数据库操作的冲突,其加锁粒度最小,但加锁的开销也最大
    2)表级锁是MySQL中锁定粒度最大的,表示对当前操作整张表枷锁,实现简单,资源消耗少,被大部分MySQL引擎支持。最常使用的MyISAM与InnoDB都支持表级锁定
    3)页级锁:锁粒度介于行和表锁之间的锁。表级锁速度快,但冲突多;行级冲突少,但速度慢。因此,采取折衷的页级锁,一次锁定相邻的一组记录

  • 按锁级别分类,有共享锁、排他锁和意向锁
    1)共享锁(读锁):读取操作创建的锁。其他用户可以并发读取数据,但任何事务都不能对数据进行修改(获取数据上的排他锁),直到以释放所有共享锁
    2)排他锁(写锁、独占锁):如果事务T对数据A加上排他锁后,则其他事务不能再对A加上排他锁后,则其他事务不能再对A加所任类型的读锁。获准排他锁的事务技能读数据,又能修改数据.
    3)意向锁(表级锁):主要是为了在一个事务中揭示下一行要被请求锁的类型。InnoDB中的两个表锁:(意向锁是InnoDB加的,不需要用户干预)
    ①意向共享锁(IS):表示事务准备给数据行加入共享锁,也就是说一个数据行加共享锁前必须先获得该表的IS锁
    ②意向排他锁(IX):表示事务准备数据行加入排他锁,说明事务在一个数据加排他锁前必须先取得该表的IX锁

①对于INSERT、UPDATE和DELETE,InnoDB会自动给涉及的数据加排他锁
②对于一般的SELECT语句,InnoDB不会加任何锁
③事务可以通过以下语句显示加共享锁或排他锁
共享锁:
SELECT … LOCK IN SHARE MODE;
排他锁:
SELECT … FOR UPDATE;

四、mysql的游戏邮件数据不跟据人物登录捞取引起的内存爆满

  • 前提
    玩家的单个邮件数据在MySQL大概有210字节,当每个人的模板邮件有100封时,每个人的邮件数据就达到了21KB,不包括大型的邮件64K一封。但这是在mysql内部磁盘存储占用的空间,但是当mysql的数据比如说varchar(512)字节捞到内存时,字符串占用的内存就是实打实的512字节。由此以来,我司在真正生产环境下用到的机器内存是4到8G,那么不能起服的时候就把玩家邮件全部都捞出来,内存会被挤爆

  • 系统背景
    1)32位程序的寻址能力是2^32,也就是4G。对于32位程序只能申请到4G的内存。而且这4G内存中,在windows下有2G,linux下有1G是保留给内核态使用,用户态无法访问。故只能分配2G、3G的内存使用。
    2)但是,如果系统是64位的话,且有8G内存,那么用户申请的内存就能达到32位机器的顶峰,能实打实的用到4G,毕竟系统有64位的寻址能力没必要再去和你的小底盘上抢资源

  • 导致的问题
    当玩家邮件不跟玩家一起捞出来时,全部邮件数据一起捞到内存,占用内存1.8G,当时机器就宕机了,所以邮件数据

五、为什么数据库字段建议设置为NOT NULL?

  • 原因
    1)如果不设置NOT NULL,那么后插入数据的时候默认值就是NULL。(NULL和NOT NULL使用空值表示的意义是不一样的,NULL可以认为这一列的值是未知的,空值则可以认为我们知道这个值,只不过他是空的而已)举例:比如成绩字段,0表示0分,空表示未知、未设置。因为设置空值会导致程序空指针的问题
    2)导致聚合函数不准确:对于NULL值的列,使用聚合函数的时候会忽略NULL值,会造成统计结果不准确
    3)导致等于号"="失效,对于NULL值的列,是不能使用=表达式进行判断的,会造成统计结果不准确
    4)NULL和其他任何值进行运算都是NULL,包括表达式的值都是NULL。举例比如对值为NULL的字段+1,结果还是NULL
    5)使用NULL值相比于空字符值会占用更多的空间(一个bit的标志位)

六、一些sql语句题

1)查询01课程比02课程成绩高的学生信息及课程分数

在这里插入图片描述

#学生表
create table Student(
Sid varchar(10),
Sname varchar(10),
Sage datetime,
Ssex varchar(10));

#教师表
create table Teacher(
Tid    varchar(10),
Tname  varchar(10));

#科目表
create table Course(
Cid    varchar(10),
Cname  varchar(10),
Tid    varchar(10));

#成绩表
create table SC(
Sid     varchar(10),
Cid     varchar(10),
Score   decimal(18,1));  //总长度为18,小数位数占一位的数值
-------------------------------------
insert into Student values('01','赵雷','1990-01-01','男');
insert into Student values('02','钱电','1990-12-21','男');
insert into Student values('03','孙凤','1990-05-20','男');
insert into SC values('02','03',80);
insert into SC values('03','01',76);
insert into SC values('07','02',87);
.........

①通过主键Sid连接学生表和成绩表(sc是主表,)

select * 
from sc a
left join student d
	on a.sid = d.sid;

②要对同一学生,那就Sid相同,Cid不同进行关联

select *
from sc a
left join student d
	on a.sid = d.sid
inner join sc b
	on a.sid = b.sid and a.Cid != b.Cid;

要求是01课程和02课程

select *
from sc a
left join student d
	on a.sid = d.sid
inner join sc b
	on a.sid = b.sid and a.Cid = 01 and b.Cid = 02;

③01成绩比02成绩高

select *
from sc a
left join student d
	on a.sid = d.sid
inner join sc b
	on a.sid = d.sid and a.Cid = 01 and b.Cid = 02
where a.score > b.score;

或这么写

select * 
from Student a
inner join sc b
	a.Sid = b.Sid
inner join sc c
	a.Sid = c.Sid and b.id = 01 and c.Sid = 02
where b.score > c.score;

2)查询同时存在01课程和02课程的情况

在这里插入图片描述

select * 
from (select * from sc where Cid = '01') a
inner join (select * from sc where Cid = '02') b
where a.Sid = b.Sid;

select * 
from  sc  a
inner join  sc  b
on a.Sid = b.Sid
where a.Cid = '01' and b.Cid = '02';

3)查询存在’01’课程但可能不存在’02’课程的情况

先左连接,因为01课程肯定存在

select * 
from (select *  from sc a where a.Cid='01' )
left join sc b
on a.Sid = b.Sid and b.Cid = '02';

4)查询不存在’01’课程但存在’02’课程的情况

select * 
from (select * from sc where Sid not in (select Sid from sc where Cid = '01'))
where Cid = '02';

select * 
from sc a 
where Sid not in (select * from sc where Cid = '01')
and Cid = '02';

5)查询平均成绩大于等于60分的同学学生编号和学生姓名和平均成绩

①先找出大于60的学生信息表(Sid)
②再用这个表去对比Sid匹配

select * 
    a.Sid,
    a.Sname,
    b.avg_score
from student a
inner join
(select 
    Sid
    avg(score) as avg_score
from sc 
group by Sid having avg(score) >= 60 ) b
where a.Sid = b.Sid;

6)查询所有同学的学生编号、学生姓名、选课总数、所有课程的总成绩(没成绩的显示为null)

①先出总成绩和选课总数
②再根据学号Sid去匹配名字和成绩

select 
    a.Sid,
    a.Sname,
    b.cons,
    b.sum_score
from sc a
left join
(select 
    Sid,
    count(Cid) as cons,
    sum(score) as sum_score
from sc 
group by Sid)b
on a.Sid = b.Sid;

7)查询“李”姓老师的数量

select count(1) as cons from Teacher  where tname like '李%';

8)查询接受过张三老师授课同学的信息

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
tid: 教师ID
sid:学生ID

①得到教师和课程的关系

select * from course a inner join teacher b on a.Tid = b.Tid;

②得到老师和成绩的关系

select * from sc a inner join (select a.*,b.tname from course a inner join
teacher b on a.tid=b.tid) b  on a.cid = b.cid; //课程划分

在这里插入图片描述
③得到学生和教师的关系

select * from student a inner join
(select * from sc a.*,b.tname,b.tid,b.cname. inner join (select a.*,b.tname from course a inner join
teacher b on a.tid=b.tid) b  on a.cid = b.cid;

) b
on a.sid=b.sid;

④得到张三老师的学生信息

select * from student a
inner join
()
where tname = '张三';

在这里插入图片描述

9)查询没有学完所有课程的同学信息

①学生表和成绩表交叉

select * from student a inner join sc b on a.sid=b.sid  

②以课程表分类

group by a.sid

③统计下总课程数

having count(b.sid) <(select count(cid) from course);

合起来

select * from student a inner join sc b on a.sid=b.sid  
group by a.sid
having count(b.sid) <(select count(cid) from course);

连接传送门

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值