面试汇总(一):针对百度面试总结

时间:2022-07-24
本文章向大家介绍面试汇总(一):针对百度面试总结,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

面试概述

  首先跟大家说声抱歉,由于最近有面试和笔试,所以一直刷题就没时间更新博客了。随着秋招进入了一个白热化阶段,我们所投的岗位也已经进入了面试阶段。就昨天第一次面试——百度的面试做一次简单的总结,方便投百度的同学参考。当然也方便自己学习。   本人投递的是百度的测试岗,前几天约的昨天面试,我一直在准备测试相关的最基本的知识,由于是一面,应该不会问很难的知识。因此,我就是简单的看了那几种常见的测试以及白盒测试和黑盒测试,还有就是看了相关的测试的流程。但是,在面试的时候,几乎没有问到测试的任何问题,问的都是与我们计算机相关的最基础的知识点,大致包括:mysql 问的最多, redis缓存、计算机网络、Linux 等相关的知识。面试的时间是半个小时(可能太菜了,面试官没法问了…)接下来给大家详细介绍相关的问题。

面试题及参考答案

1、请围绕你的项目经验或者实习经历做一个介绍。   这是针对你的简历进行的简单提问,初步对你有一个简单的了解,同时也是为了缓减紧张,让我们快速进入状态。这里的话,我们只需要针对自己的项目以及实习做一个简单的汇总即可。 2、你是怎么实现一个项目的,项目最后实现了哪些功能?   这个问题是针对上一个问题的继续追问,了解你对项目流程的了解以及整体知识点的把控以及应用。这里我们只需要简单的选择一个自己熟悉的项目展开描述即可,在概述项目的时候需要我们注意的是:你讲述的项目应该包括以下几点:

  • 项目名称
  • 项目功能(项目最后实现的功能)
  • 项目涉及的技术点(项目用到了哪些技术:mysql、ssm、redis等)
  • 做项目时的具体步骤(从需求了解、数据库设计最后的项目的上线)

3、我看到你项目中同时用到了mysql和redis,并且redis是作为缓存,那么你在更新数据的时候,是先更新数据库还是先更新缓存?

   首先缓存的目的是为了减少数据库的压力,而且redis支持事务处理机制,请求优先命中redis,缓存中没有在命中数据库,所以优先删除缓存,然后更新数据库,即使删除缓存成功,但是更新数据库失败,但是数据依然存在。其次,如果在更新数据库的时候报错,或者服务死机,最后只能采用补偿机制,保证最后达到最终的数据的一致性。不过还会存在一个问题那就是数据库回滚,这个问题就留给大家自己去想。    正常同学的思维应该是先更新数据库,后更新缓存(当然我是这么回答的,你就知道有多尴尬了),然后面试官说,如果多个用户一起访问,如果你把数据库更新了,但是用户访问的是缓存,这不就出现了数据库与redis的不一致性的问题,你又该如何解决?(瞬间我就懵圈了)。我只能说我下来好好想想这个问题。

4、如何解决redis和mysql数据不一致性问题

  读取缓存步骤一般没有什么问题,但是一旦涉及到数据更新:数据库和缓存更新,就容易出现缓存(Redis)和数据库(MySQL)间的数据一致性问题。其实,不管是先写MySQL数据库,再删除Redis缓存;还是先删除缓存,再写库,都有可能出现数据不一致的情况。 1.如果删除了缓存Redis,还没有来得及写库MySQL,另一个线程就来读取,发现缓存为空,则去数据库中读取数据写入缓存,此时缓存中为脏数据。 2、如果先写了库,在删除缓存前,写库的线程宕机了,没有删除掉缓存,则也会出现数据不一致情况。 因为写和读是并发的,没法保证顺序,就会出现缓存和数据库的数据不一致的问题。那么如何解决呢?

  • 第一种方案:采用延时双删策略    在写库前后都进行redis.del(key)操作,并且设定合理的超时时间。具体的流程是:先删除缓存—>再写数据库—>休眠500毫秒---->再次删除缓存.那么这里有一个小的问题:这个500毫秒怎么确定的,具体该休眠多久呢? 需要评估自己的项目的读数据业务逻辑的耗时。这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。当然这种策略还要考虑redis和数据库主从同步的耗时。最后的的写数据的休眠时间:则在读数据业务逻辑的耗时基础上,加几百ms即可。比如:休眠1秒。接下来是设置缓存过期时间,其实从理论上来说,给缓存设置过期时间,是保证最终一致性的解决方案。所有的写操作以数据库为准,只要到达缓存过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存。当然该方案也存在一定的弊端:结合双删策略+缓存超时设置,这样最差的情况就是在超时时间内数据存在不一致,而且又增加了写请求的耗时。
  • 第二种方案:异步更新缓存(基于订阅binlog的同步机制)    本方案的整体思路是:MySQL binlog增量订阅消费+消息队列+增量数据更新到redis。具体是: 1)读Redis:热数据基本都在Redis 2)写MySQL:增删改都是操作MySQL 3)更新Redis数据:MySQ的数据操作binlog,来更新到Redis    对于redis更新来说:数据操作主要分为两大块:(一个是全量(将全部数据一次写入到redis)、一个是增量(实时更新),这里需要特别说明的是:这里提到的增量指的是mysql的update、insert、delate变更数据。),接下来就是读取binlog后分析利用消息队列,推送更新各台的redis缓存数据。这样一旦MySQL中产生了新的写入、更新、删除等操作,就可以把binlog相关的消息推送至Redis,Redis再根据binlog中的记录,对Redis进行更新。其实这种机制,很类似MySQL的主从备份机制,因为MySQL的主备也是通过binlog来实现的数据一致性。这里可以结合使用canal(阿里的一款开源框架),通过该框架可以对MySQL的binlog进行订阅,而canal正是模仿了mysql的slave数据库的备份请求,使得Redis的数据更新达到了相同的效果。当然,这里的消息推送工具你也可以采用别的第三方:kafka、rabbitMQ等来实现推送更新Redis。

5、Mysql索引是那种数据结构?

   这个问题特别的好,有效的结合了mysql数据库和数据结构的相关知识。首先说一点,mysql索引的数据结构就是用到的B+树(当时我回答的是Hash,就挺尴尬的,然后面试官问,hash能够完全避免冲突吗?一旦发生冲突怎么处理)。其实B+树就是B树的变种,而B树在红黑树的基础上,每个节点可以存放多个数据。而我们提到的红黑树其实就是平衡二叉树。那么mysql索引为什么用B+树呢?对于MyISAM存储引擎索引文件和数据文件是分离的。 Usertabmyisam表使用的myisam存储引擎,表相关文件有三个,.frm是存放表结构数据,MYD是表数据。MYI是存放索引,索引树上会存储数据在MYD文件里面的位置。Usertab使用的Innodb存储引擎,表相关文件只有两个同样.frm文件是存放表结构数据,.ibd存放的数据和索引。表数据文件本身就是按B+Tree组织的一个索引结构文件,主键索引叶节点包含了完整的数据记录。我们以InnoDB为例:数据是放在主键索引上面,也就是说实际上在每个节点上还会存放所有的数据,使用B树存放数据之后实际是这样子的,会在每个对应的索引列的值上存放上对应的数据。而B+树则不同,它只会在叶子节点上面挂载数据,非叶子节点不会存放数据,数据只会存在叶子节点上面,非叶子节点只存放索引列的数据。 这样一个节点就可以存放很多个索引列数据,一次IO就可以拿到很多数据,叶子节点之间也有双向指针连接,提高区间范围性能,范围查找。创建索引的时候,可以选择索引数据类型,一个是btree一个是hash,hash查找当然也快,但是当遇到范围查找的时候hash就尴尬了,所以根据实际业务需求来看是用btree还是hash。

6、Hash支持范围查找吗?为什么Msql索引不用此数据结构?

  首先说明的是:Hash表不支持范围查找,因为Hash表是一种key-value 形式的数据结构,底层采用数组+链表结构来实现,它是将 key 通过一个哈希函数计算出一个数字,然后以该数字作为数组的下标,然后将 value 存放到对应下标的数组中。对于不同的 key,在经过哈希函数计算后,可能出现相同的值,这种情况叫做哈希冲突,这时候就意味着同一个数组下标处要存放两个元素了,所以这个时候将数组中的元素变为一个链表,通过链表将这两个元素串联起来。根据上面哈希表的特点来看,哈希表对于删除、查找、更新、插入操作,都是先根据 key 计算出一个值,就能定位到数据的目标位置了,时间复杂度都是 O(1),速度特别快。但是当我们经常会遇到查找某个范围内的数据,这时候就不行了。因为哈希表的所有 key 都会经过哈希函数计算,然后再存放数据,本来可能有序的 key,但经过哈希函数计算出来的值就不是有序的了,所以这个时候,如果要在哈希表中进行范围查找,就只能对整个哈希表进行遍历了,只有符合条件范围的数据,才取出来。当我们数据库中的数据越来越多,达到几百万甚至几千万条的时候,这个时候,对整个表的遍历是非常耗时的。因此,从范围查询的场景来看,哈希表也不太适合作为 MySQL 索引的数据结构。哈希表适用于什么场景呢?适用于等值查询的场景,最经典的场景就是 NOSQL 数据库,例如我们最常用的 redis,redis 中不就是全都是 key-value 吗?实际上,MySQL 中也有地方使用哈希作为索引。 但是,通常情况下,不建议将索引的结构选为 HASH,除非业务的场景的确符合 key-value 这种场景,例如业务系统的一些 key-value 形式的配置项的表、数据字典等功能。

7、HASH索引的适用场景和限制

HASH索引只有精确匹配索引所有列的查询才有效。 因为索引自身只需要存储对应的哈希值,所以索引的结构十分紧凑,这也让哈希索引查找的速度非常快,然而,哈希索引也有限制,如下:

  • 哈希索引只包含哈希值和行指针,而不存储字段值,所以不能使用索引中的值来避免读取行(即不能使用哈希索引来做覆盖索引扫描),不过,访问内存中的行的速度很快(因为memory引擎的数据都保存在内存里),所以大部分情况下这一点对性能的影响并不明显。
  • 哈希索引数据并不是按照索引列的值顺序存储的,所以也就无法用于排序
  • 哈希索引也不支持部分索引列匹配查找,因为哈希索引始终是使用索引的全部列值内容来计算哈希值的。如:数据列(a,b)上建立哈希索引,如果只查询数据列a,则无法使用该索引。
  • 哈希索引只支持等值比较查询,如:=,in(),<=>(注意,<>和<=>是不同的操作),不支持任何范围查询(必须给定具体的where条件值来计算hash值,所以不支持范围查询)。
  • 访问哈希索引的数据非常快,除非有很多哈希冲突,当出现哈希冲突的时候,存储引擎必须遍历链表中所有的行指针,逐行进行比较,直到找到所有符合条件的行。
  • 如果哈希冲突很多的话,一些索引维护操作的代价也很高,如:如果在某个选择性很低的列上建立哈希索引(即很多重复值的列),那么当从表中删除一行时,存储引擎需要遍历对应哈希值的链表中的每一行,找到并删除对应的引用,冲突越多,代价越大。

8、说一下drop delete truncate区别

  • DELETE语句执行删除的过程是每次从表中删除一行,并且同时将该行的删除操作作为事务记录在日志中保存以便进行进行回滚操作。 TRUNCATE TABLE 则一次性地从表中删除所有的数据并不把单独的删除操作记录记入日志保存,删除行是不能恢复的。并且在删除的过程中不会激活与表有关的删除触发器。执行速度快。
  • 表和索引所占空间。( 当表被TRUNCATE 后,这个表和索引所占用的空间会恢复到初始大小, DELETE操作不会减少表或索引所占用的空间。 drop语句将表所占用的空间全释放掉。)
  • 一般而言,drop > truncate > delete
  • 应用范围: TRUNCATE 只能对TABLE;而 DELETE可以是table和view。
  • TRUNCATE 和DELETE只删除数据, DROP则删除整个表(结构和数据)。
  • truncate与不带where的delete :只删除数据,而不删除表的结构(定义)drop语句将删除表的结构被依赖的约束(constrain),触发器(trigger)索引(index);依赖于该表的存储过程/函数将被保留,但其状态会变为:invalid。
  • delete语句为DML(data maintain Language),这个操作会被放到 rollback segment中,事务提交后才生效。如果有相应的 tigger,执行的时候将被触发。
  • truncate、drop是DLL(data define language),操作立即生效,原数据不放到 rollback segment中,不能回滚
  • 在没有备份情况下,谨慎使用 drop 与 truncate。要删除部分数据行采用delete且注意结合where来约束影响范围。回滚段要足够大。要删除表用drop;若想保留表而将表中数据删除,如果于事务无关,用truncate即可实现。如果和事务有关,或老师想触发trigger,还是用delete。
  • Truncate table 表名 速度快,而且效率高,因为: truncate table 在功能上与不带 WHERE 子句的 DELETE 语句相同:二者均删除表中的全部行。但 TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少。DELETE 语句每次删除一行,并在事务日志中为所删除的每行记录一项。TRUNCATE TABLE 通过释放存储表数据所用的数据页来删除数据,并且只在事务日志中记录页的释放。
  • TRUNCATE TABLE 删除表中的所有行,但表结构及其列、约束、索引等保持不变。新行标识所用的计数值重置为该列的种子。如果想保留标识计数值,请改用 DELETE。如果要删除表定义及其数据,请使用 DROP TABLE 语句。
  • 对于由 FOREIGN KEY 约束引用的表,不能使用 TRUNCATE TABLE,而应使用不带 WHERE 子句的 DELETE 语句。由于 TRUNCATE TABLE 不记录在日志中,所以它不能激活触发器。   总之: 在速度上,一般来说,drop> truncate > delete。在使用drop和truncate时一定要注意,虽然可以恢复,但为了减少麻烦,还是要慎重。如果想删除部分数据用delete,注意带上where子句,回滚段要足够大; 如果想删除表,当然用drop; 如果想保留表而将所有数据删除,如果和事务无关,用truncate即可; 如果和事务有关,或者想触发trigger,还是用delete; 如果是整理表内部的碎片,可以用truncate跟上reuse stroage,再重新导入/插入数据。

9、简单介绍having和where的区别

  • 用的地方不一样: where可以用于select、update、delete和insert into values(select * from table where …)语句中。 having只能用于select语句中
  • 执行的顺序不一样: where的搜索条件是在执行语句进行分组之前应用, having的搜索条件是在分组条件后执行的。即如果where和having一起用时,where会先执行,having后执行。
  • 子句有区别: where子句中的条件表达式having都可以跟,而having子句中的有些表达式where不可以跟;having子句可以用集合函数(sum、count、avg、max和min),而where子句不可以。   总之,WHERE 子句用来筛选 FROM 子句中指定的操作所产生的行。GROUP BY 子句用来分组 WHERE 子句的输出。HAVING 子句用来从分组的结果中筛选行。

10、某个表格存着s_name subject score 三个字段,比如某一行是 张三 数学 76,现在要选取出所有科目成绩都大于80分的学生名字,请写出sql语句

select s_name from table_name where s_name not in (select s_name from table_name where score <80)

11、你对Linux熟悉吗?请分别说明查看磁盘内存、网络端口、网络状态以及ip地址的命令。

查看磁盘的使用量:df;fdisk用于磁盘分区;du:检查磁盘空间使用量
查看端口号:netstat –tunlp|grep
查看网络状态:netstat
IP地址:ifconfig
查看网络连接:ping

12、TCP建立连接为什么是三次不是两次?

TCP三次建立连接过程如图所示:

第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态; 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。   在服务端对客户端的请求进行回应(第二次握手)后,就会理所当然的认为连接已建立,而如果客户端并没有收到服务端的回应呢?此时,客户端仍认为连接未建立,服务端会对已建立的连接保存必要的资源,如果大量的这种情况,服务端会崩溃。

13、请简要介绍SSL机制

   SSL是一种在客户端和服务器端之间建立安全通道的协议。SSL是Secure socket Layer英文缩写,它的中文意思是安全套接层协议,指使用公钥和私钥技术组合的安全网络通讯协议。SSL协议是网景公司(Netscape)推出的基于WEB应用的安全协议,指定了一种在应用程序协议(如Http、Telenet、NMTP和FTP等)和TCP/IP协议之间提供数据安全性分层的机制,它为TCP/IP连接提供数据加密、服务器认证、消息完整性以及可选的客户机认证,主要用于提高应用程序之间数据的安全性,对传送的数据进行加密和隐藏,确保数据在传送中不被改变,即确保数据的完整性。SSL以对称密码技术和公开密码技术相结合,可以实现如下三个通信目标: (1)秘密性:SSL客户机和服务器之间传送的数据都经过了加密处理,网络中的非法窃听者所获取的信息都将是无意义的密文信息。 (2)完整性:SSL利用密码算法和散列(HASH)函数,通过对传输信息特征值的提取来保证信息的完整性,确保要传输的信息全部到达目的地,可以避免服务器和客户机之间的信息受到破坏。 (3)认证性:利用证书技术和可信的第三方认证,可以让客户机和服务器相互识别对方的身份。为了验证证书持有者是其合法用户(而不是冒名用户),SSL要求证书持有者在握手时相互交换数字证书,通过验证来保证对方身份的合法性。    总之,SSL利用数据加密、身份验证和消息完整性验证机制,为网络上数据的传输提供安全性保证。SSL支持各种应用层协议。由于SSL位于应用层和传输层之间,所以可以为任何基于TCP等可靠连接的应用层协议提供安全性保证。 过程是身份验证机制—>数据传输的机密性—>消息完整性验证—>利用非对称密钥算法保证密钥本身的安全—>利用PKI保证公钥的真实性。

14、请就发朋友圈消息写一个测试用例

  对于一个待测试的对象,我们通常通过以下几个方面来进行测试:功能测试、可靠性测试、易用性测试、效率、可维护性、可移植性、安全性测试、界面测试等。本题从两个角度来分析,站在测试人员的技术测试角度(功能测试、可靠性测试、兼容性、可维护性、效率、可移植性、安全性测试、可维护性)和站在用户的角度(功能测试、易用性测试)。 站在测试人员的技术测试角度: 1、功能测试:   发朋友圈、删除朋友圈,看朋友圈;朋友圈的类型(图、文、混合);评论朋友圈;朋友圈的对外接口(例如,王者荣耀,把战绩分享至朋友圈等);屏蔽与被屏蔽,不能查看对应好友的朋友圈;发朋友圈:我们可以通过短按或者常按朋友圈中的照相机图标,分别发起图片版或文字版的朋友圈操作,在此过程中,我们需要关注进行发起操作的响应时间是否符合需求。然后就需要对发朋友圈进行全面的测试了,其中包括,正常发朋友圈,取消发朋友圈,多次发朋友圈等。如果需求中对朋友圈的内容有限定,例如不允许出现敏感字眼等。 2、可靠性测试:   短时间内频繁进行发送、取消、以及删除朋友圈的组合测试,看朋友圈相关功能是否正常;微信打开后,手机锁屏或切换到主界面,微信在后台是否会失效出现bug,并且朋友圈的功能是否会失效。 3.性能测试:   如果发起朋友圈操作之前,手机的CPU使用率为30%,发起操作之后,忽然涨到了80%,不关闭朋友圈的相关操作,CPU使用率降不下来,那么对于整个朋友圈的性能问题就得需要我们去好好找原因了。 4.其他测试:    在弱信号的情况,进行发朋友圈、看朋友圈等操作,测试其是否会产生其它未知故障。(例如对WiFi信号进行限速)、在不同的客户端的兼容性测试,使用不同平台的客户端进行朋友圈的功能测试。(例如使用不同厂商的手机、平板)、安全性测试(例如在朋友圈儿中输入一些脚本程序代码什么的,测试是否会将微信客户端搞崩溃什么的。 站在用户的角度   用性是其评价软件好坏最主要的一点,功能操作是否简单明了,给出的提示是否清楚明白无二意,还有就是界面布局否美观合理。除此之外,我们还要模拟不同的用户场景下的使用。把自己想象为不同的用户(小白用户,资深用户),因为不同的用户有不同的使用习惯,这也类似于发散测试,因人而异。

15、给你一个字符串,你怎么判断是不是ip地址?手写这段代码

思路:常规思路: 仅用判断是不是IP地址的格式即可,具体实现如下:

package com.wmmx.test;
 
public class TestIP {
    public static void main(String[] args) {
        Solution solution = new Solution();
        String str = "123.78.64.7";
        System.out.println(solution.isIPAddress(str));
    }
 
    static class Solution {
        /**
         *    字符串的长度 0.0.0.0 7位 ~ 000.000.000.000 15位
         *    将字符串拆分成四段
         *    检查每段是否都是纯数字
         *    检查每段是否都在0-255之间
         *    以上条件都满足的话返回true
         */
        public boolean isIPAddress(String str) {
            // 如果长度不符合条件 返回false
            if(str.length()<7 || str.length() >15) return false;
            String[] arr = str.split("\.");
            //如果拆分结果不是4个字串 返回false
            if( arr.length != 4 )    return false;
            for(int i = 0 ; i <4 ; i++ ){
                for(int j = 0; j<arr[i].length();j++){
                    char temp = arr[i].charAt(j);
                    //如果某个字符不是数字就返回false
                    if(!( temp>'0' && temp< '9' ) ) return false;
                }
            }
            for(int i = 0 ; i<4;i++){
                int temp = Integer.parseInt( arr[i] );
                //如果某个数字不是0到255之间的数 就返回false
                if( temp<0 || temp >255)    return false;
            }
            return true;
        }
    }
}

测试结果如图所示:

让然我们也有更好的思路:内部用正则表达式进行判断,这是一个典型的正则表达式的判断。具体实现如下:

public class TestIP {
   public static void main(String[] args) {
        isIPAddressByRegex solution = new isIPAddressByRegex();
        String str = "123.78.64.7";
        System.out.println(solution.isIPAddress(str));
    }
  public boolean isIPAddressByRegex(String str) {
            String regex = "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}";
            // 判断ip地址是否与正则表达式匹配
            if (str.matches(regex)) {
                String[] arr = str.split("\.");
                for (int i = 0; i < 4; i++) {
                    int temp = Integer.parseInt(arr[i]);
                    //如果某个数字不是0到255之间的数 就返回false
                    if (temp < 0 || temp > 255) return false;
                }
                return true;
            } else return false;
        }
    }

测试结果如图所示:

当然我们也可以用python将其实现,具体代码实现如下:

def isip(ip:str):
    if not isinstance(ip, str):
        print('请输入一个字符串')
        return False
    if ip.count('.') !=3:
        print('分隔符号数量不对')
        return False
    iplist = ip.strip().split('.')
    for i in range(4):
        if iplist[i] == '':
            print('IP包含空字符')
            return
        try:
            iplist[i] = int(iplist[i])
        except:
            print('IP包含非数字')
            return False
        if 0<=iplist[i]<=255:
            continue
        else :
            print('IP超出数字范围')
            return False
    print('合理的IP地址')
 
#设计测试数据校验程序的正确性
isip('1.2.2.3') #正常的ip
isip('a.2.2.3') #包含非数字
isip('.2.2.3')  #有字段为空
isip('1.2.4.2.3') #多出一个段
isip('1.2.3')    #少一个段
isip(1)      #非字符串
isip('123')  #无分割符
isip('1.2#2.3') #其他分割符
isip('') #为空
isip('1.2.2.300') #数字超出范围
isip('256.2.2.1')  #数字超出范围

测试结果如下:

总结

  本文针对本人昨天的面试做了一个总结,一方面是为了方便自己以后面试的复习,另外也是给大家再次面试百度的测试岗位的时候提供复习方向以及思路解答。从上面的面试题发现,考察的都是最基础的、也是经常遇到的问题。并且面试的内容比较全面,不仅仅是我们所涉及的专业知识,还有相关的基础知识,包括数据库以及数据结构,这里就需要我们对数据结构以及数据库有一个较为深层次的理解。于是,我们在准备的时候,首先就应该夯实基础,只有这样才能在众多的面试者中脱颖而出。最后希望大家不断进步,都能尽早拿到自己比较满意的offer!!!!继续加油,未来可期!!!!

参考文献

[1] 更新数据是先更新缓存(redis),还是先更新数据库(mysql) [2] 高并发问题 - 如何解决Redis缓存和MySQL数据一致性的问题 [3] mysql索引数据结构 [4] MySQL HASH索引的适用场景和限制 [5] drop、truncate和delete的区别 [6] sql中where和having的区别 [7] 测试用例设计——微信发朋友圈 [8] Java判断一个字符串是不是IP地址 [9] 判断是不是一个IP地址的测试用例设计(Python实现) [10] 请你回答一下TCP三次握手,以及为什么不是两次