面经总结(四)

目录

1、多线程执行顺序怎么保证

2、平时遇到幻读的场景

3、幻读在 InnoDB 中是被如何解决的?

4、为什么 在 RC 级别下,binlog 格式要设置成 row?

5、concurrentHashMap中是否存在死锁?

6、主键怎么设计

 7、什么是数据库的水平分片

8、什么是垂直分表,以及为什么垂直分表

9、什么是水平分表

10、什么是垂直分库

11、什么是雪花算法

12、ThreadLocal这个类用过吗?

13、操作系统中的线程和Java中的线程区别

14、HashMap中链表节点都存什么数据,为什么要存哈希值

15、虚拟机栈的栈帧中的方法返回地址是什么?

16、B+树一个节点有多大?一千万条数据,B+树多高?

17、https如何验证证书

18、加密算法

19、什么是SSL

20、已知一天内用户登录登出的日志(数据量较大),求这一天用户在线的最大峰值和持续时间段


1、多线程执行顺序怎么保证

  • 使用join()方法,让其他线程等待。使用join的线程会独占执行资源,直到使用完毕,其它线程才能获取执行权。
  • 使用juc包下的Executors线程池保证子线程按顺序执行,我们使用Executors中的newSingleThreadExecutor()方法,创建一个单线程的线程池,也可以达到控制线程执行顺序的目的。

2、平时遇到幻读的场景

        幻读:是指在同一个事务中,前后两次查询相同范围时,得到的结果不一致,后一次查询到新插入的行。

        这里需要注意的是,由于在 RR 级别下,普通的读是快照读(一致性读),所以幻读仅发生在当前读的基础上

3、幻读在 InnoDB 中是被如何解决的?

        间隙锁,会将行之间的空隙锁住。行锁(Record Lock)按照类型分为读锁和写锁,并且行锁与行锁在不同的事务间是互斥的。但间隙锁不同,正由于它解决的是幻读插入的问题,所以间隙锁仅仅对插入操作本身互斥,不同事务之间的间隙锁并不互斥。为了加锁时的方便,间隙锁和行锁的合集称为 next-key lock.行锁锁住的是存在的记录行,间隙锁锁住的是行之间的空隙。

4、为什么 在 RC 级别下,binlog 格式要设置成 row?

        先来看下 binlog 的三种格式:

  • --binlog-format=STATEMENT :在 Master 向 Slave 同步时,会以原生的 SQL 语句进行同步。
  • --binlog-format=ROW :Master 会把被操作后的表中的行记录在日志中, 向 Slave 同步。简单来说同步的就是表中的数据。
  • --binlog-format=MIXED :默认会以 STATEMENT 的方式记录,但在一些情况下可以自动的切换成 ROW 方式,比如执行用户自定义的函数 UUID.

        隔离级别 为RC时,不存在间隙锁了。就会发生幻读,使用binlog进行数据恢复或主从复制时会造成错误。

5、concurrentHashMap中是否存在死锁?

        在concurrentHashMap中使用computeIfAbsent方法中调用putIfAbsent,会出现死锁。


package com.zl.map.concurrent;
 
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
 
public class ConcurrentHashMapBug {
    public static void main(String[] args) {
 
        Map<String, Integer> map = new ConcurrentHashMap<>(16);
        map.computeIfAbsent(
                "AaAa",
                key -> {
                    return map.computeIfAbsent(
                            "BBBB",
                            key2 -> 42);
                }
        );
 
 
        System.out.println("======  end  =============");
    }
}
 

        执行上面的代码片段会产生死锁。当map中不存在key="AaAa"时,computeIfAbsent会插入该key,并将以下lamda函数的返回值(42)作为它的value。

        而这个lamda函数其实会继续去对key="BBBB"的Node进行同样操作,并设置value=42。但是由于这里的“AaAa”和“BBBB”这个字符串的hashCode一样,导致执行出现死锁

6、主键怎么设计

        一般情况下,我们都使用 Mysql 的自增 ID,来作为表的主键,但是在分库分表的情况情况下,自增 ID 则不能满足需求。

MySQL主键设计原则

  • MySQL主键应当是对用户没有意义的。
  • MySQL主键应该是单列的,以便提高连接和筛选操作的效率
  • 永远也不要更新MySQL主键
  • MySQL主键不应包含动态变化的数据,如时间戳、创建时间列、修改时间列等
  • MySQL主键应当有计算机自动生成。

主键设计的常用方案

  • 自增ID

    • 优点

      • 数据库自动编号,速度快,而且是增量增长,聚集型主键按顺序存放,对于检索非常有利。

      • 数字型,占用空间小,易排序,在程序中传递方便。

    • 缺点

      • 不支持水平分片架构,水平分片的设计当中,这种方法显然不能保证全局唯一。

      • 表锁:在MySQL5.1.22之前,InnoDB自增值是通过其本身的自增长计数器来获取值,该实现方式是通过表锁机制来完成的(AUTO-INC LOCKING)。锁不是在每次事务完成后释放,而是在完成对自增长值插入的SQL语句后释放,要等待其释放才能进行后续操作。比如说当表里有一个auto_increment字段的时候,innoDB会在内存里保存一个计数器用来记录auto_increment的值,当插入一个新行数据时,就会用一个表锁来锁住这个计数器,直到插入结束。如果大量的并发插入,表锁会引起SQL堵塞。

      • 自增主键不连续

  • UUID

    • 优点

      • 全局唯一性、安全性、可移植性。

      • 能够保证独立性,程序可以在不同的数据库间迁移,效果不受影响。

      • 保证生成的ID不仅是表独立的,而且是库独立的,在你切分数据库的时候尤为重要

    • 缺点

      • 针对InnoDB引擎会徒增IO压力,InnoDB为聚集主键类型的引擎,数据会按照主键进行排序,由于UUID的无序性,InnoDB会产生巨大的IO压力。InnoDB主键索引和数据存储位置相关(簇类索引),uuid 主键可能会引起数据位置频繁变动,严重影响性能。

      • UUID长度过长,一个UUID占用128个比特(16个字节)。主键索引KeyLength长度过大,而影响能够基于内存的索引记录数量,进而影响基于内存的索引命中率,而基于硬盘进行索引查询性能很差。严重影响数据库服务器整体的性能表现。

 7、什么是数据库的水平分片

        水平分片(也叫水平分库)指的是将整体存储在单个数据库中的数据,通过某种策略分摊到多个表结构与其相同的数据库中,这样每个数据库中的数据量就会相对减少很多,并且可以部署在不同物理服务器上,理论上能够实现数据库的无限横向拓展。所以水平分片是数据库性能问题的最终解决方案。

水平分片策略

  • 基于 id 的区间分片,例如:将 id 为 1-2w 的数据存放在 A 数据库,2w-4w 的数据存放在 B 数据库。缺点是单库能承受的数据量需要预估,如果预估的不准确容易造成性能不够用或者浪费
  • 基于 id 的 hash 分片,例如:将 id%2=0 的数据存放在 A 数据库,id%2=1 的数据放在 B 数据库。缺点是在动态增加数据库时,hash 的结果会发生变化,所以需要对已有数据进行迁移,一般是用一致性 hash 或在分库初期就建立足够的数据库避免这个问题

  • 基于时间的区间分片,大部分软件都会有一个特征:越新的数据被操作的几率越大,老数据几乎不会被操作。所以通过数据的插入时间进行分库(也称为冷热分离),例如:将 2015年1月-5月 的数据放在一个库,6月 到 12月 的数据放在另一个库。这样做的缺点是在查询时需要额外提供数据的创建时间才能找到数据存放在哪个库,所以比较适合微博等主要以时间轴(Timeline)功能为主的软件。

  • 基于检索表分片,通过额外建立一张检索表保存 id 与所在数据库节点的对应关系,优点是逻辑简单,自由且不会有迁移问题,缺点是每次查询都需要额外查询检索表,所以一般会选择将检索表缓存到内存中。

  • 基于地理位置分片,像点评、滴滴打车之类的软件由于不同城市的数据不需要互通,可以按照城市分片,将不同城市的数据存放在不同数据库中,这样做的一个优点是可以将数据库服务器部署到离对应城市最近的节点上,以提高访问速度。

主键冲突

        在数据库表设计时,为了保证 id 唯一,大部分人都会将主键设为自增的 int 类型。但是由于 auto_increment 是和表所绑定的,所以在分库后每个表的自增 id 也是独立的。这样就肯定会发生主键冲突,解决办法有以下几种:

  • 最简单的方法是使用公用的 id 生成器,比如在 MySQL 中建立一张公共的表用于生成 id,或者通过 Redis 的 incr 生成 id。
  • 果已经预先分配好了数据库的个数,并且不会改变,可以直接对每个库的所有 auto_increment 设定步长和初始值。例如一开始就分配了 36 个数据库:
  • 如果只需要主键唯一,而不一定是自增整数的话,也可以使用 uuid,它会生成 36 位(去掉”-“是 32 位)的字符串,并且几乎不会重复。
  • 但是很多人都希望主键即使不是连续自增,也是一个有序的整数,这样在排序等情况下会有用。这时候就需要自己实现一个 id 生成算法了,一般都会使用 unix 时间戳保持有序,混入 Mac 地址等保证唯一。

8、什么是垂直分表,以及为什么垂直分表

        将表中的列分开,用来建立不同的表

        如果一张表中 有一个大字段 而且并不是必须要展示的或者不是当前需要用的  那么虽然没有刻意去查询  但是在根据id或者其他索引进行查询的时候就会把大字段一起查出来,会严重影响查询的性能,所以才有的垂直分表

9、什么是水平分表

        对数据库进行水平分表,建立多个结构相同的表分摊数据,使得每个表的数据量减少从而提升速度。分表和最开始提到的数据库分片看起来很像,实际各有优劣:分表后各个子表还是可以通过 union 等命令联合查询,分库后则不行。但是分库后每个数据库可以独立部署在不同的服务器上,充分利用多台服务器的性能,分表却只能在单台机器的单个数据库上,如果是服务器本身的性能达到瓶颈,则分表不会有明显作用。

10、什么是垂直分库

        对数据库进行垂直分库,将业务彼此无关的表放在单独的数据库中,分库后不同库中的表无法进行联合查询等操作,但是可以平摊压力,并且独立做读写分离。

11、什么是雪花算法

        SnowFlake是Twitter公司采用的一种算法,目的是在分布式系统中产生全局唯一且趋势递增的ID。

组成部分(64bit)

  • 第一位 占用1bit,其值始终是0,没有实际作用。
  • 时间戳 占用41bit,精确到毫秒,总共可以容纳约69年的时间。
  • 工作机器id 占用10bit,其中高位5bit是数据中心ID,低位5bit是工作节点ID,做多可以容纳1024个节点。
  • 序列号 占用12bit,每个节点每毫秒0开始不断累加,最多可以累加到4095,一共可以产生4096个ID。

12、ThreadLocal这个类用过吗?

        ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况 下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。

        一个线程放的东西,除了自己谁也拿不到。线程局部变量。在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。

        而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数 据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线 程代码时,可以把不安全的变量封装进ThreadLocal。


13、操作系统中的线程和Java中的线程区别

        Java线程在JDK1.2之前,是基于称为“绿色线程”(Green Threads)的用户线程实现的,而在JDK1.2中,线程模型替换为基于操作系统原生线程模型来实现。因此,在目前的JDK版本中,操作系统支持怎样的线程模型,在很大程度上决定了Java虚拟机的线程是怎样映射的,这点在不同的平台上没有办法达成一致,虚拟机规范中也并未限定Java线程需要使用哪种线程模型来实现。线程模型只对线程的并发规模和操作成本产生影响,对Java程序的编码和运行过程来说,这些差异都是透明的。
        也就说JDK1.2之前,程序员们为JVM开发了自己的一个线程调度内核,而到操作系统层面就是用户空间内的线程实现。而到了JDK1.2及以后,JVM选择了更加稳健且方便使用的操作系统原生的线程模型,通过系统调用,将程序的线程交给了操作系统内核进行调度。现在的Java中线程的本质,其实就是操作系统中的线程。

14、HashMap中链表节点都存什么数据,为什么要存哈希值

        key,value,hash,next指针,存储hash值为了加快数据搜索,当查找一个key对应的value时,先找到对应数组下标,再在对应的链表或红黑树中寻找hash值与key值都相同的项,先比较hash值,hash值不同,则两个数据一定不同。对于红黑树来说,它的存储是有序的,以hash值的大小进行排序。

15、虚拟机栈的栈帧中的方法返回地址是什么?

          存放调用该方法的PC寄存器的值,方法正常退出时,调用者的pc寄存器的值作为返回地址,即调用该方法的指令的下一条指令的地址。而通过异常退出时,返回地址是要通过异常表来确定,栈帧中一般不会保存这部分信息。

16、B+树一个节点有多大?一千万条数据,B+树多高?

        在 MySQL 中我们的InnoDB页的大小默认是16k,当然也可以通过参数设置。单个叶子节点(页)中的记录数=16K/1K=16。(这里假设一行记录的数据大小为1k,实际上现在很多互联网业务数据记录大小通常就是1K左右)。

        我们假设主键ID为bigint类型,长度为8字节,而指针大小在InnoDB源码中设置为6字节,这样一共14字节,我们一个页中能存放多少这样的单元,其实就代表有多少指针,即 16384/14=1170 。那么可以算出一棵高度为2的B+树,能存放 1170*16=18720 条这样的数据记录。       

        根据同样的原理我们可以算出一个高度为3的B+树可以存放: 1170*1170*16=21902400 条这样的记录。

17、https如何验证证书

        假设这是一个浏览器的HTTPS请求

  • 首先浏览器通过URL网址去请求服务端,服务端接收到请求后,就会给浏览器发送一个自己的CA数字证书
  • 浏览器接收到证书以后,就要开始进行验证工作了。首先从证书中得知证书的颁发机构,然后从浏览器系统中去寻找此颁发机构的根证书。上面我们也看到,世界上权威CA机构的根证书都是预先嵌入到浏览器中的,如果在浏览器系中没有找到对应的根证书,就代表此机构不是受信任的,那么就会警告无法确认证书的真假,比如以前打开12360网站就会提示,现在不会了
  • 如果我们找到了证书颁发机构的根证书,那么就从根证书中取得那个根公钥,用根公钥去解密此证书的数字签名,成功解密的话就得到证书的指纹和指纹算法,指纹是证书内容通过指纹算法计算得到的一个hash值,这里我们称之为h1,h1代表证书的原始内容;然后用指纹算法对当前接收到的证书内容再进行一次hash计算得到另一个值h2,h2则代表当前证书的内容,如果此时h1和h2是相等的,就代表证书没有被修改过。如果证书被篡改过,h2和h1是不可能相同的,因为hash值具有唯一性,不同内容通过hash计算得到的值是不可能相同的
  • 在证书没有被修改过的基础上,再检查证书上的使用者的URL(比如csdn.net)和我们请求的URL是否相等,如果相等,那么就可以证明当前浏览器链接的网址也是正确的,而不是一些钓鱼网之类的。
  • 但如果浏览器的连接被某个中间人截取了,中间人也可以发一个由权威的CA机构颁发的证书给浏览器,然后也可以通过证书没有被篡改的验证,但是在证书没有被篡改的前提下,通过对比证书上的URL和我们请求的URL是否相同,我们还是可以判断当前证书是不是服务器发的证书。可以这么理解,因为URL具有唯一性,所以中间人的证书的上的URL和我们的证书的URL是不可能相同的,如果中间人修改了自己证书上的URL,那么就通过不了证书没有被篡改的验证,所以中间人的证书也是欺骗不了我们的
  • 到这里我们认证了三点信息:
    • 证书是否为受信任的权威机构颁发的
    • 证书是否被篡改
    • 证书是否为服务器发过来的,而不是第三方发的
  • 基于上面的三点信息认证都没有问题的情况下,下一步我们有一个重要的任务就是,如何将一个对称加密算法的秘钥安全地发给服务器
  • 首先随机生成一个字符串S作为我们的秘钥,然后通过证书公钥加密成密文,将密文发送给服务器。因为此密文是用公钥加密的,这是一个非对称加密,我们知道,这个密文只有私钥的持有者才能进行解密,在这里私钥的持有者当然是服务器了,所以说任何第三方截取到密文也是没用的,因为没有对应的私钥无法解密得到我们的密文。
  • 还有一个关键步骤,发送密文的时候也会对消息内容进行签名操作。签名上面讲解过,就是对密文内容进行hash计算得到的hash值再通过公钥或私钥加密得到的一段数字串,这个签名和消息内容一起发送出去。接收方收到消息以后,通过私钥或公钥解析出密文和签名的hash值,同时也会对接收的消息内容进行同样的hash计算得到另一个hash值,比对两个hash值是否相同来判断消息有没有被篡改过

18、加密算法

  • 对称加密:加密使用的秘钥和解密使用的秘钥是相同的,也就是说加密和解密都使用同一个秘钥,加密算法是公开的,秘钥是加密者和解密者绝对保密的
  • 非对称加密:加密使用的秘钥和解密使用的秘钥是不相同的,HTTPS在数字证书验证的时候,采用的RSA密码体制就是一种非对称加密

19、什么是SSL

        SSL(Secure Sockets Layer安全套接字协议),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层与应用层之间对网络连接进行加密。

20、已知一天内用户登录登出的日志(数据量较大),求这一天用户在线的最大峰值和持续时间段

        可以通过编程实现,将时间段分为24段,用一个数组记录各个时间段的登录人数,最后一起统计

 for (int i = item.LoginTime; i < item.LogoutTime; i++)
                {
                    array[i]++;
                }
            }

        

        

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值