2023年4月

又一个针对LoRA的改进方法:

DyLoRA: Parameter-Efficient Tuning of Pretrained Models using Dynamic Search-Free Low Rank Adaptation

https://arxiv.org/pdf/2210.07558v2.pdf

https://github.com/huawei-noah/KD-NLP/tree/main/DyLoRA

Part1

前言

LoRA存在的问题

  • rank的值是固定的,训练完成后不能修改。
  • 优化rank的值需要大量的搜索和努力。

提出的方法
:引入了一种动态低秩适应(Dy-LoRA)技术。通过对适配器模块在训练期间的不同秩所学到的表示进行排序,为一系列的秩而不是单一的秩训练LoRA块。

主要贡献

  • 动态LoRA:在LoRA的基础上,我们开发了一种新的算法(DyLoRA),使其在推理时是动态的,而不会产生额外的费用。
  • 无需搜索的LoRA:我们证明,通过在性能上做出可忽略不计的妥协,有可能避免为LoRA选择最佳秩的昂贵的搜索过程。

Part2

介绍

在每一个LoRA模块中,有一个向上投影

和向下投影

的矩阵。设我们想训练LoRA模块在

的范围内操作,其中



可以被视为新的超参数。为了使LoRA模块在一系列的秩中工作,而不是单一的秩,我们需要确保增加或减少秩不会明显阻碍模型的性能。实现这种行为的一种方法是在LoRA模块的训练过程中对不同秩的信息内容进行排序。在这方面,在每个训练步骤中,我们对

进行抽样。形成一个预先定义的分类分布其(支持度为Range[rmin, rmax]),并相应地截断



矩阵。




是W的b截断版本。

前向传播计算时是这么计算的:

损失的计算:

另外在训练的时候增加了一个新的模块:
frozen
,即只更新截断块中第b个相关的行或者列。

整个流程如下:

需要注意反向传播时是否是更新整个截断块还是第b个行或列。

Part3

实验结果

首先是说明不同rank对结果的影响:

接着是该方法和其他方法的对比:

作者:京东物流 李光新

1 Shell可以帮我们做什么

作为一名测试开发工程师,在与linux服务器交互过程中,大都遇到过以下这些问题:

•一次申请多台服务器,多台服务器需要安装相同软件,配置相同的环境,同样的操作需要重复多次;

•工作中经常会使用命令行命令来完成我们的一些操作,但是有些命令使用率很高,而且很长,每次都全部敲进去势必会浪费很多时间(比如查日志)

长此以往,以上两个问题可能会导致:重复性工作,个人能力得不到任何提高,浪费时间,而且还容易出错,作为一名技术人员,当同一个操作重复了三次,我们就应该考虑是否可以通过工具来帮我们实现。

而shell脚本正好擅长,
把复杂问题分解成简单的小问题,然后再把各个部分功能组合起来解决复杂问题
。 当然,有些命令我们只能节省三五秒的时间,短期看不到时间的节省,但是从长期来看这个价值将是巨大的。我们可以将这些时间专注于更有意义,更重要的事情 - 提高生命质量。

2 功能介绍

Shell脚本语言的优势在于处理偏操作系统底层的业务,例如,Linux系统内部很多应用是用shell脚本语言开发的,因为有众多的Linux系统命令为它作支撑,特别是Linux正则表达式和三剑客grep、awk、sed等命令。

对于一些常见的系统脚本,使用shell开发会更简单、更快速。就像让软件一键自动化安装、优化,监控报警脚本,软件启动脚本,日志分析脚本等,虽然PHP和Python语言也能做到这些,但是由于掌握难度、开发效率和开发习惯等,可能就不如shell脚本语言流行及有优势了。

shell是一个C语言编写的脚本语言,它是用户与linux的桥梁,用户输入命令交给shell来处理,shell将相应的操作传递给内核,内核把处理的结果输出给用户。

可参考如下流程示意图:

简单来说:shell就是一个用户跟操作系统之间交互的命令解释器

3 基本用法

下面首先来介绍下日常使用较多的基本指令:

3.1 文件拷贝

3.2 文件合并

有时候,要遇到将多个文件合并成一个的需求,除了重复的复制和粘贴,还可以通过简单指令来辅助实现。

cat命令

默认地,cat命令可以直接接收多个参数,这样,通过重定向可以很方便地合并文件:

效果如下:

4 案例分享

4.1 自动添加注释

下面我们从日常工作入手,和大家一起来看下,shell有哪些提高工作效率的应用场景~

首先,我们编写的脚本有时候并不仅仅是我们个人使用,可能是团队内部使用,所以通常在脚本正式编写前,通常需要添加部分注释,注明脚本的作用,创建日期,创建人等信息;

而如果编写脚本比较多的话,这些重复性工作就会成为我们的负担,所以,我们又可以将这些交给shell来帮我们实现,
用shell脚本来解决创建shell脚本的重复性工作

下面给大家分享一个shell脚本的模板文件,把它拷贝到用户的根目录下并命名成 .vimrc 名称,这样我们以后再次打开以 .sh结尾的文件时就会成自动生成一些注释信息,只要稍稍修改一下即可如图所示:

vimrc文件是vim的环境设置文件,在启动vim时,当前用户根目录下的.vimrc文件会被自动读取,该文件可以包含一些设置甚至脚本,所以,一般会在.vimrc文件中根据个人喜好进行一些自定义设置;

下面脚本我都注释了具体含义,可参考:

实现效果展示:

4.2 内存使用率监控预警

日常工作中,经常会遇到由于服务器被日志打满而不得不重启的场景,而对于一些必要的单据,就需要时常注意日志的备份,所以可以编写如下一个脚本,来做提醒;

如下脚本实现了,当服务器内存使用率超过90%时,提示保存日志操作,如果未超过90%,则不作任何处理;

实现效果展示:

4.3 后台服务启动/停止/重启脚本

对于一些小范围使用,暂未接入j-one部署的服务器,每次后台服务代码更新,代码部署都是一项比较繁琐的工作,而这就到了shell发挥威力的时候了。

实现原理就是,将停止服务和启动服务需要执行的命令写进脚本,通过shell交互来实现,外加一些必要的判断逻辑,比如:

1.执行启动服务时判断服务是否正在启动中;

2.执行停止服务时判断根据交互条件筛选出的服务是否只有一个;

3.执行重启服务时判断服务是否正在启动中;

4.保证中间所有交互过程中的唯一性,避免失误将其他人的服务停止

实现脚本如下:

实现效果展示:

该脚本可在团队内部通用,只需要输入任务类型编号和对应任务关键字两步即可,脚本在执行过程中会自动提示你确认执行任务对象是否正确,以免误伤;

1)停止服务

2)启动服务:

3)重启服务

大大减少了团队服务部署的工作量~

4.4 函数化封装

对于一些比较复杂的功能,或者需要多次执行的功能,shell也支持将功能封装为函数,直接执行函数即可;

比如服务器的部署基本可以分为以下几个步骤:

•服务部署目录创建

•服务配置部署

•服务应用部署

•服务应用启动

则该四个步骤可以通过shell脚本封装为四个函数,如下:

将每个模块编写为函数,最后,只需要调用main函数,即可执行上述一系列操作;

以此类推,安装nginx,Java,jenkins等各种软件操作,也可以封装成多个函数,实现自动化一键完成~

5 结语

综上,linux服务器上,所有的重复性工作都可以交给shell来打理,日常工作中增加一个万能助手,何乐而不为呢

shell脚本小巧且功能强大,以上只是给大家分享了些日常使用到的脚本,其他功能大家也可以举一反三,通过各种流程控制组合来实现,让shell来帮我们做工具人,我们就可以腾出更多的时间来做更重要且更有意义的事情了,与君共勉。

截止到目前,Springboot最新稳定版本已经迭代到3.0.5,而我们项目中使用的行为验证码框架
ja-captcha
还没有适配Springboot3,码云上类似的请求也没有得到过回应,于是决定自己动手适配一下,研究下来发现适配3.x并没有那么难,这里记录一下适配过程,希望能帮到他人

image

首先搭建一个Springboot2的项目,以最简单的方法整合验证码,这里我选择使用
springboot2.7.5

ja-captcha1.3.0
,在Maven中添加依赖后直接启动项目即可

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.5</version>
    </parent>
    <groupId>site.hanzhe.boot3.captcha</groupId>
    <artifactId>springboot3-ja-captcha</artifactId>
    <version>0.0.1</version>
    <description>Springboot3整合ja-captcha行为验证码解决方案</description>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.anji-plus</groupId>
            <artifactId>spring-boot-starter-captcha</artifactId>
            <version>1.3.0</version>
        </dependency>
    </dependencies>

</project>

image

可以看到已经整合成功了,而且调用接口测试也一切正常,现在将Springboot版本改成
3.0.5
,然后刷新Maven后重启一下项目试试看,发现控制台已经不打印
ja-captcha
的日志了,而且接口也返回404

image

在整合之前,我们需要了解一下Springboot3.x的两个重要的变化

第一个变化是Springboot3以JakartaEE 9为基准并支持JakartaEE 10,其实Jakarta就是Java,Oracle将JavaEE移交给了Eclipse基金会,并不允许其使用原本的java名称,所以Java改名为Jakarta,原javax包名也修改为了jakarta

第二个变化是Springboot自动配置文件的变更,Springboot3废弃了factories的自动配置,改为使用imports文件

了解这两点后我们回到整合验证码的项目中,首先可以观察到最明显的一点,启动项目后控制台不在打印
ja-captcha
的日志了,这就代表自动配置并没有生效,在外部库中找到
ja-captcha
的自动配置,发现它是使用factories进行自动配置的,该方法已经失效,我们需要创建新的自动配置文件来启用它

在resource目录下新建
META-INF/spring
两个目录,在目录下添加自动配置文件,并将
ja-captcha
的自动配置类添加进去然后重启项目查看效果,文件名太长我就放在代码块中了

# 文件名
org.springframework.boot.autoconfigure.AutoConfiguration.imports

# 文件内容
com.anji.captcha.config.AjCaptchaAutoConfiguration

image

可以看到项目启动报错了,不过不用担心,报错是好事儿,说明自动配置生效了,提取报错中的主要信息,是创建
captchaController
这个Bean的时候报错了,报错的原因是没有找到
javax/servlet/http/HttpServletRequest
这个类,因为Springboot3中javax的包名统一修改为了jakarta,所以这个类会找不到

这里直接使用替换法简单粗暴的解决这个问题,找到报错的这个类,在项目中创建一个相同包名的的类,将里面的javax替换为jakarta,这样在创建Bean的时候Java找到的类是我们自己的jakarta的类,就不会调用旧的javax的类了

image

替换后重启项目查看效果,可以发现项目启动成功,并且控制台打印了
ja-captcha
的启动日志,调试接口也成功了,整合完成,
点击查看代码

本文使用笨方法在Springboot3中使用
ja-captcha
,还是希望作者早日做适配

从0开始构建一条完成的CI CD流水线

前文中已经讲述了静态、动态增加agent节点,以动态的k8s cloud为例,下面就以Maven构建Java程序为例,开始构建出一条完整的CI CD流水线。
实现功能目标:
1.分别可以根据分支和tag从源码仓库clone代码
2.拿到源码后开始编译
3.构建image,并push到镜像仓库
4.部署到对应k8s集群
5.部署成功后,钉钉告警
以上是此pipeline实现的功能,后续计划:
1.通过webhooks实现源码仓库push代码后,自动出发pipeline运行
2.增加SonarQube代码质量检测步骤
3.配合argoCD实现自动CD
后续文章会陆续更新,敬请期待。
镜像准备
以k8s cloud当做agent的话,肯定需要一个基础镜像,镜像中需要有git、java和maven这些必要的工具环境,当然,可以使用jenkins提供的tools功能来配置工具导入到环境中,例如:
tools {
maven
'apache-maven-3.8.6'}
这种方式是比较方便的,但是每到一个新宿主机都要去主动下载一遍,还需要在jenkins中配置下载地址和方式,迁移时也比较麻烦,所以这里就采用一劳永逸的方法,把这些环境都提前打包到agent的镜像中,方便以后使用。
这里选用的基础镜像是jenkins官方的agent镜像:
docker pull jenkins/agent:latest
这个官方镜像内已经包含git、java环境,以及后边需要和jenkins master建立连接的agent.jar包,所以我们只需再将maven包打包进去即可。

maven工具包的准备

官方下载maven包:
wget https://dlcdn.apache.org/maven/maven-3/3.8.6/binaries/apache-maven-3.8.6-bin.tar.gz
maven的配置(可选)
很多java程序会用maven来进行构建,maven中又存在很多依赖组件
(常用的是jar包、war包、pom等,也可把Zip包等通过POM文件定义为依赖组件),这个时候就会有一个仓库的概念,这个仓库分为三种类型,即:
  1. central
    :中央仓库,是由Maven社区提供的资源仓库,它包含了大量的常用程序库组件(jar包)。默认Maven的中央仓库地址为:
    http://repo1.maven.org/maven2/
  2. local
    :本地仓库,是存放maven环境本地的一个文件夹,此文件夹在第一次运行Maven命令时就创建了。Maven在执行构建任务时,根据依赖关系从中心仓库、或远程仓库下载依赖组件到本地仓库,然后本地仓库的内容供项目引用。
  3. remote
    :远程仓库,例如项目需要指定外部其他公司、或开源组织的jar包,这些依赖组件通用性等原因,未纳入Maven中央仓库,这个时候就要手动指定一个私有的远程仓库来拉取依赖。
修改
Maven中央仓库地址
一般
Maven的中央仓库由于网络问题会访问不到,这个时候可以修改地址为国内的Maven仓库地址或者公司私有的仓库地址,例如阿里的:
http://maven.aliyun.com/nexus/content/groups/public
修改步骤是要修改apache-maven-3.8.6/conf/settings.xml文件中以下字段:
0
修改为:
    <mirror>
      <id>nexus-aliyun</id>
      <mirrorOf>central</mirrorOf>
      <name>Nexus aliyun</name>
      <url>http://maven.aliyun.com/nexus/content/groups/public</url>
    </mirror>
若修改后未生效,可以检查代码pom.xml中是否指定了仓库地址,类似语句:
<repositories>
    <repository>
        <id>springsource-repos</id>
        <name>SpringSource Repository</name>
        <url>http://repo.spring.io/release/</url>
    </repository>
</repositories>

修改
Maven本地仓库路径
Maven本地仓库路径默认为 ${user.home}/.m2/repository
0
可以直接在此进行修改,也可以在构建时用参数指定:
mvn clean install -Dmaven.repo.local=/home/maven/local_repo/
也可以在构建时指定配置文件地址:
mvn clean install -s /home/maven/settings.xml

开始构建镜像
准备好的物料包及Dockerfile:
[root@node01 agent-jenkins]# lsapache-maven-3.8.6.tar.gz  Dockerfile  jenkins-agent kubectl.tar.gz
这里要说下jenkins-agent这个脚本文件,这个脚本文件也是官方提供的,源码文件在这里:
https://github.com/jenkinsci/docker-inbound-agent
,这是专门用来agent连接jenkins master的,采用的jnlp的方式。
查看Dockerfile内容
[root@node01 agent-jenkins]# catDockerfile 
FROM jenkins
/agent:latest

USER root

ADD apache
-maven-3.8.6.tar.gz /opt/ADD kubectl.tar.gz /usr/local/bin/ENV PATH $PATH:/opt/apache-maven-3.8.6/bin/COPY jenkins-agent /usr/local/bin/CMD ["/bin/sh","-c","/usr/local/bin/jenkins-agent"]
用于CD环节的工具,这里添加了kubectl命令,可根据需要添加。
构建镜像
# docker build -t registry.example.com:5000/jenkins/agent:v1 .
# docker push registry.example.com:
5000/jenkins/agent:v1
registry.example.com:5000 是我的私有仓库
配置k8s cloud的pod Template
前边镜像准备完毕,下边要准备一个pod yaml模板,来运行每次临时加入和运行job的agent,默认情况下,k8s cloud会有一个名称为jnlp的容器专门来和jenkins master连接,然后我们可以再启动一个容器专门来跑Pipeline的job,但这里有一点要注意,如果pod中有多个容器,我们需要在Pipeline中指定某个在哪个容器中运行,这个具体怎么指定后边再说,我们这里采用覆盖截jnlp容器的方式来实现全部的工作都由一个container来完成,最终pod Template如下:
apiVersion: "v1"
kind: "Pod"
metadata:
name: jenkins-agent
namespace: "default"
spec:
containers:
- env:
- name: "MAVEN_HOME"
value: "/opt/apache-maven-3.8.6/"
image: "registry.example.com:5000/jenkins/agent:v1"
imagePullPolicy: "IfNotPresent"
name: "jnlp"
resources:
limits:
memory: "2G"
cpu: "1500m"
requests:
memory: "1G"
cpu: "100m"
volumeMounts:
- mountPath: "/root/.m2"
name: "m2"
readOnly: false
- mountPath: "/home/jenkins/agent"
name: "workspace-volume"
readOnly: false
- mountPath: "/usr/bin/docker"
name: "docker-client"
readOnly: true
- mountPath: "/var/run/docker.sock"
name: "docker-engine"
readOnly: true
volumes:
- hostPath:
path: "/root/.m2"
type: "DirectoryOrCreate"
name: "m2"
- hostPath:
path: "/home/jenkins"
name: "workspace-volume"
- hostPath:
path: "/usr/bin/docker"
type: File
name: "docker-client"
- hostPath:
path: "/var/run/docker.sock"
type: Socket
name: "docker-engine"
这里有四个volume:
  • m2:这个是用作maven的本地仓库路径,使用hostpath挂载到了本地目录,当然也可以存储到某些共享存储中,目的就是让依赖包只下载一次。
  • workspace-volume:这个是将jenkins的工作目录也使用hostpath挂载。
  • docker-client:docker命令的挂载,用于build、push等命令
  • docker-engine:docker engine的挂载,用于build、push等
gitlab项目克隆
simple-java-maven-app项目地址:
https://github.com/jenkins-docs/simple-java-maven-app
,将此项目克隆到本地gitlab即可。
网访问github慢的话,可以git我的码云:
https://gitee.com/vfancloud/simple-java-maven-app.git
Pipeline编写
创建凭证
1.代码仓库我们使用前边搭建的gitlab,需要提前将gitlab的用户凭证在Jenkins创建好,方便后边Jenkins下载代码使用:
系统管理—>凭证管理—>创建Username with password类型凭证(id需要记住,Pipeline中会使用)
0
2.我们的服务是部署在k8s集群中,所以还需要目标k8s的kubeconfig凭证,用来管理操控目标k8s:
系统管理—>凭证管理—>创建Secret file类型凭证
0
一般项目都会有多个环境,所以每个环境的kubeconfig凭证都要提前创建好。
3.镜像仓库的账号密码也要提前准备好,Username with password类型即可。
0
安装插件
一些常用的必须插件,要提前安装:
  1. Git
  2. Git Parameter
  3. DingTalk
  4. build user vars plugin
Pipeline
此Pipeline起一个示例效果,有些功能点可以省略或者选择使用,酌情增删即可:
pipeline {
agent {
kubernetes {
cloud
'kubernetes-internal' //指定cloud name inheritFrom 'jenkins-agent' //指定podTemplate,新版本已经不再用label指定 namespace 'default'}
}
environment {
GIT_CERT
= credentials('vfan-gitlab') //gitlab用户凭证 HARBOR_HOST = 'registry.example.com:5000'SERVER_NAME= 'simple-java-maven-app'}/*tools {
maven 'apache-maven-3.8.6' 镜像有maven环境了,可以不指定
}
*/options {
buildDiscarder(logRotator(numToKeepStr:
'10')) //保持历史构建的最大个数 timeout(20) //默认单位分钟,20分钟 timestamps() //Pipeline开始时间以及每个step执行开始时间 }
parameters {
choice(
name:
'GIT_REPO_URL',
choices:
'http://10.85.122.128:880/vfan/simple-java-maven-app.git',
description:
'Git Repo example environment')
choice(
name:
'GIT_TYPE',
choices: [
'branch', 'tag'],
description:
'Git Repo example brance')
choice(
name:
'GIT_REPO_BRANCE',
choices: [
'master', 'dev', 'test'],
description:
'Git Repo example brance')
gitParameter name:
'GIT_TAG',
type:
'PT_TAG',
branch:
'master',
branchFilter:
'.*',
defaultValue:
'',
selectedValue:
'TOP',
sortMode:
'DESCENDING_SMART',
listSize:
'1',
description:
'Select you git tag.'choice(
name:
'ENVIRONMENT',
choices: [
'INT', 'DEV', 'PROD'],
description:
'Select deployment environment')
}
stages {
stage(
'git clone branch') {
when {
expression { params.GIT_TYPE
== "branch"}
}
steps {
git(
branch: params.GIT_REPO_BRANCE,
credentialsId:
env.GIT_CERT,
url: params.GIT_REPO_URL
)
}
post {
success {
sh ''' echo "use branch build"git status'''}
}
}
stage(
'git clone tag') {
when {
expression { params.GIT_TYPE
== "tag"}
}
steps {
checkout([$class:
'GitSCM',
branches: [[name:
"${GIT_TAG}"]],
userRemoteConfigs: [[credentialsId:
env.GIT_CERT, url: params.GIT_REPO_URL]]])
}
post {
success {
sh ''' echo "use tag build"git status'''}
}
}
stage(
'Maven Build') {
steps {
sh 'mvn -B -DskipTests clean package'}
}
stage(
'Test') {
steps {
sh 'mvn test'}
post {
always {
junit
'target/surefire-reports/*.xml'}
}
}
stage(
'Deliver') {
steps {
sh './jenkins/scripts/deliver.sh'}
}
stage(
'Docker build && push') {
steps {
withCredentials([usernamePassword(credentialsId:
'harbor-auth', passwordVariable: 'HARBOR_PASSWD', usernameVariable: 'HARBOR_USER')]) {sh ''' echo "Other operations..." echo "Start building..." date -d "+8 hour" +%Y%m%d_%H%M%S > /tmp/dateBUILD_TIME=`cat /tmp/date`
docker build
--build-arg APP_NAME=simple-java-maven-app -t ${HARBOR_HOST}/${SERVER_NAME}:${GIT_REPO_BRANCE}_${BUILD_TIME} .echo "Build complete."dockerlogin $HARBOR_HOST -u $HARBOR_USER -p $HARBOR_PASSWD
docker push ${HARBOR_HOST}
/${SERVER_NAME}:${GIT_REPO_BRANCE}_${BUILD_TIME}
docker rmi ${HARBOR_HOST}
/${SERVER_NAME}:${GIT_REPO_BRANCE}_${BUILD_TIME}'''}
}
}
stage(
'Deploy to k8s'){
input{
message
"Should we continue deploy?"ok"Yes, we should."}
environment {
//提前创建好secret file类型的凭据 KUBE_CONFIG_INT = credentials('mycluster_int')//KUBE_CONFIG_DEV = credentials('mycluster_dev')//KUBE_CONFIG_PROD = credentials('mycluster_prod') }
steps{
sh''' BUILD_TIME=`cat /tmp/date`case $ENVIRONMENT in "INT")
kubectl set image deployment ${SERVER_NAME}
--kubeconfig=${KUBE_CONFIG_INT} app=${HARBOR_HOST}/${SERVER_NAME}:${GIT_REPO_BRANCE}_${BUILD_TIME}
kubectl rollout status deployment ${SERVER_NAME}
--kubeconfig=${KUBE_CONFIG_INT}
;;
"DEV")
kubectl set image deployment ${SERVER_NAME}
--kubeconfig=${KUBE_CONFIG_DEV} app=${HARBOR_HOST}/${SERVER_NAME}:${GIT_REPO_BRANCE}_${BUILD_TIME}
kubectl rollout status deployment ${SERVER_NAME}
--kubeconfig=${KUBE_CONFIG_DEV}
;;
esac echo "Deployment complete." '''}
}
}
post {
success{
echo 'Deployment succeeded.'dingtalk (
robot:
'myapp-dingding-robot',
type:
'MARKDOWN', //发什么类型的消息,有TEXT、LINK、MARKDOWN、和ACTION_CARD,参考https://jenkinsci.github.io/dingtalk-plugin/guide/pipeline.html at: [],
atAll:
false,
title:
'Jenkins发版成功',
text: [
"## 构建结果:**${currentBuild.result}**",'---',"## 构建信息",'---',"- 项目名称:${SERVER_NAME}","- 构建环境:${ENVIRONMENT}","- 构建分支:${GIT_REPO_BRANCE}","- 构建标签:${GIT_TAG}","- 项目地址:${GIT_REPO_URL}","- 构建用户:${env.BUILD_USER}"],//messageUrl: '',//picUrl: '',//singleTitle: '',//btns: [],//btnLayout: '',//hideAvatar: false )
}
failure{
echo "Deployment failed."dingtalk (
robot:
'myapp-dingding-robot',
type:
'MARKDOWN', //发什么类型的消息,有TEXT、LINK、MARKDOWN、和ACTION_CARD,参考https://jenkinsci.github.io/dingtalk-plugin/guide/pipeline.html at: [],
atAll:
false,
title:
'Jenkins发版失败',
text: [
"## 构建结果:**${currentBuild.result}**",'---',"## 构建信息",'---',"- 项目名称:${SERVER_NAME}","- 构建环境:${ENVIRONMENT}","- 构建分支:${GIT_REPO_BRANCE}","- 构建标签:${GIT_TAG}","- 项目地址:${GIT_REPO_URL}","- 构建用户:${env.BUILD_USER}"],//messageUrl: '',//picUrl: '',//singleTitle: '',//btns: [],//btnLayout: '',//hideAvatar: false )
}
}
}

测试运行Pipeline
0
运行完成,钉钉也已收到通知,后续更新更多内容。

首先我们介绍一下
MySQL的缓存机制

【MySQL缓存机制】
简单的说就是缓存sql文本及查询结果,如果运行完全相同的SQL,服务器直接从缓存中取到结果,而不需要再去解析和执行SQL。

但如果表中任何数据或是结构发生改变,包括INSERT、UPDATE、DELETE、TRUNCATE、ALTER TABLE、DROP TABLE或DROP DATABASE等,最简单的就是我们在SQL语句中多了空格啥的,注释,或者是大小写,那么hashmap中的key和value对应都是不一样的,那么使用这个表的所有缓存查询将不再有效。。

缓存是对系统性能优化的重要手段。。。

主要是因为

1.
MySQL缓存是基于Hashmap的:
查询语句的字符大小写、空格或者注释的不同,缓存查询都会认为是不同的查询(因为他们的hash值会不同)

2.MySQL会对每条接收到的SELECT类型的查询进行hash计算,然后查找这个查询的缓存结果是否存在。虽然hash计算和查找的效率已经足够高了,一条查询语句所带来的开销可以忽略,但一旦涉及到
高并发
,有成千上万条查询语句时,
hash计算和查找所带来的开销
就必须重视了。。。

3.也就是修改某个表的内容时,之前缓存的内容对于现在的数据来说就是错误的数据,所以我们要将之前的缓存数据删除,来保证数据的正确性。当向某个表写入数据的时候,必须将和这个表相关的所有缓存设置为失效,
如果缓存内容很多,则消耗也会很大
,可能使系统僵死,因为这个操作是靠全局锁操作来保护的。。

总结

所以综合上述的内容,我们知道原因就是如果数据都要缓存起来,一旦这些数据都发生改变时,需要更改,我们又需要把这些缓存删除,数据量大时,就会很慢,效率低。

解决思路
是:把一些容易变动的数据还是存在磁盘上,而不是在缓存中,这样就是省去了缓存改来改去的问题。。

解决方案
:运用了新的数据库Redis

介绍一下redis ----Redis是当前比较热门的NoSQL系统之一---,
Redis数据都是缓存在计算机内存中
。。

NoSQL
,泛指非关系型的数据库区别于关系数据库,它们不保证关系数据的
ACID特性。。。。

这里我们又要引入
事务
的概念,如下:

事务定义

事务是一系列操作组成的工作单元
,该工作单元内的操作是不可分割的,即要么所有操作都做,要么所有操作都不做,这就是事务。

理解一:

事务可以看做是一次大的活动
,它由不同的小活动组成,这些活动要么全部成功,要么全部失败。

理解二:

事务可以看做是一个大的操作,它由一系列操作组成,这些操作要么全部成功,要么全部失败。

ACID---事务的四大特性 ---原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)

A:事务是一个不可分割的整体,事务中的操作要么全部成功,要么全部失败。。

举一个SQL事务的例子:
begin
transaction;update account set money = money-100 where name = '张三';update account set money = money+100 where name = '李四';commit transaction;
这里面的两个操作必须都成功或者都失败。。。。。

B:我们来看一段狗屁话:
事务必须使数据库从一个一致性状态变换到另外一个一致性状态,能理解?????

个人认为就是执行这操作能够达到你的目的,然后不出错,在编写中体现在语法和实现是否可行,在功能上就是可以保证最后的结果和你的想法是一致的。。。

I:隔离性体现在:多个用户并发访问数据库时,数据库为每
一个用户开启的事务,不能被其他事务的操作数据所干扰
,多个并发事务之间要相互隔离。

D:持久性是指一个
事务一旦被提交
,它对数据库中数据的改变就是永久性的,
接下来即使数据库发生故障也不应该对其有任何影响

也就是说,给你造成了影响,你一旦告诉我你操作成功了,是不可以不执行的。。。。

本地事务的实现:begin transaction;//1.本地数据库操作:张三减少100元。//2.本地数据库操作:李四增加100元。commit transaction;


分布式事务的实现:
begin transaction;//1.本地数据库操作:张三减少100元。//2.远程调用:让李四增加100元。commit transaction;