秒杀订单表,秒杀商品/商品抢购,普通商品表,普通订单表,用户表,订单撤回/取消订单,订单到期未回退
用户下单功能,商品页面,搜索功能,商品详情,购物车,用户登录和鉴权
答:使用jwt,登录的时候会发送给鉴权中心,密码正确则会颁发给用户一个jwt token,用户后面的每一次请求都会携带这个token,其他的应用服务器收到一个请求后就会首先检查这个token是否有效,如果有效则允许用户请求打进来
答:假设一条订单的初始值为0,表示用户下单但是仍然未支付,1表示成功支付了,待发货中,2表示用户主动取消订单了,3表示过期没有支付所以取消订单,4是终态,不会再变化了。
答:消费者主动从kafka消息队列中拉取下单消息,进行消费的时候,会进行下单操作。
答:
(1)用户点击下单按钮会生成一笔订单,此时订单的状态是0,消费者主动从kafka消息队列中拉取下单消息,进行消费的时候,会进行下单操作,此时变成初始状态0
(2)用户主动调用取消订单的接口,会使得状态由0变为2
(3)用户调用支付接口,状态由0变为1
(4)用户过了三十分钟没有支付,延时消息会到期被消费,将订单状态由0变为3
(5)用户收到货之后点击收货按钮,就会变为终态
答:考虑到新增的状态,我们可以定义以下状态值:
5 - 退换货中
6 - 退换货完成
7 - 退款中
8 - 退款完成
9 - 交易关闭
基于以上的状态定义,以下是各状态间的可能流转以及在什么时间点流转:
用户点击下单按钮
用户行为:点击下单按钮
状态流转:初始 -> 0 (未支付)
用户取消订单
用户行为:主动调用取消订单的接口
状态流转:0 (未支付) -> 2 (用户主动取消)
用户支付
用户行为:调用支付接口
状态流转:0 (未支付) -> 1 (已支付待发货)
用户超时未支付
系统行为:处理延时消息
状态流转:0 (未支付) -> 3 (过期未支付)
用户确认收货
用户行为:点击收货按钮
状态流转:1 (已支付待发货) -> 4 (交易完成)
用户申请退换货
用户行为:点击申请退换货
状态流转:4 (交易完成) -> 5 (退换货中)
退换货完成
系统行为:确认退换货请求已处理
状态流转:5 (退换货中) -> 6 (退换货完成)
用户申请退款
用户行为:点击申请退款
状态流转:4 (交易完成) 或 6 (退换货完成) -> 7 (退款中)
退款完成
系统行为:确认退款已处理
状态流转:7 (退款中) -> 8 (退款完成)
交易关闭
系统或用户行为:在任何非终态下,由于某些原因(如长时间不活跃、交易异常等)交易关闭
状态流转:任意状态 -> 9 (交易关闭)
以上流转可能因业务需求的具体性而有所不同,但大致能够涵盖大部分场景。
Kafka零拷贝
结论:为什么写入不直接用 sendfile 呢,因为 sendfile 不支持中途对文件做变更,mmap 支持在写入磁盘之前,直接在 page Cache 中修改文件。
sendfile 和 mmap 是零拷贝的两种不同实现,在 kafka 中也对应两种不同场景。针对 producer 往 broker 上写入消息,使用的是 mmap,consumer 从 broker 上拉取数据,使用的是 sendfile。
producer 往 broker 发消息:对应消息文件从网卡–>磁盘。这个文件在写入过程中是会动态“变化”的(试想一下本身是写的场景,因为要处理消息丢失等问题),假设使用 sendfile 的方式就是直接把文件从网卡 copy 到 socket buffer 之后,然后直接落到磁盘,中间是【不能有变更操作的】,这显然不符合写入的要求。
再说 consumer 从 broker 拉消息:对应消息文件从磁盘–>网卡。这个过程中使用的是 sendfile。这个过程中【只涉及到读操作,对消息这个文件是不需要有任何变更操作的】,将文件从磁盘 copy 到 readBuffer 之后,直接发送到网卡了,这中间没有经过 socketBuffer。
答:(1)数据隔离(2)保证同一个分区内的消息的有序性(3)可以提升并行读写速度,提升吞吐量
gpt4的答案:
Kafka 将消息进行分区的主要原因有以下几点:
可扩展性(Scalability):
并行处理:
数据的有序性:
负载均衡:
持久性与容错性:
日志段的管理:
分区键(Partition Key):
综上所述,Kafka 的分区设计使得 Kafka 能够提供高吞吐量、扩展性、并行处理能力以及高可用性等优点。
讲了讲concurrentHashMap是如何解决线程安全问题的
自定义线程池,任务来了之后怎么加入线程池,,怎么流转
答:
(1)非核心线程数不能设置的无限大,因为线程切换非常耗费cpu资源,实际上得不偿失
(2)队列也不能无界,对调用者的体验不好,如果线程池的任务队列很大,新来的任务在末尾,那么可能需要让调用方等待很久才能执行,响应时间过长
答:从socket套接字缓冲区中取得数据到执行任务然后到写回发送缓冲区中的这么过程是单线程
答:aof,rdb以及同步给从节点
答:一般会先把数据写入到缓冲池中,然后将缓冲池中的数据定期或者按照规则同步到本地磁盘中,另外还会定期生成rdb快照,进行和本地持久化
答:一般可以每秒刷一次盘,这样宕机了也可以重启加载rdb快照进行快速恢复,然后执行aof中的命令恢复到最接近完整的状态
答:刷的太频繁了,可能会降低redis的整体性能
答:(1)还是得合理配置持久化的时间点,尽量做到数据一致性和性能的均衡(2)如果丢失了大量的数据,看一下从节点的日志是否完好,如果可以就使用从节点的日志,或者接收新leader节点的数据补充丢失的大量数据
答:行,间隙锁,临键锁,表锁
答:myISAM常用表锁,其他类型常被innodb使用
答:
答:
答:
答:查询频率,索引选择性好/区分度高,经常用于排序的字段
答:新增、删除和修改记录时候需要修改索引,会影响执行的性能
答:
我的答案:可能会也可能不会,因为可能会存在页内碎片问题,如果innodb没有对删除数据后的数据页进行相关的合并和整理操作,则即使删除了其中的数据也不会解决问题,也就是说这张表所占用的数据页数不变。这跟jvm的元空间内存不足的问题很像。
gpt4:
如果MySQL的磁盘已满,删除数据是一种方式来释放空间。但仅仅从应用层面删除数据并不意味着磁盘空间会立即得到释放。这取决于MySQL的存储引擎,例如InnoDB。如果使用InnoDB引擎,空间不会立即返回给操作系统,而是被标记为可用并由MySQL重新使用。为了真正释放空间,你可能需要进行一些其他操作,如优化表(OPTIMIZE TABLE
)或者重新导出、导入数据库。
因此,删除一年的记录可能可以解决问题,但并不是所有情况都可以。另外,如果数据增长的速度迅速,你很快可能会再次遇到空间问题。最佳做法是监控磁盘空间并根据需要增加。
答:面试官的提示指向了一个很重要的方面:索引。
在MySQL中,当数据被删除时,相关的索引项通常不会立即被删除或收缩。这意味着,即使你从表中删除了很多数据,由于相关的索引结构没有变小,磁盘空间的实际使用可能不会显著减少。
具体来说,如果一个InnoDB表有很多的索引,这些索引可能占据的空间与数据本身同样多,甚至更多。当你删除表中的数据时,虽然数据页可能被标记为可重新使用,但这并不意味着相关的索引页也是这样。你可能需要重新构建或优化这些索引来真正释放空间。
这里有几种方法可以解决这个问题:
OPTIMIZE TABLE: 这是最直接的方法。此命令将重建表和索引,释放未使用的空间。但是这个操作对于大型表可能非常慢。
DROP INDEX and CREATE INDEX: 如果你知道哪些索引占据了大量空间,你可以选择删除并重新创建它们。
重导表: 导出表的数据,删除表,然后重新导入数据。这在某种程度上与OPTIMIZE TABLE相似,但你会有更多的控制,例如可以选择不重新导入某些不再需要的数据或索引。
不论选择哪种方法,都应当在低峰时间进行,因为这些操作可能会消耗大量的I/O,影响到其他运行中的查询或操作。
所以,面试官的提示是很有道理的。即使删除了大量数据,仍然需要考虑索引占据的空间,特别是在一个索引重的数据库或表中。
当我们谈到覆盖索引时,实际上我们是在描述一个查询使用的索引,而不是描述索引本身的类型。覆盖索引查询是指查询所需的所有数据都包含在索引中,无需访问数据表的实际行。对于InnoDB,非聚簇索引(普通索引)的叶子节点通常存储主键的值,但如果一个查询只需要索引中的列,那么叶子节点就已经包含了所有必要的信息,使得该查询成为一个覆盖索引查询。
为什么索引项不会立即收缩:
在实践中,数据库管理员通常会监视索引的健康状况,并根据需要进行维护,以确保性能处于最佳状态。
对于InnoDB的聚簇索引(即主键索引),叶子节点中直接包含了表的数据行。当你从表中删除一行数据时,对应的索引条目确实会从聚簇索引中删除。但这并不意味着磁盘上对应的空间会立即被释放回操作系统。
具体行为如下:
逻辑删除: 当你执行DELETE操作时,InnoDB首先会逻辑地删除记录,这意味着它会标记记录为已删除,但实际上记录还留在页中。
空间利用: 虽然这些标记为已删除的记录仍然占据物理空间,但当新的记录插入到这个页时,这些空间可以被重新利用。
物理空间释放: 即使一个数据页变得完全空闲(例如,你删除了该页上的所有行),它也不会立即被释放回文件系统或操作系统。相反,它会留在表空间文件中并被标记为可重新使用。只有在特定的维护操作(如表的OPTIMIZE操作)中,这些空页才可能被物理地释放。
这种策略的一个主要原因是性能。频繁地进行磁盘空间的回收和再分配可能会导致大量的磁盘I/O和碎片化,这对性能不利。相反,通过重用已分配的空间,InnoDB可以更有效地处理新的数据插入操作。
答:
(1)走索引
(2)建立分区表
(3)走从库
(4)走mysql或者redis缓存
慢查询:
慢查询是指在数据库中执行时间超过某个阈值的查询。这个阈值是可配置的,例如,如果你设定了一个查询执行时间超过2秒就被认为是慢查询,那么所有执行时间超过2秒的查询都会被记录在慢查询日志中。
慢查询可能的原因:
如何治理慢查询:
开启慢查询日志:首先,确保你的MySQL服务器已经开启了慢查询日志。这会帮助你跟踪那些执行时间超过阈值的查询。
使用性能分析工具:使用如EXPLAIN
命令,来查看查询是如何在数据库中执行的。这可以帮助你识别查询为何会慢,例如它是否正在进行全表扫描或是否使用了合适的索引。
优化查询:
优化表结构:添加或删除索引,根据查询的需求进行调整。有时,使用如DENORMALIZATION
的技术来提高查询性能也是合适的。
调整数据库配置:根据服务器的硬件和负载,调整MySQL的配置参数,例如innodb_buffer_pool_size
,以适应你的工作负载。
硬件升级:增加RAM、使用更快的磁盘、升级到更快的CPU,或考虑使用SSD。
考虑分区:对于特别大的表,使用分区技术可以提高某些类型查询的性能。
使用缓存:考虑在应用层或数据库层使用缓存,例如使用Redis或Memcached,以减少对数据库的请求。
监控和定期审查:持续监控数据库的性能,并定期审查慢查询日志。数据库和应用的工作负载可能会随时间变化,所以持续的监控和优化是必要的。
治理慢查询是一个持续的过程。随着数据量的增长和应用需求的变化,新的慢查询可能会出现,因此需要定期的监控和调整。
答:分区相当于是按照某一个字段给大表进行逻辑划分,本来mysql是为一张表只建立一个B+树聚簇索引的,有了分区后,每一个分区内都有对应的聚簇索引,比如现在一张表每一年会新增100w的量,五年就是500w,现在按照年份进行分区,则新建5个分区,每一个分区内都有一个索引了这一年份的聚簇索引树,当一个sql语句按照日期查询时,首先会判断自己在哪一个分区中,然后拿到对应分区内的B+树索引,再进行查找。这样做的好处是,原本不分区时的查找复杂度是log(x,500w),现在是log(x,100w),如果分区再多一些,则查找的复杂度会进一步降低。
答:因为redis的缓存是可能会过期或者失效,最终慢查询还是可能会走mysql,因此在mysql的上层服务器进行缓存也是有必要的。