new 操作的流程
- 分配内存: 在堆上分配足够的内存来存储新对象。
- 初始化对象: 调用构造函数来设置对象的初始状态。这包括设置成员变量的初始值。
- 返回引用: 返回指向新创建对象的引用。
对象在堆和栈上的存储
- 堆(Heap): 对象本身存储在堆上。堆是动态分配的内存区域,用于存储程序运行时创建的对象。
- 栈(Stack): 对象的引用(或指针)存储在栈上。栈用于存储局部变量和函数调用信息。
Spring 创建对象的几种方式
- 通过构造函数: Spring 可以使用类的构造函数来创建对象。
- 通过静态工厂方法: Spring 可以调用类的静态工厂方法来创建对象。
- 通过实例工厂方法: Spring 可以调用工厂类的实例方法来创建对象。
- 使用
<bean>标签 (XML配置): 在 XML 配置文件中使用<bean>标签来定义 bean,Spring 容器负责创建和管理这些 bean。 - 使用
@Component和@Autowired注解 (注解配置): 使用@Component及其衍生注解(如@Service,@Repository,@Controller)标记类,然后使用@Autowired注解进行依赖注入,Spring 容器会自动创建和管理这些 bean。
CPU 占用率 100% 如何排查
- 查看进程: 使用任务管理器(Windows)或
top命令(Linux/macOS)来识别占用 CPU 最高的进程。 - 查看线程: 确定是哪个进程后,查看该进程中的线程,找出占用 CPU 最高的线程。可以使用一些性能分析工具,如
jstack(Java) 或perf(Linux)。 - 分析代码: 分析占用 CPU 最高的线程的代码,找出导致 CPU 占用率高的原因。可能是死循环、复杂的计算、频繁的 I/O 操作等。
- 优化代码: 针对找到的原因进行代码优化,比如避免死循环、减少计算复杂度、使用更高效的算法等。
签到表在 Redis 中的缓存结构设计
可以使用以下结构:
- Key:
sign:{user_id}:{year}-{month}(例如:sign:123:2023-10) - Value: 使用 Bitmap。 Bitmap 的每一位代表当月的一天。例如,如果用户在 10 月 3 日签到,则将 Bitmap 的第 3 位设置为 1。
优点:
- 节省空间: Bitmap 存储非常紧凑。
- 方便查询: 可以使用 Redis 的 Bitmap 命令(如
GETBIT,SETBIT,BITCOUNT)来查询和统计签到情况。
JWT 令牌签名的作用
JWT 令牌签名用于验证令牌的完整性和真实性。签名确保令牌没有被篡改,并且是由授权方颁发的。具体来说,签名使用私钥对令牌的头部和载荷进行签名,然后接收方可以使用公钥来验证签名。如果签名验证通过,则表示令牌是有效的。
**OJ **
OJ 系统会提供测试用例,包括各种输入数据和对应的期望输出。OJ 系统会运行提交的代码,并将输出与期望输出进行比较,以判断代码是否正确。可以通过多种测试用例覆盖冒泡排序的各种情况(例如:已经排序好的数组,倒序数组,包含重复元素的数组等)。
好的,下面我将用简单清晰的语言介绍你提到的问题:
ArrayList 和 LinkedList 的介绍
-
ArrayList:
- 底层数据结构:动态数组。
- 特点:查询速度快(通过索引直接访问),增删速度相对较慢(涉及到元素的移动)。
- 适用场景:查询操作频繁,增删操作较少的场景。
-
LinkedList:
- 底层数据结构:双向链表。
- 特点:增删速度快(只需修改指针),查询速度相对较慢(需要从头或尾遍历)。
- 适用场景:增删操作频繁,查询操作较少的场景。
MySQL 索引失效的场景
- 未使用索引列: 查询条件中没有使用索引列。
- 使用函数或表达式: 在索引列上使用了函数或表达式,导致索引失效,例如
WHERE DATE(column) = '2023-10-26'。 - 类型不匹配: 查询条件中的数据类型与索引列的数据类型不匹配,例如索引列是字符串类型,但查询条件是数字类型。
LIKE查询以%开头:LIKE '%abc'这样的查询会导致索引失效,因为无法利用索引的前缀匹配。- OR 条件: 如果 OR 连接的多个条件中,有一个条件没有使用索引,那么整个查询都可能导致索引失效(取决于 MySQL 版本和优化器的选择)。
- 联合索引未使用最左前缀: 如果联合索引是 (a, b, c),但查询条件只使用了 b 或 c,那么索引不会生效。
- 优化器认为全表扫描更快: 在某些情况下,即使有索引,MySQL 优化器也可能认为全表扫描比使用索引更快,从而放弃使用索引。
查询接口执行很慢,从哪些方面入手排查
- 数据库层面:
- 慢查询日志: 开启 MySQL 的慢查询日志,分析执行时间较长的 SQL 语句。
EXPLAIN命令: 使用EXPLAIN命令分析 SQL 语句的执行计划,查看是否使用了索引,以及扫描的行数。- 索引优化: 检查索引是否正确创建,是否需要添加新的索引。
- SQL 语句优化: 优化 SQL 语句的写法,例如避免使用
SELECT *,避免在 WHERE 子句中使用函数。 - 数据库服务器性能: 检查数据库服务器的 CPU、内存、I/O 是否存在瓶颈。
- 锁: 检查是否存在锁等待。
- 应用层面:
- 代码逻辑: 检查代码逻辑是否存在问题,例如循环调用数据库。
- 连接池配置: 检查数据库连接池的配置是否合理,例如连接数是否过小。
- 资源竞争: 检查是否存在资源竞争,例如多个线程同时访问数据库。
- 日志: 查看应用日志,是否有异常或错误信息。
- 网络层面:
- 网络延迟: 检查应用服务器和数据库服务器之间的网络延迟。
- 其他:
- 缓存: 考虑使用缓存来减少数据库的访问压力,例如 Redis。
会从哪些方面编写测试用例
- 功能测试: 验证每个功能是否符合需求规格。
- 边界值测试: 测试输入和输出的边界值,例如最大值、最小值、空值。
- 等价类划分: 将输入数据划分为不同的等价类,每个等价类选择一个代表性的数据进行测试。
- 错误推测: 根据经验推测可能出现的错误,设计相应的测试用例。
- 异常测试: 测试系统在异常情况下的处理能力,例如网络中断、数据库连接失败。
- 性能测试: 测试系统的性能指标,例如响应时间、吞吐量、并发用户数。
- 安全测试: 测试系统的安全性,例如是否存在 SQL 注入、XSS 攻击等漏洞。
- 兼容性测试: 测试系统在不同的操作系统、浏览器、设备上的兼容性。
- 用户体验测试: 测试系统的用户体验,例如界面是否友好、操作是否便捷。
用 JMeter 压测会关注哪些指标,如果有问题如何排查
- 关注指标:
- 响应时间: 平均响应时间、最大响应时间、最小响应时间。
- 吞吐量: 每秒处理的请求数 (TPS)。
- 错误率: 请求失败的百分比。
- 并发用户数: 系统能够承受的最大并发用户数。
- CPU 使用率: 服务器的 CPU 使用率。
- 内存使用率: 服务器的内存使用率。
- 磁盘 I/O: 服务器的磁盘 I/O。
- 网络 I/O: 服务器的网络 I/O。
- 问题排查:
- 监控服务器资源: 使用
top、vmstat、iostat等命令监控服务器的 CPU、内存、磁盘 I/O、网络 I/O。 - 分析 JMeter 结果: 查看 JMeter 的聚合报告,分析响应时间、吞吐量、错误率等指标。
- 数据库层面: 检查数据库服务器的性能,例如 CPU、内存、I/O,以及 SQL 语句的执行效率。
- 应用层面: 检查应用服务器的代码逻辑是否存在性能瓶颈,例如是否存在死循环、资源竞争等。
- 网络层面: 检查网络延迟、带宽是否足够。
- 调整 JMeter 参数: 调整 JMeter 的线程数、循环次数、Ramp-Up Period 等参数,观察性能变化。
- 代码剖析: 使用性能分析工具(例如 Java 的 JProfiler、VisualVM)分析代码的性能瓶颈。
- 监控服务器资源: 使用
了解 AOP
AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,旨在将横切关注点(cross-cutting concerns)与核心业务逻辑分离。
- 横切关注点: 指的是那些散布在多个模块中,但与模块核心业务逻辑无关的功能,例如日志记录、安全检查、事务管理等。
- 切面(Aspect): 封装横切关注点的模块。
- 连接点(Join Point): 程序执行过程中可以插入切面的点,例如方法调用、异常抛出等。
- 切入点(Pointcut): 用于指定哪些连接点需要应用切面的表达式。
- 通知(Advice): 切面在连接点上执行的具体操作,例如在方法调用前后执行某些代码。
AOP 的优点是可以提高代码的模块化程度、可维护性和可重用性。常见的 AOP 实现方式有:
- 静态代理: 在编译时生成代理类,将切面逻辑织入到代理类中。
- 动态代理: 在运行时动态生成代理类,例如 Java 的 JDK 动态代理和 CGLIB 动态代理。
- 编译时织入: 在编译时将切面逻辑织入到目标类中,例如 AspectJ。
Linux 命令
例如:ls、cd、mkdir、rm、cp、mv、cat、grep、ps、top、netstat、ping 等等。
-
MongoDB和MySQL的区别: MongoDB是NoSQL文档数据库,MySQL是关系型数据库。主要区别在于数据存储方式、数据结构、可扩展性、事务支持等方面。MongoDB以文档形式存储数据,更灵活,易于扩展,但事务支持相对较弱。MySQL以表格形式存储数据,结构化强,事务支持完善,但扩展性相对较差。
-
相比ES或其他数据库有什么好处: (需要根据你的实际项目情况和数据库选型考虑回答)。例如,如果主要需求是全文搜索,ES可能更合适。如果需要存储大量的图形数据,图形数据库可能更合适。需要综合考虑数据类型、查询需求、性能要求、可扩展性、开发成本等因素。
-
了解Redis吗?都了解Redis的哪些内容: (需要根据你的实际情况回答)。例如,可以提到 Redis 的数据结构(字符串、哈希、列表、集合、有序集合)、缓存机制、发布订阅、事务、持久化、集群等等。具体例子: "我了解 Redis 的常见数据结构,例如字符串、哈希、列表等,也了解它可以用作缓存,并且支持发布订阅模式。"
-
有一批号码需要处理、去重,可能还要顺序,用Redis什么数据结构好: 有序集合(Sorted Set)比较合适。 有序集合可以自动去重,并且可以根据分数(score)进行排序。可以将号码作为成员(member),将号码本身或者其他与顺序相关的属性作为分数(score)。
-
有64匹马,8个赛道,每次只能8匹比赛,要找出前4名,最少需要几场比赛:
-
第一轮: 将64匹马分成8组,每组8匹马进行比赛。需要8场比赛,得到每组的第一名到第八名。
-
第二轮: 将每组的第一名拿出来进行比赛。需要1场比赛,得到所有马的第一名,并确定8组马的排名顺序。(假设这一轮的排名为A1,B1,C1,D1,E1,F1,G1,H1)
-
确定前四名:
- 第一名肯定是第二轮的第一名(A1)。
- 第二名只可能在以下马匹中产生:第二轮的第二名(B1),以及第一轮第一名所在组(A组)的第二名(A2)。
- 第三名只可能在以下马匹中产生:第二轮的第三名(C1),第一轮第一名所在组(A组)的第三名(A3),第二轮第二名所在组(B组)的第二名(B2)。
- 第四名只可能在以下马匹中产生:第二轮的第四名(D1),第一轮第一名所在组(A组)的第四名(A4),第二轮第二名所在组(B组)的第三名(B3),第二轮第三名所在组(C组)的第二名(C2)。
所以,需要将B1, A2, C1, A3, B2, D1, A4, B3, C2 这些马再进行一场比赛 (如果存在相同的马,比如A组的马也进入了第二轮的前四名,那么只需要考虑不同的马)。 这一场比赛可以确定第二到第四名。 需要1场比赛。
总共需要8 + 1 + 1 = 10 场比赛。
HashMap底层实现:HashMap基于哈希表实现,使用数组存储键值对,通过哈希函数计算键的索引位置。如果发生哈希冲突(多个键计算出相同的索引),则使用链表或红黑树来解决冲突。
HashMap退化成链表:HashMap在元素数量减少到一定程度时,不会直接退化成链表。HashMap使用树化阈值(treeifyThreshold)和反树化阈值(untreeifyThreshold)来控制链表和红黑树之间的转换。当链表长度超过树化阈值时,链表会转换成红黑树。当红黑树节点数量低于反树化阈值时,红黑树会转换成链表。两者设置的值不对称是为了防止频繁的树化和反树化操作,减少性能抖动。这可以理解为"临界防抖动"。
联合索引题目:联合索引是包含多个字段的索引。在使用联合索引时,需要注意索引优化的情况,例如最左前缀原则。如果查询条件不满足最左前缀原则,则可能无法使用该联合索引。可能需要根据实际情况调整字段顺序,以更好地利用索引。
本地缓存:本地缓存是将数据存储在应用程序本地内存中的缓存。
本地缓存和分布式缓存的区别:本地缓存速度快,但容量有限,且数据不共享。分布式缓存容量大,数据共享,但速度相对较慢,且需要考虑数据一致性问题。
Redis replication原理:Redis replication是指将Redis服务器的数据复制到其他Redis服务器。主要原理是主节点接收写操作后,将写命令同步给从节点,从节点执行这些命令以保持与主节点数据一致。
Redis数据结构及其应用场景:Redis提供了多种数据结构,例如字符串(String)、哈希表(Hash)、列表(List)、集合(Set)、有序集合(ZSet)等。每种数据结构都有其特定的应用场景。例如,字符串可以用于缓存数据,哈希表可以用于存储对象,列表可以用于实现队列,集合可以用于存储唯一值,有序集合可以用于实现排行榜。
游戏积分和时间排序:可以使用Redis的有序集合(ZSet)来实现。将积分作为score,将时间戳作为member。当积分相同时,时间戳越小(越早),排名越靠前。可以使用如下命令添加数据:ZADD leaderboard score timestamp,然后使用ZREVRANGE leaderboard 0 -1获取排行榜。
- HashMap 的实现原理:HashMap 基于哈希表实现。它使用键的哈希码来确定键值对在数组中的存储位置。当发生哈希冲突时(即不同的键具有相同的哈希码),HashMap 使用链表或红黑树来解决冲突,将具有相同哈希码的键值对存储在同一个位置。
- Redis 的三种缓存问题及其解决方法:
- 缓存穿透:大量请求查询不存在的 key,导致请求直接到达数据库。解决方法包括:缓存空对象、使用布隆过滤器。
- 缓存击穿:某个热点 key 在缓存失效的瞬间,大量请求同时到达数据库。解决方法包括:设置热点 key 永不过期、使用互斥锁。
- 缓存雪崩:大量 key 同时失效,导致大量请求直接到达数据库。解决方法包括:设置不同的过期时间、使用互斥锁、构建多级缓存。
- RabbitMQ 如何保证消息不丢失的:
- 生产者:开启事务或使用 Confirm 机制,确保消息成功发送到 RabbitMQ。
- RabbitMQ:使用持久化机制,将消息存储到磁盘上。设置消息的 deliveryMode 为 2(persistent)。
- 消费者:手动确认消息(Ack),消费者处理完消息后,再通知 RabbitMQ 删除消息。
- 一般来说,RabbitMQ 用于异步处理任务、解耦服务、消息通知等。例如,可以使用 RabbitMQ 来处理用户注册后的邮件发送、订单支付后的物流通知等。
- MySQL 中索引的数据结构有哪些:常见的索引数据结构包括 B-Tree、B+Tree、Hash 索引、Fulltext 索引。其中,B+Tree 是 MySQL 中最常用的索引数据结构。
抽奖系统项目主要用于实现各种抽奖活动,可以设置奖品、参与条件、中奖概率等。用户参与后,系统会根据预设规则进行抽奖,并公布中奖结果。
项目中可能使用 Redis 的原因:
高性能: Redis 是内存数据库,读写速度快,适合高并发的抽奖场景。
缓存: 可以缓存奖品信息、参与人数等,减少数据库压力。
计数器: 可以用于控制参与人数或奖品数量,例如限制每个用户参与次数。
过期时间: Redis 可以设置键的过期时间,可以用来实现例如:
活动有效期: 设置活动信息的过期时间,活动结束后自动失效。
临时资格: 例如参与活动的临时资格,过期后需要重新获取。
保存方式:可以使用 Redis 的字符串 (String)、哈希 (Hash) 等数据结构存储数据。例如,奖品信息可以存储为哈希,键为奖品 ID,值为奖品名称、数量等。过期时间可以通过 EXPIRE 命令设置。
MQ (消息队列) 在抽奖系统中的作用:
异步处理: 将抽奖逻辑放入消息队列,例如用户参与抽奖后,发送一条消息到队列,由消费者异步处理抽奖逻辑。这可以提高系统响应速度,避免阻塞用户请求。
流量削峰: 高峰期大量用户参与抽奖,MQ 可以起到缓冲作用,防止系统崩溃。
解耦: 抽奖系统与通知系统(例如发送中奖短信)解耦,抽奖成功后发送一条消息到通知队列,由通知系统异步发送通知。
SQL 优化:
索引: 为经常用于查询的字段添加索引,可以加快查询速度。
避免全表扫描: 尽量使用 WHERE 子句缩小查询范围,避免全表扫描。
优化 JOIN 查询: 尽量减少 JOIN 的表数量,使用合适的 JOIN 类型(例如 INNER JOIN、LEFT JOIN)。
*避免使用 SELECT : 只查询需要的字段,减少数据传输量。
使用 LIMIT 分页: 对于大量数据查询,使用 LIMIT 分页可以提高查询效率。
优化子查询: 尽量将子查询转换为 JOIN 查询。
定期分析表: 使用数据库提供的工具分析表,优化查询计划。
批量操作: 尽量使用批量操作(例如批量插入、批量更新)减少数据库交互次数。
-
==与equals的区别:==:对于基本数据类型,比较的是值是否相等;对于引用类型,比较的是引用是否指向同一个对象。equals():是 Object 类的一个方法,默认行为与==相同(比较引用)。但通常会被重写,用于比较对象的内容是否相等(例如,String 类就重写了 equals() 方法)。
-
hashCode() 和 equals() 的关系:
- 如果两个对象通过
equals()方法比较是相等的,那么它们的hashCode()值必须相等。 - 如果两个对象的
hashCode()值相等,它们通过equals()方法比较不一定相等。 - 如果重写了
equals()方法,通常也需要重写hashCode()方法,以保证满足上述约定。 这样做是为了确保在使用哈希表(如 HashMap, HashSet)时,相同的对象能够被正确地识别和处理。
- 如果两个对象通过
-
数据库的三种范式:
- 第一范式 (1NF):数据库表的每一列都是不可分割的原子数据项。
- 第二范式 (2NF):在满足 1NF 的基础上,非主属性完全依赖于主键。即,每个非主属性都必须完全依赖于整个主键,而不是部分主键。
- 第三范式 (3NF):在满足 2NF 的基础上,非主属性之间不能存在传递依赖关系。即,任何非主属性都不能依赖于其他非主属性。
-
线程为什么调用 start() 而不是直接调用 run() 方法:
- 调用
start()方法会创建一个新的线程,并在这个新线程中执行run()方法。这样可以实现真正的并行执行。 - 直接调用
run()方法,相当于在当前线程中调用一个普通方法,不会创建新的线程,也就无法实现并发执行。start()方法负责启动线程,并调用操作系统底层的线程创建机制。
要理解 IoC (控制反转) 和 AOP (面向切面编程),可以这样来解释:
- 调用
IoC (控制反转):
- 核心思想: 传统的程序中,对象创建和依赖关系的管理是由对象自己负责的。IoC 就像一个容器,它负责创建对象并管理对象之间的依赖关系。
- 简单比喻: 就像你去餐馆吃饭,不需要自己买菜、做饭,而是由餐馆 (IoC 容器) 来提供食物 (对象) 和服务。你只需要点餐 (发出请求)。
- 好处: 降低了对象之间的耦合度,提高了代码的可维护性和可测试性。
AOP (面向切面编程):
- 核心思想: 将一些与业务逻辑无关,但又需要在多个地方重复使用的功能 (例如:日志记录、性能监控、安全检查) 抽取出来,形成一个"切面",然后将这个切面织入到程序的各个地方。
- 简单比喻: 就像给房子贴壁纸或装摄像头。壁纸和摄像头不是房子本身的核心功能,但它们可以美化房子或增强安全性。你可以根据需要在不同的房间 (程序的各个部分) 贴上壁纸或安装摄像头。
- 好处: 减少了代码的重复,提高了代码的可重用性和可维护性。
关于 ApplicationContext 实例化对象时创建了几个对象:
ApplicationContext 在实例化时,会根据你的配置 (例如:XML 文件或注解) 创建相应的 Bean (对象)。 具体创建多少个对象取决于你的配置中定义了多少个 Bean。 每一个被定义为 Bean 的类都会被 ApplicationContext 实例化一个对象 (默认情况下是单例的,即只创建一个)。
-
JDK 17中的特性包括:增强的伪随机数生成器、密封类、文本块、switch表达式、record类、移除不常用的GC组合等。
-
Lambda表达式的优点包括:代码简洁、易于理解、可以并行执行。缺点包括:调试困难、可读性降低(如果表达式过于复杂)、可能导致性能问题(如果使用不当)。
-
Lambda表达式不能直接完成单纯的 i++ 操作,因为Lambda表达式通常用于函数式编程,避免副作用。如果需要在Lambda表达式中修改外部变量,需要使用原子类或者其他线程安全的方式。
- 前端抽奖结果展示后,由于异步返回,后端处理数据失败,前端得到错误数据的问题:如果只能前端再发送请求来解决,这意味着当前架构依赖于前端的重试机制来保证最终一致性。另一种解决方案是修改为长连接形式,这种方式允许后端在处理数据失败后,立即通过长连接通知前端,前端可以立即回滚显示,避免显示错误的结果。长连接能够提供更快的反馈和更可靠的通信。
-
抽奖活动时间特别长,Redis的过期时间如何设置的问题:如果活动时间很长,可以设置Redis的过期时间较短,例如几分钟或几小时。当Redis中数据过期后,下次查询时,再从MySQL中读取并保存在Redis中。如果一段时间内没有查询,那么这个数据就不是热点数据,没有必要一直保存在Redis中。面试官可能期望回答使用RDB(Redis Database)快照和AOF(Append Only File)持久化方案,这样即使Redis重启,数据也不会丢失。RDB定期将数据快照保存到磁盘,AOF记录每次写操作,可以在Redis重启后重放这些操作来恢复数据。这两种方式结合可以提供较高的数据安全性。
-
MQ异步处理流程:消息生产者(Producer)将消息发送到消息队列(Message Queue)。消息队列接收并存储消息。消息消费者(Consumer)从消息队列中获取消息。消费者处理消息。如果处理成功,则通知消息队列删除消息(或标记为已处理)。如果处理失败,根据配置,可以将消息重新放回队列、发送到死信队列(Dead Letter Queue)或记录错误日志并进行人工干预。整个流程实现了生产者和消费者之间的解耦,提高了系统的吞吐量和响应速度。

==是逻辑运算符。既可以比较基本数据类型,也可以比较引用数据类型,比较基本数据类型,比较的是具体的值,比较引用数据类型比较是地址值
equals只能比较引用数据类型,是Object类的方法,默认比较的是引用数据类型的地址值。如果参与比较的对象对应的类有重写当前方法则按照重写之后的方法逻辑进行比较。比如String种比较的是当前字符串的值是否相等
抽象类有哪些特点?
1).抽象类内部包含普通的一切元素,同时还包含抽象方法(普通不具备)
2).抽象类虽然具备构造器,但是无法实例化(不能创建对象)
3).抽象类可以继承抽象类(不能多继承),可以无需实现父类中的方法
4).普通类一旦继承抽象类,就必须实现抽象类中的所有抽象方法
5).抽象类必然存在抽象方法
6).抽象类必然存在子类
7).有抽象方法的类必然是抽象类
8).从本质来说抽象类还是一种类(仅仅无法实例化)
重写有哪些特点?
1).必须存在继承关系
2).子类重写父类的方法
3).被重写的方法名称必须和父类方法一致
4).被重写的方法参数列表必须要跟父类方法一致
5).被重写的方法返回值类型必须跟父类方法一致
6).被重写的方法访问权限不能低于父类方法
分别写重载和重写的特点?
方法重载的:
-
方法的重载与返回值无关。
-
在同一个类或接口中。方法的名称必须相同。
-
方法的参数:个数,顺序,类型,任意有一项不一致。





非等值连接(内连接)

外连接

自连接

多表联查

sum...

分组



having

子查询

增改
创建


CREATE TABLE student(
sid int auto_increment PRIMARY KEY,
sex char(2) NOT NULL,
sname VARCHAR(20) NOT NULL,
birthday DATE,
height DECIMAL(5,2));
DROP TABLE IF EXISTS student;