分布式事务
简介 我们在过去总是使用本地事务,也就是数据库提供的事务操作,其中具有ACID的特性,但在如今我们的各个模块儿被拆分,服务与服务间相互调用,简单来说就是需要跨进程的事务,我们来想一下现有的本地事务是否能解决分布式事务。情况1:跨JVM,跨数据库产生分布式事务
graph TD;
用户下订单-->订单模块;
订单模块-->物流模块;
订单模块-->订单表;
物流模块-->物流表;
用户直接调用订单模块儿,开启事务,然后在订单表中存入数据,然后再远程调用物流模块儿,去操作物流模块儿,我们可以想到如果物流模块儿出现问题,订单模块儿远程调用发生错误,是会进行事务回滚的。应该是没问题的。那如果物流模块儿确实修改成功了,但网络传输出现了问题,订单模块儿就进行回滚了,则就导致了物流模块儿有数据,订单模块儿没数据的问题。我们再考虑下面的问题:
情况二:跨数据库实例产生分布式事务
graph TD;
人员管理模块儿-->用户数据库;
人员管理模块儿-->订单数据库;
前置理论
CAP理论 CAP ...
后端演化过程
后端风云 本文简单概况了一下刘欣老师的《码农翻身》的后端风云章,这本书强烈推荐给大家,每看一遍都有不同的感觉,通俗易懂且知识面大而全。居家旅行,必备良药!!!
早期初级阶段:
在我们早期学习 javaWeb阶段,将订单模块儿,购物车模块儿,支付模块儿都写在了一个项目中,并访问一个Mysql数据库,这在自己练手项目中是没问题的,但如果系统上线,有大量的用户同时访问该系统,Tomcat服务器首先需要考虑能不能支撑住大的并发量(默认150,当超过250时,就应该考虑的服务器的集群),其次是Mysql数据库,在高并发两三千也就差不多了,要知道Mysql数据库读取数据是很慢的操作,它本质是从硬盘中读取文件的。
优化点1:使用缓存
可以看到我们给中间加了一个Redis中间件作为缓存,现在服务器需要获取数据,先到缓存中找数据,如果找不到再去Mysql中找数据,找到数据后再写回Redis中,这样下次请求数据可以直接在缓存中获取数据,那缓存的好处是啥,缓存是加载在内存中的,读取数据那肯定是快的多。这样就减轻Mysql数据库的压力。但是如果用户每次请求先去缓存找,如果都没 ...
Mysql的事务与索引
Mysql事务与索引
需求:先从数据库中读取一个字段的值,然后修改该字段,由于操作不是原子性,无法保证线程安全,也就是当A线程读取值为1时,想在原有基础上+1,赋值为2。而B线程已经将数据修改为2了,那么A线程应该是在2的基础上+1,赋值为3。但实际中可能该值都到8了,A又给人家改回2了。所以我们该如何解决数据安全问题?
本能想到利用JVM层面的锁机制,保证每次只有一个线程进行操作,但这个性能太差,而且JVM锁无法在分布式中生效。
利用数据库事务操作,下文我们主要讲Mysql事务的知识。
数据库事务 在讲事务之前,我们需要明确Mysql中只有Inndb存储引擎支持事务,如果是其他存储引擎则无法使用事务,所以我们应先检查自己的存储引擎是否正确。
查看数据库支持的存储引擎:show engines
查看自己的表使用的引擎:show table status from 数据库名 where name = '表名';
修改表的存储引擎:ALTER TABLE 表名 ENGINE=引擎名称;
查看是否是自动提交:show variables like & ...
封装自己的SDK
封装自己的SDK 我们在开发Spring项目时常常会引入各种xxx-spring-boot-starter的依赖包,然后在配置文件中填入必要的信息,就可以使用依赖提供好的容器。这里是在鱼皮新项目直播中学习到的,特此记录一下。可在未来封装自己的SDK进行封装与装逼。
SDK项目下
将pom.xml中的标签删除
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>o ...
第三章:二叉树问题
树形dp套路在做树结构的题目中,如果求解规则可以分为,以某个节点为子树,先向左要数据,再向右要数据,最后返回总数据,我们就可以使用该套路。
递归进行遍历
public static void QTraverse2(Point tree) {
if (tree == null) {
return;
}
System.out.println("先序");
QTraverse2(tree.left);
System.out.println("中序");
QTraverse2(tree.right);
System.out.println("后续");
}
非递归进行遍历
//非递归前序遍历 使用栈
public static void QTraverse1(Point tree) {
Stack<Point> stack = new Stack<> ...
并发编程
基础的概念什么是并发操作?利用多核CPU去完成任务,类似于使用影分身去完成不同的作业,这肯定比我一个人一个个写要快的多
什么时候可以使用并发?当有多核CPU时。如果只有一个CPU,那并发实际上只是线程的不断切换,好比我在数学作业上先写点儿,再到语文上再写点儿,因为处理的很快,给人的感觉是同时操作的。但实际上只会比原来更慢(因为有线程切换的损耗)。
如何使用并发编程?public class BasicLearn {
public static void main(String[] args) {
new Thread(new A()).start();
new Thread(new FutureTask<String>(new B())).start();
new C().start();
}
}
// 无返回值
class A implements Runnable {
@Override
public void run() {} ...
Shell脚本
Shell输入输出重定向
我们在后台挂在jar包时,常使用过一个命令:nohup java -jar xxx.jar > java.log 2>&1 &
这里就有几个知识点:
末尾的 & 代表后台默认启动,页面关闭后,程序终止。
头部的 nohup 表示页面关闭后,后台仍继续运行,如果没有指定日志文件,则默认将输出重定向到nohup.log文件
java -jar xxx.jar最基本的启动jar包
> java.log 将运行jar包后的输出日志文件重定向到 java.log文件中
2>&1将标准输出与标准错误输出合并,在当前语义下就是将所有信息输出到 java.log文件。
如果只写 > 表示只将正常输出重定向到log文件中,错误信息直接显示页面
如果写 2> 表示只将错误输出重定向到log文件中,正常信息直接显示页面
> 默认表示重定向,并覆盖原来内容
>> 表示以追加的方式重定向
这样我们就可以监控到后台运行程序的日志信息了。
shell脚本保证程序运行
需求:当我们将项目挂在到服务 ...
IO流复习
今天面试的时候问了一道IO流的题,鉴于之前在java基础篇的时候学过一遍,后再无使用和复习,就再跟着韩顺平老师的课件复习一遍。
IO流IO流是什么?当我们使用程序读取或修改本地文件时, 文件是以流的形式加载在内存中的。
InputStream:输入流,从数据源加载至内存。
OutputStream:输出流,从存在中加载至目的地。
IO流有哪些分类
输入流,输出流是最宽泛的概念,也是我们上面讲的概念。那为什么还需要分成字节流,字符流,节点流,处理流呢?
字节我们都知道:byte,是一个二进制单位,而我们的文件可以是txt(存储字符的),png(存储图片的),mp3(存储音频的),mp4(存储视频的),但传输的时候我们都将其转化为二进制,因为计算机只能识别二进制,也就是字节。所以字节流的意思就是说:将该文件以字节为单位进行读取,可以一个字节一个字节读取,或者一段字节数组进行重复读取。这样不管是什么文件都可以进行传输了。
那为啥还需要字符流呢?这是已因为字节流是直接作用于文件的,而字符流使用了缓冲区,先将数据写入缓冲区进行读写操作,当输出流close或者flush时,才会把内容写 ...
反射复习
反射复习能干嘛 当我们创建一个对象时,需要事先在源码中进行编写,例如:new Object(),但如果未来发生变化,我就不得不在源码中进行修改,这也违反了开闭原则,我们能不能仅修改配置文件就可以调整系统中需要创建的类。比如说我在配置文件写:
classPathName=org.Mysql.SQLBuilder
mathod=getSqlSession
我想未来我的系统可以读取该配置文件,知道需要的是一个org.Mysql.SQLBuilder类,并且调用getSqlSession方法。等到未来如果修改成:
classPathName=org.Oracle.SQLBuilder
mathod=getSqlSessions
那系统也能动态的感知到现在需要的是org.Oracle.SQLBuilder,并要调用getSqlSessions方法。在使用反射前我们是无法解决的,因为所有使用的类,在编译期就固定了。所以引出了反射技术。也就是为了在运行期,可动态获取类信息或者创建对象。
这在我们学习框架例如Spring,Mybatis时就经常使用,比如我想动 ...
Java基础
List
集合中线程安全的方案:
Collections.synchronizedList(arrayList);
Vector
CopyOnWriteArrayList
Collections.synchronizedListCollections.synchronizedList 使用了装饰者模式,将原本的List包装了一层,在调用List原本的方法时,通过多态进行加锁操作,这里我们注意:读写操作加锁,获取迭代器不加锁
public boolean add(E e) {
synchronized (mutex) {return c.add(e);}
}
public E get(int index) {
synchronized (mutex) {return list.get(index);}
}
public Iterator<E> iterator() {
return list.iter ...