本文已参与「新人创作礼」活动,一起开启掘金创作之路。
解决超卖的问题
方法一(悲观锁)
数据库设置字段为无符号型(设置无符号类型后,字段不能小于0)比较危险,有发生故障的可能 当并发超卖时直接报异常 通过捕获异常提示已经售空
方案二:缓存方案
由于数据库的性能问题,无法应对高并发的秒杀场景,所以通常的解决方案是利用缓存来完成,先在缓存中完成计数,然后再通过消息队列异步地入库。 redis由于其高速+单进程模型,省掉了很多并发的问题,所以可以被选来进行高速秒杀的工作。
如何采用一些技术手段防止恶意用户抢购商品
对地址加密 或
每一种商品加一个token: 在秒杀前不公布token。前台用户调用秒杀接口,需要传输秒杀基本参数外,还有一个token。如果token有值,秒杀生效,如果没值,不进行处理。
如何防止重复下单
⼀般解决1: 前端页⾯加校验控制,防⽌重复提交表单
⼀般解决2:
幂等 保存前sql先查询下,是否已经存在重复订单。
List,map,set的实现类有哪些
1,Map
(1) HashMap基于 hash 表的 Map 接口实现,非线程安全,高效,支持 null 值和 null 键;无序既插入的顺序的取出的顺序不一致
(2) LinkedHashMap是map结构也是链表结构,有序的既插入顺序和取出一致。
(3) HashTable 线程安全,低效,不支持 null 值和 null 键;
(4)ConcurrentHashMap
线程安全 高效率 动态扩容
2、Set
(1) HashSet底层是由 Hash Map 实现,不允许集合中有重复的值。
(2) LinkedHashSet继承于 HashSet,同时又基于 LinkedHashMap 来进行实现,底层使用的是 LinkedHashMap。LinkedHashSet中的元素顺序是可以保证的,也就是说遍历序和插入序是一致的。
3、List
List 集合中对象按照索引位置排序,可以有重复对象,允许按照对象在集合中的索引位置检索对象,例如通过list.get(i)方法来获取集合中的元素;list本身就是有序的,既插入顺序和取出顺序一致。
(1) ArrayList 查找快,增删慢。因为底层是数组。
(2) LinkedList 基于链表实现,链表内存是散列的,增删快,查找慢;可以用作栈和队列。 (3) Vector 基于数组实现,线程安全,效率低,增删慢,查找慢;
String和StringBuffer StringBuilder的区别
| String | StringBuffer | StringBuilder |
|---|---|---|
| String的值是不可变的,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且浪费大量优先的内存空间 | StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量 | 可变类,速度更快 |
| 不可变 | 可变 | 可变 |
| 线程安全 | 线程不安全 | |
| 多线程操作字符串 | 单线程操作字符串 |
为什么使用mq
异步 削峰 解耦
消息丢失如何保证
ACK机制
简单来说,就是必须关闭 RabbitMQ 的自动 ack,可以通过一个 api 来调用就行,然后每次在自己代码里确保处理完的时候,再在程序里 ack 一把。这样的话,如果你还没处理完,不就没有 ack了?那 RabbitMQ 就认为你还没处理完,这个时候 RabbitMQ 会把这个消费分配给别的 consumer 去处理,消息是不会丢的。
持久化
开启RabbitMQ的持久化。当生产者把消息成功写入RabbitMQ之后,RabbitMQ就把消息持久化到磁盘。结合上面的说到的confirm机制,只有当消息成功持久化磁盘之后,才会回调生产者的接口返回ack消息,否则都算失败,生产者会重新发送。存入磁盘的消息不会丢失,就算RabbitMQ挂掉了,重启之后,他会读取磁盘中的消息,不会导致消息的丢失。
redis在项目中的实际用途
各种订单缓存
防止缓存穿透和雪崩
接口限流(redis实现限流器)
分布式锁
redis和DB一致性
redis的雪崩穿透如何解决
雪崩(设置不同过期时间 搭建集群)
穿透(设一个null)
redis的脑裂问题
redis集群由于网络的原因可能会出现脑裂的问题,脑裂就是因为主服务器、从服务器和哨兵不在同一个网络中,导致哨兵没有及时的检测到主服务的心跳,在这个时候会在从服务器中去选举一个新的主服务器,这样就有两个主服务器了就像大脑分裂一样,但是这样会导致客户依旧 在旧的主服务器中去写东西,而新的主服务器中没有东西。当网络恢复后,哨兵会把旧的主服务器变成从服务器,这个 时候在去同步数据,可能会造成一个数据的丢失
解决方法:
redis中需要加入两个配置
(旧版本)
min-slaves-to-write 3 最少有3个从服务器
min-slaves-max-lag 10 数据复制和同步的延迟不超过10秒
(新版本)
min-replicas-to-write 3
min-replicas-max-lag 10
如果加了这两个配置的话,原来的主服务器当客户端再次进行写操作的时候会拒绝接受,此时就发送到新的主服务器中去了
springCloud组件
Eureka 注册中心
ribbon:负载均衡策略
hystrix:熔断器
zuul:网关
config:配置中心
feign:服务调用
Eureka如何理解,和zoolKeeper区别
Eureka和ZooKeeper区别
- eureka是基于AP设计的,zookeeper是基于CP设计的
c:一致性 a:可用性 p:分区容错性
-
ZooKeeper有Leader和Follower角色,Eureka各个节点平等
-
eureka可以很好的解决出现故障导致的部分结点失去联系,zookeeper如果出现故障整个服务器都会瘫痪
-
ZooKeeper只是一个进程,Eureka本质上是一个工程
-
zookeeper采用半数存活原则,eureka采用自我保护机制解决分区问题
Eureka保证AP
eureka保证了可用性,eureka各个节点都是平等的,几个结点挂掉是不会影响正常工作的,如果eureka的客户端注册失败的话,会自动切换其他节点,只要还有一台eureka还在的话就能保证可用性,eureka还有 自我保护机制,如果超过了15分钟有85%的结点都没有心跳了,那么就认为客户端出现了网络故障
Zookeeper保证CP
zookeeper保证了一致性,但是我们在注册信息的时候是没有办法容忍服务器宕机的。也就是说服务的可用性要高于一致性。zookeeper会出现一种情况当主节点因为网络原因与其他的结点失去联系的时候,剩余的结点会进行一个leader选举,但是选举的过程中是时间很长的达到30—120秒,在选举的过程中zookeeper是没有办法使用的,在云部署的情况下zookeeper主结点丢失这种情况的概率是很大的,虽然可以恢复但是时间长。
Eureka的底层原理
eureka主要是通过心跳检测去判断的,有一个发送者和客户端,发送者会每隔30秒发送一个心跳到eureka上去,服务端会把eureka上的客户端发送的数据进行一个接受并调用的过程,如果说生产者没有发送心跳到注册中心上,那么就直接剔除掉,将所有的接口都剔除掉,如果说中途eureka发生了宕机,那么也是可以进行一个调用的,因为将原来的数据放到了一个缓存中 去,并且eureka还有自我保护机制,如果说在15分钟内检测到有85%的服务都宕机了那么这个时候就会认为是一个网络的问题导致的
myatis的#和$有什么区别
- #{ }可以防止Sql 注入,它会将所有传入的参数作为一个字符串来处理。
- 所对应的参数应该由服务器端提供,前端可以用参数进行选择,避免 Sql 注入的风险
mybatis的一级缓存和二级缓存
一级缓存
一级缓存作用域是sqlsession级别的,同一个sqlsession中执行相同的sql查询(相同的sql和参数),第一次会去查询数据库并写到缓存中,第二次从一级缓存中取。
一级缓存是基于 PerpetualCache 的 HashMap 本地缓存,默认打开一级缓存。
二级缓存
它指的是Mybatis中SqlSessionFactory对象的缓存。由同一个SqlSessionFactory对象创建的SqlSession共享其缓存。
二级缓存是 mapper 映射级别的缓存,多个 SqlSession 去操作同一个 Mapper 映射的 sql 语句,多个SqlSession 可以共用二级缓存,二级缓存是跨 SqlSession 的。
mybatis的批量操作如何实现---batch操作
通过反射机制 放入缓冲区进行执行sql
mybatis插入后的主键id的返回结果,如何实现---insert标签属性,usergeneratekey,keyproperties映射到实体id
通过select LAST_INSERT_ID()来获得会话内的insert后Id,并且只支持自增主键