6-1 HashMap和ConurrentHashMap的区别是什么? ConurrentHashMap底层结构在1.7和1.8有什么不同?
HashMap和ConcurrentHashMap是Java中用于存储键值对的两个常见的哈希表实现类。它们之间的主要区别如下:
-
线程安全性:HashMap是非线程安全的,而ConcurrentHashMap是线程安全的。在多线程环境中,多个线程可以同时访问ConcurrentHashMap的不同部分而不会导致数据不一致或抛出异常。
-
锁的粒度:HashMap在进行修改操作时需要锁住整个容器,这可能导致在高并发环境下性能下降。而ConcurrentHashMap使用了分段锁(Segment),只锁定部分容器,不同线程可以同时访问不同的分段,从而提高了并发性能。
-
迭代器的弱一致性:由于ConcurrentHashMap的设计目标是支持高并发,所以其迭代器提供了弱一致性的特性。这意味着迭代器可能会返回已经被其他线程修改的数据,但不会抛出ConcurrentModificationException异常。
至于ConcurrentHashMap在Java 1.7和1.8中底层结构的不同,主要体现在以下方面:
在Java 1.7中,ConcurrentHashMap的底层结构采用了分段锁的方式,将整个容器分为多个Segment,每个Segment独立地持有一把锁。这样可以确保在并发环境下,不同线程可以同时访问不同的Segment,提高并发性能。
而在Java 1.8中,ConcurrentHashMap的底层结构进行了进一步的优化。它引入了一种称为CAS(Compare and Swap)的无锁算法,用于更新哈希表的数据结构。这种算法避免了显式的锁操作,提高了并发性能。此外,在Java 1.8中,ConcurrentHashMap还引入了一种称为红黑树的数据结构,用于优化哈希冲突的处理,提高了查找、插入和删除操作的效率。
总之,HashMap和ConcurrentHashMap在线程安全性、锁的粒度和迭代器的弱一致性等方面存在差异,而Java 1.7和1.8中的ConcurrentHashMap底层结构也有所不同,都是为了提高并发性能和减少锁竞争。
6-2 假设你有一批历史积分数据要存储,数量在kw条左右 存入mysql 你的方案是什么
对于数据库的海量数据存储,方案有很多,常见的有:分区、分表、分库、集群。
对于此问题我的方案是,先按照项目模块进行分库,每个微服务使用独立的数据库,即垂直分库。
然后使用分表,根据赛季的不同进行水平分表,如果字段过多,还会进行垂直分表。
最后为了保证单节点的高可用性,我会给数据库建立主从集群,主节点向从节点同步数据,即水平扩展,最终建立起数据库存储的综合集群。
6-3 请你说一说你的排行榜功能是如何实现的
我们的排行榜是根据积分进行排序的,在项目中积分排行榜分为实时排行榜和历史排行榜.
实时积分排行榜的并发访问数量很大,我们采用了Redis的Zset数据结构进行存储,当年月份作为key键,用户ID作为member成员,个人积分总和作为score分数。这样解决了高并发访问问题,而且还可以根据积分值score自然排序
历史排行榜访问并发比较小,但数据量较大,我们采用了mysql数据库进行了存储,并且按照赛季进行了水平分表,每个旧赛季都是一张新的表,以便海量数据存储。
当现赛季过期时,会将旧赛季存储在Redis中积分排行数据保存到历史积分排行的数据库中,而后清理缓存。
6-4 历史赛季积分是如何生成的
历史赛季积分的生成就是将redis中上一个赛季的积分排行完整数据保存到mysql中,这个过程使用了xxl-job定时任务,在每赛季结束半夜0辰按照任务链执行顺序执行下面的业务
1.创建历史赛季表,一个赛季一张表
2.从redis中将数据保存到mysql中对应的赛季表中
3.删除redis中历史赛季数据