MySQL事务隔离(2)
到底是隔离还是不隔离假如现在有如下表:
id
k
1
1
2
2
现在执行如下操作:
在可重复读的隔离下,这里面事务B读取到的k的值是3,而事务A读取到的k是1。下面看具体原因。
在MySQL中,有两个视图概念:
第一个是view,这里就是查询语句定义的虚拟表,在调用的时候执行查询语句并生成结果。
另一个是InnoDB在实现MVCC时用到的一致性读视图,即consistent read view,用于支持RC(Read Committed,读提交)和RR(Repeatable Read,可重复读)隔离级别的实现。
可重复读隔离主要用到第二个视图。
“快照”在MVCC里是怎么工作的?在可重复读隔离级别下,事务启动时就拍了个快照,这个快照是基于整个库的。但是这个快照并不需要拷贝整个数据库的数据,具体做法如下:
InnoDB的每一个事务都有一个唯一的事务ID,叫作transaction id。它是在事务开始的时候向InnoDB的事务系统申请的,是按申请顺序严格递增的。
而每一行的数据都是有多个版本。每次事务进行更新数据时,都会生成一个新的版本的数据,然后这个新数据会绑定 ...
Java对象的共享
可见性12345678910111213141516171819202122public class NoVisibility { private static boolean ready; private static int number; private static class ReaderThread extends Thread { public void run() { while (!ready) { // 暂停当前正在执行的线程,放弃CPU资源,并执行其他线程。 Thread.yield(); } System.out.println(number); } } public static void main(String[] args) { new ReaderThread().start(); ...
HotSpot虚拟机中的对象
本文所涉及的内容都是基于HostSpot虚拟机而言的。
对象的创建一个对象的创建(这里不包括数组和Class对象),在Java中仅仅是一个new关键字,当虚拟机遇到字节码new指令时,它会先检查指令中的参数能否在常量池(①)中定位到一个符号引用(②),并且检查这个符号引用代表的类是否已经被加载、解析和初始化过,如果没有,那必须先执行类加载。
类加载检验通过后,虚拟机会在堆中为它划分一块区域,区域的大小在类加载完成后可以确定。这里划分区域根据不同虚拟机的设计,会有不同的方案。
如果Java堆内存是绝对规整的,一半放用过的,一半放空闲的,中间有一个指针用作分界。那么内存分配只需要将那个指针向空闲的方向移动与对象大小的位置即可,这种分配方式称为指针碰撞。
如果内存是不规整的,即使用的和未使用的内存交错在一起,虚拟机就需要维护一个列表,记录哪些内存是可以使用的,在分配时就需要从虚拟机中找到足够大小的空间划分给对象,并更新列表记录,这种称为空闲列表。
采用那种分配方案取决于堆是否规整,而是否规整又取决于垃圾收集器是否带有空间压缩整理的能力。
由于内存分配是特别频繁的一件事,指针移动这一操作并不是 ...
MySQL全局锁和表锁
根据加锁的范围,MySQL里面的锁大致可以分成全局锁、表级锁和行锁三类
全局锁全局锁就是对整个数据库实例进行加锁。它的一个经典使用场景就是做全库的逻辑备份,也就是把整个库的数据都查出来存成文本。
加了全局锁之后,整个数据库系统就变为了只读状态,意味着很多业务不可以进行。但是如果不加,那么考虑以下情况:
现在有一个用户余额表以及用户库存表,假设用户购买商品时发起了逻辑备份,如果先备份余额表,后备份商品表,那么在扣除余额前余额表备份完成,添加商品到用户库存后才备份的库存表,这时如果用备份进行恢复,那么就会导致用户账户没有扣钱,但是却多了库存。如果反过来,则会导致用户账户被扣,而且没有商品。
而我们会发现,在真正导出数据时,是可以对数据库做修改。这是因为导出数据前,数据库开启了一个可重复读隔离级别的事务,保证了在事务执行期间读到的数据和事务开始时是一致的。
表级锁MySQL的表级锁有两种,一种是表锁,一种是元数据锁(MDL),
表锁可以使用lock tables … read/write给数据库加表锁,可以使用unlock tables主动释放锁,也可以在客户端断开时自动释放。
...
Java内存区域划分(JVM内存模型)
运行时数据区域
1、程序计数器该部分可以看作当前线程所执行的字节码的行号指示器。字节码解释器就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。
由于Java虚拟机的线程是通过轮流切换线程,然后分配处理器时间来实现的。任何时间,一个处理器(多核处理器就是一个内核)都只会执行一个线程中的一条指令。所以说切换线程后,之前执行到哪里都需要进行保存,所以每一个线程都会有一个独立的程序计数器。
2、Java虚拟机栈Java虚拟机栈也是也是线程私有的,它的生命周期与线程相同。
该部分描述的是Java方法执行的线程内存模型:每个方法执行时,虚拟机都会创建一个栈帧用于存储局部变量表,操作数栈,动态连接,方法出口等信息。方法被调用到结束,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
局部变量表该部分存放了编译器可知的Java虚拟机基本数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它并不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或者其他与此对象相关的位置)和re ...
MySQL索引
索引的出现其实就是为了提高数据查询的效率。
索引常见的模型哈希表适合等值查询,不适合范围查询。因为会出现哈希冲突,冲突后采用链地址法解决。就会导致发生冲突后要遍历整个链表。
有序数组适合等值查询以及范围查询。有序数组在单值查询可以使用二分法快速查找,范围找到一个然后往左右遍历即可,但是它插入数据时要维护这个有序数组就比较麻烦,适合用于静态存储引擎。
搜索树二叉搜索树的特点是:左边小,右边大(相对根节点)查找效率和二分一样,O(LogN)。但是为了保证这个效率,就需要保证它是平衡二叉树(考虑插入数据是正序或者倒序情况,会退化成链表),为了确保平衡,插入时间复杂度也是O(LogN)
多叉树:孩子节点从左到右依次递增。
二叉树搜索效率最高,但是往往采用多叉树,因为索引还要写入磁盘,意味着我们查询索引可能也要读取磁盘。考虑一种情况,100万数据量,那么二叉树高20,如果这些索引没有在内存当中,那么就需要去读取20个数据块。
每次从磁盘读取数据都是按照数据块来读取的,而不是仅仅查找那一条记录。而MySQL并不是一开始就把所有的索引都加载到内存当中,而是按需加载。MySQL有一个缓冲池,当需要访问 ...
Java锁优化
HostSpot虚拟机在 JDK 6 中实现了大量的锁优化技术,比如说适应性自旋,锁消除,锁膨胀,轻量级锁,偏向锁等。
自旋锁与自适应自旋自旋锁由于Java线程设计的原因,把一个线程挂机或者恢复都需要由用户态切换到核心态,这些都会给Java虚拟机带来很大的压力。但是由于现在用户的cpu都是多核的,可以让多个任务并行运行,所有很多共享数据锁的持有时间是很短的,如果因为这么短的时间就让一个线程挂起再恢复,很不值当。可以让一个线程等一小会儿,但不放弃处理时间,看看持有锁的线程是否很快会释放。而等的这个过程,我们只需要让线程忙循环(自旋),这就是自旋锁。
自旋锁并不能代替阻塞。如果持有锁的时间很短,那么自旋锁效率就很高,如果持有很长,自旋的线程占用着cpu却没有做有用的事,导致资源浪费。所以自旋锁一般设置循环次数,如果超过还没获取到锁,就挂起。
自适应自旋JDK6中引入了自适应自旋,这时候自旋的次数不再是固定的了,改变为由上一次获取锁的时间以及锁持有者的状态来决定的。
如果在同一个对象上,自旋等待刚刚获取到锁,并且持有锁的线程正在运行,那么虚拟机就会认为这次自旋也会成功,而且允许这次自旋的持续 ...
Redis如何解决缓存雪崩、击穿、穿透
缓存雪崩缓存雪崩是指大量的应用请求无法在 Redis 缓存中进行处理,紧接着,应用将大量请求发送到数据库层,导致数据库层的压力激增。
原因以及解决办法1、缓存中有大量数据同时过期,导致大量请求无法得到处理。
解决方案:
1)可以避免给数据设置相同的过期时间,如果业务需要,可以给这个过期时间增加一个小的随机数,使其过期时间相差1~3分钟,这样可以避免大量的key同时过期。
2)我们还可以通过服务降级,指发生缓存雪崩时,针对不同的数据采取不同的处理方式。
当业务应用访问的是非核心数据时,暂时停止从缓存中查询这些数据,而是直接返回预定义信息、空值或是错误信息;
当业务应用访问的是核心数据时,仍然允许查询缓存,如果缓存缺失,也可以继续通过数据库读取。
2、Redis 缓存实例发生故障宕机了
解决方案:
1)在业务系统中实现服务熔断或请求限流机制。在发生缓存雪崩时,为了避免影响整个系统,我们可以将这部分请求不做处理,直接返回错误,以免导致整个系统崩溃。
2)提前预防。通过主从节点的方式构建 Redis 缓存高可靠集群。如果 Redis 缓存的主节点故障宕机了,从节点还 ...
MySQL事务隔离
什么是事务事务就是要保证一组数据库操作,要么全部成功,要么全部失败。也就是ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性)。
隔离性与隔离级别当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念(隔离级别越高,效率越低)。
SQL标准的隔离级别1、读未提交:一个事务还没提交时,它做的变更就能被其他事务看到。
2、读提交:一个事务提交后,它做的变更才能被其他事务看到。
3、可重复读:一个事务执行过程中看到的数据,总是和 这个事务开始时看到的数据时一致的。该事务未提交的变更对其他事务也是不可见的。
4、串行化:将事务串行执行,如果出现冲突,则后执行的事务必须等前面的执行完才可以执行。
假如数据库只有一列,且只有一个值c = 1,执行上面事务时,不同隔离级别得到的结果如下:
1、读未提交:虽然事务b没提交,但是可以被事务A看到,所以V1的值是 ...
Java线程安全
Java中的线程安全一个比较严格的线程安全定义:当多个线程同时访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那就称这个对象是线程安全的
Java语言中的各种操作的共享数据可以分为以下五类:不可变,绝对线程安全,相对线程安全,线程兼容和线程对立。
不可变不可变的对象,他的线程一定是安全的。
绝对线程安全绝对线程安全需要满足上面提到的定义。而Java API中提到的线程安全的类,大多都是相对线程安全。比如说Vector是一个线程安全的容器,因为他的add(), get(), size()的方法都是用synchronized修饰的。尽管这样,并不意味着它永远不需要同步手段。
比如以下代码:
123456789101112131415161718192021222324252627282930313233private static Vector<Integer> vector = new Vector<Integer>();public stati ...