InnoDB如何引擎通过Next-Key Lock部分解决幻读

1. 幻读问题概述

幻读是数据库并发事务中最棘手的一类问题。它指在同一事务内,连续执行两次相同的查询语句,第二次查询所返回的结果集与第一次查询的结果集不同。具体表现为:新增了符合查询条件的行

示例场景

1
2
3
4
5
6
7
8
9
10
-- 事务A
BEGIN;
SELECT * FROM users WHERE age BETWEEN 20 AND 30;

-- 此时事务B插入了一条新记录
INSERT INTO users (name, age) VALUES ('NewUser', 25);

-- 事务A再次查询
SELECT * FROM users WHERE age BETWEEN 20 AND 30;
-- 发现多了一条记录,即"幻读"
阅读更多

SQL事务隔离级别详解

为什么需要事务隔离级别?

在并发环境下,多个事务同时操作数据库时会产生各种并发问题。如果不进行隔离控制,可能会导致数据的不一致性。主要会出现以下问题:

1. 脏读(Dirty Read)

  • 定义:一个事务读取到另一个事务未提交的数据
  • 危害:可能导致业务决策建立在不可靠的数据之上
阅读更多

深入理解堆(Heap)数据结构

1. 堆的基本概念

堆是一种特殊的完全二叉树,分为大根堆(大顶堆)和小根堆(小顶堆):

  • 大根堆(大顶堆):父节点的值总是大于或等于子节点的值
  • 小根堆(小顶堆):父节点的值总是小于或等于子节点的值

堆通常用数组实现,对于数组中的第i个节点:

  • 其左子节点的位置:2i + 1
  • 其右子节点的位置:2i + 2
  • 其父节点的位置:(i - 1) / 2

2. 堆的基本实现

下面是一个小根堆的Java实现:

阅读更多

阿里巴巴中间件-Canal

简介

img

**canal [kə’næl]**,译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费

早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同步的业务需求,实现方式主要是基于业务 trigger 获取增量变更。从 2010 年开始,业务逐步尝试数据库日志解析获取增量变更进行同步,由此衍生出了大量的数据库增量订阅和消费业务。

阅读更多

缓存与数据库一致性问题研究

缓存的查询

先查询缓存,如果查询失败,那么去查询DB,之后重建缓存,基本上不存在异议。

缓存的更新

先更新DB还是先更新缓存?是更新缓存还是删除缓存?在常规情况下,怎么操作都可以,但一旦面对高并发场景,就值得细细思量了。

先更新数据库再更新缓存

线程A:更新数据库(第1s)——> 更新缓存(第10s)

阅读更多

Java 阻塞队列

阻塞队列简介

阻塞队列的历史

Java 阻塞队列的历史可以追溯到 JDK1.5 版本,当时 Java 平台增加了 java.util.concurrent,即我们常说的 JUC 包,其中包含了各种并发流程控制工具、并发容器、原子类等。这其中自然也包含了我们这篇文章所讨论的阻塞队列。

为了解决高并发场景下多线程之间数据共享的问题,JDK1.5 版本中出现了 ArrayBlockingQueueLinkedBlockingQueue,它们是带有生产者-消费者模式实现的并发容器。其中,ArrayBlockingQueue 是有界队列,即添加的元素达到上限之后,再次添加就会被阻塞或者抛出异常。而 LinkedBlockingQueue 则由链表构成的队列,正是因为链表的特性,所以 LinkedBlockingQueue 在添加元素上并不会向 ArrayBlockingQueue 那样有着较多的约束,所以 LinkedBlockingQueue 设置队列是否有界是可选的(注意这里的无界并不是指可以添加任务数量的元素,而是说队列的大小默认为 Integer.MAX_VALUE,近乎于无限大)。

阅读更多

ConcurrentHashMap 为什么不允许key和value为null

在Java中,ConcurrentHashMap是一种线程安全的哈希表实现,广泛用于并发编程场景中。与传统的HashMap不同,ConcurrentHashMap不允许null键或null值。这种设计并非随意而为,而是经过深思熟虑的决定。


1. 并发操作中的歧义问题

1.1 null键的歧义

ConcurrentHashMap中,如果允许null键,当我们调用map.get(null)时,返回null意味着什么?

阅读更多

Java ConcurrentHashMap

ConcurrentHashMap 1.7

存储结构

Java 7 ConcurrentHashMap 存储结构

Java 7 中 ConcurrentHashMap 的存储结构如上图,ConcurrnetHashMap 由很多个 Segment 组合,而每一个 Segment 是一个类似于 HashMap 的结构,所以每一个 HashMap 的内部可以进行扩容。但是 Segment 的个数一旦初始化就不能改变,默认 Segment 的个数是 16 个,你也可以认为 ConcurrentHashMap 默认支持最多 16 个线程并发。

阅读更多

Java 集合研究


允许存储 null 的集合

List 接口的实现类

  • **ArrayList**:允许 null
  • **LinkedList**:允许 null
阅读更多

Java集合使用经验

集合判空

《阿里巴巴 Java 开发手册》的描述如下:

判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size()==0 的方式。

这是因为 isEmpty() 方法的可读性更好,并且时间复杂度为 O(1)

绝大部分我们使用的集合的 size() 方法的时间复杂度也是 O(1),不过,也有很多复杂度不是 O(1) 的,比如 java.util.concurrent 包下的 ConcurrentLinkedQueueConcurrentLinkedQueueisEmpty() 方法通过 first() 方法进行判断,其中 first() 方法返回的是队列中第一个值不为 null 的节点(节点值为null的原因是在迭代器中使用的逻辑删除)

阅读更多