一 怎么实现redis和mysql数据库的数据同步?
- 双写:在数据写入MySQL时,也将数据写入Redis。需要确保两者的数据一致性,这通常通过事务机制或消息队列来保证。
- 数据迁移工具:使用工具如
Redis-MySQL-Sync
或Debezium
来进行数据同步。Debezium
通过捕获数据变更事件来实现数据同步。 - 定期同步:定期将MySQL中的数据批量同步到Redis,以保证Redis中的数据是最新的。
- 缓存失效:当MySQL中的数据变更时,及时更新或失效Redis中的缓存。
二 你知道set和list结构的区别吗,如果发生散列冲突怎么解决?
- Set:无序且唯一的集合,不允许重复元素。适用于需要唯一性和不关心顺序的场景。
- List:有序的集合,允许重复元素。适用于需要保持元素顺序的场景。
解决散列冲突的方法有:
- 链地址法:每个哈希槽维护一个链表,冲突的元素被加入链表。
- 开放地址法:当发生冲突时,寻找下一个空位存储元素。
三 B树和B+树的区别?
B树:
- 所有节点都存储数据。
- 内部节点用于搜索,叶子节点和内部节点都有数据。
- 在节点中存储数据,使得插入和删除操作可能需要更新多个节点。
B+树:
- 内部节点仅用于索引,不存储实际数据。
- 所有数据都存储在叶子节点中,叶子节点形成一个链表。
- 插入和删除操作更加高效,因为只需修改叶子节点而非多个节点。
四 Arraylist和Linkedlist的区别?进行增删查改的时候效率有什么区别?
-
是否保证线程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
-
底层数据结构: Arraylist 底层使用的是Object数组;LinkedList 底层使用的是双向循环链表数据结构;
-
插入和删除是否受元素位置的影响: ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行
add(E e)
方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element)
)时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 LinkedList 采用链表存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是近似 O(1)而数组为近似 O(n)。 -
是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而ArrayList 实现了RandmoAccess 接口,所以有随机访问功能。快速随机访问就是通过元素的序号快速获取元素对象(对应于
get(int index)
方法)。 -
内存空间占用: ArrayList的空 间浪费主要体现在在list列表的结尾会预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。
五 SQL优化有哪些方法?
- 索引优化:创建合适的索引以加快查询速度。
- 查询优化:优化SQL查询语句,避免不必要的全表扫描。
- 表设计:规范化表结构,减少数据冗余。
- 缓存:使用缓存机制减少数据库查询负载。
- 分区:将表数据分区,提升查询性能。
- 分析和优化:使用工具如
EXPLAIN
来分析查询执行计划并优化。
六 垃圾回收机制?什么样的对象会被回收?
1.标记-清除法
流程:利用可达性去遍历内存,标记出所有需要回收的对象;再遍历一遍,将所有标记的对象回收掉。
特点:效率不稳定,标记和清除两个过程的执行效率都随对象数量增长而降低;内存空间碎片化,标记和清除后会产生大量的不连续的空间分片,可能会导致之后程序运行的时候需分配大对象而找不到连续分片而不得不触发一次GC。
2.标记-复制算法(复制算法)
将内存按照容量大小分为大小相等的两块,每次只使用一块,当一块使用完了,就将还存活的对象移到另一块上,然后在把使用过的内存空间移除;
特点:实现简单,运行高效。但是可用内存缩小到了原来的一半。
3.标记-整理法
流程:利用可达性去遍历内存,把存活对象或垃圾对象进行标记;将所有的存活的对象向一端移动,将端边界以外的对象都回收掉。
特点:如果移动存活对象,尤其是在老年代这种每次回收都有大量对象存活区域,移动存活对象并更新所有引用这些对象的地方将会是一种极为负重的操作,而且这种对象移动操作必须全程暂停用户应用程序才能进行,即“Stop The World”。
4.分代收集算法
根据内存对象的存活周期不同,将内存划分成几块,java虚拟机一般将内存分成新生代和老生代。在新生代中,有大量对象死去和少量对象存活,所以采用复制算法,只需要付出少量存活对象的复制成本就可以完成收集;老年代中因为对象的存活率极高,没有额外的空间对他进行分配担保,所以采用标记清理或者标记整理算法进行回收。
【面试】面试常考知识点:请你谈谈JVM垃圾收集算法?-CSDN博客
七 项目中怎么保证用户的数据信息的安全性?
- 加密:对敏感数据进行加密存储和传输。
- 访问控制:使用强认证和授权机制,控制对数据的访问权限。
- 数据备份:定期备份数据,以防数据丢失或损坏。
- 审计日志:记录数据访问和操作日志,监控异常行为。
- 输入验证:验证用户输入,防止SQL注入和XSS攻击。
八 线程池的核心参数有哪些?
-
corePoolSize : 核心线程大小。线程池一直运行,核心线程就不会停止。
-
maximumPoolSize :线程池最大线程数量。非核心线程数量=maximumPoolSize-corePoolSize
-
keepAliveTime :非核心线程的心跳时间。如果非核心线程在keepAliveTime内没有运行任务,非核心线程会消亡。
-
workQueue :阻塞队列。ArrayBlockingQueue,LinkedBlockingQueue等,用来存放线程任务。
-
defaultHandler :饱和策略。ThreadPoolExecutor类中一共有4种饱和策略
-
ThreadFactory :线程工厂。新建线程工厂。
九 有没有用过数据库的分页分表,怎么进行处理的?
1.判断是横向拆分还是纵向拆分
如果某些字段比较独立且较少被访问,或者表的字段特别多(几十甚至上百个),且访问时经常只需要部分字段,可以考虑垂直拆分。
如果表的行数非常大,影响了查询和写入性能,或者业务需求导致数据量增长迅速,预计将来会超过单表或单库的处理能力,可以考虑水平拆分。
2.拆分数量的判断
我们可以根据业务逻辑的模块、字段的访问频率等进行拆分,经常访问的字段可以保留在一个表中,较少访问或大字段(如图片、文档)可以独立拆分出去。
水平拆分可以根据表的数量进行预测,例如,如果预计未来数据量为 1 亿条,单表容量限制为 1000 万,则需要拆分为 10 个表。或者结合系统并发量、服务器容量和架构而定。
3.如何实现均匀拆分
哈希取模:根据某个字段(如用户 ID、订单 ID)取哈希值,然后对拆分表的数量取模,将数据分布到不同表或库中。哈希取模是一种非常常用的分片策略,能够确保数据均匀分布。
范围分片:根据某个字段的值范围进行划分,将数据分布到不同表中。例如根据订单日期进行分片。
一致性哈希:使用一致性哈希算法来分配数据,可以动态增加或减少节点(表或库),并且能保证数据分布均匀,适用于分布式系统中。
选择合理的分片键:分片键应当是查询时常用的字段,并且其值的分布要尽量均匀。避免选择高度聚集的字段(如创建时间、递增 ID 等),否则会导致数据倾斜。
避免热点:在某些情况下,特定的数据可能会成为访问热点,导致部分表或库的访问频率过高。为了避免这种情况,可以使用合理的分片策略(如哈希分片)来均衡负载。
动态扩容:当数据量持续增长,可以考虑通过增加分片表的数量来进行扩容。为了避免在扩容过程中发生数据倾斜,可以采用一致性哈希等动态分片算法。
【面试】一文搞懂MySQL的分库分表!_mysql 分片规则-CSDN博客
十 Java中sleep和wait的区别?
-
sleep方法:是Thread类的静态方法,当前线程将睡眠n毫秒,线程进入阻塞状态。当睡眠时间到了,会解除阻塞,进入可运行状态,等待CPU的到来。睡眠不释放锁(如果有的话)。
-
wait方法:是Object的方法,必须与synchronized关键字一起使用,线程进入阻塞状态,当notify或者notifyall被调用后,会解除阻塞。但是,只有重新占用互斥锁之后才会进入可运行状态。睡眠时,会释放互斥锁。
-
sleep 方法没有释放锁,而 wait 方法释放了锁 。
-
sleep 通常被用于暂停执行Wait 通常被用于线程间交互/通信
-
sleep() 方法执行完成后,线程会自动苏醒。或者可以使用 wait(long timeout)超时后线程会自动苏醒。wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或者 notifyAll() 方法