【cfengDB】自己实现数据库第0节 ---整体介绍及事务管理层实现

2023-11-06

LearnProj


本文作为数工底层的项目CfengDB开始篇章,介绍开发缘由和实现思路


cfeng之前对数据库研究不深入,之前只是能够做到基本的SQL查询和基本的慢SQL优化,之前拿到数据库系统工程师证书还是只在业务上对于DB系统使用更深入,但是cfeng基于work的理解,当作为一个优秀的产品使用者之后,再来作为产品的开发者,二者是互相促进的, 现在信息时代的发展大数据量变得非常普遍,了解DB的设计开发对于我们的code是非常有帮助的,从SQLboy变成engineer

MySQL系统结构

工作中最常使用的DB系统应该就是MySQL了,当然随着信创生态,一些国产数据库也广泛普及,如何让我们的SQL更高效,查询更迅速,一致性问题等都是需要考虑的问题,MySQL最常见的几个概念应该就是索引,锁 + 事务MVCC了, 本文就不探究这些业务层面的实现了,这里主要介绍一下MySQL的架构。

img

这是网络上一个比较简化的结构图, 整体SQL的执行来说就4个部分:

  • 连接器: 管理相关MySQL客户端与服务端的连接同时会进行旋前的验证
  • 分析器: 词法分析和语法分析,也就是MySQL需要读懂整条SQL的意思
  • 优化器: 客户端传送的SQL可能非常臃肿繁杂,server端需要进行简单的优化
  • 执行器: MySQL读懂SQL含义并优化之后,开始执行,底层的存储引擎是插件式的,可供选择

一条SQL执行流程

上面的各个模块都是比较简化版的,这里再实例详述一遍。

img

后台应用和MySQL之间都维护了连接池,用于管理所有的连接,通过Connect就可以将待执行的SQL传送给MySQL服务端执行。

SQL语句到达MySQL服务端之后

  1. server的线程会将接收到的语句交给SQL接口(组件),该interface专门由于执行SQL语句
  2. SQL接口将语句传递给SQL解析器parser,进行词法语法分析,理解SQL语句的意思(从哪张表,什么过滤条件,提取哪些字段…)
  3. 之后将语句传送给查询优化器,选择最优的路径,达到最佳的性能(比如select x from student where id =1, 可以先查找所有的x再过滤,或者先过滤再取x)这类的路径选择就是优化器的工作
  4. 选择最佳路径后,再传递给执行器将SQL语句按逻辑执行(比如调用存储引擎的接口,获取user表的数据,判断,继续…)
  5. 调用存储引擎,执行,执行器会调用存储引擎完成 SQL的操作,存储引擎按照一定的步骤查询内存缓存数据,更新磁盘数据。

为了提高效率,MySQL中也存在缓存,如果SQL命中缓存,就不会再走流程,节约时间

cfengDB整体结构

就模块划分来说,MySQL是十分优秀的,cfengDB最多只能说是帮助us更加理解数据库这种产品的一种最简单的设计而已。

因为cfengDB需要完成的最基础的任务就是识别并正确执行SQL,因此1.0.0就只针对这一过程进行结构设计(其余的向连接池化,用户管理…都后续再设计)

cfengDB整体上也是分为前端和后端,前后端通过网络Socket同学,前端的职责就是读取用户的SQL并发送给后端server指向,输出返回的结果,后台和MySQL流程一样需要识别合法的SQL并执行

为了整体的可扩展性和实现的难度,采用分层和分治的思想进行模块划分,整体上划分为事务管理模块Transaction Manager, 数据管理模块Data Manager, 版本锁管理Version Manager,索引管理Index Manager,表管理Table Manager

最终需要实现的就是表管理Table Manager,通过表管理模块就可以提供server的相关服务,模块分层和依赖关系如下:在这里插入图片描述

  • 事务管理TM: 维护TID文件来维护事务的状态,提供接口供其他的模块来查询事务(参考的MySQL的MVCC)
  • 数据管理DM: 直接管理数据库DB文件和日志文件,需要分页管理DB文件并且需要缓存,同时需要管理日志文件以供故障回复,抽象DB文件类提供上层使用
  • 锁管理VM: 并发管理模块,基于2PL协议实现调度可串行化,利用MVCC实现隔离级别
  • 索引管理IM: 基于B+树实现索引
  • 表管理TBM: 整体上的表和字段管理,完成SQL解析和执行

事务管理TM模块

从模块层级来看,TM作为底层是最基础的部分,所以先从最基本的模块开始讲解:

TM主要是通过维护TID来维护事务的状态,提供接口供其他的模块查询状态

TID文件规则定义

cfengDB中每一个事务都会有一个TID进行表示,事务ID从1开始自增,不重复; 特殊规定 ----- TID为0表示无事务noneTransaction,代表事务不申请事务直接执行,该类型事务状态永远都是committed

TM中维护一个TID格式文件,记录Transaction的状态,其中cfengDB中规定事务的状态为三种:

  • 0 running: 正在执行,还没有结束
  • 1 commited: 已提交
  • 3 rollback: 撤销回滚

TID中,每一个事务有1字节空间保存状态,同时,TID头部还有8字节的数字,记录TID文件管理的事务的个数,那么事务tid在文件中的状态就存储在8 +(tid-1) 字节位置,【从0开始计数,并且TID为0代表none】

文件读写 – NIO

IO种类很多,有磁盘IO和网络IO, BIO、NIO、IO多路复用、AIO的概念大多用于网络IO — 用户程序从网络中获取数据socket

  1. 用户进程系统调用进入内核态
  2. OS等待远程客户端发送数据(TCP建立连接),OS从网卡设备获取数据,从Socket协议栈拷贝到内核缓冲区
  3. 把内核缓冲区的数据拷贝到用户缓冲区
  4. 用户进程获取到数据,继续执行

img

NIO相比BIO,AIO的区别就是:

步骤1,用户进程要进行IO了,是阻塞挂起,还是非阻塞

步骤3: 内核缓冲区数据拷贝到用户缓冲区,用户进程是阻塞还是非阻塞

BIO是同步阻塞,数据的读写都会阻塞在一个线程中

NIO是同步非阻塞,通过Selector监听不同的channel上的数据变化,当channel数据发生变化时,通知该线程进行读写操作,然后线程就会自己进行读写

AIO是异步,接收到客户端管道后,交给底层处理IO通信,自己本身做其余的事情

(同步和异步的意思是是否需要自己处理,而阻塞和非阻塞就是点餐后是否需要一直等着饭做好)

【步骤一只是通知(点餐),3就是真正开始读写】,1,3都阻塞,就是BIO,1不阻塞,3阻塞,就是NIO; 1,3都不阻塞,就是AIO

  • BIO:阻塞IO,包括常见的ServerSocket/Socket, accept(); //连接阻塞; InputStream/OutputStream; IO读写阻塞
  • NIO: new IO,noBlock; Selector复路器,缓冲区Buffer,ServerSocketChannel; IO未就绪的时候都是非阻塞的; 通过Linux提供的epoll + Selector + Channel实现多路复用【一个线程处理N个连接】

NIO核心组件:

  • Channel: 通道,NIO数据源头/目的地,缓冲区Buffer的唯一接口: 双向读写【可读出和写入】、数据in/out总是先到缓冲区; {文件IO: FileChannel 从文件中读取数据, DataChannel: UDP读写网络数据,SocketChannel: TCP读写 ServerSocketChannel:服务端监听新TCP连接,每进入一个创建SocketChannel}
  • Buffer: 缓冲区, NIO数据读写的中转地【一块连续内存块】,Buffer和传入的数组共享相同的数据存储区域; 可以简单理解两指针指向相同的块,作为数据缓存, 适用于除了boolean之外所有基本类型, 比如ByteBuffer、IntBuffer… allocate()动态分配yi

文件读写采用的是NIO方式,(NIO非阻塞,支持基于通道的IO操作,区别传统的Input/OutputStream),使用FileChannel,创建TxManager后,需要对TID文件校验,保证TID文件合法

校验方式: 文件头的8byte数字推断文件的理论长度,如果不同就认为不合法,不合法的,直接panic强制停机【对于无法回复的错误只能先粗暴停机】

//事务tid在tid文件中的位置
LEN_TID_HEADER_LEN + (tid - 1)* TID_FIELD_SIZE  [头长度 + (序号 -1* 每个tid长度]

所有的文件操作,执行后立刻刷入文件,防止崩溃后丢失, 使用NIO的fileChannel的force方法,和BIO的flush类似

【为了方便,java8开始支持在interface中定义静态方法体,static的方法: 调用时直接采用接口名称调用即可】

create()和open() 创建TID文件,从TID文件创建TransactionManager,创建XID文件时需要写一个空的TID的header,设置tidCounter为0(数量为0),否则后续会不合法

RandomAccessFile、FileChannel、ByteBuffer

IO的底层涉及的概念: 缓冲区操作内核空间与用户空间虚拟内存分页技术

在这里插入图片描述

磁盘操作属于内存管理,是OS系统级功能,需要在内核态执行,所以读取的数据是到内核缓冲区,之后再拷贝到用户态缓冲区

java中的IO常见操作read()和write()完成的作用: 数据在内核缓冲区和用户缓冲区进行交换, 传统IO是面向流(按字节读取),而NIO则是面向Buffer的

在java的内存结构中, 直接内存不受JVM管理, 而堆heap是受JVM用户进程管理; NIO中的Selector可以监听channel,【channel是数据源的抽象】,只有当某个channel准备好之后,线程才会阻塞去取数据操作,而不需要一直等待【BIO从最开始准备数据就开始阻塞】

  • RandomAccessFile允许随机读写文件,也就是可以按照设定的位置开始读取(部分读取); 访问模式包括: r: 只读; rw: 读写; rws:读写,每次文件内容和元数据修改都同步磁盘; rwd: 读写,每次文件内容同步磁盘
  • FileChannel: 通道, 通过文件位置指定开始读写, instance可以通过RandomAccessFile.getChannel()获得, 读写buffer后会自动向后移动pos
  • ByteBuffer: 缓冲区,对byte数组的一种封装, position表示当前的小标,limit结束标记,capacity就是底层的byte数组的容量, 可以通过wrap或者allocate进行内存的分配,通过getXXX方法可以进行类型转换; 每次进行write的时候可以进行force来避免数据丢失

在这里为了方便进行各种类型的转换, 实现了一个byte[] 和 类型的转换工具类:

	public static byte[] long2Byte(long value) {
        return ByteBuffer.allocate(Long.SIZE/Byte.SIZE).putLong(value).array();
    }

    public static long parseLong(byte[] buf) {
        ByteBuffer buffer = ByteBuffer.wrap(buf,0,8);
        return buffer.getLong();
    }

接口实现

首先TM中都是对于事务的操作,NO Transaction使用TID为0, TID的状态变化和事务的新增都是通过操作TID文件实现的, 所以TM的实现就是对于TID文件的创建和修改

//事务管理大多是对tid文件的管理,所以需要nio的RandomAccessFile和相关的channel
    private RandomAccessFile randomAccessFile;
    private FileChannel fileChannel;
    private long tidCounter; //tid计数器
    private Lock counterLock; //使用Lock锁保证线程安全

再进行TM初始化的时候就会进行TIdCounter的检查,检查的方式就是先检查头的长度,之后再整体计算文件长度

文件合法检测

getTidPosition就可以获取tid对应事务的状态字节开始的位置, 而实际的长度就需要再加上一个TID状态的长度 再和 fileLen相比即可

fileLen = randomAccessFile.length(); //文件实际长度

if(fileLen < TransactionConstant.LEN_TID_HEADER_LEN)
    
this.tidCounter = ByteBufferParser.parseLong(buffer.array()); //tid就是头8个字节表示的数据
long end = getTidPosition(this.tidCounter) + TransactionConstant.TID_FIELD_SIZE; //getTidPosition相当于取得的是tid该状态byte开始的位置,结束位置需要
if(end != fileLen)
    
	private long getTidPosition(long tid) {
        return  TransactionConstant.LEN_TID_HEADER_LEN + (tid - 1) * TransactionConstant.TID_FIELD_SIZE;
    }

而修改事务TID的状态的方法也很简单, 直接获取到TID的开始位置,之后将待写入的byte进行wrap为Buffer,写入再force即可

	long offset = getTidPosition(tid);
        byte[] tmp = new byte[TransactionConstant.TID_FIELD_SIZE];
        tmp[0] = status; //修改状态为status
        ByteBuffer buffer = ByteBuffer.wrap(tmp);
        try {
            fileChannel.position(offset);
            fileChannel.write(buffer);
        } catch (IOException e) {
            FaultHandler.forcedStop(e);
        }
        //每次写buffer时候强制刷新一下,避免丢失
        try {
            fileChannel.force(false);

而检查状态就是读取对应位置的Buffer通过buffer.array()获取之后再进行比较即可

对于定义的相关的接口对于事务的相关操作,就是修改TID文件中的TID的状态; eg:

begin()

开始一个新的事务,所以首先tid ++,之后将该新事务的状态设置为运行RUNNING, 再将头部tidCounter数量加一写入头部

因为需要修改类属性,在并发状态下存在安全问题,所以使用ReentrantLock进行加锁修改

	tidCounter ++;
        //修改数量
        ByteBuffer buffer = ByteBuffer.wrap(ByteBufferParser.long2Byte(tidCounter));
        try {
            fileChannel.position(0);
            fileChannel.write(buffer);
        } catch (IOException e) {
            FaultHandler.forcedStop(e);
        }
        //每次写buffer时候强制刷新一下,避免丢失
        try {
            fileChannel.force(false);
            
            
           this.counterLock.lock();
        try {
            long tid = tidCounter + 1; //当前事务
            updateStatus(tid, TransactionConstant.FIELD_TRAN_RUNNING);  //事务激活
            incrTidCounter(); //头部size增加
            return tid;
        } finally {
            this.counterLock.unlock();
        }

commit(tid)

提交事务,有了之前的操作,这里就很好实现 — 直接修改事务TID文件中tid的状态为commited即可

rollback(tid)

和commit同理, 直接修改TID的状态(文件中)

isXXXX(tid)

状态检查,直接buffer读取对应tid位置的事务的状态再进行比较即可

tid文件创建

tid的文件创建可以直接利用File即可,因为TM的类属性有RandomAccessFile和FileChannel,所以再初始化一下

	//创建文件
        File file = new File(path + TransactionConstant.TID_SUFFIX);
        try {
            if(!file.createNewFile()) {
                //文件创建失败已存在,直接粗暴处理,因为不能进行后续操作了
                FaultHandler.forcedStop(new DatabaseException(ErrorCode.FILE_EXISTS));
            }
        }catch (Exception e) {
            FaultHandler.forcedStop(e);
        }
        //查看文件是否可读写,直接调用File的接口
        if(!file.canRead() || !file.canWrite()) {
            FaultHandler.forcedStop(new DatabaseException(ErrorCode.FILE_CANNOT_READ_OR_WRITE));
        }
        //使用NIO进行文件读写
        FileChannel fc = null;
        RandomAccessFile raf = null; //与简单IO不同,可以调转到文件任何位置进行IO,访问文件部分内容
        try {
            raf = new RandomAccessFile(file,"rw"); //将file转为随机读写File,文件权限为rw
            fc = raf.getChannel(); //利用randomAccessFile创建channel通道
        } catch (FileNotFoundException e) {
            FaultHandler.forcedStop(e);
        }

        //利用buffer和channel进行文件读写
        //写空的文件头,将byte[]包装为buffer
        ByteBuffer buf = ByteBuffer.wrap(new byte[TransactionConstant.LEN_TID_HEADER_LEN]);
        try {
            fc.position(0); //RandomAccessFile定位到0处开始
            fc.write(buf); //缓冲区写入
        } catch (IOException e) {
            FaultHandler.forcedStop(e);
        }
        return new TransactionManagerImpl(raf, fc);
    }

整个TM的实现都是依靠的tid文件的操作,主要是定义好规则,实现不难,这里的两重点技术: 可重入锁保证线程安全 + NIO方式进行文件操作提升性能

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【cfengDB】自己实现数据库第0节 ---整体介绍及事务管理层实现 的相关文章

  • 使用匕首柄作为依赖注入来处理多个改造客户端?

    我想在我的 android 应用程序中使用两个不同的后端 具有不同的响应格式 我使用 hilt 作为依赖注入 并对网络调用进行改造 这非常适合工作 因为我已经添加了第二个服务器网络文件和应用程序模块 所以它给了我错误 该错误列在最后 我需要
  • 如何用Android做交互动画(翻译)

    我在 Android 中有一些 png 序列 我需要将它们的 x 和 y 位置从屏幕顶部到底部的翻译动画化 当动画发生时 我需要对象来接收单击事件 我知道这在 3 0 之前的 Android 版本中效果不太好 因为display对象的位置与
  • 无法获取项目的未知属性“assembleRelease”

    将 Android Studio 更新到版本 2 2 并将 gradle 插件更新到 2 2 0 后 出现以下错误 错误 32 1 评估项目 jobdispatcher 时出现问题 无法获取 org gradle api Project 类
  • Android Studio Canary 3.4 Canary 4:错误:功能插件不支持variant.getApplicationId()

    自从我在新版本上更新了我的项目以来Android Studio 3 4 金丝雀 4 gradle 同步失败 因为 ERROR variant getApplicationId is not supported by feature plug
  • Android Widget ID 是否持久

    在从桌面删除该 Widget 实例之前 您从操作系统收到的用户桌面上特定 Widget 实例的 Widget ID 是否一致 我找不到任何明确说明这一点的文档 但我假设这是因为文档说您可以使用小部件 id 来存储任何实例配置信息 我想将一些
  • 配置项目 ':react-native-gesture-handler' 时出现问题

    大家好 我已经尝试了很长时间来解决这个问题 但不幸的是我还没有弄清楚如何解决 希望你们能帮助我 所以我有一个反应本机项目和我的朋友 以及我的一位朋友添加 React native gesture handler 包供我们使用 他对这个包没有
  • 需要对某些片段禁用 CollapsingToolbarLayout 的展开

    我有一个AppCompatActivity控制替换许多片段 这是我的布局 活动 main xml
  • 与通用地图相比,MapView 的分辨率较差

    我刚刚收到 HTC Desire 进行测试 我注意到 残留在小于整个屏幕的框架中的地图视图不如通用地图应用程序那么清晰 有什么办法解决这个问题吗 您应该使用 API 级别 4 或更高级别编译应用程序 然后在 AndroidManifest
  • 将项目添加到 android 框架的设置中

    我正在 android 框架中工作 我想向 android 操作系统中的现有设置添加一个项目 您能告诉我如何执行此操作吗 首先阅读有关偏好活动 http developer android com reference android pre
  • 如何连接到Google Play服务并加载排行榜

    我想将我的游戏与 Google Play 服务连接 我已阅读有关 Android 开发人员的文档 并尝试遵循输入数字示例 但仍然无法加载排行榜 我有导入baseGameUtils 但我使用andengine 所以我没有使用来自谷歌的exte
  • Android 在 Windowmanager 中调整视图大小

    这是我的代码 menubuttonClosed li inflate R layout menu button null menubutton ImageButton menubuttonClosed findViewById R id m
  • 使用 RoboSpice 有没有办法从异常中获取 HTTP 错误代码?

    我正在编写一个使用 RoboSpice 的应用程序 在请求侦听器 onRequestFailure SpiceException arg0 中 有没有办法确定该错误是由于发生 401 HTTP 错误而导致的 我有一个后端服务 当令牌过期时
  • 使用 START_STICKY 启动时服务进程被终止后的 onStartCommand

    我一直在阅读 Android 文档 我想知道是否有人可以阐明当以 START STICKY 启动的服务的进程被终止时服务实例会发生什么情况 我假设本地状态数据 实例变量 也丢失了 Android 在重新创建服务时是否会采取任何措施来帮助重新
  • 如何防止应用程序被盗(针对Android应用程序)?

    我想知道防止人们窃取我的应用程序的最有效方法是什么 在线下载 apk 的副本而不是购买它 我已经花了一个lot特别是 Droidbox 上的时间 并且不会发布 Sync 直到我可以保证提供专业版本的非法副本的人无法发布 有人实施过这个吗 我
  • 如何在Room的数据库迁移中正确添加索引?

    我在迁移 Room 数据库时遇到问题 在更新的数据库中 我必须将一个字段从整数更改为双精度值 我读到它并不像听起来那么容易 为了做到这一点 我必须使用这个更改后的属性创建新的临时表 复制前一个表中的所有值 删除旧的值 最后重命名临时表 我的
  • Android:从 PhoneGap 应用打开 Play 商店链接

    我想从我的phonegap 3 4 应用程序打开一个指向Google Play 商店的链接 呼唤market details id com google android apps maps导致 ActivityNotFoundExcepti
  • android - 如何让按钮每次按下时单击播放声音文件?

    我打开了一个新项目 现在我想做的是 通过按下按钮 我想要播放一个 mp3 文件 而且每次按下按钮时 声音文件都会再次从头开始播放 所以假设 mp3 长 10 秒 我按下按钮 它正在播放 4 秒后我再次按下按钮 声音将再次播放 现在我想知道的
  • Android SDK WebView调用Activity

    我试图在单击 WebView 组件内的链接时启动活动 我的Webview已加载到里面Main java我想启动SubActivity java当点击网站内的链接时Main java 另外 如何将参数传递给此活动 Example inspec
  • 如何从另一个活动更新 Recyclerview 数据

    我有两个活动 MainActivity 和 Addlogactivity 我正在更新 Addlogactivity 中的数据 该数据应显示在 mainactivity recyclerview 中 数据未在数据库中更新 MianActivi
  • 使用 PDFBox 在 Android 中创建 PDF

    我正在尝试通过我的 Android 应用程序创建 PDFPDFBoxapi 但出现以下错误 java lang NoClassDefFoundError org apache pdfbox pdmodel PDDocument 我已经将以下

随机推荐

  • 啊哈!算法中用深度优先搜索(dfs)实现全排列

    include
  • Web网络安全-----红蓝攻防之信息收集(web、安卓...)

    系列文章目录 Web网络安全 Log4j高危漏洞原理及修复 文章目录 系列文章目录 前言 一 为什么要做信息收集 蓝队 红队 红蓝对抗 无疑是一场信息对抗大赛 二 空间搜索引擎类 1 FOFA https fofa info 2 鹰图 奇安
  • 九、Redis Shell

    Redis提供了redis cli redis server redis benchmark等Shell工具 它们虽然比较简单 但是麻雀虽小五脏俱全 有时可以很巧妙地解决一些问题 一 redis cli详解 第一章曾介绍过redis cli
  • Ubuntu用tar命令来备份系统

    文章目录 备份系统 1 进入root用户 2 进入根目录 3 开始备份 命令格式 选项 压缩文档的路径及名称 欲备份目录 恢复备份 参考 http nerotux tuxfamily org index php Articles TarCo
  • IDEA安装Gradle,解决IDEA与Gradle版本不匹配问题

    IDEA安装Gradle 解决IDEA与Gradle版本不匹配问题 文章目录 IDEA安装Gradle 解决IDEA与Gradle版本不匹配问题 一 检查IDEA适配的Gradle版本 二 下载Gradle并解压 三 配置环境变量 四 配置
  • SqlServer查询死锁进程,结束死锁进程

    查询死锁 select request session id spid OBJECT NAME resource associated entity id tableName from sys dm tran locks where res
  • 网卡相关

    如何查看本机的网卡 操作步骤 1 win R 输入cmd 2 然后 输入命令 ipconfig all 然后按回车键 3 找到本地连接中的描述 如下
  • 5.38版本keil5MDK编译标准库工程问题解决

    1 首先 在keil官网下载安装keil5 ARM MDK 5 38版本 然后安装芯片资源包 Keil STM32F1xx DFP 2 4 0 关于芯片资源包的安装 由于选用的是STM32F1系列的芯片 可以安装资源包 Keil STM32
  • 面试官:烂大街的 Spring 循环依赖问题你都不会,我怎么敢录用你

    在关于Spring的面试中 我们经常会被问到一个问题 Spring是如何解决循环依赖的问题的 这个问题算是关于Spring的一个高频面试题 因为如果不刻意研读 相信即使读过源码 面试者也不一定能够一下子思考出个中奥秘 本文主要针对这个问题
  • 电脑外接显示屏导致屏幕翻转不回来解决办法

    电脑外接显示屏导致屏幕翻转不回来解决办法 一条命令解决 xrandr的通常用法 一条命令解决 xrandr o normal xrandr的通常用法 xrandr o left 向左旋转90度 xrandr o right 向右旋转90度
  • Linux安装、查看、卸载软件、更换yum源

    Linux安装 查看 卸载软件 更换yum源 1 知识点 1 Linux安装软件有那些方式 2 Linux各种安装方式如何安装 更新软件 3 如何查看软件包是否安装 如何卸载安装过的软件包 4 Linux如何更换国内yum仓库源 2 实现
  • redis集群部署

    目录 简介 开启多实例 1 复制一份 redis conf 2 修改一下conf文件 3 复制配置文件修改端口 4 启用多实例 简介 本地redis集群是基于两台服务器 每台服务器分别运行三个实例 一共六个实例搭建集群 两台服务器为10 1
  • 使用Canal订阅binlog发送到RabbitMQ的删除补偿

    Canal k n l 译意为水道 管道 沟渠 主要用途是基于 MySQL 数据库增量日志解析 提供增量数据订阅和消费 工作原理 Canal的工作原理相对简单 就是把自己伪装成MySQL slave 模拟MySQL slave的交互协议向M
  • 微信小程序下载图片到本地

    downloadImg function e 触发函数 console log e currentTarget dataset url wx downloadFile url e currentTarget dataset url 需要下载
  • 性能测试 —— 什么是全链路压测?

    随着互联网技术的发展和普及 越来越多的互联网公司开始重视性能压测 并将其纳入软件开发和测试的流程中 阿里巴巴在2014 年双11 大促活动保障背景下提出了全链路压测技术 能更好的保障系统可用性和稳定性 什么是全链路压测 全链路压测是一种全面
  • 4.7 期货每日早盘操作建议

    期货期权日评 静待反抽 PMI数据显示国内疫情基本控制后复工已较明显 经济数据将在二季度逐步改善 同时近期高层在贷款 汽车消费方面政策频出 有望支持实体经济复苏 当前A股已处于低位 期指继续做空的风险收益比在下降 因此建议可在股指期权上轻仓
  • Failed to execute ‘addColorStop‘ on ‘CanvasGradient‘: The value provided (‘undefined‘) could not be

    在echarts使用属性visualMap对折线图进行区间的变色设置 结果写完直接报错 Uncaught DOMException Failed to execute addColorStop on CanvasGradient The v
  • springboottest注解

    SpringBoot test 好习惯要坚持下去 CSDN博客 springboot test springboot使用 SpringBootTest注解进行单元测试 灰太狼 CSDN博客 springboot test
  • 如何为模型不同层设置不同的学习率?

    在模型调参中常用的一种方法是针对不同层设置不同的学习率 以此避免因难易程度不一致引起的过拟合等问题 一 模型举例 class Model nn Module def init self input size hidden size outp
  • 【cfengDB】自己实现数据库第0节 ---整体介绍及事务管理层实现

    LearnProj 内容管理 MySQL系统结构 一条SQL执行流程 cfengDB整体结构 事务管理TM模块 TID文件规则定义 文件读写 NIO RandomAccessFile FileChannel ByteBuffer 接口实现