一条sql更新语句是如何执行的
一条修改语句的过程也会涉及到查询语句的流程,不过它会额外涉及到两个日志操作,分别是redo log(重做日志)和 binlog(归档日志)。
redo log(重做日志)在MySQL中,如果每一次更新都要写进磁盘,而磁盘又需要找到对应记录的位置,然后再更新,整个过程I/O成本,查找都很高,所以MySQL采用WAL技术,全称是Write-Ahead Logging,关键点就是先写日志,然后再写磁盘。
具体做法是,当有一条记录需要更新的时候,InnoDB引擎就会先把记录写到redo log里面,并更新内存,这个时候更新就算完成了。同时,InnoDB引擎会在适当的时候,将这个操作记录更新到磁盘里面,而这个更新往往是在系统比较空闲的时候。
而InnoDB的redo log大小是固定的,可以进行配置,如果被写满,那么就会从头开始写。可以理解为一个循环队列,如下图所示:
write pos是当前记录的位置,一边写一边后移,写到第3号文件末尾后就回到0号文件开头。checkpoint是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。write pos和chec ...
一条sql查询语句是如何执行的
整体结构Mysql的架构图如下所示:
上图架构中的查询缓存,在myssql8.0及其以上版本已经被移除。
MySQL可以分为Server层和存储引擎层两部分。
Server层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖MySQL的大多数核心服务功能,以及所有的内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等。
而存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持InnoDB、MyISAM、Memory等多个存储引擎。现在最常用的存储引擎是InnoDB,它从MySQL 5.5.5版本开始成为了默认存储引擎。
连接器连接器是负责客户端和MySQL进行连接的,在连接器这里会验证用户输入的账号和密码,以及对应的权限。
连接完成后,如果客户端太长时间没有动静,连接器就会将它断开。这个时间由wait_timeout控制,默认8小时。
数据库里面,长连接是指连接成功后,如果客户端持续有请求,则一直使用同一个连接。短连接则是指每次执行完很少的几次查询就断开连接,下次查询再重新建立一个。
分析器这一步的主要工作就是检查sql语 ...
如何使用Redis作为消息队列
消息队列的存取需求在分布式系统中,当两个组件要基于消息队列进行通信,一个组件会把消息传递给消息队列,然后就去做其他的事情,另一个组件会从消息队列中读取数据,在进行处理。我们把发送消息的称为生产者,消费消息的称为消费者。
这样处理的好处是,如果生产者发送消息的速度很快,消费者来不及处理也没问题,可以将这些消息暂存在消息队列当中,然后消费者可以按照一定的处理速度去异步的处理这些消息,从而达到一个流量消峰的效果。
不过,消息队列在存取消息时,必须要满足三个需求,分别是消息保序、处理重复的消息和保证消息可靠性。
消息队列对可靠性的要求消息保序虽然消费者是异步处理这些消息的,但是需要按照消息的顺序进行消费。不然可能会出现错误。
假如现在有一个x=3,第一个消息要把x * 2,然后第二个消息要把x + 3,如果消息正常执行,x的值最终为9,如果先执行了x + 3,那么x的值最终就会变为12。
重复消息处理消费者从消息队列读取消息时,有时会因为网络堵塞而出现消息重传的情况。此时,消费者可能会收到多条重复的消息。对于重复的消息,消费者如果多次处理的话,就可能造成一个业务逻辑被多次执行,如果业 ...
如何避免单线程的Redis阻塞
Redis有哪些阻塞点客户端:网络 IO,键值对增删改查操作,数据库操作;
磁盘:生成 RDB 快照,记录 AOF 日志,AOF 日志重写;
主从节点:主库生成、传输 RDB 文件,从库接收 RDB 文件、清空数据库、加载 RDB 文件;
切片集群实例:向其他实例传输哈希槽信息,数据迁移。
一、和客户端交互时的阻塞点Redis采用多路复用I/O机制,避免了主线程一直处在等待网络连接或请求到来的状态,所以与客户端的网路通信不会是阻塞点。
1、查询时的阻塞而Reddis中涉及到集合的操作,复杂度通常为O(N),例如集合元素全量查询操作 HGETALL、SMEMBERS,以及集合的聚合统计操作,例如求交、并和差集。这些操作可以作为 Redis 的第一个阻塞点:集合全量查询和聚合操作。
2、删除时的阻塞删除操作的本质是要释放键值对占用的内存空间,释放内存只是第一步,为了更加高效地管理内存空间,在应用程序释放内存时,操作系统需要把释放掉的内存块插入一个空闲内存块的链表,以便后续进行管理和再分配。这个过程本身需要一定时间,而且会阻塞当前释放内存的应用程序,所以,如果一下子释放了大量内存 ...
Java与线程
线程的实现线程是比进程更轻量级的调度执行单位,线程的引入可以把一个进程的资源分配和执行调度分开, 各个线程既可以共享进程资源(内存地址,I/O等),又可以独立调度。
实现线程主要有3种方式,使用内核线程实现(1:1实现),使用用户线程实现(1:N实现),使用用户线程加轻量级进程混合实现(N:M实现)。
内核线程的实现内核线程(KLT)就是直接由操作系统内核支持的线程。这种线程由内核完成线程切换,由内核操纵调度器调度线程,并负责将线程的任务映射到处理器上。但是程序一般不直接使用内核线程,而是使用他的一种接口,轻量级进程(LWP),就是我们通常意义说的线程,每一个线程都有一个内核线程支持,这种轻量级进程与内核线程是1:1的关系。具体结构如下图所示:
因为每个线程都有内核线程支持,可以当作一个独立的调度单元,即使其中一个被阻塞,也不影响整个进程继续工作。
局限性:由于是基于内核线程,所以线程的创建,同步等操作都需要系统调用。而系统调用需要从用户态切换为内核态。而且每个轻量级进程都需要一个内核线程支持,会消耗一定的内核资源。
用户线程实现从广义上讲,一个线程只要不是内核线程,他就是 ...
Java先行发生原则
先行发生是Java内存模型中定义的两项操作之间的偏序关系,比如说操作A先行发生于操作B,其实就是说在发生操作B之前,操作A产生的影响能被操作B观察到,“影响”包括修改了内存中共享变量的值、发送了消息、调用了方法等。
Java语言无须任何同步手段保障就能成立的先行发生规则有且只有以下几种:
1、程序次序规则(Program Order Rule):在一个线程内,按照控制流顺序,书写在前面的操作先行发生于书写在后面的操作。注意,这里说的是控制流顺序而不是程序代码顺序,因为要考虑分支、循环等结构。
2、管程锁定规则(Monitor Lock Rule):一个unlock操作先行发生于后面对同一个锁的lock操作。这里必须强调的是“同一个锁”,而“后面”是指时间上的先后。
3、volatile变量规则(Volatile Variable Rule):对一个volatile变量的写操作先行发生于后面对这个变量的读操作,这里的“后面”同样是指时间上的先后。
4、线程启动规则(Thread Start Rule):Thread对象的start()方法先行发生于此线程的每一个动作。
5、线程终止规则( ...
Java内存模型的可见性、原子性和有序性
原子性由Java内存模型来直接保证的原子性变量操作包括read、load、assign、use、store和write这六个,我们大致可以认为,基本数据类型的访问、读写都是具备原子性的。这里的访问和读写只是包括了访问一个变量的值以及给变量赋值,像i++这种操作并不是原子性的。还要注意long 和double的非原子协定,但是这个概率发生很小,可以忽略。
如果应用场景需要一个更大范围的原子性保证,可以使用synchronized关键字。
可见性可见性就是指当一个线程修改了共享变量的值时,其他线程能够立即得知这个修改。Java内存模型是通过在变量修改后将新值同步回主内
存,在变量读取前从主内存刷新变量值这种依赖主内存作为传递媒介的方式来实现可见性的,无论是普通变量还是volatile变量都是此。
普通变量与volatile变量的区别是,volatile的特殊规则保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。
Java的synchronized和fina也具有可见性。
同步块的可见性是由“对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store、wri ...
Cluster规模对于通信的影响
实例通信方法和对集群规模的影响redis官方给出了Redis Cluster的规模上限为1000个实例,其中一个限制实例规模的因素就是实例间的通信会随着实例规模的增加而增大,因此在实例超过一定规模后,实例增加吞吐量反而会下降。
Redis Cluster 运行时,每个实例都会保存slot和实例的对应关系,以及自身的状态信息。为了让集群中每个实例都知道其他实例的状态,所以实例之间需要进行通信,采用的是Gossip 协议。
Gossip 协议原理1、每个实例之间会按照一定的频率,从集群中随机挑选一些实例,把 PING 消息发送给挑选出来的实例,用来检测这些实例是否在线,并交换彼此的状态信息。PING 消息中封装了发送消息的实例自身的状态信息、部分其它实例的状态信息,以及 Slot 映射表。
2、一个实例在接收到 PING 消息后,会给发送 PING 消息的实例,发送一个 PONG 消息。PONG 消息包含的内容和 PING 消息一样。
Gossip 协议可以保证在一段时间后,集群中的每一个实例都能获得其它所有实例的状态信息。
不难看出,实例间使用 Gossip 协议进行通信时,通信开销受到 ...
Volatile关键字
内存模型对volatile的特殊处理当一个变量被定义成volatile之后,它将具备两项特性:
特征一、保证此变量对所有线程的可见性,这里的“可见性”是指当一条线程修改了这个变量的值,新值对于其他线程来说是可以立即得知的。
而普通变量并不能做到这一点,普通变量的值在线程间传递时均需要通过主内存来完成。比如A修改了一个值,这个值要写回主内存,而线程B只有在A写回主内存后并且读取主内存,才会得知该值改变了。
volatile变量在各个线程的工作内存中是不存在一致性问题的(从物理存储的角度看,各个线程的工作内存中volatile变量也可以存在不一致的情况,但由于每次使用之前都要先刷新,执行引擎看不到不一致的情况,因此可以认为不存在一致性问题),但是Java里面的运算操作符并非原子操作,这导致volatile变量的运算在并发下一样是不安全的。
12345678910111213141516171819202122232425262728public class VolatileTest { public static volatile int race = 0; public ...
Java内存模型之主内存与工作内存
每秒事务处理数量(TPS)是衡量一个服务性能高低好坏的重要指标之一。它代表着一秒内服务端平均能响应的请求综述,而且TPS值与程序的并发能力又有密切的关系。
为了解决计算机存储设备与处理器运算速度的差距,除了增加缓存外,还有一种优化方法,就是处理器可能会对输入的代码进行乱序执行,处理器在计算后会把乱序的结果进行重组,保证该结果与顺序执行时的一致,但是并不保证程序中各个语句计算的先后顺序与输入代码中的顺序一致。因此,出现一个计算任务依赖另一个计算任务的中间结果,那么其顺序性并不能靠代码的先后顺序来保证。
内存模型可以理解为在特定的操作协议下,对特定的内存或高速缓存进行读写访问的过程抽象。
主内存与工作内存Java内存模型的主要目的是定义程序中各种变量的访问规则,即关注虚拟机中把变量值存储到内存和从内存中取出变量值这样的底层细节。此处的变量包括了实例字段、静态字段和构成数组对象的元素,但是不包括局部变量与方法参数。因为后两个是线程私有的,并不会被共享,也不存在竞争。
Java内存模型没有限制执行引擎使用处理器的特定寄存器或缓存来和主内存进行交互,也没有限制即时编译器是否需要进行调整代码的执行 ...