数据库事务

数据库事务概念

什么是数据库事务

事务(transaction)是指逻辑上对数据的一组操作, 这组操作要么全部成功,要么全部失败,是不可分割的一个工作单位。

数据库事务的 4 个基本性质(ACID)

原子性(Atomicity)

事务的原子性是指事务是一个不可分割的工作单位,这组操作要么全部成功,要么全部失败。

一致性(Consistency)

在事务开始以前,被操作的数据的完整性处于一致性的状态,事务结束后,被操作的数据的完整性也必须处于一致性状态。

拿银行转账来说,一致性要求事务的执行不应改变A、B 两个账户的金额总和。如果没有这种一致性要求,转账过程中就会发生钱无中生有,或者不翼而飞的现象。事务应该把数据库从一个一致性状态转换到另外一个一致性状态。

隔离性(Isolation)

事务隔离性要求系统必须保证事务不受其他并发执行的事务的影响,也即要达到这样一种效果:对于任何一对事务T1 和 T2,在事务 T1 看来,T2 要么在 T1 开始之前已经结束,要么在 T1 完成之后才开始执行。这样,每个事务都感觉不到系统中有其他事务在并发地执行。

查看更多

MySQL索引

索引优化

索引失效案例

  1. 全值匹配
  2. 最佳左前缀法则。如果索引了多列,要遵守最左前缀法则,指的是查询从索引的最左前列开始并且不跳过索引中的列
  3. 不在索引列上做任何操作(计算、函数、「自动或者手动」类型转换),会导致索引失效而转向全表扫描
  4. 存储引擎不能使用索引中范围条件右边的列
查看更多

Redis zset 实现简单限流

除了控制流量,限流还有一个应用目的是用于控制用户行为,避免垃圾请求。比如在 UGC 社区,用户的发帖、回复、点赞等行为都要严格受控,一般要严格限定某行为在规定时间内允许的次数,超过了次数那就是非法行为。对非法行为,业务必须规定适当的惩处策略。

如何使用 Redis 来实现简单限流策略?

首先我们来看一个常见的简单的限流策略。系统要限定用户的某个行为在指定的时间里只能允许发生 N 次,如何使用 Redis 的数据结构来实现这个限流的功能?

我们先定义这个接口,理解了这个接口的定义,读者就应该能明白我们期望达到的功能。

// 指定用户 user_id 的某个行为 action_key,在特定的时间内 period,只允许发生一定的次数 max_count
public boolean isActionAllowed(String userId, String actionKey, int period, int maxCount) {}

// 调用这个接口,5 分钟之内只能发帖 10 次
limiter.isActionAllowed("xiaoming", "publish", 5 * 60, 10)

查看更多

java死锁编程及定位分析

死锁编程及定位分析

是什么

死锁是指两个或者两个以上的进程在执行过程中,因抢夺资源而造成的一种互相等待的现象,若无外力干涉它们将都无法推进下去,如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性也就很低,否则就会因争夺有限的资源而陷入死锁。

产生死锁的主要原因

  • 系统资源不足
  • 进程运行推进的顺序不合适
  • 资源分配不当
查看更多

java公平锁/非公平锁/可重入锁/递归锁/自旋锁

公平锁和非公平锁

是什么

公平锁:是指多个线程按照申请锁的顺序来获取锁,类似排队打饭,先来后到。

非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。在高并发的情况下,有可能会造成优先级反转或者饥饿现象。

两者区别

公平锁/非公平锁:并发包中 ReentrantLock 的创建可以指定构造函数的 boolean 类型来得到公平锁或非公平锁,默认是非公平锁。

关于两者区别:

公平锁,就是很公平,在并发情况下,每个线程在获取锁时会查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个,就占有锁,否则就会加入到等待队列中,以后会按照 FIFO 的规则从队列中取到自己。

查看更多

CopyOnWriteArrayList

写时复制

CopyOnWrite 容器即写时复制的容器。
往一个容器添加元素的时候,不直接往当前容器 Object[] 添加,而是先将当前 object[] 进行 Copy,复制出一个新的容器 Object[] newElements,
然后新的容器 Object[] newElements 里添加元素,
添加完元素之后,再将原容器的引用指向新的容器 setArray(newElements)
这样做的好处是可以对 copyonwrite 容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。
所以 copyonwrite容器也是一种读写分离的思想,读和写不同的容器。

原子类Atomiclnteger的ABA问题

ABA问题怎么产生的

CAS会导致 ABA 问题

CAS 算法实现一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并替换,那么在这个时间差类会导致数据的变化。

尽管线程 one 的 CAS 操作成功,但是不代表这个过程就是没问题的。

原子引用

解决ABA问题

时间戳原子引用

AtomicStampledReference
ABADemo

查看更多

CAS理解

比较并交换

CAS底层原理?如果知道,谈谈比对Unsafe的理解

Unsafe

  1. Unsafe 是 CAS 的核心类,由于 Java 方法无法直接访问底层系统,需要通过本地(native)方法来访问,Unsafe 相当于一个后门,基于该类可以直接操作特定内存的数据。Unsafe 类存在于 sun.misc 包中,其内部方法操作可以像 C 的指针一样直接操作内存,因为 Java 中 CAS 操作的执行依赖于 Unsafe 类的方法。

    注意 Unsafe 类中的所有方法都是 native 修饰的,也就是说 Unsafe 类中的方法都直接调用操作系统底层资源执行相应任务。

  2. 变量 valueOffset,表示该变量在内存中的偏移地址,因为 Unsafe 就是根据内存偏移地址获取数据的。

查看更多

volatile

volatile 的理解

volatile 是 Java 虚拟机提供的轻量级的同步机制

保证可见性

各个线程对主内存中共享变量的操作都是各个线程各自拷贝到自己的工作内存进行操作后再写回主内存中的。

这就可能存在一个线程 A 修改了共享变量 X 的值但还未写回主内存时,另一个线程 B 又对准内存中同一个共享变量 X 进行操作,但此时 A 线程工作内存中共享变量 X 对线程 B 来说并不是可见,这种工作内存与主内存同步存在延迟现象就造成了可见性问题。

不保证原子性

禁止指令重排

volatile 实现禁止指令重排优化,从而避免多线程环境下程序出现乱序执行的现象。

先了解一个概念,内存屏障又称内存栅栏,是一个CPU指令,它的作用有两个:
一是保证特定操作的执行顺序
二是保证某些变量的内存可见性(利用该特性实现volatile的内存可见性)

查看更多