前言
谨以本文记录下自己被字节面试官按在地上摩擦的面试经历😭
1、自我介绍
2、从技术角度讲一下实习期间所做的内容
3、对当前所在项目数据存储原理的了解、存量还是分片
数据库分片
1️⃣水平拆分
2️⃣垂直拆分
数据库分片–Mycat的简单使用
4、MySQL主从复制原理、同步复制、半同步复制、异步复制场景
为了保证MySQL数据库的可靠性,(同时也可基于此进行读写分离等操作,以提高可用性)通常使用主从复制的方式,MySQL主从是异步方式,大致过程如下:需要3个线程,master IO线程 ,slave开启 IO线程、SQL线程,
①master开启bin-log功能,日志文件用于记录数据库的读写增删
②Slave 通过IO线程连接master,并且请求某个bin-log,position之后的内容。
③MASTER服务器收到slave IO线程发来的日志请求信息,io线程去将bin-log内容,position返回给slave IO线程。
④slave服务器收到bin-log日志内容,将bin-log日志内容写入relay-log中继日志,创建一个master.info的文件,该文件记录了master ip 用户名 密码 master bin-log名称,bin-log position。
⑤slave端开启SQL线程,实时监控relay-log日志内容是否有更新,解析文件中的SQL语句,在slave数据库中去执行。
主从同步复制有以下几种方式:
(1)同步复制,master的变化,必须等待slave-1,slave-2,…,slave-n完成后才能返回。
(2)异步复制,master只需要完成自己的数据库操作即可,至于slaves是否收到二进制日志,是否完成操作,不用关心。MYSQL的默认设置。
(3)半同步复制,master只保证slaves中的一个操作成功,就返回,其他slave不管。这个功能,是由google为MYSQL引入的。
5、MySQL数据库写入数据过程 Double Write
InnoDB 通过事务日志把随机IO变成顺序IO,这大大提高了InnoDB写入时的性能
我们都知道MySQL的InnoDB不仅能够高效、快速的执行插入、更新、删除语句,而且不管是断电还是进程崩溃,它总能保证数据完整性,实际上,当InnoDB中提交了一个事务时,底层做了如下两件事:
1、InnoDB存储引擎将事务写入日志缓冲(log buffer),日志缓冲把事务刷新到事务日志
2、InnoDB把事务写入缓冲池(Buffer pool)
这一块儿东西还是蛮重要的,值得花点时间好好研究一下,强烈推荐三篇博文
1️⃣一条SQL语句的坎坷之旅(MySQL底层执行流程分析)
2️⃣ undo log与 redo log 原理分析
3️⃣mysql 一次数据更新过程(undo redo binlog 内存缓冲池扮演了什么角色)
6、C++、Java、python的区别,java执行效率为什么比C++低
解释性语言和编译性语言的区别:参考文章
python 慢的原因:
①GIT 全局解释器锁 控制着线程执行,一次只能执行一个操作
②解释性语言、而非编译性语言
③动态类型语言
java 慢的原因:
①面向对象,对象创建、删除等需要额外开销
②平台无关性实现所带来的开销
③一些检测机制的开销:数组越界、对象引用、类型检测
④垃圾回收机制
⑤半解释、半编译
7、java内存机制
待补充
8、内存泄漏和内存越界
9、C++中sizeof一个空的struct会是多少、里面加一个构造函数和析构函数呢、析构函数改成虚函数呢
这道题感觉属实考的有点无聊了,试过就知道,没试过就不知道,运行一下即可
#include <iostream>
using namespace std;
// 空类
class ClassA
{
};
// 继承空类的空类
class ClassB : public ClassA
{
};
// 空结构体
struct StructC
{
};
class ClassD
{
ClassD();
~ClassD();
};
class ClassE : public ClassD{
};
class ClassF
{
virtual void test()=0;
};
class ClassG : public ClassF{
void test(){
cout<<"hhh";
}
};
// 主函数
int main()
{
cout<<"A: "<<sizeof(ClassA)<<endl;
cout<<"B: "<<sizeof(ClassB)<<endl;
cout<<"C: "<<sizeof(StructC)<<endl;
cout<<"D: "<<sizeof(ClassD)<<endl;
cout<<"E: "<<sizeof(ClassE)<<endl;
cout<<"F: "<<sizeof(ClassF)<<endl;
cout<<"G: "<<sizeof(ClassG)<<endl;
return 0;
}
空类,没有任何成员变量或函数,即没有存储任何内容;
但是由于空类仍然可以实例化,编译器就需给它分配内存空间,来指示类实例的地址。
这里编译器默认分配了一个字节(如:char),以便标记可能初始化的类实例,同时使空类占用的空间也最少(即1字节)。
深度探索c++对象模型中是这样说的: 那是被编译器插进去的一个char ,使得这个class的不同实体(object)在内存中配置独一无二的地址。 也就是说这个char是用来标识类的不同对象的。
1)一个类中若有虚函数,(不论是自己的虚函数,还是继承而来的),那么类中就有一个成员变量:虚函数指针,这个指针指向一个虚函数表,虚函数表的第一项是类的typeinfo信息,之后的项为此类的所有虚函数的地址。
2)假设经过成员对齐后的类的大小为size个字节。那么类的sizeof大小可以这么计算:size + 4*(虚函数指针的个数n)。代码中,DerivedFromTwo继承自2个分支,所以有2个虚函数指针,所以sizeof大小为0 + 4* 2 = 8。
3)带有虚函数的类的sizeof大小,实际上和虚函数的个数不相关,相关的是虚函数指针。
以上截图为在64位机器上运行的结果,因此指针大小为8字节。
10、tcp断开连接 time-wait
1、全双工连接可靠释放,确认最后一个报文到达对方
2、保证属于该连接的报文失效
11、https加密机制
数字证书、对称加密&非对称加密,推荐之前写的一篇 文章
12、算法题:二维数组遍历,一个二维数组,斜向上按顺序返回nums中对角线上的数,注意,二维数组中的每一个数组长度不一定相等
Eg:
输入: nums = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,4,2,7,5,3,8,6,9]
输入:nums = [[1,2,3,4,5],[6,7],[8],[9,10,11],[12,13,14,15,16]]
输出:[1,6,2,8,7,3,9,4,12,10,5,13,11,14,15,16]
找规律,遍历即可,O(n)