daily commit
This commit is contained in:
@@ -207,4 +207,11 @@
|
||||
* git rebase复杂操作
|
||||
```shell
|
||||
# 调用git rebase,可以将一个分支基于另一个分支的改变replay到第三分支上
|
||||
$ git rebase --onto target-branch base-branch topic-branch
|
||||
$ git rebase --onto target-branch base-branch topic-branch
|
||||
```
|
||||
* 相较于merge,rebase操作可能会产生一些问题。
|
||||
* 如一个分支已经被push到远程库,且被其他协作者pull并在此基础之上进行开发工作。那么,当分支的创建者在本地对分支进行rebase操作后,再用git push --force命令对远程分支的history进行覆盖时,那么其他协作者对更新之后的远程库进行pull操作,merge操作会破坏history
|
||||
* rebase操作的守则:
|
||||
* 一个rebase操作只能再本地库进行
|
||||
* 一旦分支被push到远程库,就有可能被其他开发者clone并在此基础之上进行修改
|
||||
* 如果分支被push到远程库后,那么就无法再进行rebase操作。因为rebase操作可能会破坏已有的history结构,如果用git push --force强制覆盖remote仓库的hsitory结构,那么其他开发者再次pull的时候就会进一步破坏history结构
|
||||
@@ -1,21 +1,21 @@
|
||||
# HTTPS原理
|
||||
* ## HTTPS加密步骤:
|
||||
1. ### client hello(客户端向服务器):
|
||||
1. 客户端支持的TLS版本
|
||||
2. 客户端支持的加密组件(Cipher Suites,内含支持的加密算法信息)
|
||||
3. 客户端生成的随机数
|
||||
2. ### server hello(服务器端向客户端发送):
|
||||
1. 服务器端的TLS版本
|
||||
2. 服务器端从客户端支持的加密组件中挑选中的组件(Cipher Suite)
|
||||
3. 服务器端生成的随机数
|
||||
3. ### Certificate(服务器向客户端发送):
|
||||
1. 服务器会向客户端发送其被CA签名的证书
|
||||
4. ### Server Key Exchange(服务器端向客户端发送):
|
||||
1. 服务器向客户端发送密钥交换算法的参数(Server Params),服务器会用自己的私钥对Server Params进行签名,防止客户端收到的Server Params被伪造
|
||||
5. ### Server hello Done(服务器端向客户端发送):
|
||||
1. 服务器端向客户端告知协商部分已经结束
|
||||
### (以上步骤客户端和服务器之间交换数据是明文的)
|
||||
***
|
||||
6. ### CLient Key Exchange(客户端向服务端发送):
|
||||
1. 在验证服务器证书可信后,客户端生成另一个密钥交换算法参数Client Params,根据之前服务端发送过来的Server Params,结合会生成一个随机数R3。由于之前客户端和服务器端都生成过自己的随机数,故而结合3个随机数会生成会话密钥。生成的会话密钥会通过服务器端公钥加密后发送给服务器端
|
||||
7. ### 之后客户端和服务器端之间的http通信都会通过会话密钥进行对称加密
|
||||
# HTTPS原理
|
||||
* ## HTTPS加密步骤:
|
||||
1. ### client hello(客户端向服务器):
|
||||
1. 客户端支持的TLS版本
|
||||
2. 客户端支持的加密组件(Cipher Suites,内含支持的加密算法信息)
|
||||
3. 客户端生成的随机数
|
||||
2. ### server hello(服务器端向客户端发送):
|
||||
1. 服务器端的TLS版本
|
||||
2. 服务器端从客户端支持的加密组件中挑选中的组件(Cipher Suite)
|
||||
3. 服务器端生成的随机数
|
||||
3. ### Certificate(服务器向客户端发送):
|
||||
1. 服务器会向客户端发送其被CA签名的证书
|
||||
4. ### Server Key Exchange(服务器端向客户端发送):
|
||||
1. 服务器向客户端发送密钥交换算法的参数(Server Params),服务器会用自己的私钥对Server Params进行签名,防止客户端收到的Server Params被伪造
|
||||
5. ### Server hello Done(服务器端向客户端发送):
|
||||
1. 服务器端向客户端告知协商部分已经结束
|
||||
### (以上步骤客户端和服务器之间交换数据是明文的)
|
||||
***
|
||||
6. ### CLient Key Exchange(客户端向服务端发送):
|
||||
1. 在验证服务器证书可信后,客户端生成另一个密钥交换算法参数Client Params,根据之前服务端发送过来的Server Params,结合会生成一个随机数R3。由于之前客户端和服务器端都生成过自己的随机数,故而结合3个随机数会生成会话密钥。生成的会话密钥会通过服务器端公钥加密后发送给服务器端
|
||||
7. ### 之后客户端和服务器端之间的http通信都会通过会话密钥进行对称加密
|
||||
|
||||
@@ -21,6 +21,12 @@
|
||||
* 通过覆盖State对象的build方法,返回一个Widget对象来显示StatefulWidget的UI
|
||||
* 每次对State中维护的状态进行修改时,都要调用setState方法,并且传入一个函数
|
||||
* 每次调用setState方法时,都会重新调用build方法来对StatefulWidget的UI进行重新渲染
|
||||
* StatefulWdiget管理状态的三种方式:
|
||||
* widget管理自己的状态
|
||||
* 此时widget为stateful的,并在state中存储状态信息
|
||||
* 由父widget管理状态
|
||||
* 此时widget是stateless的,而父widget是stateful的,当前无状态widget只负责显示信息,而父widget则会通过构造函数向stateless widget传递存储在父widget中的状态信息,父widget也会传递一个回调用来对存储在父widget中的数据进行修改
|
||||
* 一部分状态存储在父widget中,一部分状态存储在自身的widget中
|
||||
* ## Flutter中的布局
|
||||
* Row和Column:通常用于创建行和列的布局
|
||||
* mainAxisAlignment/crossAxisAlignment:用于控制主轴和交叉轴如何对其元素
|
||||
@@ -39,4 +45,57 @@
|
||||
* ListTile:用于显示行信息,具有如下属性:
|
||||
* title:为行指定title信息
|
||||
* subtilte:为行指定附加信息,如果isThreeLine为false,subtitle不能换行,如果为true,subtitle可以为两行
|
||||
* leading:为行定义icon
|
||||
* leading:为行定义icon
|
||||
* ## 添加图片和资源
|
||||
* 在flutter程序中,通过pubspec.yaml中的flutter:assets:来app需要的资源
|
||||
* 资源变体:
|
||||
* 当一个资源在pubspec.yaml中被指定时,在该资源路径的同级子目录下,具有相同名称的资源也会被包含。
|
||||
```yaml
|
||||
<!--
|
||||
例如,当.../background.png文件在yaml文件中被指定时,
|
||||
.../dark/background.png文件也会被包含的app中
|
||||
-->
|
||||
flutter:
|
||||
assets:
|
||||
- img/background.png
|
||||
- img/dark/background.png
|
||||
```
|
||||
* 加载文本资源:
|
||||
* 可以通过rootBundle静态全局变量来加载资源
|
||||
* 在当前构建上下文中,通常推荐使用DefaultAssetBundle来获取文本资源,相比较于rootBundle,DefaultAssetBundle能够在运行时替换AssetBundle
|
||||
* 在上下文之外,可以使用rootBundle来导入文本资源
|
||||
* 加载图片资源:
|
||||
* 为了加载图片资源,可以通过AssetImage来加载
|
||||
* 使用依赖包中的图片,在通过AssetImage来获取图象时需要指定package包名
|
||||
* ## Navigation(导航机制)
|
||||
* ### 从一个页面中传回数据
|
||||
* 可以通过Navigator.push方法来将新页面压入,该方法是异步方法,应该要通过await关键字等待返回结果。
|
||||
* Navigator.push方法接受如下参数
|
||||
* context(BuildContext)
|
||||
* MaterialPageRouter(builder:(context)=>return a widget here...)
|
||||
* 可以通过Navigator.pop方法回退界面并且返回结果数据
|
||||
* Navigator.pop方法接受如下参数
|
||||
* context
|
||||
* 返回的结果
|
||||
* ### 从一个页面向另一个页面中传参
|
||||
* 为了向另一个路由中进行传参,可以在MaterialPageRouter中为settings赋值RouteSettings,并且向arguments属性中加入参数
|
||||
* 在另一个页面中,可以通过Modal.of(context)方法来获取route对象
|
||||
* ### 在两个路由之间进行跳转
|
||||
* 在路由之间进行跳转可以分为如下部分
|
||||
* 创建两个路由
|
||||
* 使用Navigator.push来跳转到第二个路由
|
||||
* 使用Navigator.pop回退到第一个路由
|
||||
* ### 导航到命名路由中
|
||||
* 步骤:
|
||||
* 定义两个界面
|
||||
* 定义路由
|
||||
* 通过Navigator.pushNamed方法跳转到命名的路由
|
||||
* 通过Navigator.pop方法返回
|
||||
* 在创建命名路由时,需要在MaterialApp中指定initialRoute属性和routes属性
|
||||
* initialRoute属性指定开始时的路由路径
|
||||
* routes属性指定了所有可用的命名路由,以及为每个路由指定构建的Widget
|
||||
* 通过Navigator.pushNamed方法,可以跳转到指定的路由,而不用像push方法一样指定builder
|
||||
* ### 向命名路由传递参数
|
||||
* 可以通过pushNamed方法中的arguments参数向命名路由传递参数,传递的参数可以通过ModalRoute.of方法来获取,参数位于settings.arguments属性中
|
||||
* ### 为组件之间添加关联动画
|
||||
* 可以通过Hero组件为组件之间添加关联
|
||||
@@ -1,147 +1,147 @@
|
||||
# Java文件操作
|
||||
|
||||
* ## Path路径
|
||||
* path路径可分为绝对路径和相对路径,绝对路径从根路径开始,而相对路径则不是
|
||||
* Path类的api如下:
|
||||
```java
|
||||
/*
|
||||
* 获取路径,其会将参数中的给定字符串连接起来,创建路径
|
||||
* 如果不是文件系统中的合法路径,会抛出InvalidPathException
|
||||
* Paths.get并不要求文件系统中一定存在对应的文件
|
||||
*/
|
||||
Paths.get(String first,String... more)
|
||||
|
||||
// 解析路径
|
||||
// 如果path2是绝对路径,则会返回path2
|
||||
// 否则,以path1为基本路径,返回path2想对于path1产生的路径
|
||||
path1.resolve(path2)
|
||||
path1.resolve(String)
|
||||
|
||||
// 将path1父路径作为基本路径解path2的析兄弟路径
|
||||
path1.resolveSibling(path2)
|
||||
path1.resolveSibling(String)
|
||||
|
||||
// 以path1为基本路径,返回path2想对于path1的相对路径
|
||||
path1.relativize(path2)
|
||||
|
||||
// 移除.和..等多余元素
|
||||
path1.toAbsolutePath()
|
||||
|
||||
// 获取文件名称
|
||||
path1.getFileName()
|
||||
|
||||
// 获取根路径
|
||||
path1.getRoot()
|
||||
|
||||
// 获取父路径,如果当前文件没有父路径,返回null
|
||||
path1.getParent()
|
||||
|
||||
// 以当前路径创建元素
|
||||
path1.toFile()
|
||||
|
||||
# 上述操作都不要求path所对应的文件在文件系统中已经存在
|
||||
|
||||
```
|
||||
|
||||
* ## 文件读写
|
||||
* Files类相关文件操作的api
|
||||
```java
|
||||
// 读取路径所对应文件的所有字节
|
||||
Files.readAllBytes(path)
|
||||
|
||||
// 按行读入文件的内容,并且将文件的行保存在字符串的集合中
|
||||
Files.readAllLines(path,charset)
|
||||
|
||||
// 向文件中写入数据
|
||||
// 可以通过StandardOpenOpition来指定数据是append还是覆盖
|
||||
Files.write(path,bytes,charset,StandardOpenOpition.xxx)
|
||||
Files.write(path,lines,charset,StandardOpenOption.xxx)
|
||||
|
||||
// 通过FIles类获取输入输出流
|
||||
Files.newInputStream(path,options)
|
||||
Files.newOutputStream(path,options)
|
||||
FIles.newBufferedReader(path,charset)
|
||||
Files.newBufferedWriter(path,charset,options)
|
||||
|
||||
|
||||
```
|
||||
|
||||
* ## 创建文件和目录
|
||||
```java
|
||||
// 通过Files类创建目录
|
||||
// 其中,createDirectory方法中path路径中的中间目录都应该已
|
||||
// 存在,而且当想要创建的目录已经存在时会抛出异常
|
||||
// 而createDirectories方法中中间路径可以不存在,在通过path创
|
||||
// 建目录时会联通中间路径一起创建,但是,此方法在想要创
|
||||
// 建的目录已经存在时不会抛出异常
|
||||
Files.createDirectory(path)
|
||||
Files.createDirectories(path)
|
||||
# 注意,其中createDirecotry(path)操作是原子的
|
||||
# 但是createDirectories()不是原子的,多进程条件下可能存在竞争
|
||||
|
||||
// 通过FIles类创建文件
|
||||
// 当文件已经存在时,会抛出一场
|
||||
// 该创建文件的操作是原子的
|
||||
Files.createFile(path)
|
||||
|
||||
// 创建临时文件和临时文件夹
|
||||
Files.createTempFile(basedir,prefix,suffix)
|
||||
FIles.createTempFile(prefix,suffix)
|
||||
Files.createTempDirecotry(basedir,prefix)
|
||||
FIles.createTempDirectory(prefix)
|
||||
|
||||
# 可以为生成的临时文件调用deleteOnExit方法来让临时文件在进程退出时删除
|
||||
# 在linux环境下,临时文件和目录生成在/tmp目录下
|
||||
|
||||
```
|
||||
|
||||
* ## 移动、复制、删除文件
|
||||
```java
|
||||
// 移动、复制文件
|
||||
Files.copy(from,to)
|
||||
FIles.move(from,to)
|
||||
|
||||
# 对于move和copy操作,如果目标路径已经存在,那么操作将失败并抛出异常
|
||||
# 可以指定REPLACE_EXISTING来覆盖已经存在的路径
|
||||
# 默认情况下,移动和复制都不是原子的,可以通过ATOMIC_MOVE来指定移动操
|
||||
# 作是原子的,但是复制操作并不支持原子性操作
|
||||
# 可以将输入流中的内容复制到path对应的文件中,也可以将path对应文件的
|
||||
# 内容输出到流中
|
||||
|
||||
// 删除文件
|
||||
// 若该文件不存在,会抛出异常
|
||||
// 删除操作并不是原子的,由于要检查该文件是否是目录
|
||||
// 如果文件是目录,只有当该目录为空时才能删除成功
|
||||
Files.delete(path)
|
||||
|
||||
```
|
||||
|
||||
* ## 获取文件属性
|
||||
```java
|
||||
// 文件是否存在
|
||||
Files.exists()
|
||||
// 文件是否是隐藏文件爱你
|
||||
Files.isHidden()
|
||||
// 对文件是否有读写执行权限
|
||||
Files.isReadable()
|
||||
Files.isWritable()
|
||||
FIles.isExecutable()
|
||||
// 文件类型是否是目录、普通文件、符号链接
|
||||
Files.isRegularFIle()
|
||||
FIles.isDirectory()
|
||||
FIles.isSymbolicLink()
|
||||
// 获取文件长度
|
||||
FIles.size()
|
||||
|
||||
```
|
||||
|
||||
* ## 访问目录中的项
|
||||
```java
|
||||
// 将目录中的项以Stream的形式返回
|
||||
// Files.list并不会递归的列出子目录中的项
|
||||
Files.list(dirPath)
|
||||
|
||||
// 递归列出子目录的项
|
||||
// 可以通过指出depth来指定遍历的深度
|
||||
FIles.walk(rootPath,depth)
|
||||
# Java文件操作
|
||||
|
||||
* ## Path路径
|
||||
* path路径可分为绝对路径和相对路径,绝对路径从根路径开始,而相对路径则不是
|
||||
* Path类的api如下:
|
||||
```java
|
||||
/*
|
||||
* 获取路径,其会将参数中的给定字符串连接起来,创建路径
|
||||
* 如果不是文件系统中的合法路径,会抛出InvalidPathException
|
||||
* Paths.get并不要求文件系统中一定存在对应的文件
|
||||
*/
|
||||
Paths.get(String first,String... more)
|
||||
|
||||
// 解析路径
|
||||
// 如果path2是绝对路径,则会返回path2
|
||||
// 否则,以path1为基本路径,返回path2想对于path1产生的路径
|
||||
path1.resolve(path2)
|
||||
path1.resolve(String)
|
||||
|
||||
// 将path1父路径作为基本路径解path2的析兄弟路径
|
||||
path1.resolveSibling(path2)
|
||||
path1.resolveSibling(String)
|
||||
|
||||
// 以path1为基本路径,返回path2想对于path1的相对路径
|
||||
path1.relativize(path2)
|
||||
|
||||
// 移除.和..等多余元素
|
||||
path1.toAbsolutePath()
|
||||
|
||||
// 获取文件名称
|
||||
path1.getFileName()
|
||||
|
||||
// 获取根路径
|
||||
path1.getRoot()
|
||||
|
||||
// 获取父路径,如果当前文件没有父路径,返回null
|
||||
path1.getParent()
|
||||
|
||||
// 以当前路径创建元素
|
||||
path1.toFile()
|
||||
|
||||
# 上述操作都不要求path所对应的文件在文件系统中已经存在
|
||||
|
||||
```
|
||||
|
||||
* ## 文件读写
|
||||
* Files类相关文件操作的api
|
||||
```java
|
||||
// 读取路径所对应文件的所有字节
|
||||
Files.readAllBytes(path)
|
||||
|
||||
// 按行读入文件的内容,并且将文件的行保存在字符串的集合中
|
||||
Files.readAllLines(path,charset)
|
||||
|
||||
// 向文件中写入数据
|
||||
// 可以通过StandardOpenOpition来指定数据是append还是覆盖
|
||||
Files.write(path,bytes,charset,StandardOpenOpition.xxx)
|
||||
Files.write(path,lines,charset,StandardOpenOption.xxx)
|
||||
|
||||
// 通过FIles类获取输入输出流
|
||||
Files.newInputStream(path,options)
|
||||
Files.newOutputStream(path,options)
|
||||
FIles.newBufferedReader(path,charset)
|
||||
Files.newBufferedWriter(path,charset,options)
|
||||
|
||||
|
||||
```
|
||||
|
||||
* ## 创建文件和目录
|
||||
```java
|
||||
// 通过Files类创建目录
|
||||
// 其中,createDirectory方法中path路径中的中间目录都应该已
|
||||
// 存在,而且当想要创建的目录已经存在时会抛出异常
|
||||
// 而createDirectories方法中中间路径可以不存在,在通过path创
|
||||
// 建目录时会联通中间路径一起创建,但是,此方法在想要创
|
||||
// 建的目录已经存在时不会抛出异常
|
||||
Files.createDirectory(path)
|
||||
Files.createDirectories(path)
|
||||
# 注意,其中createDirecotry(path)操作是原子的
|
||||
# 但是createDirectories()不是原子的,多进程条件下可能存在竞争
|
||||
|
||||
// 通过FIles类创建文件
|
||||
// 当文件已经存在时,会抛出一场
|
||||
// 该创建文件的操作是原子的
|
||||
Files.createFile(path)
|
||||
|
||||
// 创建临时文件和临时文件夹
|
||||
Files.createTempFile(basedir,prefix,suffix)
|
||||
FIles.createTempFile(prefix,suffix)
|
||||
Files.createTempDirecotry(basedir,prefix)
|
||||
FIles.createTempDirectory(prefix)
|
||||
|
||||
# 可以为生成的临时文件调用deleteOnExit方法来让临时文件在进程退出时删除
|
||||
# 在linux环境下,临时文件和目录生成在/tmp目录下
|
||||
|
||||
```
|
||||
|
||||
* ## 移动、复制、删除文件
|
||||
```java
|
||||
// 移动、复制文件
|
||||
Files.copy(from,to)
|
||||
FIles.move(from,to)
|
||||
|
||||
# 对于move和copy操作,如果目标路径已经存在,那么操作将失败并抛出异常
|
||||
# 可以指定REPLACE_EXISTING来覆盖已经存在的路径
|
||||
# 默认情况下,移动和复制都不是原子的,可以通过ATOMIC_MOVE来指定移动操
|
||||
# 作是原子的,但是复制操作并不支持原子性操作
|
||||
# 可以将输入流中的内容复制到path对应的文件中,也可以将path对应文件的
|
||||
# 内容输出到流中
|
||||
|
||||
// 删除文件
|
||||
// 若该文件不存在,会抛出异常
|
||||
// 删除操作并不是原子的,由于要检查该文件是否是目录
|
||||
// 如果文件是目录,只有当该目录为空时才能删除成功
|
||||
Files.delete(path)
|
||||
|
||||
```
|
||||
|
||||
* ## 获取文件属性
|
||||
```java
|
||||
// 文件是否存在
|
||||
Files.exists()
|
||||
// 文件是否是隐藏文件爱你
|
||||
Files.isHidden()
|
||||
// 对文件是否有读写执行权限
|
||||
Files.isReadable()
|
||||
Files.isWritable()
|
||||
FIles.isExecutable()
|
||||
// 文件类型是否是目录、普通文件、符号链接
|
||||
Files.isRegularFIle()
|
||||
FIles.isDirectory()
|
||||
FIles.isSymbolicLink()
|
||||
// 获取文件长度
|
||||
FIles.size()
|
||||
|
||||
```
|
||||
|
||||
* ## 访问目录中的项
|
||||
```java
|
||||
// 将目录中的项以Stream的形式返回
|
||||
// Files.list并不会递归的列出子目录中的项
|
||||
Files.list(dirPath)
|
||||
|
||||
// 递归列出子目录的项
|
||||
// 可以通过指出depth来指定遍历的深度
|
||||
FIles.walk(rootPath,depth)
|
||||
```
|
||||
106
jvm/垃圾回收.md
106
jvm/垃圾回收.md
@@ -1,54 +1,54 @@
|
||||
# 垃圾回收器
|
||||
* ## 垃圾回收器分类:
|
||||
* 串行垃圾回收器:
|
||||
* 单线程情况下使用
|
||||
* 吞吐量优先的垃圾回收器:
|
||||
* 适合在多线程情况下使用
|
||||
* 其会让单位时间内STW时间之和尽可能变短
|
||||
* 响应时间优先的垃圾回收器:
|
||||
* 同样适用于多核cpu情况
|
||||
* 其会尽可能让单次垃圾回的STW时间变短
|
||||
* ## 串行垃圾回收器:
|
||||
* 开启串行垃圾回收器:
|
||||
```java
|
||||
// 可以通过指定JVM参数来指定使用串行垃圾回收器
|
||||
// 其会使用Serial和Serial Old来作为垃圾回收器
|
||||
// Serial:年轻代垃圾回收器(使用复制算法)
|
||||
// Serial Old:老年代垃圾回收器(使用标记整理算法)
|
||||
-XX:+UseSerialGC
|
||||
```
|
||||
* 串行垃圾回收器的回收流程:
|
||||
* 串行垃圾回收器只有一个垃圾回收的线程,当垃圾回收线程进行垃圾回收时,会暂停所有的用户线程,直到垃圾回收完成后用户线程才继续运行
|
||||
* ## 吞吐量优先的垃圾回收器:
|
||||
* 开启吞吐量优先的垃圾回收器:
|
||||
```java
|
||||
// 指定JVM参数-XX:+UseParallelGC可以开启吞吐量优先的垃圾回收器
|
||||
// 其是JDK1.8环境下的默认垃圾回收器
|
||||
// 其会允许多个垃圾回收线程并行的执行垃圾回收工作
|
||||
-XX:+UseParallelGC
|
||||
```
|
||||
* ## 响应时间优先的垃圾回收器:
|
||||
* 响应时间优先的垃圾回收器采用cms垃圾回收其作为老年代垃圾回收器,而使用ParNewGC作为新生代垃圾回收器
|
||||
```java
|
||||
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC
|
||||
```
|
||||
* CMS垃圾回收工作流程:
|
||||
* 初始标记:此时需要STW,用户线程会暂停
|
||||
* 并发标记:并发标记时用户线程不需要暂停
|
||||
* 再标记:再标记时用户线程需要暂停,会产生STW
|
||||
* 并发清除:此阶段用户线程不会暂停,不需要STW
|
||||
* CMS垃圾回收缺点:
|
||||
* 在CMS进行并发清除时,并没有STW,其他用户线程在运行时可能会产生垃圾。产生的垃圾在这次垃圾回收时无法被回收,会留给下一次垃圾回收时进行清理。留给下一次垃圾回收进行清理的垃圾称之为浮动垃圾。、
|
||||
* CMS垃圾回收器采用的是标记清除算法,在长时间运行后可能会产生内存碎片。在产生内存碎片过多后,CMS会退化为Serial Old垃圾回收器并通过标记整理算法来避免内存碎片过多的情况
|
||||
|
||||
* ## G1垃圾回收器
|
||||
* G1垃圾回收器特性:
|
||||
* G1垃圾回收器同时注重了低延时和吞吐量
|
||||
* G1垃圾回收器适合与超大堆内存的管理
|
||||
* G1垃圾回收器会将对内存划分为多个大小相等的Region,每个Region可以独立作为Eden区、Surivor区或老年代区域
|
||||
* G1垃圾回收气整体上采用的是标记整理算法,但是两个Region之间采用的是复制算法
|
||||
* G1垃圾回收器的开启操作:
|
||||
```java
|
||||
// 通过指定JVM参数开启G1垃圾回收器
|
||||
-XX:+UseG1GC
|
||||
# 垃圾回收器
|
||||
* ## 垃圾回收器分类:
|
||||
* 串行垃圾回收器:
|
||||
* 单线程情况下使用
|
||||
* 吞吐量优先的垃圾回收器:
|
||||
* 适合在多线程情况下使用
|
||||
* 其会让单位时间内STW时间之和尽可能变短
|
||||
* 响应时间优先的垃圾回收器:
|
||||
* 同样适用于多核cpu情况
|
||||
* 其会尽可能让单次垃圾回的STW时间变短
|
||||
* ## 串行垃圾回收器:
|
||||
* 开启串行垃圾回收器:
|
||||
```java
|
||||
// 可以通过指定JVM参数来指定使用串行垃圾回收器
|
||||
// 其会使用Serial和Serial Old来作为垃圾回收器
|
||||
// Serial:年轻代垃圾回收器(使用复制算法)
|
||||
// Serial Old:老年代垃圾回收器(使用标记整理算法)
|
||||
-XX:+UseSerialGC
|
||||
```
|
||||
* 串行垃圾回收器的回收流程:
|
||||
* 串行垃圾回收器只有一个垃圾回收的线程,当垃圾回收线程进行垃圾回收时,会暂停所有的用户线程,直到垃圾回收完成后用户线程才继续运行
|
||||
* ## 吞吐量优先的垃圾回收器:
|
||||
* 开启吞吐量优先的垃圾回收器:
|
||||
```java
|
||||
// 指定JVM参数-XX:+UseParallelGC可以开启吞吐量优先的垃圾回收器
|
||||
// 其是JDK1.8环境下的默认垃圾回收器
|
||||
// 其会允许多个垃圾回收线程并行的执行垃圾回收工作
|
||||
-XX:+UseParallelGC
|
||||
```
|
||||
* ## 响应时间优先的垃圾回收器:
|
||||
* 响应时间优先的垃圾回收器采用cms垃圾回收其作为老年代垃圾回收器,而使用ParNewGC作为新生代垃圾回收器
|
||||
```java
|
||||
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC
|
||||
```
|
||||
* CMS垃圾回收工作流程:
|
||||
* 初始标记:此时需要STW,用户线程会暂停
|
||||
* 并发标记:并发标记时用户线程不需要暂停
|
||||
* 再标记:再标记时用户线程需要暂停,会产生STW
|
||||
* 并发清除:此阶段用户线程不会暂停,不需要STW
|
||||
* CMS垃圾回收缺点:
|
||||
* 在CMS进行并发清除时,并没有STW,其他用户线程在运行时可能会产生垃圾。产生的垃圾在这次垃圾回收时无法被回收,会留给下一次垃圾回收时进行清理。留给下一次垃圾回收进行清理的垃圾称之为浮动垃圾。、
|
||||
* CMS垃圾回收器采用的是标记清除算法,在长时间运行后可能会产生内存碎片。在产生内存碎片过多后,CMS会退化为Serial Old垃圾回收器并通过标记整理算法来避免内存碎片过多的情况
|
||||
|
||||
* ## G1垃圾回收器
|
||||
* G1垃圾回收器特性:
|
||||
* G1垃圾回收器同时注重了低延时和吞吐量
|
||||
* G1垃圾回收器适合与超大堆内存的管理
|
||||
* G1垃圾回收器会将对内存划分为多个大小相等的Region,每个Region可以独立作为Eden区、Surivor区或老年代区域
|
||||
* G1垃圾回收气整体上采用的是标记整理算法,但是两个Region之间采用的是复制算法
|
||||
* G1垃圾回收器的开启操作:
|
||||
```java
|
||||
// 通过指定JVM参数开启G1垃圾回收器
|
||||
-XX:+UseG1GC
|
||||
```
|
||||
14
jvm/类加载过程.md
14
jvm/类加载过程.md
@@ -1,8 +1,8 @@
|
||||
# 类加载过程
|
||||
* ## 类加载过程
|
||||
* 加载:次过程中会根据要加载类的全类名获取该类字节吗文件的二进制流,并且将其加载到内存的方法区中。并且,为该要加载的类生成一个Class对象,可以作为方法区类数据的访问入口
|
||||
* 链接:链接过程又分为验证、准备、解析三个阶段
|
||||
* 验证:验证文件格式是否正确,并且加载的字节码数据能否被正确
|
||||
* 准备:正式为类变量(静态变量)分配内存空间并且设置默认初始值
|
||||
* 解析:将常量池中的符号引用替换为直接引用的过程
|
||||
# 类加载过程
|
||||
* ## 类加载过程
|
||||
* 加载:次过程中会根据要加载类的全类名获取该类字节吗文件的二进制流,并且将其加载到内存的方法区中。并且,为该要加载的类生成一个Class对象,可以作为方法区类数据的访问入口
|
||||
* 链接:链接过程又分为验证、准备、解析三个阶段
|
||||
* 验证:验证文件格式是否正确,并且加载的字节码数据能否被正确
|
||||
* 准备:正式为类变量(静态变量)分配内存空间并且设置默认初始值
|
||||
* 解析:将常量池中的符号引用替换为直接引用的过程
|
||||
* 初始化:初始化阶段会调用clinit方法,调用该方法会为类变量进行赋值操作(对于用final修饰的类变量除外,其并不是在clinit阶段被赋予最终值,而是在准备阶段就赋予了最终值
|
||||
@@ -1,27 +1,27 @@
|
||||
# 常用的linux命令
|
||||
> ## 文件、字符串操作命令
|
||||
* **awk命令**:
|
||||
awk是好用的行处理工具,其能够将每行的文本当作参数传递给自定义的动作,其数据既可以来源于管道的输入,也可以来源于指定的文件
|
||||
* awk命令的格式类似于
|
||||
```shell
|
||||
awk '条件1{动作1} 条件2{动作2}...' filename
|
||||
```
|
||||
* awk处理行数据时分割字段默认用'空格'或者‘tab'进行分割
|
||||
* awk内建变量:
|
||||
1. NR: 当前行的行号
|
||||
2. NF: 当前行字段的数量
|
||||
3. FS: 目前的分隔字符,默认是空格
|
||||
* 若想要在动作中定义FS,必须在定义FS的动作之前加上BEGIN关键字,表示预先设定FS。否则,FS的设定对第一行不会生效,因为只有将第一行读入并按默认的FS进行分割后,为FS赋值的动作才会执行
|
||||
```shell
|
||||
#例如,为awk命令定义BEGIN 的FS赋值动作
|
||||
cat /etc/passwd | head -n 10 | awk 'BEGIN{FS=":";}
|
||||
{print NR "\t" $1 "\t" $3 ""}'
|
||||
```
|
||||
* uniq命令:
|
||||
uniq命令将整合相邻且相同的行为一行,通常配合sort使用来消去排序后重复的行,若指定-c选项,则在字符串前加上一列表示各行字符串的频数
|
||||
* sort命令:
|
||||
* -r : 反向排序
|
||||
* -f : 忽略大小写差异
|
||||
* -n : 使用纯数字来排序
|
||||
* -b : 忽略前面的空格部分
|
||||
* -f : 针对特定的域来进行排序
|
||||
# 常用的linux命令
|
||||
> ## 文件、字符串操作命令
|
||||
* **awk命令**:
|
||||
awk是好用的行处理工具,其能够将每行的文本当作参数传递给自定义的动作,其数据既可以来源于管道的输入,也可以来源于指定的文件
|
||||
* awk命令的格式类似于
|
||||
```shell
|
||||
awk '条件1{动作1} 条件2{动作2}...' filename
|
||||
```
|
||||
* awk处理行数据时分割字段默认用'空格'或者‘tab'进行分割
|
||||
* awk内建变量:
|
||||
1. NR: 当前行的行号
|
||||
2. NF: 当前行字段的数量
|
||||
3. FS: 目前的分隔字符,默认是空格
|
||||
* 若想要在动作中定义FS,必须在定义FS的动作之前加上BEGIN关键字,表示预先设定FS。否则,FS的设定对第一行不会生效,因为只有将第一行读入并按默认的FS进行分割后,为FS赋值的动作才会执行
|
||||
```shell
|
||||
#例如,为awk命令定义BEGIN 的FS赋值动作
|
||||
cat /etc/passwd | head -n 10 | awk 'BEGIN{FS=":";}
|
||||
{print NR "\t" $1 "\t" $3 ""}'
|
||||
```
|
||||
* uniq命令:
|
||||
uniq命令将整合相邻且相同的行为一行,通常配合sort使用来消去排序后重复的行,若指定-c选项,则在字符串前加上一列表示各行字符串的频数
|
||||
* sort命令:
|
||||
* -r : 反向排序
|
||||
* -f : 忽略大小写差异
|
||||
* -n : 使用纯数字来排序
|
||||
* -b : 忽略前面的空格部分
|
||||
* -f : 针对特定的域来进行排序
|
||||
|
||||
464
linux/shell.md
464
linux/shell.md
@@ -1,233 +1,233 @@
|
||||
# **Shell脚本编程**
|
||||
>## **shell脚本结构**
|
||||
* **shell脚本头部:**
|
||||
在shell脚本中,首行通过 #!bin/bash 来指定该shell脚本所使用的语法和执行的shell环境。
|
||||
```shell
|
||||
#!/bin/bash
|
||||
#除了首行之外,其他以'#'开头的行均为注释,在执行时会被忽略
|
||||
```
|
||||
* **程序体:**
|
||||
程序体中包含了该脚本的具体执行逻辑,并且在执行结束后,通过exit命令来返回该脚本的执行执行结果。
|
||||
```shell
|
||||
#用exit命令来返回脚本的执行状态,若返回值status为0,代表正常退出
|
||||
exit status
|
||||
```
|
||||
>## **Shell语法**
|
||||
* **输出shell脚本中定义的变量或是环境变量:**
|
||||
可以通过${VARNAME}的形式来输出变量的值,例如:
|
||||
```shell
|
||||
#注意”“和‘’的区别,
|
||||
# 当使用”“来包围字符串时,字符串中的$(expression)和${varname}
|
||||
# 都会被替换为变量值和执行结果
|
||||
# 而当使用‘’来包围字符串时,$(expression)和${varname}并不会被
|
||||
# 替换为执行结果,而是以字面量的形式输出
|
||||
echo "PATH VALUE IS : ${PATH}
|
||||
```
|
||||
* **${expr}的使用:**
|
||||
1. 字符串的摘取:
|
||||
* '#'符号用来去掉左前缀的部分,’#‘作用于于第一个匹配,’##‘作用于最后一个匹配
|
||||
```shell
|
||||
#设置路径为/var/log/mysql.log
|
||||
export VARPATH=/var/log/mysql.log
|
||||
|
||||
#通过 '#*/'去掉第一'/'字符之前的子串
|
||||
#输出结果为var/log/mysql.log
|
||||
echo ${VARPATH#*/}
|
||||
|
||||
#通过'##*/'来去掉最后一个'/'字符之前的子串
|
||||
#输出结果为mysql.log
|
||||
echo ${VARPATH##*/}
|
||||
```
|
||||
* '%'符号用来去掉右后缀的部分,'%'作用于从右往左的第一条匹配,而'%%'则作用于从右往左的最后一条匹配
|
||||
```shell
|
||||
#通过'%/*'可以去除最后一个'/'字符之后的部分
|
||||
#输出结果为/var/log
|
||||
echo ${VARPATH%/*}
|
||||
|
||||
#通过'%%/*'可以删除第一个’/'之后的部分
|
||||
#结果为空串
|
||||
echo ${VARPATH%%/*}
|
||||
```
|
||||
* 可以通过'\${\${expr1}expr2}'对前一次的操作结果进行再一次操作,例如
|
||||
```shell
|
||||
#设置路径为/etc/init.d/idea.tar.gz
|
||||
export VARPATH=/etc/init.d/idea.tar.gz
|
||||
|
||||
#截取文件扩展名tar.gz
|
||||
#输出结果为tar.gz
|
||||
echo ${${VARPATH##*/}#*.}
|
||||
```
|
||||
2. **字符串子串的截取和替换**
|
||||
* \${STR:from:len}可以截取字符串从第from个字符开始,长度为len的子串
|
||||
```shell
|
||||
export STR=/var/log/mysql.log
|
||||
|
||||
#截取第6到第8个字符,结果为log
|
||||
echo ${STR:5:3}
|
||||
```
|
||||
* 可用/src/target来将字符串中第一个src子串替换为target,用//src/target可以将字符串中所有的src子串替换为target
|
||||
```shell
|
||||
export STR='linux is not unix'
|
||||
|
||||
echo ${STR/n/N} #输出liNux is not unix,替换第一个‘n’
|
||||
echo ${STR//n/N} #输出liNux is Not uNix,替换所有‘n’
|
||||
```
|
||||
* 字符串长度可用${#STR}来获取
|
||||
```shell
|
||||
export STR=linux
|
||||
|
||||
echo ${#STR} #输出结果为5
|
||||
|
||||
```
|
||||
3. **根据状态为字符串赋值**
|
||||
* ${VAR-'value'}:如果当前VAR没有设置,则返回‘value'值
|
||||
* ${VAR:-'value'}:如果当前VAR没有设置或为空串,返回'value'值
|
||||
* ${VAR+'value'}:如果当前VAR没有设置,返回空值,否则返回'value'值
|
||||
* ${VAR='value'}:如果当前VAR没有设置,将VAR设置为'value',并且返回'value'
|
||||
* ${VAR:='value'}:如果当前VAR没有设置或者为空串,将VAR设置为'value',并且返回'value'
|
||||
|
||||
4. **数组**
|
||||
* 可以通过(ele1 ele2 ele3 ...)来定义数组
|
||||
* 可以通过\${ARR[i]}来访问数组中的元素
|
||||
* ${ARR[@]}或者${ARR[*]}可以返回数组中所有的元素
|
||||
* 获取数组中元素的个数可以使用如下表达式
|
||||
```shell
|
||||
${#ARR[@]}
|
||||
#注意,获取数组中元素数量必须用${#ARR[@]}而不是${#ARR},
|
||||
#在bash中,${ARR}只会返回ARR数组的第一个元素,
|
||||
#在bash中,${#ARR}也只会返回数组中的第一个字符串的字符数量
|
||||
```
|
||||
* 在bash中,向数组中添加元素需要用如下方式
|
||||
```shell
|
||||
ARR[${#ARR[@]}]=value
|
||||
```
|
||||
|
||||
|
||||
* **整数运算**
|
||||
* 在shell脚本中,可以使用 var=$((expr)) 来执行浮点数运算和赋值,但是,在bash中,expr仅支持整形的运算,并不支持浮点数运算,而zsh中expr既可以是整形也可以是浮点型运算
|
||||
```shell
|
||||
#在bash中,可以通过bc和管道来实现浮点数的运算
|
||||
#注意在使用bc命令执行浮点数运算时,必须指定scale精度,否则起默认情况下并不保留小数
|
||||
echo "scale=3;1.0/3" | bc
|
||||
```
|
||||
* **test命令和条件判断**
|
||||
* test命令可以用于判断表达式为true/false
|
||||
1. 文件判断:
|
||||
* -e : 文件是否存在
|
||||
* -f : 文件是否存在且为普通文件
|
||||
* -d : 文件是否存在且为目录类型
|
||||
* -b : 文件是否存在且是块设备
|
||||
* -c : 文件是否存在且是字符型设备
|
||||
* -S : 文件爱呢是否存在且为sock
|
||||
* -p : 文件是否存在且为FIFO(pipe)
|
||||
* -L : 文件是否存在且为链接(link)
|
||||
2. 权限判断:
|
||||
* -r : 文件是否存在且对其有读权限
|
||||
* -w : 文件是否存在且对其有写权限
|
||||
* -x : 文件是否存在且对其有执行权限
|
||||
* -s : 文件是否非空白,若为空文件,返回为false
|
||||
3. 文件之间的比较:
|
||||
* f1 -ef f2 : 比较f1和f2两个文件是否是同一个文件
|
||||
* f1 -ot f2 : 比较f1文件的mtime是否比f2文件老
|
||||
* f1 -nt f2 : 比较f1的mtime是否比f2新
|
||||
* test命令用来比较整数(浮点数无效)
|
||||
* a -eq b : 比较a和b是否相等
|
||||
* a -ne b : 比较a和b是否不等
|
||||
* a -lt b : 比较a是否小于b
|
||||
* a -gt b : 比较a是否大于b
|
||||
* a -le b : 比较a是否小于等于b
|
||||
* a -ge b : 比较a是否大于等于b
|
||||
* test判断字符串
|
||||
* -n : 判断字符串是否不为空
|
||||
* -z : 判断字符串是否为空
|
||||
* == : 判断字符串是否相等
|
||||
* != : 判断字符串是否不相等
|
||||
* 多重条件判断
|
||||
* -a : and
|
||||
* -o : or
|
||||
* ! : not
|
||||
* 可以用[]来代替test命令,例如 test a -eq b 可以被替换为[ a -eq b ],注意[ expr ]中'['和expr之间、expr和']'之间都需要有空格分隔
|
||||
* 通常,在test或者[ expr]表达式中使用\${VARNAME}时,都需要用" "将起扩起来,因为\${VARNME}可能在字符串中包含空格,例如"touma kazua",若是不用”“将其包含,那么起将被命令行识别为两个参数,会导致too many arguments异常
|
||||
* **shell传参**
|
||||
* shell脚本可以在脚本名之后附加参数,如 mysum.sh 1 2 ,其中 $0 为 mysum.sh ,$1 为1,$2 为2,以此类推。还有如下特殊参数:
|
||||
* $# : 除了脚本名称之外的参数个数
|
||||
* $@ : 除了脚本名之外其他参数组成的数组,各个变量仍然独立
|
||||
* $* : 除了脚本名之外所有参数聚合成的字符串,默认使用空格分隔
|
||||
* **Shell条件判断**
|
||||
* if-then结构:
|
||||
```shell
|
||||
if [ expr ];then
|
||||
some operations...
|
||||
fi
|
||||
```
|
||||
* if-else-if结构:
|
||||
```shell
|
||||
if [ expr1 ];then
|
||||
some operations...
|
||||
elif [ expr2 ];then
|
||||
some operations...
|
||||
elif [ expr3 ];then
|
||||
some operations...
|
||||
else
|
||||
some operations...
|
||||
fi
|
||||
```
|
||||
* switch-case结构:
|
||||
```shell
|
||||
case ${var} in
|
||||
"str1")
|
||||
some operations...
|
||||
;;
|
||||
"str2")
|
||||
some operations...
|
||||
;;
|
||||
"str3")
|
||||
some operations...
|
||||
;;
|
||||
*)
|
||||
some operations...
|
||||
;;
|
||||
esac
|
||||
```
|
||||
* Shell函数
|
||||
* Shell函数可以通过function关键字来创建,格式如下
|
||||
```shell
|
||||
function FunctionName()
|
||||
{
|
||||
Function Body...
|
||||
#函数中也可以通过$1、$2来获取传递给函数的参数
|
||||
}
|
||||
|
||||
#函数调用
|
||||
FunctionName arg1 arg2 ...
|
||||
#函数内通过$1只能读取传递给函数的参数变量,而无法读取shell程序中的参数变量
|
||||
```
|
||||
* Shell循环
|
||||
* while循环
|
||||
```shell
|
||||
#while循环结构
|
||||
while [ expr]
|
||||
do
|
||||
some operations...
|
||||
done
|
||||
```
|
||||
* until循环
|
||||
```shell
|
||||
until [ expr ]
|
||||
do
|
||||
some operations...
|
||||
done
|
||||
```
|
||||
* for-in循环
|
||||
```shell
|
||||
#可以用seq命令指定首数/尾数/增量,以便进行1~n的遍历
|
||||
#seq 尾数
|
||||
#seq 首数 尾数
|
||||
#seq 首数 增量 尾数
|
||||
for var in obj1 obj2 obj3...
|
||||
do
|
||||
some operations...
|
||||
done
|
||||
```
|
||||
* Shell语法检测
|
||||
# **Shell脚本编程**
|
||||
>## **shell脚本结构**
|
||||
* **shell脚本头部:**
|
||||
在shell脚本中,首行通过 #!bin/bash 来指定该shell脚本所使用的语法和执行的shell环境。
|
||||
```shell
|
||||
#!/bin/bash
|
||||
#除了首行之外,其他以'#'开头的行均为注释,在执行时会被忽略
|
||||
```
|
||||
* **程序体:**
|
||||
程序体中包含了该脚本的具体执行逻辑,并且在执行结束后,通过exit命令来返回该脚本的执行执行结果。
|
||||
```shell
|
||||
#用exit命令来返回脚本的执行状态,若返回值status为0,代表正常退出
|
||||
exit status
|
||||
```
|
||||
>## **Shell语法**
|
||||
* **输出shell脚本中定义的变量或是环境变量:**
|
||||
可以通过${VARNAME}的形式来输出变量的值,例如:
|
||||
```shell
|
||||
#注意”“和‘’的区别,
|
||||
# 当使用”“来包围字符串时,字符串中的$(expression)和${varname}
|
||||
# 都会被替换为变量值和执行结果
|
||||
# 而当使用‘’来包围字符串时,$(expression)和${varname}并不会被
|
||||
# 替换为执行结果,而是以字面量的形式输出
|
||||
echo "PATH VALUE IS : ${PATH}
|
||||
```
|
||||
* **${expr}的使用:**
|
||||
1. 字符串的摘取:
|
||||
* '#'符号用来去掉左前缀的部分,’#‘作用于于第一个匹配,’##‘作用于最后一个匹配
|
||||
```shell
|
||||
#设置路径为/var/log/mysql.log
|
||||
export VARPATH=/var/log/mysql.log
|
||||
|
||||
#通过 '#*/'去掉第一'/'字符之前的子串
|
||||
#输出结果为var/log/mysql.log
|
||||
echo ${VARPATH#*/}
|
||||
|
||||
#通过'##*/'来去掉最后一个'/'字符之前的子串
|
||||
#输出结果为mysql.log
|
||||
echo ${VARPATH##*/}
|
||||
```
|
||||
* '%'符号用来去掉右后缀的部分,'%'作用于从右往左的第一条匹配,而'%%'则作用于从右往左的最后一条匹配
|
||||
```shell
|
||||
#通过'%/*'可以去除最后一个'/'字符之后的部分
|
||||
#输出结果为/var/log
|
||||
echo ${VARPATH%/*}
|
||||
|
||||
#通过'%%/*'可以删除第一个’/'之后的部分
|
||||
#结果为空串
|
||||
echo ${VARPATH%%/*}
|
||||
```
|
||||
* 可以通过'\${\${expr1}expr2}'对前一次的操作结果进行再一次操作,例如
|
||||
```shell
|
||||
#设置路径为/etc/init.d/idea.tar.gz
|
||||
export VARPATH=/etc/init.d/idea.tar.gz
|
||||
|
||||
#截取文件扩展名tar.gz
|
||||
#输出结果为tar.gz
|
||||
echo ${${VARPATH##*/}#*.}
|
||||
```
|
||||
2. **字符串子串的截取和替换**
|
||||
* \${STR:from:len}可以截取字符串从第from个字符开始,长度为len的子串
|
||||
```shell
|
||||
export STR=/var/log/mysql.log
|
||||
|
||||
#截取第6到第8个字符,结果为log
|
||||
echo ${STR:5:3}
|
||||
```
|
||||
* 可用/src/target来将字符串中第一个src子串替换为target,用//src/target可以将字符串中所有的src子串替换为target
|
||||
```shell
|
||||
export STR='linux is not unix'
|
||||
|
||||
echo ${STR/n/N} #输出liNux is not unix,替换第一个‘n’
|
||||
echo ${STR//n/N} #输出liNux is Not uNix,替换所有‘n’
|
||||
```
|
||||
* 字符串长度可用${#STR}来获取
|
||||
```shell
|
||||
export STR=linux
|
||||
|
||||
echo ${#STR} #输出结果为5
|
||||
|
||||
```
|
||||
3. **根据状态为字符串赋值**
|
||||
* ${VAR-'value'}:如果当前VAR没有设置,则返回‘value'值
|
||||
* ${VAR:-'value'}:如果当前VAR没有设置或为空串,返回'value'值
|
||||
* ${VAR+'value'}:如果当前VAR没有设置,返回空值,否则返回'value'值
|
||||
* ${VAR='value'}:如果当前VAR没有设置,将VAR设置为'value',并且返回'value'
|
||||
* ${VAR:='value'}:如果当前VAR没有设置或者为空串,将VAR设置为'value',并且返回'value'
|
||||
|
||||
4. **数组**
|
||||
* 可以通过(ele1 ele2 ele3 ...)来定义数组
|
||||
* 可以通过\${ARR[i]}来访问数组中的元素
|
||||
* ${ARR[@]}或者${ARR[*]}可以返回数组中所有的元素
|
||||
* 获取数组中元素的个数可以使用如下表达式
|
||||
```shell
|
||||
${#ARR[@]}
|
||||
#注意,获取数组中元素数量必须用${#ARR[@]}而不是${#ARR},
|
||||
#在bash中,${ARR}只会返回ARR数组的第一个元素,
|
||||
#在bash中,${#ARR}也只会返回数组中的第一个字符串的字符数量
|
||||
```
|
||||
* 在bash中,向数组中添加元素需要用如下方式
|
||||
```shell
|
||||
ARR[${#ARR[@]}]=value
|
||||
```
|
||||
|
||||
|
||||
* **整数运算**
|
||||
* 在shell脚本中,可以使用 var=$((expr)) 来执行浮点数运算和赋值,但是,在bash中,expr仅支持整形的运算,并不支持浮点数运算,而zsh中expr既可以是整形也可以是浮点型运算
|
||||
```shell
|
||||
#在bash中,可以通过bc和管道来实现浮点数的运算
|
||||
#注意在使用bc命令执行浮点数运算时,必须指定scale精度,否则起默认情况下并不保留小数
|
||||
echo "scale=3;1.0/3" | bc
|
||||
```
|
||||
* **test命令和条件判断**
|
||||
* test命令可以用于判断表达式为true/false
|
||||
1. 文件判断:
|
||||
* -e : 文件是否存在
|
||||
* -f : 文件是否存在且为普通文件
|
||||
* -d : 文件是否存在且为目录类型
|
||||
* -b : 文件是否存在且是块设备
|
||||
* -c : 文件是否存在且是字符型设备
|
||||
* -S : 文件爱呢是否存在且为sock
|
||||
* -p : 文件是否存在且为FIFO(pipe)
|
||||
* -L : 文件是否存在且为链接(link)
|
||||
2. 权限判断:
|
||||
* -r : 文件是否存在且对其有读权限
|
||||
* -w : 文件是否存在且对其有写权限
|
||||
* -x : 文件是否存在且对其有执行权限
|
||||
* -s : 文件是否非空白,若为空文件,返回为false
|
||||
3. 文件之间的比较:
|
||||
* f1 -ef f2 : 比较f1和f2两个文件是否是同一个文件
|
||||
* f1 -ot f2 : 比较f1文件的mtime是否比f2文件老
|
||||
* f1 -nt f2 : 比较f1的mtime是否比f2新
|
||||
* test命令用来比较整数(浮点数无效)
|
||||
* a -eq b : 比较a和b是否相等
|
||||
* a -ne b : 比较a和b是否不等
|
||||
* a -lt b : 比较a是否小于b
|
||||
* a -gt b : 比较a是否大于b
|
||||
* a -le b : 比较a是否小于等于b
|
||||
* a -ge b : 比较a是否大于等于b
|
||||
* test判断字符串
|
||||
* -n : 判断字符串是否不为空
|
||||
* -z : 判断字符串是否为空
|
||||
* == : 判断字符串是否相等
|
||||
* != : 判断字符串是否不相等
|
||||
* 多重条件判断
|
||||
* -a : and
|
||||
* -o : or
|
||||
* ! : not
|
||||
* 可以用[]来代替test命令,例如 test a -eq b 可以被替换为[ a -eq b ],注意[ expr ]中'['和expr之间、expr和']'之间都需要有空格分隔
|
||||
* 通常,在test或者[ expr]表达式中使用\${VARNAME}时,都需要用" "将起扩起来,因为\${VARNME}可能在字符串中包含空格,例如"touma kazua",若是不用”“将其包含,那么起将被命令行识别为两个参数,会导致too many arguments异常
|
||||
* **shell传参**
|
||||
* shell脚本可以在脚本名之后附加参数,如 mysum.sh 1 2 ,其中 $0 为 mysum.sh ,$1 为1,$2 为2,以此类推。还有如下特殊参数:
|
||||
* $# : 除了脚本名称之外的参数个数
|
||||
* $@ : 除了脚本名之外其他参数组成的数组,各个变量仍然独立
|
||||
* $* : 除了脚本名之外所有参数聚合成的字符串,默认使用空格分隔
|
||||
* **Shell条件判断**
|
||||
* if-then结构:
|
||||
```shell
|
||||
if [ expr ];then
|
||||
some operations...
|
||||
fi
|
||||
```
|
||||
* if-else-if结构:
|
||||
```shell
|
||||
if [ expr1 ];then
|
||||
some operations...
|
||||
elif [ expr2 ];then
|
||||
some operations...
|
||||
elif [ expr3 ];then
|
||||
some operations...
|
||||
else
|
||||
some operations...
|
||||
fi
|
||||
```
|
||||
* switch-case结构:
|
||||
```shell
|
||||
case ${var} in
|
||||
"str1")
|
||||
some operations...
|
||||
;;
|
||||
"str2")
|
||||
some operations...
|
||||
;;
|
||||
"str3")
|
||||
some operations...
|
||||
;;
|
||||
*)
|
||||
some operations...
|
||||
;;
|
||||
esac
|
||||
```
|
||||
* Shell函数
|
||||
* Shell函数可以通过function关键字来创建,格式如下
|
||||
```shell
|
||||
function FunctionName()
|
||||
{
|
||||
Function Body...
|
||||
#函数中也可以通过$1、$2来获取传递给函数的参数
|
||||
}
|
||||
|
||||
#函数调用
|
||||
FunctionName arg1 arg2 ...
|
||||
#函数内通过$1只能读取传递给函数的参数变量,而无法读取shell程序中的参数变量
|
||||
```
|
||||
* Shell循环
|
||||
* while循环
|
||||
```shell
|
||||
#while循环结构
|
||||
while [ expr]
|
||||
do
|
||||
some operations...
|
||||
done
|
||||
```
|
||||
* until循环
|
||||
```shell
|
||||
until [ expr ]
|
||||
do
|
||||
some operations...
|
||||
done
|
||||
```
|
||||
* for-in循环
|
||||
```shell
|
||||
#可以用seq命令指定首数/尾数/增量,以便进行1~n的遍历
|
||||
#seq 尾数
|
||||
#seq 首数 尾数
|
||||
#seq 首数 增量 尾数
|
||||
for var in obj1 obj2 obj3...
|
||||
do
|
||||
some operations...
|
||||
done
|
||||
```
|
||||
* Shell语法检测
|
||||
* 可以使用sh -n来检测shell脚本的语法是否正确
|
||||
120
linux/分区.md
120
linux/分区.md
@@ -1,61 +1,61 @@
|
||||
# linux环境下的分区操作
|
||||
* ## 查看分区信息的命令:
|
||||
* lsblk : 列出设备上所有的磁盘列表和分区信息
|
||||
```shell
|
||||
# 通过lsblk命令可以列出所有的磁盘设备和分区信息
|
||||
# 通过指定-f选项还可以输出分区的UUID
|
||||
lsblk -f
|
||||
```
|
||||
* parted :输出分区类型和分区表信息
|
||||
```shell
|
||||
# 输出分区类型(ext/ntfs)和分区表类型(gpt/mbr)还有其他信息
|
||||
parted /dev/sda print
|
||||
```
|
||||
* ## 对磁盘进行分区
|
||||
* 对于GPT格式进行分区的硬盘来说,可以用gdisk命令对其进行操作
|
||||
* 在通过gdisk /dev/sda进入gdisk程序后,
|
||||
```shell
|
||||
# 在控制台上输出帮助信息
|
||||
?
|
||||
|
||||
# 打印整颗磁盘的分区信息
|
||||
p
|
||||
|
||||
# 新增分区
|
||||
n
|
||||
|
||||
# 修改分区表后对内核的分区表信息进行更新
|
||||
# 在shell中使用partprobe
|
||||
partprobe
|
||||
|
||||
# 删除分区
|
||||
# 在删除后应该重启或者用partprobe命令更新
|
||||
d
|
||||
|
||||
# 在对分区进行处理时,应该先取消对分区的挂载,否则会出现问题
|
||||
```
|
||||
* 对mbr分区类型的磁盘进行分区修改时,应该用fdisk命令,操作与gdisk类似
|
||||
|
||||
* ## 分区后对分区进行格式化
|
||||
* 在分区后,只有对分好的分区进行格式化后,分区才能被操作系统使用
|
||||
```shell
|
||||
# 对于ext4文件系统,可以采用mkfs.ext4来进行格式化
|
||||
# 可以使用-L来指定卷标
|
||||
mkfs.ext4 /dev/sdaxx
|
||||
|
||||
# 对于ext4文件系统,如果文件系统出现问题,可以尝试使用
|
||||
# fsck.ext4命令进行修复
|
||||
fsck.ext4 /dev/sdaxx
|
||||
|
||||
# 可以通过tune2fs -L name /dev/sdaxx来修改文件系统的卷标
|
||||
```
|
||||
|
||||
* 分区的挂载和卸载
|
||||
```shell
|
||||
# 分区的挂载可以使用mount命令
|
||||
# 卸载可以使用umount命令
|
||||
# 挂载时,可以使用-o选项指定挂载参数
|
||||
# remount可以制定重新挂载
|
||||
# rw、ro可以指定是以只读模式挂载还是以读写模式挂载
|
||||
# 要想设置自动挂载,可以将想要挂载的设备添加到/etc/fstab中
|
||||
# linux环境下的分区操作
|
||||
* ## 查看分区信息的命令:
|
||||
* lsblk : 列出设备上所有的磁盘列表和分区信息
|
||||
```shell
|
||||
# 通过lsblk命令可以列出所有的磁盘设备和分区信息
|
||||
# 通过指定-f选项还可以输出分区的UUID
|
||||
lsblk -f
|
||||
```
|
||||
* parted :输出分区类型和分区表信息
|
||||
```shell
|
||||
# 输出分区类型(ext/ntfs)和分区表类型(gpt/mbr)还有其他信息
|
||||
parted /dev/sda print
|
||||
```
|
||||
* ## 对磁盘进行分区
|
||||
* 对于GPT格式进行分区的硬盘来说,可以用gdisk命令对其进行操作
|
||||
* 在通过gdisk /dev/sda进入gdisk程序后,
|
||||
```shell
|
||||
# 在控制台上输出帮助信息
|
||||
?
|
||||
|
||||
# 打印整颗磁盘的分区信息
|
||||
p
|
||||
|
||||
# 新增分区
|
||||
n
|
||||
|
||||
# 修改分区表后对内核的分区表信息进行更新
|
||||
# 在shell中使用partprobe
|
||||
partprobe
|
||||
|
||||
# 删除分区
|
||||
# 在删除后应该重启或者用partprobe命令更新
|
||||
d
|
||||
|
||||
# 在对分区进行处理时,应该先取消对分区的挂载,否则会出现问题
|
||||
```
|
||||
* 对mbr分区类型的磁盘进行分区修改时,应该用fdisk命令,操作与gdisk类似
|
||||
|
||||
* ## 分区后对分区进行格式化
|
||||
* 在分区后,只有对分好的分区进行格式化后,分区才能被操作系统使用
|
||||
```shell
|
||||
# 对于ext4文件系统,可以采用mkfs.ext4来进行格式化
|
||||
# 可以使用-L来指定卷标
|
||||
mkfs.ext4 /dev/sdaxx
|
||||
|
||||
# 对于ext4文件系统,如果文件系统出现问题,可以尝试使用
|
||||
# fsck.ext4命令进行修复
|
||||
fsck.ext4 /dev/sdaxx
|
||||
|
||||
# 可以通过tune2fs -L name /dev/sdaxx来修改文件系统的卷标
|
||||
```
|
||||
|
||||
* 分区的挂载和卸载
|
||||
```shell
|
||||
# 分区的挂载可以使用mount命令
|
||||
# 卸载可以使用umount命令
|
||||
# 挂载时,可以使用-o选项指定挂载参数
|
||||
# remount可以制定重新挂载
|
||||
# rw、ro可以指定是以只读模式挂载还是以读写模式挂载
|
||||
# 要想设置自动挂载,可以将想要挂载的设备添加到/etc/fstab中
|
||||
```
|
||||
@@ -1,6 +1,6 @@
|
||||
# binlog 日志
|
||||
* ## binlog(二进制日志)
|
||||
* 定义:binlog日志文件也称之为变更日志文件,记录了数据库记录的所有DDL、DML等数据库更新事件的语句,但是不包括任何没有更新数据的语句。
|
||||
* 用途:
|
||||
* 数据恢复:如果mysql数据库进程意外停止,可以通过binlog中的记录来恢复数据库中的数据
|
||||
# binlog 日志
|
||||
* ## binlog(二进制日志)
|
||||
* 定义:binlog日志文件也称之为变更日志文件,记录了数据库记录的所有DDL、DML等数据库更新事件的语句,但是不包括任何没有更新数据的语句。
|
||||
* 用途:
|
||||
* 数据恢复:如果mysql数据库进程意外停止,可以通过binlog中的记录来恢复数据库中的数据
|
||||
* 数据复制:可以通过binlog来实现mysql数据库的主从一致
|
||||
@@ -1,49 +1,49 @@
|
||||
# 多版本并发控制
|
||||
* ## 多版本并发控制解决的问题:
|
||||
* 在多个事务并发访问资源时,可能会发生并发安全问题。为了解决多个事务同时对资进行读写的问题,可以采用如下解决方案:
|
||||
* 多事务同时对资源进行读取:并发安全
|
||||
* 多事务同时对资源进行写操作:非并发安全,可以对写操作进行加锁来解决并发安全问题
|
||||
* 多事务同时对资源进行读和写操作:非并发安全
|
||||
1. 对读操作和写操作都加锁:性能较差
|
||||
2. 通过MVCC机制来解决并发读写的问题,对写操作加锁,并用MVCC机制来使读操作读到先前的值
|
||||
* ## 多版本并发控的概念:
|
||||
* MVCC通过数据库行的多个版本管理来实现数据库的并发控制。当一个写事务正在更新数据行的值时,通过MVCC机制,可以在另一个事物对数据行进行读操作时读取到被写事务更新之前的值。
|
||||
* 通过MVCC机制,可以更好的解决事务在读-写操作同时发生时的性能问题。MVCC可以避免在写事务时另一个读事务必须等待当前写事务释放排他锁,而是可以通过MVCC读取资源被写事务修改之前的值。
|
||||
|
||||
* ## mysql中读操作的种类:
|
||||
* 当前读:读取数据库中当前的值,为了解决并发安全问题,需要对读操作进行加锁操作,可以尝试加排他锁或者共享锁,当当前事务对资源进行写操作时,读事务会阻塞直到写事务释放锁:
|
||||
```mysql
|
||||
# 为select语句加上共享锁
|
||||
select * from user where... lock in share mode
|
||||
|
||||
# 为select语句加上排他锁
|
||||
select * from user where... for update
|
||||
```
|
||||
* 快照读:mysql中默认select语句的读取方式,当当前事务对资源进行读操作时,如果另一个事务正在对资源进行写操作,那么读操作并不会阻塞而是会读取资源被写事务修改之前的值
|
||||
|
||||
* ## MVCC原理
|
||||
* 隐藏字段:对于使用innodb存储引擎的表,其聚簇索引包含两个隐藏字段:
|
||||
1. trx_id:每个事务对每条记录进行更改时,该trx_id字段都会记载最后一次修改该行记录的事务id
|
||||
2. roll_pointer:指向undo log中关于修改该条数据的记录
|
||||
* ReadView:ReadView是MVCC机制中事务对数据进行快照读时产生的一个读视图。
|
||||
* ReadView原理:
|
||||
* 当事务开启时,会产生当前数据库系统的一个快照,innodb为每个事务构造了一个数组,用来维护当前系统中活跃事务的ID(活跃事务为当前开启了但是尚未提交的事务id)
|
||||
* ReadView中保存了如下信息:
|
||||
* creator_trx_id:创建当前ReadView的事务id
|
||||
* trx_ids:记录创建失误时当前mysql系统中活跃的事务id集合(已经开始但是还没有被提交的事务id)
|
||||
* up_limit_id:trx_ids中最小的事务id
|
||||
* low_limit_id:表示生成ReadView中当前系统应该分配给下一个事务的id
|
||||
* MVCC仅针对读已提交和可重复读的情况,在读已提交的情况下,事务中每执行一次select操作,ReadView都会重复生成;而在可重复读的隔离级别下,事务仅仅会在第一次select操作时生成ReadView
|
||||
* MVCC细节:
|
||||
* 当select语句想要对一条记录中的数据进行读取时,首先会查看记录的trx_id是是否对当前事务的读操作是可见的,判断事务是否可见的标准如下所示:
|
||||
* 如果记录的trx_id大于low_limit_id,那么说明在创建ReadView时对记录进行修改的事务还没有被创建,当然修改对当前读事务来说是不可见的
|
||||
* 如果trx_id小于up_limit_id,那么说明对该记录进行修改的事务id小于创建ReadView时最小的活跃事务id,在创建ReadView时修改记录的事物已经被提交,修改对当前事务来说可见
|
||||
* 如果trx_id位于up_limit_id和low_limit_id之间,那么:
|
||||
* trx_id如果与trx_ids中保存的某个活跃事务id相同,那么说明在创建ReadView时修改事务的id尚未被提交,修改对当前失误不可见
|
||||
* 如果trx_id与trx_ids中每个活跃事物id都不相同,那么修改事务在创建ReadView事物之时已经被提交,修改对当前事务可见
|
||||
* 根据上述规则,如果想要读取的记录trx_id对当前事务来说可见,那么获取该事务id所对应的数据值;如果trx_id对当前ReadView来说不可见,那么沿着roll_pointer沿着undo log向前寻找,知道找到对当前ReadView可见的事务id;如果undo log中所有的事务id对当前ReadView来说都不可见,那么对当前事物来说数据表中该条记录并不可见
|
||||
|
||||
* ## MVCC与幻读问题
|
||||
* MVCC仅在读已提交和可重复读的情况下起作用,而关于幻读问题,MVCC仅在可重复读的隔离级别下解决。
|
||||
* 在可重复读的隔离级别下,ReadView仅仅在第一次select语句时生成,故而在同一事务中多次读取之间,其他事务插入了新数据,那么该新数据对应的trx_id在readView创建时应该处于活跃或者未创建的状态,故而对ReadView所对应事物来说,即使其他事务插入了新数据,那么新插入的数据也不可见。
|
||||
# 多版本并发控制
|
||||
* ## 多版本并发控制解决的问题:
|
||||
* 在多个事务并发访问资源时,可能会发生并发安全问题。为了解决多个事务同时对资进行读写的问题,可以采用如下解决方案:
|
||||
* 多事务同时对资源进行读取:并发安全
|
||||
* 多事务同时对资源进行写操作:非并发安全,可以对写操作进行加锁来解决并发安全问题
|
||||
* 多事务同时对资源进行读和写操作:非并发安全
|
||||
1. 对读操作和写操作都加锁:性能较差
|
||||
2. 通过MVCC机制来解决并发读写的问题,对写操作加锁,并用MVCC机制来使读操作读到先前的值
|
||||
* ## 多版本并发控的概念:
|
||||
* MVCC通过数据库行的多个版本管理来实现数据库的并发控制。当一个写事务正在更新数据行的值时,通过MVCC机制,可以在另一个事物对数据行进行读操作时读取到被写事务更新之前的值。
|
||||
* 通过MVCC机制,可以更好的解决事务在读-写操作同时发生时的性能问题。MVCC可以避免在写事务时另一个读事务必须等待当前写事务释放排他锁,而是可以通过MVCC读取资源被写事务修改之前的值。
|
||||
|
||||
* ## mysql中读操作的种类:
|
||||
* 当前读:读取数据库中当前的值,为了解决并发安全问题,需要对读操作进行加锁操作,可以尝试加排他锁或者共享锁,当当前事务对资源进行写操作时,读事务会阻塞直到写事务释放锁:
|
||||
```mysql
|
||||
# 为select语句加上共享锁
|
||||
select * from user where... lock in share mode
|
||||
|
||||
# 为select语句加上排他锁
|
||||
select * from user where... for update
|
||||
```
|
||||
* 快照读:mysql中默认select语句的读取方式,当当前事务对资源进行读操作时,如果另一个事务正在对资源进行写操作,那么读操作并不会阻塞而是会读取资源被写事务修改之前的值
|
||||
|
||||
* ## MVCC原理
|
||||
* 隐藏字段:对于使用innodb存储引擎的表,其聚簇索引包含两个隐藏字段:
|
||||
1. trx_id:每个事务对每条记录进行更改时,该trx_id字段都会记载最后一次修改该行记录的事务id
|
||||
2. roll_pointer:指向undo log中关于修改该条数据的记录
|
||||
* ReadView:ReadView是MVCC机制中事务对数据进行快照读时产生的一个读视图。
|
||||
* ReadView原理:
|
||||
* 当事务开启时,会产生当前数据库系统的一个快照,innodb为每个事务构造了一个数组,用来维护当前系统中活跃事务的ID(活跃事务为当前开启了但是尚未提交的事务id)
|
||||
* ReadView中保存了如下信息:
|
||||
* creator_trx_id:创建当前ReadView的事务id
|
||||
* trx_ids:记录创建失误时当前mysql系统中活跃的事务id集合(已经开始但是还没有被提交的事务id)
|
||||
* up_limit_id:trx_ids中最小的事务id
|
||||
* low_limit_id:表示生成ReadView中当前系统应该分配给下一个事务的id
|
||||
* MVCC仅针对读已提交和可重复读的情况,在读已提交的情况下,事务中每执行一次select操作,ReadView都会重复生成;而在可重复读的隔离级别下,事务仅仅会在第一次select操作时生成ReadView
|
||||
* MVCC细节:
|
||||
* 当select语句想要对一条记录中的数据进行读取时,首先会查看记录的trx_id是是否对当前事务的读操作是可见的,判断事务是否可见的标准如下所示:
|
||||
* 如果记录的trx_id大于low_limit_id,那么说明在创建ReadView时对记录进行修改的事务还没有被创建,当然修改对当前读事务来说是不可见的
|
||||
* 如果trx_id小于up_limit_id,那么说明对该记录进行修改的事务id小于创建ReadView时最小的活跃事务id,在创建ReadView时修改记录的事物已经被提交,修改对当前事务来说可见
|
||||
* 如果trx_id位于up_limit_id和low_limit_id之间,那么:
|
||||
* trx_id如果与trx_ids中保存的某个活跃事务id相同,那么说明在创建ReadView时修改事务的id尚未被提交,修改对当前失误不可见
|
||||
* 如果trx_id与trx_ids中每个活跃事物id都不相同,那么修改事务在创建ReadView事物之时已经被提交,修改对当前事务可见
|
||||
* 根据上述规则,如果想要读取的记录trx_id对当前事务来说可见,那么获取该事务id所对应的数据值;如果trx_id对当前ReadView来说不可见,那么沿着roll_pointer沿着undo log向前寻找,知道找到对当前ReadView可见的事务id;如果undo log中所有的事务id对当前ReadView来说都不可见,那么对当前事物来说数据表中该条记录并不可见
|
||||
|
||||
* ## MVCC与幻读问题
|
||||
* MVCC仅在读已提交和可重复读的情况下起作用,而关于幻读问题,MVCC仅在可重复读的隔离级别下解决。
|
||||
* 在可重复读的隔离级别下,ReadView仅仅在第一次select语句时生成,故而在同一事务中多次读取之间,其他事务插入了新数据,那么该新数据对应的trx_id在readView创建时应该处于活跃或者未创建的状态,故而对ReadView所对应事物来说,即使其他事务插入了新数据,那么新插入的数据也不可见。
|
||||
* 而在读已提交的隔离级别下,ReadView在每次读操作的情况下都会被创建,故而在两次读操作之间,如果新的数据被插入,那么新插入的数据对后一次读操作创建的ReadView来说是已经提交的数据,是可见的。故而MVCC在读已提交的隔离级别下并不能够解决幻读的问题。
|
||||
126
mysql/mysql加锁.md
126
mysql/mysql加锁.md
@@ -1,64 +1,64 @@
|
||||
# mysql锁
|
||||
* ## msyql中锁的分类
|
||||
* 从数据库操作的类型划分,mysql中的锁可以分为读锁/共享锁和写锁/排他锁
|
||||
* 读锁(S锁,Share)
|
||||
* 写锁(X锁,Exclusive)
|
||||
* 写锁和读锁示例
|
||||
```mysql
|
||||
# 为语句加上写锁
|
||||
select * from table_name for udpate
|
||||
|
||||
# 为查询语句加上读锁
|
||||
select * from table_name lock in share mode
|
||||
# or
|
||||
select * from table_name for share(mysql 8.0新增语法)
|
||||
```
|
||||
* 在获取读锁或者写锁之后,只有当事务结束之后,获取到的读锁或者写锁才会被释放。在innodb中,for update或者for share语句只会为查询匹配的行数据上锁,并且,当事务结束或者回滚之后,会释放为数据行所加的排他锁或者共享锁
|
||||
* 在对mysql中数据进行写操作(UPDATE、INSERT、DELETE)时,会通过如下方式加锁:
|
||||
* DELETE操作:对mysql表中数据执行DELETE操作,需要为要删除数据添加排他锁(X锁)
|
||||
* UPDATE操作:对mysql表中数据执行UPDATE操作分如下情况:
|
||||
* update操作不改变记录的键值,且更新的列在更新前后占用的空间未发生变化:
|
||||
* 此时会在B+数中定位到该条记录,并且为该条记录添加X锁,在修改完成之后释放X锁
|
||||
* update操作没有修改键值,但是更新前后该条记录的某一列占用空间发生了变化:
|
||||
* 此时,会先对原先的记录加上X锁,并进行DELETE操作,并且在DELETE操作后,通过INSERT操作并添加隐式锁来插入修改后的数据
|
||||
* update操作修改了键值:
|
||||
* 同样,也会通过先DELETE再INSERT的操作来更新数据
|
||||
* INSERT操作:通过添加隐式锁的操作来保证mysql中多事务的并发安全
|
||||
* 从mysql数据库的粒度来对锁进行划分
|
||||
* 表锁:
|
||||
* 表级锁会锁住mysql中的整张表,其粒度较大,受其影响并发性能较低
|
||||
* 表级锁是mysql中最基本的加锁策略,并不依赖于存储引擎,不管是innodb还是myisam都支持表级锁
|
||||
* 表级锁的使用场景:
|
||||
* 通过的select或者update、delete、insert操作innodb并不会加表级锁,但是对于alter table、drop table这类ddl语句对表的结构进行修改时,需要对其表的元数据进行加锁(MDL),对元数据加锁的过程中,想要对该表中数据进行select、delete、update、insert的操作会被阻塞
|
||||
* 对表锁进行加锁的语句:
|
||||
```mysql
|
||||
# 对表锁进行加锁
|
||||
# 在对表进行上锁操作后,无法再去读取其他未上锁的表
|
||||
lock tables table_name read/write
|
||||
|
||||
# 对上锁的表进行释放操作
|
||||
unlock tables;
|
||||
```
|
||||
* 对于myisam存储引擎,读操作之前会为涉及到的表加上读锁,并且在读操作执行完成之后释放上锁的表;而对于写操作,myisam存储引擎会在写操作执行之前为涉及到的表加上写锁。反之,innodb存储引擎在对数据进行查找和修改时不会在表级别加锁,而是会在更细的粒度上为行数据进行加锁,以此来提高程序的并发性能。
|
||||
* 元数据锁(MDL):对于表结构的修改(DDL操作),会对其元数据锁进行加锁,而对于该表的增删改操作,则是会默认加上元数据的读锁。故而在对表结构进行修改时,想要对表中数据进行増删改操作需要获取其元数据的读锁,此时对表数据的増删改操作会被阻塞。
|
||||
* 意向锁:意向锁通常用来简化行级锁和表级锁是否兼容的判断操作。如果为一个表中某条数据加上行级锁,那么innodb存储引擎会自动的为该行记录所在的页和表加上页级和表级的意向锁,那么当接下来有事务想要对该表进行表级加锁操作时,就无需查看该表中所有数据来判断是否存在表中数据的锁和表级锁冲突。只需判断该表的意向锁和想要加上的表级锁是否冲突即可。
|
||||
* 行锁
|
||||
* 相对与表锁,行锁的粒度更小,故而其并发度更高,并发性能更好。但是行锁可能会造成死锁问题,加锁的开销也更大。
|
||||
* 是否支持行锁取决与存储引擎,比如myisam不支持行锁但是innodb却支持行锁
|
||||
* 行锁种类:
|
||||
* 记录锁:记录锁针对于单条记录,在一个事物中,如果存在update、delete等修改操作,其默认会获得想要修改的那条记录的记录锁,并且在执行修改操作之后才会被释放。若一个事务中对某条记录调用update操作,那么直到当前事务提交或者回滚前,若其他事务想要修改同一行记录,会进入阻塞状态,知道持有记录写锁的退出才能继续执行。
|
||||
* 间隙锁:间隙锁对记录的范围进行加锁。间隙锁是不冲突的,多个事务可以同时持有某一个范围的间隙锁。用间隙锁或者mvcc机制都能够解决死锁问题。但是间隙锁可能会导致死锁问题,如果多个事务各自持有自己范围的间隙锁,并同时向对方持有间隙锁的范围插入数据,此时两个事务都会等待对方释放间隙锁,在这种情况下会发生死锁问题。
|
||||
* 临键锁(next-key lock):在锁住某条记录的同时也锁定某个范围内的数据,使之无法被其他事务插入数据
|
||||
* 页锁:在页面的粒度上进行上锁
|
||||
* 在粒度上,不同层级的所的数量是有上限的,例如innodb,其优先会选用行锁来进行加锁。当行锁数量过多,占用空间超过表空间上限时,会进行锁升级的行为。行锁会升级为页面锁,此时锁的粒度会增加,锁花费的开销也会变小,但是粒度变大后并发性能也会变低。
|
||||
* 通过对待锁的方式进行划分
|
||||
* 乐观锁:乐观锁假设每次对数据进行修改时,其他事务不会访问数据,故而不会对数据正真的加锁,只是会在修改时检查数据是否被其他事务修改过。
|
||||
* 乐观锁的实现方式:
|
||||
* cas
|
||||
* 版本号机制
|
||||
* 悲观锁:
|
||||
* 悲观锁假设一个事务在对数据进行修改时,其他事务也会对数据进行修改,故而在每次修改数据时都会对要修改的数据进行加锁,悲观锁是通过mysql内部提供的锁机制来实现的
|
||||
* 通过加锁方式进行划分:
|
||||
* 隐式锁:隐式锁通常用于插入操作。在某一个事务在对所记录进行插入操作时,如果其他事务想要访问该条记录,会查看最后修改该条记录的事务id是否是活跃的事务,如果是活跃事务,那么其会帮助插入事务创建一个X锁并且让自己进入阻塞状态。
|
||||
* 隐式锁是一种延迟加锁的机制,只有当有其他事务想要访问未提交事务插入的记录时,隐式锁才会被创建。该机制能够有效减少创建锁的数量。
|
||||
# mysql锁
|
||||
* ## msyql中锁的分类
|
||||
* 从数据库操作的类型划分,mysql中的锁可以分为读锁/共享锁和写锁/排他锁
|
||||
* 读锁(S锁,Share)
|
||||
* 写锁(X锁,Exclusive)
|
||||
* 写锁和读锁示例
|
||||
```mysql
|
||||
# 为语句加上写锁
|
||||
select * from table_name for udpate
|
||||
|
||||
# 为查询语句加上读锁
|
||||
select * from table_name lock in share mode
|
||||
# or
|
||||
select * from table_name for share(mysql 8.0新增语法)
|
||||
```
|
||||
* 在获取读锁或者写锁之后,只有当事务结束之后,获取到的读锁或者写锁才会被释放。在innodb中,for update或者for share语句只会为查询匹配的行数据上锁,并且,当事务结束或者回滚之后,会释放为数据行所加的排他锁或者共享锁
|
||||
* 在对mysql中数据进行写操作(UPDATE、INSERT、DELETE)时,会通过如下方式加锁:
|
||||
* DELETE操作:对mysql表中数据执行DELETE操作,需要为要删除数据添加排他锁(X锁)
|
||||
* UPDATE操作:对mysql表中数据执行UPDATE操作分如下情况:
|
||||
* update操作不改变记录的键值,且更新的列在更新前后占用的空间未发生变化:
|
||||
* 此时会在B+数中定位到该条记录,并且为该条记录添加X锁,在修改完成之后释放X锁
|
||||
* update操作没有修改键值,但是更新前后该条记录的某一列占用空间发生了变化:
|
||||
* 此时,会先对原先的记录加上X锁,并进行DELETE操作,并且在DELETE操作后,通过INSERT操作并添加隐式锁来插入修改后的数据
|
||||
* update操作修改了键值:
|
||||
* 同样,也会通过先DELETE再INSERT的操作来更新数据
|
||||
* INSERT操作:通过添加隐式锁的操作来保证mysql中多事务的并发安全
|
||||
* 从mysql数据库的粒度来对锁进行划分
|
||||
* 表锁:
|
||||
* 表级锁会锁住mysql中的整张表,其粒度较大,受其影响并发性能较低
|
||||
* 表级锁是mysql中最基本的加锁策略,并不依赖于存储引擎,不管是innodb还是myisam都支持表级锁
|
||||
* 表级锁的使用场景:
|
||||
* 通过的select或者update、delete、insert操作innodb并不会加表级锁,但是对于alter table、drop table这类ddl语句对表的结构进行修改时,需要对其表的元数据进行加锁(MDL),对元数据加锁的过程中,想要对该表中数据进行select、delete、update、insert的操作会被阻塞
|
||||
* 对表锁进行加锁的语句:
|
||||
```mysql
|
||||
# 对表锁进行加锁
|
||||
# 在对表进行上锁操作后,无法再去读取其他未上锁的表
|
||||
lock tables table_name read/write
|
||||
|
||||
# 对上锁的表进行释放操作
|
||||
unlock tables;
|
||||
```
|
||||
* 对于myisam存储引擎,读操作之前会为涉及到的表加上读锁,并且在读操作执行完成之后释放上锁的表;而对于写操作,myisam存储引擎会在写操作执行之前为涉及到的表加上写锁。反之,innodb存储引擎在对数据进行查找和修改时不会在表级别加锁,而是会在更细的粒度上为行数据进行加锁,以此来提高程序的并发性能。
|
||||
* 元数据锁(MDL):对于表结构的修改(DDL操作),会对其元数据锁进行加锁,而对于该表的增删改操作,则是会默认加上元数据的读锁。故而在对表结构进行修改时,想要对表中数据进行増删改操作需要获取其元数据的读锁,此时对表数据的増删改操作会被阻塞。
|
||||
* 意向锁:意向锁通常用来简化行级锁和表级锁是否兼容的判断操作。如果为一个表中某条数据加上行级锁,那么innodb存储引擎会自动的为该行记录所在的页和表加上页级和表级的意向锁,那么当接下来有事务想要对该表进行表级加锁操作时,就无需查看该表中所有数据来判断是否存在表中数据的锁和表级锁冲突。只需判断该表的意向锁和想要加上的表级锁是否冲突即可。
|
||||
* 行锁
|
||||
* 相对与表锁,行锁的粒度更小,故而其并发度更高,并发性能更好。但是行锁可能会造成死锁问题,加锁的开销也更大。
|
||||
* 是否支持行锁取决与存储引擎,比如myisam不支持行锁但是innodb却支持行锁
|
||||
* 行锁种类:
|
||||
* 记录锁:记录锁针对于单条记录,在一个事物中,如果存在update、delete等修改操作,其默认会获得想要修改的那条记录的记录锁,并且在执行修改操作之后才会被释放。若一个事务中对某条记录调用update操作,那么直到当前事务提交或者回滚前,若其他事务想要修改同一行记录,会进入阻塞状态,知道持有记录写锁的退出才能继续执行。
|
||||
* 间隙锁:间隙锁对记录的范围进行加锁。间隙锁是不冲突的,多个事务可以同时持有某一个范围的间隙锁。用间隙锁或者mvcc机制都能够解决死锁问题。但是间隙锁可能会导致死锁问题,如果多个事务各自持有自己范围的间隙锁,并同时向对方持有间隙锁的范围插入数据,此时两个事务都会等待对方释放间隙锁,在这种情况下会发生死锁问题。
|
||||
* 临键锁(next-key lock):在锁住某条记录的同时也锁定某个范围内的数据,使之无法被其他事务插入数据
|
||||
* 页锁:在页面的粒度上进行上锁
|
||||
* 在粒度上,不同层级的所的数量是有上限的,例如innodb,其优先会选用行锁来进行加锁。当行锁数量过多,占用空间超过表空间上限时,会进行锁升级的行为。行锁会升级为页面锁,此时锁的粒度会增加,锁花费的开销也会变小,但是粒度变大后并发性能也会变低。
|
||||
* 通过对待锁的方式进行划分
|
||||
* 乐观锁:乐观锁假设每次对数据进行修改时,其他事务不会访问数据,故而不会对数据正真的加锁,只是会在修改时检查数据是否被其他事务修改过。
|
||||
* 乐观锁的实现方式:
|
||||
* cas
|
||||
* 版本号机制
|
||||
* 悲观锁:
|
||||
* 悲观锁假设一个事务在对数据进行修改时,其他事务也会对数据进行修改,故而在每次修改数据时都会对要修改的数据进行加锁,悲观锁是通过mysql内部提供的锁机制来实现的
|
||||
* 通过加锁方式进行划分:
|
||||
* 隐式锁:隐式锁通常用于插入操作。在某一个事务在对所记录进行插入操作时,如果其他事务想要访问该条记录,会查看最后修改该条记录的事务id是否是活跃的事务,如果是活跃事务,那么其会帮助插入事务创建一个X锁并且让自己进入阻塞状态。
|
||||
* 隐式锁是一种延迟加锁的机制,只有当有其他事务想要访问未提交事务插入的记录时,隐式锁才会被创建。该机制能够有效减少创建锁的数量。
|
||||
* 显示锁:可以通过命令查看到的锁,通常会用for update、lock in share mode 或者 update、delete操作会生成显示锁
|
||||
@@ -1,5 +1,5 @@
|
||||
# redo log & undo log
|
||||
* ## undo log
|
||||
* undo log通常用于事务的回滚操作,用来保证事务的原子性。
|
||||
* 每当发生数据修改操作时(update、insert、delete),关于当前修改操作的相反操作会被记录到undo log中,通常用于在回滚时将数据回复到修改之前的值。
|
||||
# redo log & undo log
|
||||
* ## undo log
|
||||
* undo log通常用于事务的回滚操作,用来保证事务的原子性。
|
||||
* 每当发生数据修改操作时(update、insert、delete),关于当前修改操作的相反操作会被记录到undo log中,通常用于在回滚时将数据回复到修改之前的值。
|
||||
* undo log默认被记录到mysql的表空间中,因而对undo log进行追加时,对表中页数据进行修改时也会产生redo log,对undo log的追加会通过fsync操作持久化到redo log中。这样即使在一个事务尚未被提交或是回滚之前,mysql服务器崩溃,下次重启时,也可以通过redo log恢复对数据的修改和undo log的内容,回滚事务时也能将数据恢复到崩溃前尚未被事务修改的状态。
|
||||
46
mysql/索引.md
46
mysql/索引.md
@@ -1,24 +1,24 @@
|
||||
# mysql索引
|
||||
* ## mysql索引的分类
|
||||
* 从功能逻辑上对索引进行分类:
|
||||
* 普通索引:只是用于提升查询效率,没有任何的附加约束
|
||||
* 唯一性索引:通过unique关键字可以设定唯一性索引,其会限制该索引值必须是唯一的,但是允许为null
|
||||
* 主键索引:特殊的唯一性索引,在唯一性索引的基础上,主键索引还增加了不为空的约束
|
||||
* 单列索引:作用在一个字段上的索引
|
||||
* 联合索引:作用于多个字段上的索引
|
||||
* ## 索引的创建、删除操作
|
||||
```mysql
|
||||
# 索引的创建方式
|
||||
alter table table_name add [unique/index] idx_name (col_name...)
|
||||
# or
|
||||
create [unique/index] on table_name(col_name...)
|
||||
|
||||
# 索引的删除方式
|
||||
alter table table_name drop index idx_name
|
||||
# or
|
||||
drop index idx_name on table_name
|
||||
```
|
||||
* ## 索引的可见性
|
||||
```mysql
|
||||
# 通过修改索引的可见性,可以比较创建
|
||||
# mysql索引
|
||||
* ## mysql索引的分类
|
||||
* 从功能逻辑上对索引进行分类:
|
||||
* 普通索引:只是用于提升查询效率,没有任何的附加约束
|
||||
* 唯一性索引:通过unique关键字可以设定唯一性索引,其会限制该索引值必须是唯一的,但是允许为null
|
||||
* 主键索引:特殊的唯一性索引,在唯一性索引的基础上,主键索引还增加了不为空的约束
|
||||
* 单列索引:作用在一个字段上的索引
|
||||
* 联合索引:作用于多个字段上的索引
|
||||
* ## 索引的创建、删除操作
|
||||
```mysql
|
||||
# 索引的创建方式
|
||||
alter table table_name add [unique/index] idx_name (col_name...)
|
||||
# or
|
||||
create [unique/index] on table_name(col_name...)
|
||||
|
||||
# 索引的删除方式
|
||||
alter table table_name drop index idx_name
|
||||
# or
|
||||
drop index idx_name on table_name
|
||||
```
|
||||
* ## 索引的可见性
|
||||
```mysql
|
||||
# 通过修改索引的可见性,可以比较创建
|
||||
```
|
||||
@@ -1,14 +1,14 @@
|
||||
# qml基本概念
|
||||
* ## 坐标系概念
|
||||
* qml组件的坐标系为迪卡尔坐标系,以屏幕水平方向为x轴,垂直方向为y轴,分为以下两种:
|
||||
* scene坐标系:坐标系通常与根组件的组件坐标系相同,(0,0)位置与想要渲染的画布的左上角相对应
|
||||
* item坐标系:通常,若当前组件不是根组件,则当前组件的坐标x,y属性是相对与父组件的组件坐标系的
|
||||
* ## visual parent(可视化父组件)
|
||||
* qml对象的visual parent和qobject parent是两个不同但是相关连的概念。
|
||||
* object用于管理qml对象的生命周期和内存管理。
|
||||
* 在使用qtquick模块时,该模块中所有类型都包含了一个visual parent的概念,用来决定该组建在场景渲染时的父item坐标系。每个item都有一个parent属性,该属性对应的值就是该item的visual parent。如果某item的parent属性为null,则该item为渲染该场景的根item。
|
||||
* 如果将一个对象赋值给一个item的data属性,那么该对象在qojbect层次结构中会变成item对象的子对象;如果该对象还是item对象,那么该对象还会在visual parent层次结构中变为item对象的子对象
|
||||
* 方便起见,如果一个item中声明另一个item,并且没有将该item赋值给外层item的仍然属性,那么将会默认将内层item添加到外层item的data属性,内层item变为外层item的qobject子对象和visual层次结构的子对象
|
||||
* visual parent可以在运行时通过修改parent属性改变,故而visual parent和qobject层次的父对象并不一定相同
|
||||
* ## scene中的item的渲染顺序
|
||||
# qml基本概念
|
||||
* ## 坐标系概念
|
||||
* qml组件的坐标系为迪卡尔坐标系,以屏幕水平方向为x轴,垂直方向为y轴,分为以下两种:
|
||||
* scene坐标系:坐标系通常与根组件的组件坐标系相同,(0,0)位置与想要渲染的画布的左上角相对应
|
||||
* item坐标系:通常,若当前组件不是根组件,则当前组件的坐标x,y属性是相对与父组件的组件坐标系的
|
||||
* ## visual parent(可视化父组件)
|
||||
* qml对象的visual parent和qobject parent是两个不同但是相关连的概念。
|
||||
* object用于管理qml对象的生命周期和内存管理。
|
||||
* 在使用qtquick模块时,该模块中所有类型都包含了一个visual parent的概念,用来决定该组建在场景渲染时的父item坐标系。每个item都有一个parent属性,该属性对应的值就是该item的visual parent。如果某item的parent属性为null,则该item为渲染该场景的根item。
|
||||
* 如果将一个对象赋值给一个item的data属性,那么该对象在qojbect层次结构中会变成item对象的子对象;如果该对象还是item对象,那么该对象还会在visual parent层次结构中变为item对象的子对象
|
||||
* 方便起见,如果一个item中声明另一个item,并且没有将该item赋值给外层item的仍然属性,那么将会默认将内层item添加到外层item的data属性,内层item变为外层item的qobject子对象和visual层次结构的子对象
|
||||
* visual parent可以在运行时通过修改parent属性改变,故而visual parent和qobject层次的父对象并不一定相同
|
||||
* ## scene中的item的渲染顺序
|
||||
* 默认情况下,归属于统一父item的兄弟子item之间,后声明的item渲染在先声明的item之上。若item a渲染在item b之上,那么item a子树中所有子item都渲染在以item a为根的子树之上,即使item a中某个子item的z属性大于item a子树中某个item的z属性值。若想将item a子树中的item渲染到item b子树之上,只能够改变item a的z属性值令其渲染在item b之上。
|
||||
Reference in New Issue
Block a user