> 技术文档 > 今天两轮实习面试,谈谈主要问了那些问题?_java实习生数据库面试

今天两轮实习面试,谈谈主要问了那些问题?_java实习生数据库面试

这些都是我回答的比较含糊,比较差的,还有一些我认为比较重要的,因为一家是小厂,一家是实习一面,问的都比较基础。

  • 说说Java的集合类?

        Java中的集合有两个顶层父类接口Collection和Map

  • 谈谈mySql的索引有哪些?

        这里我答的很差,因为我只记得什么聚集索引和非聚集索引了。

       1.按数据结构分类

        B+-Tree索引:默认索引类型,InnoDB和MyISAM引擎的默认索引,适用于大多数场景。支持各种条件查询,像=><BETWEENLIKE \'prefix%\'(前缀匹配)等。范围查询和排序效率高(索引本身有序)。

        Hash索引:基于哈希表,仅支持Memory引擎(InnoDB的自适应哈希是内部优化,用户不可见)。仅精确匹配(=IN)。查询速度极快,不支持范围查询和排序,哈希冲突可能导致性能下降。

        全文索引:对文本内容(如文章、日志)进行关键词搜索。

        2.按逻辑分类(重点)

        主键索引(PRIMARY KEY)​:唯一且非空(UNIQUE + NOT NULL

        唯一索引(UNIQUE)​:确保列值的唯一性(允许NULL值)

        普通索引(INDEX/KEY)​:加速查询,无唯一性约束

        组合索引(复合索引)​:遵循最左前缀原则(查询条件需包含索引最左侧列)

        覆盖索引:将所有字段加上索引,无需回表查询

        3.按索引的存储方式

        聚集索引:索引的叶子节点直接存储整行数据(索引即数据文件)。每个表只能有唯一一个聚集索引,在innoDB中,主键自动成为聚集索引。如果未定义主键,InnoDB会选择第一个非空的唯一索引(UNIQUE NOT NULL)作为聚集索引,若既无主键也无唯一索引,InnoDB会隐式创建一个6字节的隐藏行ID(DB_ROW_ID)作为聚集索引。

       非聚集索引:索引的叶子节点存储的是数据行的指针(主键值或物理地址)。在InnoDB中,非主键索引为非聚集索引。

  • MySQL索引失效?

        这个只记起了最左前缀法则

  1. ​不遵循最左前缀原则​​:特别是在组合索引中,如果查询条件没有使用索引的最左边的列,索引可能不会被使用。例如,组合索引是(A, B, C),但查询条件只用了B和C,没有A,这时候索引可能失效。

  2. ​在索引列上进行计算或函数操作​​:如果在WHERE子句中对索引列使用函数或者计算,比如WHERE YEAR(date_column) = 2023,这会导致索引失效,因为MySQL无法直接使用索引的值,而是需要计算每一行。

  3. ​使用LIKE以通配符开头​​:比如LIKE \'%abc\',这样的查询无法利用索引,因为索引是按照前缀构建的。如果是LIKE \'abc%\',则可能使用索引。

  4. ​OR条件使用不当​​:如果OR条件中的某一列没有索引,可能导致整个查询不使用索引。例如,WHERE indexed_column = \'value\' OR non_indexed_column = \'value\',这时候可能全表扫描。

  5. ​类型转换​​:如果查询条件中的类型与索引列的类型不匹配,比如索引是字符串类型,但查询时使用了数字,导致隐式类型转换,索引可能失效。

  6. ​全表扫描比使用索引更快​​:当表中数据量较小时,MySQL可能会认为全表扫描比使用索引更快,这时候即使有索引也不会使用。比如,当数据量非常小的时候,优化器可能选择全表扫描。

  7. ​使用不等于(!= 或 )或者NOT IN​​:这些操作可能导致索引失效,因为需要扫描大部分数据,这时候优化器可能选择全表扫描。

  8. ​索引列上使用IS NULL或IS NOT NULL​​:在某些情况下,如果索引列包含大量NULL值,使用IS NULL可能不会走索引。

  • JDBC的操作流程

        这个我是真忘记了,因为平时用的是myBatis框架,上次写这个都是一年前了

不使用连接池的情况//1.注册驱动Class.forName(\"com.mysql.cj.jdbc.Driver\");//2.获取链接对象(url,user,password)Connection conn = DriverManager.getConnection(\"jdbc:mysql://localhost:3306/myDatabase\");//3.通过链接对象调用方法得到statement对象Statement statement = conn.createStatement();//4.通过statement对象调用execute方法可以对数据库数据库发起操作请求,查询操作会得到结果集ResultSetResultSet res = statement.executeQuery(\"select * from user\");//5.处理异常/关闭资源(已经抛出异常)res.close();statement.close();onn.close();使用连接池的情况dataSource = new DruidDataSource();// 基础配置dataSource.setUrl(\"jdbc:mysql://localhost:3306/my_database?useSSL=false&characterEncoding=utf8\");dataSource.setUsername(\"root\");dataSource.setPassword(\"123456\");dataSource.setDriverClassName(\"com.mysql.cj.jdbc.Driver\"); // 显式设置驱动类名 // 连接池参数dataSource.setInitialSize(5); // 初始连接数dataSource.setMinIdle(3);  // 最小空闲连接dataSource.setMaxActive(20); // 最大活跃连接数dataSource.setMaxWait(3000); // 获取连接超时时间(毫秒)Connection conn = dataSource.getConnection();//3.通过链接对象调用方法得到statement对象Statement statement = conn.createStatement();//4.通过statement对象调用execute方法可以对数据库数据库发起操作请求,查询操作会得到结果集ResultSetResultSet res = statement.executeQuery(\"select * from user\");//5.处理异常/关闭资源(已经抛出异常)res.close();statement.close();onn.close();
  • MyBatis的操作流程?在不使用连接池的情况下?

        1.编写MyBatis的配置文件(mybatis-config.xml)文件名固定,且最好放在项目的根路径下

                   

        2.创建一个mapper接口,并编写方法

public interface UserMapper { // 根据 ID 查询用户信息 User getUser(int id);}

        3.为这个mapper接口编写一个xml配置文件

     SELECT * FROM users WHERE id = #{id} 

        4.调用mapper方法

public class MyBatisExample { public static void main(String[] args) throws IOException { String resource = \"mybatis-config.xml\"; //读取mybatis的配置文件 Reader reader = Resources.getResourceAsReader(resource); //通过SqlSessionFactoryBuilder对象的.build()方法,传递reader对象,实现 //sqlSession的工厂对象的创建 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); try ( //由工厂对象创建SqlSession对象 SqlSession session = sqlSessionFactory.openSession()) { //向SqlSession对象传递想要创建的mapper接口的字节码文件,得到mapper对象 UserMapper mapper = session.getMapper(UserMapper.class); //在调用方法即可 User user = mapper.getUser(100); System.out.println(user.getUsername()); } }
  • MyBatis的优缺点?

        这个真给我问到了。

        大家都知道MyBatis的最大优点是灵活,因为可以编写动态sql,那么缺点呢?

        缺点:虽然支持多数据库,但 SQL 需针对不同数据库进行调整(全自动 ORM 可自动适配方言)。(这是面试官想让我答的,因为他问过我 “你除了使用过mybatis还使用过那些orm框架(Hibernate)”)

  • 你在那些场景下用过redis?

        这个就要结合自己的项目业务回答了

        1.缓存数据,查询的时候先查缓存,如果不存在,在查数据库,在同步缓存。降低数据库压力。

        2.分布式锁,用setnx命令或者redisson分布式锁为查询数据库到同步缓存这一步加上锁,可以防止缓存击穿。

        3.发送异步消息,基于redis的发布订阅Pub/Sub模式可以实现异步消息的发送。

  • redis的优缺点?

        这个问题也搞的我很无语,我回答的缺点是成本高,但是面试官来了一句内存成本也不高,搞得我也不知道答案了。下面是我网上搜的:     

Redis 的优点​

1. ​​极高性能​

  • ​内存存储​​:数据存储在内存中,读写速度极快(10万+ QPS)。
  • ​单线程模型​​:避免多线程竞争,简化设计(Redis 6.0+ 支持多线程网络 IO,但核心操作仍单线程)。

2. ​​支持丰富的数据结构​

  • ​多样化数据类型​​:
    • 字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(Sorted Set)。
    • 位图(Bitmap)、HyperLogLog(基数统计)、地理空间索引(GEO)等。
  • ​适用场景广泛​​:如排行榜(Sorted Set)、消息队列(List/Stream)、实时统计(HyperLogLog)。

3. ​​持久化与高可用​

  • ​持久化机制​​:
    • ​RDB​​(快照):定期生成数据快照,适合备份。
    • ​AOF​​(追加日志):记录所有写操作,数据可靠性更高。
  • ​高可用架构​​:
    • 主从复制(Master-Slave) + 哨兵(Sentinel)自动故障转移。
    • Redis Cluster 分片集群,支持横向扩展。

4. ​​功能丰富​

  • ​事务支持​​:通过 MULTI/EXEC 实现简单事务(非 ACID)。
  • ​Lua 脚本​​:原子性执行复杂逻辑。
  • ​发布订阅(Pub/Sub)​​:轻量级消息队列。
  • ​分布式锁​​:通过 SETNX 或 Redisson 实现(解决并发竞争)。

5. ​​生态成熟​

  • 客户端支持广泛(Java、Python、Go 等)。
  • 与 Spring、Kafka 等中间件集成方便。
  • 云服务支持完善(如 AWS ElastiCache、阿里云 Redis)。

Redis 的缺点​

1. ​​内存成本高​

  • 数据完全存储在内存中,存储海量数据时硬件成本较高。
  • 需配合淘汰策略(如 LRU、TTL)或持久化机制管理内存。

2. ​​持久化性能权衡​

  • ​RDB​​:生成快照时可能阻塞主线程(大数据量时延迟明显)。
  • ​AOF​​:写入频繁时影响吞吐量(可通过 everysec 配置平衡性能与安全)。

3. ​​单线程的局限性​

  • ​CPU 密集型操作阻塞​​:如 KEYS *、复杂 Lua 脚本会阻塞其他请求。
  • 多核利用率低(Redis Cluster 可通过分片解决)。

4. ​​数据规模受限​

  • 单实例内存容量受物理限制,超大规模数据需分片(Redis Cluster)。
  • 不适合存储大文件(如图片、视频)。

5. ​​复杂查询能力弱​

  • 无 SQL 支持,复杂查询需客户端处理(如排序、JOIN)。
  • 不适合替代关系型数据库(如 MySQL)作为主存储。
  • 如何查看mysql在查表时是否用过索引?

        使用explain命令查看执行计划。在sql查询语句前添加explain即可。

  • 你知道那些互联网的通信协议?udp和tcp的区别?你自己解析过tcp协议吗?

Java 提供了原生 API 直接处理 ​​TCP 协议​​,至于对TCP数据包的解析,我确实不知道了。

// 客户端try (Socket socket = new Socket(\"localhost\", 8080)) { OutputStream out = socket.getOutputStream(); out.write(\"Hello TCP!\".getBytes());}// 服务端try (ServerSocket serverSocket = new ServerSocket(8080)) { Socket clientSocket = serverSocket.accept(); InputStream in = clientSocket.getInputStream(); // 读取 TCP 数据流(字节级别) byte[] buffer = new byte[1024]; int bytesRead = in.read(buffer); System.out.println(\"收到数据:\" + new String(buffer, 0, bytesRead));}