1. 双向链表数据结构
双向链表(Doubly Linked List)是一种链表数据结构,它的每个节点除了包含数据域外,还包含两个指针,一个指向前一个节点(前驱节点),另一个指向后一个节点(后继节点)。这种结构使得双向链表在遍历和操作上更加灵活,既可以正向遍历,也可以反向遍历。
以下是双向链表的基本特点和常见操作:
特点
节点结构:每个节点包含三个部分:数据元素、指向前一个节点的指针(prev)和指向后一个节点的指针(next)。
双向遍历:可以从链表的头节点开始正向遍历,也可以从尾节点开始反向遍历。
插入和删除操作:在双向链表中插入和删除节点相对简单,因为可以通过前驱和后继指针直接定位到相关节点。
常见操作
插入节点:
在头部插入:创建新节点,将新节点的 next 指针指向原头节点,原头节点的 prev 指针指向新节点,然后将头指针指向新节点。
在尾部插入:创建新节点,将尾节点的 next 指针指向新节点,新节点的 prev 指针指向尾节点,然后将尾节点更新为新节点。
在指定节点后插入:创建新节点,新节点的 next 指针指向指定节点的 next 节点,新节点的 prev 指针指向指定节点,指定节点的 next 节点的 prev 指针指向新节点,指定节点的 next 指针指向新节点。
删除节点:
删除头节点:将头指针指向原头节点的 next 节点,原头节点的 next 节点的 prev 指针设为 null。
删除尾节点:将尾节点的 prev 节点的 next 指针设为 null,尾节点更新为原尾节点的 prev 节点。
删除指定节点:将指定节点的 prev 节点的 next 指针指向指定节点的 next 节点,指定节点的 next 节点的 prev 指针指向指定节点的 prev 节点。
遍历链表:
正向遍历:从头部开始,通过 next 指针逐个访问节点,直到节点为 null。
反向遍历:从尾部开始,通过 prev 指针逐个访问节点,直到节点为 null。
2. http所有状态码,及解决方法
HTTP 状态码是用于表示 HTTP 请求的结果的三位数字代码,分为 5 个类别,分别以 1 - 5 开头。以下是详细介绍及部分状态码常见的解决方法:
1xx(信息性状态码)
这些状态码表示临时的响应,客户端通常只需等待下一个响应。
100 Continue:
含义:客户端应当继续发送请求。这个临时响应是用来通知客户端它的部分请求已经被服务器接收,且仍未被拒绝。客户端应当继续发送请求的剩余部分,或者如果请求已经完成,忽略这个响应。
解决方法:一般无需客户端做额外处理,继续发送请求剩余部分即可。
2xx(成功状态码)
表示请求已成功被服务器接收、理解、并接受。
200 OK:
含义:请求成功,请求所希望的响应头或数据体将随此响应返回。
解决方法:正常处理响应数据。
201 Created:
含义:请求已经被实现,而且有一个新的资源已经依据请求的需要而建立,且其 URI 已经随 Location 头信息返回。
解决方法:可根据 Location 头信息访问新创建的资源。
204 No Content:
含义:服务器成功处理了请求,但不需要返回任何实体内容。
解决方法:无需处理响应数据,可根据业务逻辑进行后续操作。
3xx(重定向状态码)
表示需要客户端采取进一步的操作才能完成请求。
301 Moved Permanently:
含义:被请求的资源已永久移动到新 URI,客户端应使用新的 URI 访问。
解决方法:更新本地记录的资源 URI,使用新的 URI 重新发起请求。
302 Found:
含义:请求的资源临时从不同的 URI 响应请求。
解决方法:暂时使用响应中 Location 字段指定的 URI 重新发起请求。
304 Not Modified:
含义:表示资源未被修改,可以使用缓存的版本。
解决方法:使用本地缓存的资源。
4xx(客户端错误状态码)
表示客户端可能存在错误,妨碍了服务器的处理。
400 Bad Request:
含义:由于明显的客户端错误(如格式错误、参数缺失等),导致服务器不能或不会处理该请求。
解决方法:检查请求参数、格式等是否正确,修正后重新发起请求。
401 Unauthorized:
含义:请求需要用户验证。通常在响应中会包含一个 WWW - Authenticate 头,用于提示用户如何进行身份验证。
解决方法:提供正确的身份验证信息(如用户名和密码),重新发起请求。
403 Forbidden:
含义:服务器理解请求客户端的请求,但是拒绝执行此请求。可能是因为权限不足等原因。
解决方法:检查用户权限,联系服务器管理员获取相应权限。
404 Not Found:
含义:请求的资源未在服务器上找到。
解决方法:检查请求的 URI 是否正确,确认资源是否存在或已被移动。
405 Method Not Allowed:
含义:请求行中指定的请求方法不能被用于请求相应的资源。
解决方法:检查请求使用的 HTTP 方法(如 GET、POST 等)是否正确,使用正确的方法重新发起请求。
5xx(服务器错误状态码)
表示服务器在处理请求的过程中发生了错误。
500 Internal Server Error:
含义:服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。
解决方法:等待服务器修复问题,或者联系服务器管理员排查错误。
502 Bad Gateway:
含义:作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。
解决方法:检查网络连接,等待上游服务器恢复正常,或者联系服务器管理员处理。
503 Service Unavailable:
含义:由于临时的服务器维护或者过载,服务器当前无法处理请求。
解决方法:等待一段时间后重新发起请求,或者联系服务器管理员了解维护情况。
504 Gateway Timeout:
含义:充当网关或代理的服务器,未及时从上游服务器接收请求。
解决方法:检查网络连接,等待上游服务器响应,或者联系服务器管理员处理。
3. 水平分表后,怎么分页查询
在MySQL分表后实现高效的分页查询,可以依据不同的分表策略和业务需求选择以下方案:
1. 应用层合并与排序(适合分表较少或页码不大的场景)
- 步骤:
- 在每个分表中执行排序查询,限制返回的数据量。
- 将结果合并到应用层,进行全局排序。
- 应用分页逻辑,截取对应页的数据。
- 示例:
(SELECT * FROM table_1 ORDER BY create_time DESC LIMIT 100) UNION ALL (SELECT * FROM table_2 ORDER BY create_time DESC LIMIT 100) ORDER BY create_time DESC LIMIT 990, 10;
- 缺点:页码较大时性能下降明显,需谨慎使用。
2. 游标分页(Cursor-based Pagination)
- 原理:基于上一页最后一条记录的排序字段值,避免使用OFFSET。
- 步骤:
- 首次查询各分表,按排序字段获取前N条数据。
- 合并结果并排序,返回第一页数据,记录最后一条记录的排序字段值(如时间T和ID)。
- 后续查询使用条件
WHERE create_time < T OR (create_time = T AND id < last_id)
,保持高效。
- 优点:性能稳定,适合连续翻页。
- 缺点:不支持随机跳页,需客户端维护游标。
3. 分表策略与查询条件对齐
- 场景:分表键与排序字段相关(如按时间分表)。
- 步骤:
- 根据查询的时间范围确定涉及的分表。
- 仅查询相关分表,减少合并数据量。
- 示例:按月份分表时,查询最近3个月的数据仅需查3个表。
4. 使用数据库中间件
- 工具:ShardingSphere、MyCat等。
- 原理:中间件自动路由分表查询,合并结果后分页。
- 优点:对应用透明,简化开发。
- 缺点:需引入额外组件,大offset时仍有性能压力。
5. 维护全局索引表
- 步骤:
- 创建索引表,记录排序字段和分表位置。
- 分页时先查询索引表,确定目标数据所在分表。
- 根据索引结果到分表中获取完整数据。
- 优点:支持灵活排序。
- 缺点:增加写入开销,索引表可能成为瓶颈。
最佳实践建议:
- 优先游标分页:若业务允许连续翻页,游标分页性能最优。
- 分表设计优化:尽量使分表键与高频查询的排序字段一致,如按时间分表处理时间排序查询。
- 避免大offset:无论何种方案,大偏移量分页都可能导致性能问题,需通过业务设计规避(如限制最大页码)。
- 结合缓存:对热点数据查询结果进行缓存,减少数据库压力。
示例:游标分页实现
假设按用户ID哈希分表,需按注册时间倒序分页:
-- 第一页查询
SELECT * FROM user_0 ORDER BY register_time DESC, id DESC LIMIT 10;
SELECT * FROM user_1 ORDER BY register_time DESC, id DESC LIMIT 10;
SELECT * FROM user_2 ORDER BY register_time DESC, id DESC LIMIT 10;
-- 应用层合并排序后取前10条,记录最后一条的register_time和id为T和X-- 下一页查询
SELECT * FROM user_0
WHERE register_time < T OR (register_time = T AND id < X)
ORDER BY register_time DESC, id DESC LIMIT 10;
-- 同样查询其他分表,合并后取前10条
结论:分表后的分页需根据数据分布、查询模式及业务需求选择合适策略,游标分页和分表策略优化是常用高效方案,中间件可简化实现但需权衡架构复杂度。
4. redis list做消息队列和rabbitmq做消息队列有什么优缺点
Redis List 和 RabbitMQ 都可以用作消息队列,但它们在不同的场景下各有优缺点,以下是它们优缺点的详细对比:
Redis List 作为消息队列的优点
简单易用:Redis 是一个内存数据库,使用简单,对于那些只需要简单消息队列功能的场景,不需要复杂的配置和管理,开发者可以快速上手。例如,在一些小型项目中,使用 Redis List 实现简单的任务队列,直接通过 LPUSH 和 RPOP 命令就可以完成消息的入队和出队操作。
性能高效:基于内存操作,读写速度非常快,能够满足高并发的消息处理需求。在一些对实时性要求较高的场景,如实时日志收集,Redis List 可以迅速地接收和处理大量日志消息。
轻量级:相比 RabbitMQ 等专业消息队列中间件,Redis 占用的系统资源较少,启动和运行成本低。如果应用对消息队列的功能需求不是特别复杂,使用 Redis 可以减少系统开销。
Redis List 作为消息队列的缺点
可靠性较低:Redis 本身不是专门为消息队列设计的,在消息持久化和可靠性方面存在一定的不足。如果 Redis 服务器出现故障,未持久化到磁盘的消息可能会丢失。例如,在 Redis 配置为默认的异步持久化方式时,宕机可能导致部分数据丢失。
功能有限:与专业的消息队列相比,Redis List 缺乏一些高级功能,如消息确认、优先级队列、死信队列等。对于一些复杂的业务场景,可能无法满足需求。
缺乏监控和管理工具:Redis 虽然有一些基本的监控命令,但相比 RabbitMQ 丰富的监控和管理工具,Redis 在消息队列的监控和管理方面相对薄弱,难以对消息队列的运行状态进行全面、细致的监控。
RabbitMQ 作为消息队列的优点
功能丰富:RabbitMQ 是一个专业的消息队列中间件,支持多种消息模式(如发布 / 订阅、工作队列等),提供了消息确认、优先级队列、死信队列等高级功能,能够满足各种复杂的业务场景需求。
可靠性高:RabbitMQ 支持消息持久化,确保消息在服务器重启或故障时不会丢失。同时,它还提供了集群和镜像队列等功能,提高了系统的可用性和数据的安全性。
监控和管理工具完善:RabbitMQ 提供了丰富的监控和管理工具,如 Web 管理界面、命令行工具等,可以方便地对消息队列的运行状态、性能指标等进行监控和管理。
RabbitMQ 作为消息队列的缺点
学习成本高:RabbitMQ 功能复杂,配置和管理相对繁琐,对于初学者来说,需要花费一定的时间和精力来学习和掌握。
性能相对较低:与 Redis 相比,RabbitMQ 由于其功能的复杂性和更多的处理逻辑,在高并发场景下的性能可能不如 Redis。
资源占用大:RabbitMQ 作为一个独立的中间件,需要一定的系统资源来运行和维护,包括内存、CPU 等,对于一些资源有限的环境,可能不太适合。
综上所述,Redis List 适合简单、轻量级、对性能要求高但对消息可靠性和功能要求不高的场景;而 RabbitMQ 则适用于复杂业务场景,对消息可靠性、功能丰富性和监控管理有较高要求的应用。
5。 更新数据先删缓存还是先更新数据库,各有什么优缺点?怎么解决?
延迟双删策略
先清除缓存,然后再写入数据库。有可能存在删除缓存以后,另一个线程读取数据,发现没有数据,就去数据读取数据,然后写入缓存中,此时缓存中的数据为脏数据;解决办法:
先删除缓存
再写入数据库
休眠500ms
删除缓存 其中第三步骤的500ms,是根据业务读取数据平均耗时,这样做的目的是确保读请求可以结束,写请求可以删除读请求造成的脏数据的问题。
6. 什么是 CSRF 攻击?XSS 攻击?如何防范?
CSRF:跨站请求伪造,可以通过通过判断来源和加 Token 的方式来防范。
XSS:跨站脚本攻击,可以通过对内容转义和过滤来防范,还有 CSP
7. 为了防止SQL注入,可以采取以下措施:
- 永远不要信任用户的输入,对用户的输入进行校验,可以通过正则表达式或限制长度等方式。
- 永远不要使用动态拼装SQL,而应使用参数化的SQL或直接使用存储过程进行数据查询存取。
- 永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。
- 不要把机密信息直接存放,应加密或hash掉密码和敏感信息。
- 应用的异常信息应该给出尽可能少的提示,最好使用自定义的错误信息对原始错误信息进行包装。
8. 雪花算法
使用1位作为符号位,确定为0, 表示正
使用41位作为毫秒数
使用10位作为机器的ID : 高5位是数据中心ID, 低5位是机器ID
使用12位作为毫秒内的序列号, 意味着每个节点每秒可以产生4096(212) 个ID
9. 红包发放策略
预拆包
可能问题,某些营销群发没人抢,浪费cpu
太高内存,可能超过内存,导致失效
实时拆包
可能出现超卖
并发资源争抢问题,高逼格话语
【实用算法】 红包分配 — 二倍均值法
二倍均值法
剩余红包金额为M,剩余人数为N,那么有如下公式:每次抢到的金额 = 随机区间 (0, M / N X 2)这个公式,保证了每次随机金额的平均值是相等的,不会因为抢红包的先后顺序而造成不公平。案例
假设有10个人,红包总额30元。30/10X2 = 6, 所以第一个人的随机范围是(0,6 ),平均可以抢到 3元。假设第一个人随机到3元,那么剩余金额是30-3 = 27 元。27/9X2 = 6, 所以第二个人的随机范围同样是(0,6 ),平均可以抢到3元。假设第二个人随机到3元,那么剩余金额是27-3 = 24元。24/8X2 = 6, 所以第三个人的随机范围同样是(0,6 ),平均可以抢到3元。假设第三个人随机到3元,那么剩余金额是24-3 = 21元。21/7X2 = 6, 所以第三个人的随机范围同样是(0,6 ),平均可以抢到3元。假设第四个人随机到3元,那么剩余金额是21-3 = 18元。以此类推,每一次随机范围的均值是相等的。