平台网站建设外包,电子商务型网站建设,平面广告设计课程总结,织梦更新网站地图1.在项目中遇到难点是如何处理 数据库性能问题#xff1a;在数据库处理数据操作#xff08;如查询、插入、更新、删除等#xff09;时表现出的响应速度慢、资源利用率高、吞吐量低等不良情况#xff0c;影响了系统的整体性能和用户体验 解决方案有以下这些#xff1a; 优化…1.在项目中遇到难点是如何处理 数据库性能问题在数据库处理数据操作如查询、插入、更新、删除等时表现出的响应速度慢、资源利用率高、吞吐量低等不良情况影响了系统的整体性能和用户体验 解决方案有以下这些 优化查询语句避免使用不必要的子查询和复杂的函数确保查询条件使用了合适的索引。建立和优化索引根据经常用于查询、连接和排序的字段创建索引。调整数据库配置合理配置内存缓冲区、连接数、线程池等参数。分库分表当数据量过大时将表按照一定规则进行水平或垂直分表或者进行数据库的垂直分割和水平分割功能。解决锁竞争尽量缩短事务的执行时间减少锁的持有时间采用合适的锁级别如行锁而不是表锁。 监控和分析使用数据库自带的性能监控工具或第三方工具定期分析性能指标发现问题及时解决。 2.项目的并发量多大如何实现限流控制
如果不考虑高并发的情况即使业务系统平时运行得好好的并发量一旦增加就会频繁出现各种诡异的业务问题比如在电商业务中可能会出现用户订单丢失、库存扣减异常、超卖等问题。
限流是服务降级的一种手段顾名思义通过限制系统的流量从而实现保护系统的目的。
合理的限流配置需要了解系统的吞吐量所以限流一般需要结合 容量规划 和 压测 来进行。
当外部请求接近或者达到系统的最大阈值时触发限流采取其他的手段进行降级保护系统不被压垮。常见的降级策略包括 延迟处理 、 拒绝服务 、 随机拒绝 等。
限流后的策略其实和 Java 并发编程中的线程池非常类似我们都知道线程池在任务满的情况下可以配置不同的拒绝策略比如
AbortPolicy会丢弃任务并抛出异常 DiscardPolicy丢弃任务不抛出异常 DiscardOldestPolicy 等当然也可以自己实现拒绝策略 Java 的线程池是开发中一个小的功能点但是见微知著也可以引申到系统的设计和架构上将知识进行合理地迁移复用。
限流方案中有一点非常关键那就是 如何判断当前的流量已经达到我们设置的最大值 具体有不同的实现策略下面进行简单分析。
1. 计数器法
一般来说我们进行限流时使用的是单位时间内的请求数也就是平常说的 QPS统计 QPS 最直接的想法就是实现一个计数器。
计数器法是限流算法里最简单的一种算法我们假设一个接口限制 100 秒内的访问次数不能超过 10000 次维护一个计数器每次有新的请求过来计数器加 1。
这时候判断如果计数器的值小于限流值并且与上一次请求的时间间隔还在 100 秒内允许请求通过否则拒绝请求 如果超出了时间间隔要将计数器清零 下面的代码里使用 AtomicInteger 作为计数器可以作为参考
public class CounterLimiter { //初始时间 private static long startTime System.currentTimeMillis(); //初始计数值 private static final AtomicInteger ZERO new AtomicInteger(0); //时间窗口限制 private static final int interval 10000; //限制通过请求 private static int limit 100; //请求计数 private AtomicInteger requestCount ZERO; //获取限流 public boolean tryAcquire() { long now System.currentTimeMillis(); //在时间窗口内 if (now startTime interval) { //判断是否超过最大请求 if (requestCount.get() limit) { requestCount.incrementAndGet(); return true; } return false; } else { //超时重置 requestCount ZERO; startTime now; return true; } }
} 计数器策略进行限流可以从单点扩展到集群适合应用在分布式环境中。
单点限流使用内存即可如果扩展到集群限流可以用一个单独的存储节点比如 Redis 或者 Memcached 来进行存储在固定的时间间隔内设置过期时间就可以统计集群流量进行整体限流。
计数器策略有一个很大的缺点 对临界流量不友好限流不够平滑 。
假设这样一个场景我们限制用户一分钟下单不超过 10 万次现在在两个时间窗口的交汇点前后一秒钟内分别发送 10 万次请求。也就是说窗口切换的这两秒钟内系统接收了 20 万下单请求这个峰值可能会超过系统阈值影响服务稳定性。
对计数器算法的优化就是避免出现两倍窗口限制的请求可以使用滑动窗口算法实现感兴趣的同学可以去了解一下。
2. 漏桶和令牌桶算法
漏桶算法和令牌桶算法在实际应用中更加广泛也经常被拿来对比。
漏桶算法可以用漏桶来对比假设现在有一个固定容量的桶底部钻一个小孔可以漏水我们通过控制漏水的速度来控制请求的处理实现限流功能。
漏桶算法的拒绝策略很简单如果外部请求超出当前阈值则会在水桶里积蓄一直到溢出系统并不关心溢出的流量。
漏桶算法是从出口处限制请求速率并不存在上面计数器法的临界问题请求曲线始终是平滑的。
它的一个核心问题是 对请求的过滤太精准了 我们常说“水至清则无鱼”其实在限流里也是一样的我们限制每秒下单 10 万次那 10 万零 1 次请求呢是不是必须拒绝掉呢
大部分业务场景下这个答案是否定的虽然限流了但还是希望系统允许一定的突发流量这时候就需要令牌桶算法。
在令牌桶算法中假设我们有一个大小恒定的桶这个桶的容量和设定的阈值有关桶里放着很多令牌通过一个固定的速率往里边放入令牌如果桶满了就把令牌丢掉最后桶中可以保存的最大令牌数永远不会超过桶的大小。当有请求进入时就尝试从桶里取走一个令牌如果桶里是空的那么这个请求就会被拒绝。
不知道你有没有使用过 Google 的 Guava 开源工具包在 Guava 中有限流策略的工具类 RateLimiterRateLimiter 基于令牌桶算法实现流量限制使用非常方便。
RateLimiter 会按照一定的频率往桶里扔令牌线程拿到令牌才能执行RateLimter 的 API 可以直接应用主要方法是 acquire 和 tryAcquire 。
acquire 会阻塞 tryAcquire 方法则是非阻塞的。
下面是一个简单的示例
public class LimiterTest { public static void main(String[] args) throws InterruptedException { //允许10个permitsPerSecond RateLimiter limiter RateLimiter.create(100); for(int i1;i200;i){ if (limiter.tryAcquire(1)){ System.out.println(第i次请求成功); }else{ System.out.println(第i次请求拒绝); } } }
} 不同限流算法的比较
计数器算法实现比较简单特别适合集群情况下使用但是要考虑临界情况可以应用滑动窗口策略进行优化当然也是要看具体的限流场景。
漏桶算法和令牌桶算法漏桶算法提供了比较严格的限流令牌桶算法在限流之外允许一定程度的突发流量。在实际开发中我们并不需要这么精准地对流量进行控制所以令牌桶算法的应用更多一些。
如果我们设置的流量峰值是 permitsPerSecondN 也就是每秒钟的请求量计数器算法会出现 2N 的流量漏桶算法会始终限制 N 的流量而令牌桶算法允许大于 N但不会达到 2N 这么高的峰值。 3.mysql乐观锁与悲观锁的区别 悲观锁与乐观锁的区别 1. 悲观锁是数据库的层次去加锁的 2. 乐观锁就是表字段里面加上版本号或者使用字段条件 3. 悲观锁效率比效率低最好还是用一些缓存数据库去解决这个问题。但是涉及到钱的我觉得还是悲观锁好。乐观锁的效率要比悲观锁高。 悲观锁使用介绍 假定MySQL有客户端A与客户端B同时开启事务 在A里面进行查询数据SELECT * FROM tb WHERE id 1 for update; 由于B端还没有提交事务A端则会在执行这条SQL语句时锁住卡住不动B端结束了事务A端才会继续执行SQL。 乐观锁使用介绍 假定我的表里面有个库存字段stock 值是100、版本号字段version 值是0。当前记录id等于1的值是初始化的值。 那么我在更新他的时候可以这样写UPDATE tb SET stock stock - 1, version version 1 WHERE id 1 AND stock 100 AND version 0; 4.redis如何保证原始性
原子性是事务处理中的基本属性它要求事务中的所有操作要么全部成功要么全部失败。对于 Redis 这样的数据库来说保证原子性非常重要因为它可以防止数据损坏和不一致。
Redis 使用以下方法保证原子性
1. 原子性操作
Redis 实现了多种原子性操作例如 SET、GET、INCR、DECR 等。这些操作一次只修改单个键的值并且是不可分割的。这意味着要么整个操作成功要么整个操作失败不会出现部分成功的情况。
2. 多重执行
Redis 支持 MULTI 和 EXEC 命令可以将多个原子性操作组合成一个单一的原子性事务。在事务中执行的所有操作要么全部成功要么全部失败确保原子性。
3. 监控锁
Redis 提供了 WATCH 命令允许客户端监控一个或多个键。如果在客户端执行原子性操作之前被监控的键被其他客户端修改则该操作将失败。这可以防止多个客户端同时修改同一个键导致数据不一致。
4. 事务日志
Redis 持久化模块默认情况下会记录所有写入操作的命令日志。如果发生故障Redis 可以使用命令日志来恢复写入操作确保数据的一致性和完整性。
5. 哨兵和主从复制
哨兵Sentinel和主从复制机制可以复制 Redis 实例确保在主实例出现故障时数据可以从其他实例恢复。这可以防止数据丢失并保证在故障期间数据的原子性。 5.sql怎么优化 1.在表中建立索引优先考虑where.group by使用到的字段。 2.查询条件中一定不要使用select *因为会返回过多无用的字段会降低查询效率。应该使用具体的字段代替*只返回使用到的字段。 3.不要在where条件中使用左右两边都是%的like模糊查询如 SELECT * FROM t_order WHERE customer LIKE %zhang% 这样会导致数据库引擎放弃索引进行全表扫描。 优化尽量在字段后面使用模糊查询。如下 SELECT * FROM t_order WHERE customer LIKE zhang% 4.尽量不要使用in 和not in会造成全表扫描。如下 SELECT * FROM t_order WHERE id IN (2,3) SELECT * FROM t_order1 WHERE customer IN (SELECT customer FROM t_order2) 优化 对于连续的数值能用 between 就不要用 in 如下SELECT * FROM t_order WHERE id BETWEEN 2 AND 3 对于子查询可以用exists代替。如下SELECT * FROM t_order1 WHERE EXISTS (SELECT * FROM t_order2 WHERE t1.customer t2.customer) 5.尽量不要使用or会造成全表扫描。如下 SELECT * FROM t_order WHERE id 1 OR id 3 优化可以用union代替or。如下 SELECT * FROM t_order WHERE id 1 UNION SELECT * FROM t_order WHERE id 3 6.尽量不要在 where 子句中对字段进行表达式操作这样也会造成全表扫描。如 select id FROM t_order where num/2100 应改为: select id FROM t_order where num100*2 7.where条件里尽量不要进行null值的判断null的判断也会造成全表扫描。如下 SELECT * FROM t_order WHERE score IS NULL 优化 给字段添加默认值对默认值进行判断。如 SELECT * FROM t_order WHERE score 0 8.尽量不要在where条件中等号的左侧进行表达式.函数操作会导致全表扫描。如下 SELECT * FROM t_order2 WHERE score/10 10 SELECT * FROM t_order2 WHERE SUBSTR(customer,1,5) zhang 优化 将表达式.函数操作移动到等号右侧。如下 SELECT * FROM t_order2 WHERE score 10*10 SELECT * FROM t_order2 WHERE customer LIKE zhang% 9.尽量不要使用where 11的条件 有时候在开发过程中为了方便拼装查询条件我们会加上该条件这样会造成进行全表扫描。如下 SELECT * FROM t_order WHERE 11 优化 如果用代码拼装sql则由代码进行判断没where加where有where加and 如果用mybatis请用mybatis的where语法。 10.程序要尽量避免大事务操作提高系统并发能力。 11.一个表的索引数最好不要超过6个如果索引太多的话就需要考虑一下那些不常使用到的列上建的索引是否有必要。 6. 场景设计现在有5W条数据导入elx表络请您设计下接口保证效率。 使用更快的 Excel 读取框架(推荐使用阿里 EasyExcel)对于需要与数据库交互的校验、按照业务逻辑适当的使用缓存。用空间换时间将参加校验的数据全部缓存到 HashMap 中。直接到 HashMap 去命中。使用 values(),(),() 拼接长 SQL 一次插入多行数据使用多线程插入数据利用掉网络IO等待时间(推荐使用并行流简单易用)避免在循环中打印无用的日志 7.如何保证代码质量 1、采用编码标准 2、编写自动化测试 3、使用版本控制 4、定期重构代码 5、使用代码审查 6、使用静态代码分析工具sonarqube 7、与其他开发者合作 8.生产内存若出现问题您是如何解决
1.生产环境发生 cpu 飙高的问题 1. 查看当前的操作系统中(top) 那个进程 cpu 使用率是最高的 2. 找到该操作系统中 最高使用率 进程 分析该进程里面具体线程 谁 cpu 使用率是最高的 3. 在根据线程名称 搜索“java 代码” 找到具体发生 cpu 飙高的代码 2.生产环境发生内存泄漏问题 查找到 java 虚拟机 哪些对象占用空间最大 前 20 个 列出分析 3.生产环境遇到了报错 1. 传统的方式 在生产环境中遇到报错问题我们是通过搜索日志的方式排查具体的错误。适合于服务器端 是单机或者少量集群的节点 Tail -200f 2.采用 aop 形式拦截系统错误日志在将这些错误日志调用微信公众号接口 主动告诉给我们的开发人员生产环境发生了故障。 4.生产环境服务器宕机 1. 我们公司生产环境会对我们服务器 实现多个节点集群如果某台服务器发生了宕机 会自动实现故障转移保证服务的高可用。 2. 如果服务器宕机 我们可以在服务器上安装 keepalived 监听 java 进程如果该java 进程发生了宕机 会自动尝试重启该 java 进程这是属于软件层面。如果是物理机器比如关机了可以使用硬件方式自动重启服务器 例如向日葵 3.如果服务器发生了宕机尝试重启 n 多次还是失败我们可以使用容器快速动态的实现扩容docker 或者 k8sk8s 4.重启该服务如果重启多次还是失败 则会发送短信模板的形式通知给运维人员。 5.你在开发过程中遇到哪些难题你是怎么解决的呢
例如 我们公司提供了一个接口被其他公司进行调用。他的公司在调用我们公司接口的过程中我们的接口响应超时了最终触发了客户端重试了重试的过程当中请求的参数都是相同的导致我们接口会重复执行业务逻辑。 解决办法 全局 id 业务上防重复、 在 db 层面去重复 例如 创建唯一约束 9.常用的linux命令哪些 1.ls通过 ls 命令不仅可以查看 linux 文件夹包含的文件而且可以查看文件权限(包括目录、文件夹、文件权限)、查看目录信息等等。 2.cd切换目录 3.pwd。该命令用于查看当前工作目录的路径 4.mkdir该指令用于创建目录 5.rm指删除一个目录中的一个或多个文件或目录 6.rmdir是remove directory的缩写指删除空目录 7.mv指移动文件、目录移动时可修改文件或目录名 8.cp复制将多文件或目录复制至目标目录 9.cat文本输出命令 10.more阅读命令与 cat 类似 more 会以一页一页的显示方便逐页阅读 11.less 可以随意浏览文件less 在查看之前不会加载整个文件 12.tail常用来查看日志文件 13.head 用来显示档案的开头至标准输出中 14.tar 本身不具有压缩功能只具有打包功能 15.grep文本搜索命令 16.ps用来查看当前运行的进程状态 17.top显示当前系统正在执行的进程的 ID、内存占用率、CPU 占用率等相关信息 18.kill删除执行中的程序或者工作发送指定的信号到相应的进程 19.free显示系统内存使用情况 20.df显示磁盘空间使用情况 10.MQ常见问题
1.如何保证消息不被重复消费如何保证消息消费时的幂等性 生成者不重复发送消息到MQ mq内部可以为每条消息生成一个全局唯一、与业务无关的消息id当mq接收到消息时会先根据该id判断消息是否重复发送mq再决定是否接收该消息。 消费者不重复消费 消费者怎么保证不重复消费的关键在于消费者端做控制因为MQ不能保证不重复发送消息所以应该在消费者端控制即使MQ重复发送了消息消费者拿到了消息之后要判断是否已经消费过如果已经消费直接丢弃。所以根据实际业务情况有下面几种方式 如果从MQ拿到数据是要存到数据库那么可以根据数据创建唯一约束这样的话同样的数据从MQ发送过来之后当插入数据库的时候会报违反唯一约束不会插入成功的。或者可以先查一次是否在数据库中已经保存了如果能查到那就直接丢弃就好了。让生产者发送消息时每条消息加一个全局的唯一id然后消费时将该id保存到redis里面。消费时先去redis里面查一下有么有没有再消费。其实原理跟第一点差不多。如果拿到的数据是直接放到redis的set中的话那就不用考虑了因为set集合就是自动有去重的。 2.如何保证消息的可靠性传输如何处理消息丢失的问题
主要分为三种
生产者丢失MQ自己丢失了消费的时候丢了
生产者弄丢了消息
写消息的过程中消息都没到Rabbit MQ在网络传输过程中就丢了或者是消息到了Rabbit MQ但是MQ内部错乱没有存下来导致消息丢失。 方案1:可以使用Rabbit MQ事务机制如下
channel.txSelect();
try{// 发送消息
}catch{channel.txRollback();
}
channel.txCommit();
弊端是事务机制同步阻塞的会导致生产者发送消息的吞吐量大大下降。 方案2:把channel设置成comfirm模式发送一个消息就不用管了Rabbit MQ如果接收到了这个消息就会回调生产者本地的一个接口通知你说这条消息已经发送成功并且接收成功。反之也会通知。
该方式的吞吐量会高一些 Rabbit MQ弄丢了消息
将Rabbit MQ设置成持久化的。除非有极其罕见的情况Rabbit MQ还没来得及持久化自己就挂了可能回导致少量的数据丢失。这种概率很小。设置持久化的步骤(必须两个同时设置)
创建queue的时候将其设置为持久化的这样保证Rabbit MQ持久化queue的元数据但是不会持久化queue里的数据发送消息的时候将消息的deliveryMode设置为2.就是将消息设置为持久化。此时Rabbit MQ就会将消息持久化到磁盘上去。
消费者弄丢了消息
只有当你打开了消费者的autoAck的这样一个机制你消费到了数据之后消费者会自动通知Rabbit MQ说我已经消费到了这条数据这样会出现一种情况就是假设消费到了一条数据还没处理完此时消费者就自动autoAck了此时恰巧消费者系统服务挂掉了消息还没处理完而且Rabbit MQ以为该消息已经处理掉了。
方案关闭掉Rabbit MQ的自动ACK机制。
3. 如何保证消息的顺序性
假设做一个MySQL binlog同步系统你在MySQL里增删改一个条数据对应出来增删改3条binlog接着将这三条binlog发送到MQ里面到消费出来一次执行需要保证消息的顺序性。不然数据就会出现问题。
不同MQ错乱的场景
Rabbit MQ
Rabbit MQ一个queue多个consumer这就会出现问题因为多个消费者是同步一起执行的无法保证顺序并且也无法保证消费者消费到了哪条数据。
解决方案
解决方案每个消费者建立对应的queue并且让保持顺序的消息只发送到一个queue上这样消费者消费数据处理的时候就不会出现顺序错乱。
如何保证数据写入一个partition中去
生产者在写入数据的时候可以指定一个key比如指定某个订单id作为key这个订单相关的数据就会被分发到一个partition中去。Kafka有一个原则是一个partition只能被一个消费者消费消费者从partition中取出来数据的时候一定是有顺序的。
4.如何解决消息延时过期失效的问题
Rabbit MQ有一个TTL过期时间。关掉不要开启TTL
5.如何解决消息积压的问题
Rabbit MQ消息积压
解决思路
临时紧急扩容。具体操作如下
由于消费者故障的解决方案 如果consumer有问题先修复consumer的问题确保其恢复消费速度。然后将现有consumer都停掉。临时建立好原先10倍或者20被queue的数量。Kafka-新建一个topicpartition是原来的10倍。写一个临时分发数据的consumer程序这个程序部署上去消费积压的数据消费之后不做耗时处理直接均匀轮询写入已经建立好10倍/20倍数量的queue。接着临时征用10倍的机器来部署consumer每一批consumer来消费一个临时queue的数据。等快速消费完积压的数据之后得恢复原来部署架构重新用原来的consumer机器来消费