HashMap和CurrentHashMap的区别是什么? CurrentHashMap底层结构在1.7和1.8有什么不同?
HashMap和ConcurrentHashMap是Java中Map集合,区别点是
HashMap
:非线程安全。ConcurrentHashMap
:线程安全。- 1.7版本:
ConcurrentHashMap
主要由Segment(数组)+ HashEntry(链表)
组成,同时采用分段锁机制保证现高并发的线程安全 - 1.8版本:
ConcurrentHashMap
主要由Node数组+链表和红黑树
构成,抛弃了分段锁,采用CAS + synchronized
保证线程安全。
- 1.7版本:
- 项目中不采用
HashTable
是因为使用synchronized
对整个数据结构锁定,锁的粒度大,任何读或写操作都极大的影响性能。- 分段锁:将整个集合分成多个小的
Segment
,每个Segment
拥有自己的锁。简单说,就是缩小每次锁的 数据的数量。CAS
:乐观锁。
- 例如:
put
操作会根据当前key
定位Node
,如果为空表示当前位置可以写入数据,则先利用CAS
尝试写入,失败则使用自旋锁保证成功synchronized
:悲观锁。在扩容操作时,为保证对旧数组安全的读和写,会使用synchronized
来保证线程安全。- 自旋锁:一种基于忙等待的锁,当一个线程尝试获取锁时,如果锁已被其他线程占用,该线程不会被挂起,而是会循环检查锁的状态直到获取到锁为止
假设你有一批历史积分数据要存储,数量在千万条左右,需要存入MySQL 你的方案是什么?
MySQL5.1开始支持表分区功能。我们可以通过分库、分表的设计方案来对这些数据进行存储。
分区指的是按照某种规则,把表数据对应的ibd
文件拆分成多个文件来存储
分表:在创建表时按照业务需求对表进行拆分。此时无论是逻辑上,还是物理上,就从一张表变成了多张表来存储数据。
- 水平分表:将同一张表,通过规则拆分为多张表。
- 垂直分表:按照字段拆分表的方式,称为垂直拆分。
引发的问题:
- 增删改查时,需要自己判断访问哪张表。
- 垂直拆分还会导致事务问题及数据关联问题:单表操作变为多表操作
分库:保证数据库的高可用性
- 垂直分库:按照项目模块,给项目分配到的每个库中的表是不同的,这种分库模式成为垂直分库
- 水平扩展:给数据库建立主从集群,主节点向从节点同步数据。两者结构一样,可以看做是水平扩展
引发的问题:
- 成本高、数据聚合统计比较麻烦、需要考虑主从同步的一致性问题和分布式事务问题
请你说一说你的排行榜功能是如何实现的
排行榜是根据积分实现的,分为实时榜单和历史榜单,同时榜单按月份划分。
-
实时榜单:读写操作频繁。所以存到了reids的zset结构中,当前月份做key、用户id做为value、积分做为分数,实现排行。
-
历史榜单:数据量大,查询量小。所以从redis同步到了MySQL中,同时为防止单表数据量过多,采用了水平分表的设计方案,把一个赛季的榜单放在一张表中,查询则根据赛季标识只查一张表即可。
历史赛季积分是如何生成的
历史赛季积分的生产分为3大步,核心是把Redis中的上赛季数据,同时采用水平分表的设计方案保存到MySQL中。
- 利用定时任务,定期在月初,根据上赛季的时间在MySQL中创建新的历史榜单,
- 之后将redis中的数据同步到这张表
- 最后清理redis上赛季的缓存
因为这三步都是采用定时任务来完成的,所以采用了定时任务的任务链来完成。