首页 > 笔经面经 > 5.7 百度一面 Java实习

5.7 百度一面 Java实习 内部员工回复

头像
wonion
编辑于 2021-05-17 11:46:39 APP内打开
赞 66 | 收藏 279 | 回复27 | 浏览5282
双非大三,找的学姐组内直推;

面试时间: 5.7  3.53(一共37分钟)
1:自我介绍(甚至没有问项目
$A:$吧啦吧啦....

2:mysql事务的特性,事务的实现方式
$A:$ACID特性
1)原子性 指的是一个事务里的命令要不全部执行,要不就全部不执行,如果中间发生了错误,会导致前面的命令发生回滚,后面的命令不在执行
2)一致性 指的是事务在执行前后,数据应该是一致的,举个例子说 A向B转了100块钱,B的账户里应该多100,如果A的账户没有减100,那么就是不一致的
3)隔离性 针对并发事务来说的,事务之间应该是隔离的,不能由一个事务影响到其他事务;
4)持久性 事务执行完以后,数据可以永久的保持在数据库中,要是中间发生了故障,也能进行修复

$OS:$因为这个题我会,所以我问了一下需不需要说一下事务是怎么实现的,面试官说可以

1)原子性实现:是用undolog实现的,uodolog的意思是回滚日志,指的是每次在有写命令的时候,会先向undolog中写入一份,如果中间发生了故障,就可以通过uodolog进行回滚,比如说如果是insert命令只要delete就可以了,delete命令只要insert就可以了,update命令只要更新回原来的值就可以了

2)持久性实现:是用redolog实现的,意思是重做日志,mysql在读取或者修改数据的时候并不是直接进行读取修改的,而是用到了缓存的概念,因为如果每次读取修改都要对数据库进行操作,那么会对mysql造成压力,所以先对缓存进行操作,在对mysql进行操作,
如果要读取数据的话,是先去缓存中读取,如果缓存中没有,才去mysql中读取,然后再丢到缓存中去,修改数据的话也是先修改缓存,再修改数据库,但是这样的话就会出现问题,如果修改了缓存,但是还没有同步到数据库中去,这时候mysql宕机了,就会发生数据不一致的问题;
所以每次再有写命令的时候,先向redolog中写入,再写入缓存,要是mysql宕机了,我们也可以靠redolog进行恢复;

3)隔离性实现:隔离性的实现有两种方式,一种是锁机制,一种是MVCC多版本并发控制
锁机制的话就是加锁,行锁和表锁,加锁以后数据就不能被其他事务读取并且修改了,通过加锁的方式可以解决并发修改的问题,也就是写操作对写操作的影响

MVCC的话是多版本并发控制,在数据库的每一行后面都有一个隐藏的列,记录的事务的id和指向undolog的指针, undolog的指针又指向更早版本的undolog,通过隐藏的列和undolog可以把数据恢复到任一版本,然后还有一个readview,可重复读是在事务执行之后,第一个select语句之前生成readview,可以解决脏读,不可重复读,幻读的问题,读已提交是在事务执行之后,每个select之前生成readview,可以解决脏读的问题,但是解决不了不可重复读,幻读的问题(这里的话我本来是想举几个例子来说明一下,但是不知道怎么举,怕面试官理解不了我的想法)

它们是怎么解决的呢,是通过三方面的比较实现的,拿数据事务的id和下一个要分配的事务id(low_limit_id)进行比较,如果比low_limit_id小才可见,拿数据的事务id和当前活跃事务的最小id(up_limit_id)进行比较,如果比up_limit_id小才可见,可重复读会在事务执行之后,第一个select语句之前生成readview,保存了当前活跃事务的id,如果数据的事务id在活跃事务中,那么是不可见的,否则是可见的(这段话因为紧张没有说出来)

还有一种方式是加锁读,通过加共享锁和排他锁实现,共享锁的话是如果一个数据要读取数据,那么就会加共享锁,加了共享锁一种就只能加共享锁,不能再加排他锁;排他锁的话是一个事务如果要修改数据就会加排他锁,加了排他锁就什么锁都不能再加了,这样可以解决脏读,不可重复读的问题
如果要解决幻读的问题,还需要用到next-key lock,这个是行锁 和 间隙锁的结合,间隙锁指的是锁住一段数据的间隙,不锁存在的数据,next-key lock的话就是即要锁数据也要锁间隙,就可以解决幻读的问题;

4)一致性的实现:一致性的实现已经不能单纯靠日志实现了,而是要服务器层面 和 数据库本身实现,因为我不会mysql的架构,所以也就没有多说

3:Innodb和mylsam的区别,适应场景
首先的话, Innodb是支持外键,事务,行锁和表锁的,默认使用的是聚簇索引
mylsam不支持外键,事务,只支持表锁,默认使用的是内部索引;
$Q:$然后问了一下如果是你你会用那种存储引擎
$A:$ innodb ,因为 innodb已经经过很多公司,很多年的检验了,所以使用innodb是放心的

4:mysql优化
因为面试官没有说什么优化,我只会索引优化,所以引导索引优化上了(我知道有一些高级的优化,但是我太菜了,不会,希望大家可以多指点指点)
1:首先我们应该尽量在where子句和orderby子句后加索引,避免全表查询
2:我们不应该在索引列进行计算或者函数操作,因为这样会导致全表查询
3:我们不应该在索引列进行空值判断,也会导致全表查询
4:索引列应该少用< ,>,!=这些判断
5:尽量不要使用in和not in,可以用between代替

5:redis的5种数据类型,使用过哪种数据类型
redis的数据类型一般不是直接存储到内存中,而是放到了一个dictentry结构中,里面包括key指针,value指针,next指针,next指针指向下一个dictentry结构,key指针的话指向一个SDS结构,里面包括len表示数组使用了多少字节,free表示还没使用的,char类型的数组存储数据;value指针的话指向一个redisObject结构,里面包括ptr指针指向一个SDS结构,type表示value的类型,encoding表示value的内存编码,refcount计算value被引用的次数,lru最后一次修改数据的时间,5样东西
String是我们最常用的类型,内存编码有三种int类型,embstr类型,raw类型,如果是数字并且没有超过8字节会使用int类型,如果<=39字节会使用embstr类型,如果大于39字节会使用raw类型,
然后我说了为啥是39字节,因为redisObject + sds + 39正好是64字节,那么我们的redis的内存分配器 jemolloc 就可以分配64字节了;
List列表,有序并且是可以重复的,内存编码有 压缩链表 和 有序集合 实现的
Hash:无序,内存编码有 压缩链表哈希表 实现的
set集合:是无序并且不可以重复的,内存编码有 整数集合 和 哈希表 实现的;
zset集合:有序集合,内存编码有 压缩链表 和 跳表 实现的;
(然后的话我本来还想具体说一下 压缩链表,有序集合,哈希表,整数集合,跳表 是怎么实现的,但是面试官打断我了,可能是不想听我叨叨了,然后问我使用过那些,我说在项目中使用过String 和 set)

6:redis持久化:rdb,aof
redis持久化分为了两种方式,rdb和aof,这两个最大的区别就是rdb是把数据进行持久化,aof是把写命令进行持久化;
什么是持久化:把redis中的数据 保存到磁盘上就是持久化
为什么要持久化:因为redis是在内存中操作数据,有着断点即失的特点,所以要进行持久化
过程:
rdb默认是开启的,触发方式有两种:手动触发和自动触发,手动触发的话指的是使用save/bgsave命令,我们更加推荐使用bgsave命令,因为save会全程阻塞redis主进程,而bgsave是只会在fork子进程的时候阻塞,自动触发的话可以在redis.conf文件中进行设置,sava m n,是用一个severCron函数实现的,每秒都会检查一次redis数据库在规定的时间内,修改了一定的次数,就会进行rdb持久化;
过程如下
  • 输入bgsave命令,主进程会先进行判断,是否当前进程在执行save/bgsava/bgrewriteaof命令,bgsave进程和bgrewriteaof进程不能同时存在,因为两个子进程同时进行磁盘写的操作,会引发性能问题

  • 没有其他子进程后,redis调用fork()函数,产生一个子进程,此时主进程是被阻塞的,不能处理其他命令

  • fork()结束以后,子进程会返回”Background saving started”信息并不在阻塞主进程,可以处理其他命令

  • 子进程把内存中的数据写到一个临时的rdb文件中,当子进程写完新的rdb文件后,会把旧的rdb文件替换掉

  • 子进程发消息给主进程,表示自己已经完成

aof的话是默认关闭的,需要在redis.conf文件中开启,因为记录的是写命令,所以不需要触发,而是每次把命令写入到aof_buf缓存区中去,有三种方案,always,no,everysec,always指的是一有写命令就把数据从缓存中写入到aof文件中去,但是这样会引发redis性能问题,不推荐,no的话指的是默认30s持久化一次,但是如果中间发生了故障会导致数据丢失,不推荐,everysec指的是每秒持久化一次,相当于上面两个方案的折中,推荐使用
(然后我本来想说一下aof文件重写,bgrewriteof,但是被面试官认为我已经说完了,所以进入下一个问题了【捂脸】)

7:redis缓存穿透及解决方案
缓存穿透指的是一直拿着一个不存在的数据去查询,缓存中查不着,所以大量的请求会打到数据库上去,可能会导致mysql宕机
解决方案:
1:缓存空值:我们只要把这个数据的值设为null,然后丢到缓存中去就可以了,这样就打不到到数据库了
2:布隆过滤器:布隆过滤器的原理也很简单,首先声明一个二进制数据,里面全是0,然后把数据进行n次运算,每次运算都会找到一个位置,就把这些位置变成1,在查询数据的时候也进行n次运算,如果一个位置为0,那么我们就认为这个数据是不存在的;

8:mysql 和 redis 的适应场景
mysql用来存储数据,redis用作缓存,因为redis是在内存中操作数据,所以很快,读的速度是110000次/s,写的速度是81000次/s,可以帮助我们应对高并发的情况(ps:不知道还有没有更好的答案)

9:tcp 和 udp 的区别
tcp的话是安全的,每次建立连接都会进行三次握手,四次挥手,所以是安全的,但是也会消耗的资源多一些,连接慢一些
udb是面向无连接的,每次连接不会管数据是否发送成功和到达成功,所以是不安全的,但是消耗的资源少一些,连接快一些

10:tcp的三次握手
吧啦吧啦.....这个就不写了,太八股文了

11:linux查找一个文件名字的命令(不会)
whereis 文件名
12:linux查找一个路径下的文件名(不会)
find 路径 -name 文件名

13:手写快排,快排的时间复杂度
因为我第一个学的算法就是快排,已经写过很多遍了,所以直接秒了。
我说我可以手写,面试官说你还是在idea里写吧,估计面试官很忙
public class Test {
    public static void main(String[] args){
        int[] nums = {1,2,5,6,3,4,6,10};

        quick_sort(0, nums.length - 1, nums);

        for(int i = 0; i < nums.length; i ++) System.out.print(nums[i] + " ");
    }

    private static void quick_sort(int l, int r, int[] nums)
    {
        if(l >= r) return ;
        int mid = nums[l + r >> 1];

        int i = l - 1, j = r + 1;
        while(i < j)
        {
            do i ++; while(nums[i] < mid);
            do j --; while(nums[j] > mid);
            if(i < j)
            {
                int tmp = nums[i];
                nums[i] = nums[j];
                nums[j] = tmp;
            }
        }

        quick_sort(l, j, nums);
        quick_sort(j + 1, r, nums);
    }
}


反问:

百度一共有几面 两面 + hr

下一面是什么 技术面

您对我的评价:基础挺扎实的

总结:
1:没有问项目,也没有问java的知识,可能面试官是写go的,就没有问(没有问也好,Spring的源码看的太崩溃了,太害怕面试官问框架了【哭泣】)
2:面试官说下周约二面,但是今天都周六了,电话还没来,不知道是怎么回事,很慌,希望大佬们可以解答一下【感谢】
3:上面写的就是 我面试的时候答的,还有很多不足,希望大家多给给意见。




更多模拟面试

27条回帖

回帖
加载中...
话题 回帖

相关热帖

笔经面经近期热帖

历年真题 真题热练榜 24小时
技术(软件)/信息技术类
查看全部

近期精华帖

热门推荐