美文网首页Java必会点
各种研发问题集合-持续更新中

各种研发问题集合-持续更新中

作者: Alan1914 | 来源:发表于2021-08-20 08:52 被阅读0次

    一、Java

    Q: 关于CopyOnWriteArrayList和ConcurrentHasMap,以及ThreadLocal在实际开发中都适用于那些场景,会产生什么问题吗

    • CopyOnWriteArrayList
      介绍:支持高效率并发且是线程安全的,读操作无锁的ArrayList,所有可变操作都是通过对底层数组进行一次新的复制来实现
      原理:当容器需要被修改的时候,不直接修改当前容器,而是先将当前容器进行 Copy,复制出一个新的容器,然后修改新的容器,完成修改之后,再将原容器的引用指向新的容器。这样就完成了整个修改过程
      适用场景:CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,因此使用时要满足数据量不是非常大,不经常增删改数据这两个条件,比如用做缓存,它不存在扩容的概念。
      弊端:CopyOnWriteArrayList每次写操作都要复制一个副本,在副本的基础上改变Array引用中,因此写操作需要大面积复制数组,所以性能肯定很差,这个代价实在太高昂了,不适用在高性能的互联网应用

    • ConcurrentHashMap
      介绍:ConcurrentHashMap时支持并发的线程安全的HashMap
      原理:Java 7 采用 Segment 分段锁来保证安全,而 Segment 是继承自 ReentrantLock;Java 8 中放弃了 Segment 的设计,采用 Node + CAS + synchronized 保证线程安全。
      适用场景:只要涉及多线程,我们要优先考虑线程安全,因此优先考虑使用ConcurrentHashMap,尤其是To C的项目,比如电商的秒杀,节日大促等场景

    • ThreadLocal
      介绍:Threadlocal而是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据,
      原理:Thread 内有自己的实例副本,且该副本只能由当前 Thread 访问到并使用,相当于每个线程内部的本地变量,这也是 ThreadLocal 命名的含义。因为每个线程独享副本,而不是公用的,变量的数据对别的线程而言是相对隔离的
      适用场景:ThreadLocal 的作用主要是做数据隔离;如果是想区分线程间,数据互不干扰,不共享数据,可以采用ThreadLocal,如果想共享数据,可以使用Redis;也可以结合适用,比如登录场景,将用户token存在redis中,而用户信息存在ThreadLocal,这样的话,每次请求利用Token从ThreadLocal获取用户信息,可以服务整个线程的所有调用方法,其它线程无法访问到其他线程的数据,保证请求全局可用和数据安全隔离,同时减少了Redis中数据的存储量,因为如果不用ThreadLocal则 Redis 中将会存储大量用户信息,对于海量用户的服务来说将是一个很大的开销
      弊端:ThreadLocal如果没有正确释放引用,可能会导致内存泄漏问题,如果对象没有用,但一直不能被回收,这样的垃圾对象如果积累的越来越多,则会导致我们可用的内存越来越少,最后发生内存不够用的 OOM 错误

    Q: 自定义注解里现实的逻辑很多;例如,用到了缓存和一些定时任务;如果在引用注解的过程中,就必须要加缓存的配置了。那在自定义注解的时候,怎么可以把缓存和定时任务做成开关的形式或者怎么优化自定义注解可以避免这类问题?

    场景一:服务启动时,才进行开关的操作
    那可以通过修改配置文件就能实现,拦截器读取配置文件中参数。

    场景二:项目启动中,做这个开关
    apollo支持在项目启动中改变配置参数,可以做实时读,也可以将一份配置刷到对应服务器上,再进行读取。
    最后我提供一个做定时器的思路哈,大厂很多封装的定时任务平台,是基于 “elasticjob”做的,可做分布式定时器,并且提供开关、提供日志等功能。

    二、MySQL

    Q: 分表后怎么分页查?

    数据量小场景
    通过不同的分库规则,实现多个分片,然后每个分片分别进行分页查询,再在内存中进行汇总,最后再进行一次分页查询,就可以得出最终的结果。
    分页的总条数,是另一次查询,不在上面的步骤中计算

    数据量大场景
    引入es,单独的搜索服务。

    Q:MySQL索引设计一般遵循什么准则;怎么设计可以兼顾增删改的速度

    0、加前缀,注明是索引、还是唯一索引 等
    注:不强制,但dba会用这个前缀,做一些特殊查询,类似 like 语句查询索引。
    1、能少就少
    2、能短就短
    3、能合并就合并,看业务场景
    4、索引设计,可以只考虑查询的场景,增删改的场景可忽略不计
    刚刚说的只建一个唯一索引,是因为绝大多数场景,会使用主键,其他字段建立唯一索引,用到场景较少

    Q:MySQL垂直拆分和水平拆分适用于那些场景?一般达到什么数据量可以考虑水平拆分?拆分后采用数据库中间件时SQL语法不兼容如何处理

    第一点,如果超过30列,就需要纵向拆分了;第二点,先考虑SQL优化,再考虑水平分表。

    分库分表选型

    mycat是一个中间件的第三方应用,sharding-jdbc是一个jar包
    Mycat 是基于 Proxy,它复写了 MySQL 协议,将 Mycat Server 伪装成一个 MySQL 数据库,而 Sharding-JDBC 是基于 JDBC 的扩展,是以 jar 包的形式提供轻量级服务的。

    image.png image.png

    mycat & sharding-JDBC 区别

    mycat

    • 优点:
      1、开发无感知
      2、增删节点程序不需要重启
      3、跨语言(java 、php)
      4、中间件的第三方应用
    • 缺点:
      1、性能下降因为多了一层
      2、不支持跨数据库
      3、运维成本大

    shardingJDBC

    • 优点:
      1、性能很好的
      2、支持跨数据库jdbc
      3、sharding-jdbc是一个jar包
    • 缺点:
      1、增加了开发难度
      2、不支持跨语言(java)

    mycat的事务支持是弱XA的,事务内的SQL在各自分片上执行并且返回状态码,如果某个分片上的返回码为error,mycat就认为本次事务失败了,此时将会一次回滚事务所涉及到的所有分片。反之,如果所有的分片都返回成功的返回码,则当AP(应用程序)提交事务的时候,mycat会同时向事务涉及的所有分片发送提交事务的命令。
    之所以说是弱XA,是因为在二阶段提交的工程中,若commit时某个节点出错了,只能回滚,而不会等其恢复后再次提交。

    Mycat 从 1.6.5 版本开始支持标准 XA 分布式事务,考虑到 MySQL 5.7 之前版本XA有bug,所以推荐最佳搭配 XA 功能使用 MySQL 5.7 版本

    结合 HA 的Mycat业务


    image.png

    Q:mysql日志可用于数据恢复,假如操作mysql误删数据了,如何找回恢复

    基于binlog进行恢复
    binlog它记录了所有的 DDL 和 DML 语句(除了数据查询语句select、show等),以事件形式记录,binlog 的主要目的是复制和恢复。
    步骤:
    1.将原来实例全备份到新实例
    2.查找对应的起始位点,在新实例恢复数据
    3.将新实例的binlog备份到原来的数据库
    show binlog events查看对应的语句,找到对应的位点

    mysql> show binlog events in 'mysql-bin.000007' from 1190 limit 2\G
            *************************** 13. row ***************************
               Log_name: mysql-bin.000007
                    Pos: 1190
             Event_type: Query  //事件类型
              Server_id: 123
            End_log_pos: 1352   //结束pose点,下个事件的起点
                   Info: use `test`; insert into tb_person  set name="name__2", address="beijing", sex="man", other="nothing"
    
    mysqlbinlog --start-position=1190 --stop-position=1352
    

    Q:如果需要把A环境的数据全部复制一份到B环境,数据迁移有些什么方案,通常怎么做

    方案一:Mysql dump
    1 将本地的数据库(dbname)导出为sql文件
    mysqldump -uroot -p dbname> dbname.sql
    2 在服务器的mysql中新建同名数据库(dbname),然后退出mysql
    create database fangyuanxiaozhan charset=utf8;
    Exit
    3 将文件导入
    mysql -uroot -p dbname< dbname.sql
    优点:
    mysqldump的优点就是逻辑备份,把数据生成SQL形式保存,在单库,单表数据迁移,备份恢复等场景方便,SQL形式的备份文件通用,也方便在不同数据库之间移植。
    缺点:
    mysqldump是单线程,数据量大的时候,备份时间长,甚至有可能在备份过程中非事务表长期锁表对业务造成影响(SQL形式的备份恢复时间也比较长)。

    方案二:基于binlog的主从复制
    步骤:
    1.master配置
    开启binlog
    创建同步账号,赋予权限
    查看主服务器状态,并记录下状态 show master status;
    2.slave配置
    设置readonly
    配置master,并开启同步change master to master_host...
    优点:
    基于binlog,对数据库无压力
    方案三:考虑引入中间件例如canal
    带业务逻辑的增量数据处理
    跨数据库的数据备份(异构数据同步),
    例如mysql => oracle,mysql=>mongo,mysql =>redis,mysql => elasticsearch等;
    优点:
    canal 基于binlog,对数据库无压力,准实时,延时性低,可接入业务服务清洗数据,场景灵活。
    缺点:
    额外引入中间件,学习成本

    Q:redolog满了,会如何处理?

    A:
    1、满了之后,会暂停新事物的提交,并引发报错
    2、当留出空间后,才能继续执行

    1、通常redo log 的大小是4GB以上
    redo 是有大小限制的,不是磁盘的容量,环形结构的一块空间。
    2、redo log 在高版本后,可支持到更大,可通过修改参数,进行设定,几百G的空间也可以的。

    Q:redo log 会不会自己分割?

    redo log 不会,binlog 会

    Q:索引建多了,会有什么不好?

    A:
    增删改会创建、维护索引树,如果索引比较多,数据量比较大的话,这个时间就会比较久,另外,索引也需要占用空间的,更多的索引会占用更多的磁盘空间

    Q:可以使用多少列创建索引?

    选项A:1024、B:1023、C:1016、D:1017
    

    A:
    涉及两个知识点:事务隔离级别、事务的提交顺序
    大致是通过这些预留字段作为虚拟列,建虚拟表,通过指针的顺序控制事务顺序,通过空间进行行记录

    1023 - 3*2 = 1017

    根据事务隔离不同,不一定这些字段全部都用到,看情况会发生改变。理解起来也比较复杂,有博客或者相关介绍的文章,较少,一般人学不到这么深入。

    Q:一个索引可以有多少列

    A:
    多列索引最多允许 16 列。超过限制会返回错误。
    ERROR 1070 (42000): Too many key parts specified; max 16 parts allowed

    某公司:DBA要求是最多5列。

    Q:mysql行数据大小有没有限制 列个数有没有限制?

    A:
    blob、text 这类字段,会保存到其他地方,并且通过指针,指向数据库表字段,这样就不受65535的限制了。

    Q:mysql里有2000W数据,redis只存20WW的数据,如何保证redis中的数据都是热点数据?

    A:
    1、固定空间大小;
    2、设定延长时间;

    三、Redis

    Q: 延时双删的加锁阶段不清楚?

    加锁阶段是在B进行查询到更新缓存这个过程吗?
    如果是这个过程,那么B更新完缓存到A清除缓存这个时间段的后续线程访问的数据
    不还是B更新后的旧数据吗?
    假如在A清除缓存到更新数据库这过程加锁有些什么问题不?


    image.png

    A:
    注意看这段核心代码

    image.png

    这里面有查询缓存、加锁、查询数据库、更新缓存、解锁,几个步骤
    那么如果代码和“更新数据库”这步结合起来
    在更新数据库这一步就进行了对缓存key加锁
    在数据库更新这步一直加锁,当处理完数据库的更新,才释放锁。
    这样,及时B线程开始读缓存,也会被阻塞,一直等到数据库更新完。
    此时B线程,再拿到锁,去读缓存,读不到,再去读数据库,已经是对的数据了

    注:强一致性很难,这个方案也只是追求最终一致性。延时时间内如果有其他线程读取了缓存确实就会读到旧的,但是延时后就是正常的数据了。

    Q: 报表类应用如何设计缓存?

    背景:数据库类有约1000W明细数据,包含交易大区,交易小区,城市,业务类型,一级负责人,二级负责人等近10个维度字段。前端提供基于这些维度字段的自由分类,汇总,排序等操作的报表,也会下钻查看明细数据。这样的系统如何进行缓存的设计?
    备注:数据库内1000W的数据会在每天全量更新1-3次

    假设场景
    交易大区->华北大区
    交易小区->河北省
    城市->石家庄
    业务类型->商品、订单、库存
    一级负责人->张三
    二级负责人->李四

    image.png

    建议:使用ES
    ES 是支持更新的,并且ES的索引很适合区分维度建立,如果分的好,整个索引数据可以直接删除重建。

    Q:使用 redis 如何设计分布式锁?说一下实现思路?使用 zk 可以吗?如何实现?这两种有什

    么区别?
    A:
    Redis:
    1、自己定义,使用redis的setNx()加锁,解锁使用lua脚本;
    2、使用现成集成框架,redisson
    zk:
    1、临时节点
    2、zk保证强一致性肯定没有redis效率高

    1、实现难度:redis 相对简单,zk 相对复杂;
    2、使用广度:redis 应用广泛,zk 应用广度一般,受技术栈限制(很多公司技术栈不包含zk)

    Q:如何设计和实现TPS和QPS 统计?

    A:
    nginx 的log日志,直接使用 shell 命令统计 例如 awk '{print $6}' access.log | wc -l

    redis高级用法,HyperLogLog

    四、测试

    Q:我们这测试好像都是开发完成后禅道提测试,没有单元测试覆盖率之类的,想了解正式些的单元测试是怎样做的?

    A:
    功能测试:页面点点点,看显示以及交互;
    测试开发:除了功能测试的工作外,进行压测、抓接口、查数据库 等等

    A公司示例:
    1、dev 开发环境:
    开发、联调、单测、测试接口
    2、test 测试环境:
    提测、验证、联调、测试接口
    3、uat uat环境:
    业务验证、测试接口
    4、stage 预发环境:
    预上线、压测、联调、环境隔离、测试接口
    5、prod 生产环境:
    上线、AB灰度验证、测试接口、网络验证

    大致是这5套常用环境,还有一些项目,环境会更多,更复杂。
    你提到的单元测试,是开发阶段,需要使用的,但每次上线可通过公司平台,进行统一执行,有执行不通过的单测,是无法上线的。

    单元测试最重要的指标就是,是否通过。然后我们这边测试同学是“测开”,能够结合公司平台出具相关的数据报告,比如:BUG数量、是否延期、是否有返工、是否通过等等,其中单元测试的通过率,也会提现在报告中,当一个阶段完成时,会发对应阶段的报告。

    uat 环境,不一定都有的,看情况,正常的环境,可3套,可5套,还有7套的分法。

    toB 项目,60%要求偏低,80%以上最好,因为场景相对固定,单测容易覆盖一些。

    B公司示例:
    以前jmeter用的多,但是迭代过程中发现稍微调整页面用例就没办法用了,后面全部改称junit,封装了一层模拟请求

    五、系统设计

    Q:当TPS或者QPS达到5万,10万或者更高的时候有哪些处理方法 ?

    了解到有限流,缓存,令牌等。

    限流,缓存,令牌
    拆分服务
    分库分表,按照时间水平分、按照业务纵向分
    读写分离
    异步
    削峰(MQ)
    搜索中间件,ES、solr等
    大数据数据库

    Q: 如何根据所需QPS TPS 来配置一个服务器?比如需要多大内存 CPU 等。这种问题该怎么回答呢?

    TPS 和 QPS 是和服务有关,但和服务器没直接关联。

    Q:对接了短信三方 回掉超时 , 有什么好的方法吗?

    我们服务器在印度 短信三方服务器在新加坡 发送完短信 三方会把发送结果 通知到我,但是三方的超时时间设置的比较短 100ms 经我们手动测试 需要400ms 但是三方不想改他们的超时时间,老师有什么好的方法吗?

    这个问题,我也没办法,只能给点建议,和领导说明情况,通过公司去和三方协商。

    Q: 私有化的paas平台的license加密问题(网络隔离,离线license),目前计划选型用公私钥做加密,是license方持有私钥好还是商户端持有私钥好;

    在线license:在线授权许可,用于联网计算机的软件激活
    离线license:离线授权许可,用于未联网计算机(即单机未联网)的软件激活

    我理解是离线license,网络隔离的情况下,相当于一台没联网的机器,无法交换公钥私钥,所以在商户端(单机)上,直接存放私钥。

    Q: 多个版本迭代后,如何兼容老的接口。接口设计的一些准则和注意事项

    A:
    1:api版本号放在url路径中 www.xxx.com/v1/order/id、www.xxx.com/v2/order/id
    2:api版本号放在url参数中 api.xxx.com/api?version=v1&..
    3:api版本号放在请求的header中
    4:api版本号放在二级域名中。 v1.api.xxx.com

    大厂的业务中,多选择 方案1
    笔者查看阿里云、腾讯云之类的使用的是 方案2。阿里云腾讯云

    Q: 一个服务挂了配多长时间重试?

    两种场景:
    1、自己的服务宕机
    2、依赖的外部服务挂了

    针对第一种,我能考虑的是,无法重试,属于线上服务故障,需要及时引发报警,进行及时的问题排查以及重启;

    针对第二种,我的服务,依赖外部服务的接口,此时会考虑两个问题:
    a.是否需要重试
    b.重试的话,按照什么维度:是频率,1s、2s、3s、5s;还是次数,1秒一次,共3次,还是5秒1次,共2次;

    例如:微信小程序的支付通知场景
    2、后台通知交互时,如果微信收到商户的应答不符合规范或超时,微信会判定本次通知失败,重新发送通知,直到成功为止(在通知一直不成功的情况下,微信总共会发起多次通知,通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h - 总计 24h4m),但微信不保证通知最终一定能成功。

    相关文章

      网友评论

        本文标题:各种研发问题集合-持续更新中

        本文链接:https://www.haomeiwen.com/subject/essobltx.html