基础分库

以下实例基于shardingsphere 4.1.0 + SpringBoot 2.2.5.RELEASE版本

依赖导入:

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.compile.sourceEncoding>UTF-8</project.compile.sourceEncoding>
<springboot.version>2.2.5.RELEASE</springboot.version>
<shardingsphere.version>4.1.0</shardingsphere.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>${springboot.version}</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>${springboot.version}</version>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${springboot.version}</version>
<scope>test</scope>
</dependency> <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency> <dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>${shardingsphere.version}</version>
</dependency> </dependencies>

场景:通过id字段取余分片到两个数据库

  1. 引入依赖
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
</dependency>
  1. 参数配置
spring.shardingsphere.datasource.names=ds0,ds1

spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://localhost:3306/ds_0?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false&serverTimezone=UTC
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=0490218292 spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://localhost:3306/ds_1?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false&serverTimezone=UTC
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=0490218292 spring.shardingsphere.sharding.tables.position.database-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.position.database-strategy.inline.algorithm-expression=ds$->{id % 2}
  1. 测试插入数据
@Test
public void testAdd(){
for (int i = 0; i <= 20; i++) {
Position position=new Position();
position.setId((long) i);
position.setName("lagou"+i);
position.setSalary("1000");
position.setCity("beijing");
positionRepository.save(position);
}
}

主键生成使用雪花算法

  1. id需要设置IDENTITY
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
  1. 参数配置

增加id设置:

#id设置
spring.shardingsphere.sharding.tables.position.key-generator.column=id
spring.shardingsphere.sharding.tables.position.key-generator.type=SNOWFLAKE
  1. 测试
    @Test
public void testAdd(){
for (int i = 0; i <= 20; i++) {
Position position=new Position();
position.setName("lagou"+i);
position.setSalary("1000");
position.setCity("beijing");
positionRepository.save(position);
}
}

自定义主键生成器

  1. 自定义主键生成器类
public class MyCustomId implements ShardingKeyGenerator {

    @Override
public Comparable<?> generateKey() {
return System.currentTimeMillis()+new Random().nextInt(100000);
} @Override
public String getType() {
//自定义一个名称
return "MYID";
} @Override
public Properties getProperties() {
return null;
} @Override
public void setProperties(Properties properties) { } }
  1. 配置

在resources下创建META-INF/services目录,并创建一个文件,文件名为:org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator

里面写自定义主键生成器的全类名

  1. 配置生成器类型的地方改为和我们自定义的生成器的类型一致
#id设置
spring.shardingsphere.sharding.tables.position.key-generator.column=id
spring.shardingsphere.sharding.tables.position.key-generator.type=MYID

两表关联的分库

场景:职位表(position)和职位详情表(position_detail)是关联的两个表,关联关系是:position_detail.pid = position.id,那么我们期望在插入数据后,根据职位Id进行查询时能够只查询一个库,而不是笛卡尔积的进行查询。

  1. 完整的参数配置
spring.shardingsphere.datasource.names=ds0,ds1

spring.shardingsphere.datasource.ds0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.jdbc-url=jdbc:mysql://localhost:3306/ds_0?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false&serverTimezone=UTC
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=0490218292 spring.shardingsphere.datasource.ds1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.jdbc-url=jdbc:mysql://localhost:3306/ds_1?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false&serverTimezone=UTC
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=0490218292 #职位表设置
spring.shardingsphere.sharding.tables.position.database-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.position.database-strategy.inline.algorithm-expression=ds$->{id % 2}
#id设置
spring.shardingsphere.sharding.tables.position.key-generator.column=id
spring.shardingsphere.sharding.tables.position.key-generator.type=SNOWFLAKE
#职位表详情设置
spring.shardingsphere.sharding.tables.position_detail.database-strategy.inline.sharding-column=pid
spring.shardingsphere.sharding.tables.position_detail.database-strategy.inline.algorithm-expression=ds$->{pid % 2}
#id设置
spring.shardingsphere.sharding.tables.position_detail.key-generator.column=id
spring.shardingsphere.sharding.tables.position_detail.key-generator.type=SNOWFLAKE

可以看出position的id的分片策略和position_detail的pid的分片策略一致。

2. 测试

@Test
public void testQueryPosition(){
Object positionAndDetailById = positionRepository.findPositionAndDetailById(730545854473043968L);
System.out.println(positionAndDetailById);
}

可以看出,只查询了一个库:

广播表设置

场景:城市表属于基础表,数据量不大,每个库都可以存一样的数据。

  1. 广播表配置
#广播表设置
spring.shardingsphere.sharding.broadcast-tables=city
spring.shardingsphere.sharding.tables.city.key-generator.column=id
spring.shardingsphere.sharding.tables.city.key-generator.type=SNOWFLAKE
  1. 测试
@Test
public void testAddCity(){
City city=new City();
city.setName("成都");
city.setProvince("四川");
cityRepository.save(city);
}

和之前的不同,这一条数据的插入,两个库都有。且ID也是一致的。

分库且分表

场景:我们有一个订单表,可以根据公司id(companyId)进行分库,然后在根据id进行分表。

  1. 参数配置
#订单表分库且分表
spring.shardingsphere.sharding.tables.b_order.database-strategy.inline.sharding-column=company_id
spring.shardingsphere.sharding.tables.b_order.database-strategy.inline.algorithm-expression=ds$->{company_id%2}
spring.shardingsphere.sharding.tables.b_order.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.b_order.table-strategy.inline.algorithm-expression=b_order${id % 2}
spring.shardingsphere.sharding.tables.b_order.actual-data-nodes=ds${0..1}.b_order${0..1}
#id设置
spring.shardingsphere.sharding.tables.b_order.key-generator.column=id
spring.shardingsphere.sharding.tables.b_order.key-generator.type=SNOWFLAKE
  1. 测试
    @Test
@Repeat(100)
public void testAddBOrder(){
BOrder bOrder=new BOrder();
bOrder.setDel(false);
bOrder.setCompanyId(new Random().nextInt(10));
bOrder.setPositionId(23);
bOrder.setUserId(22);
bOrder.setPublishUserId(11);
bOrder.setResumeType(1);
bOrder.setStatus("AUTO");
bOrder.setCreateTime(new Date());
bOrder.setOperateTime(new Date());
bOrder.setWorkYear("2");
bOrder.setName("lagou");
bOrder.setPositionName("Java");
bOrder.setResumeId(23443);
bOrderRepository.save(bOrder);
}

我们发现数据插入到了ds_0.b_order0、ds_0.b_order1、ds_1.b_order0、ds_1.b_order1四个node里面。

读写分离

  1. 参数配置
spring.shardingsphere.datasource.names=master,slave0

spring.shardingsphere.datasource.master.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.master.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.master.jdbc-url=jdbc:mysql://localhost:3306/ds_0?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false&serverTimezone=UTC
spring.shardingsphere.datasource.master.username=root
spring.shardingsphere.datasource.master.password=0490218292 spring.shardingsphere.datasource.slave0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.slave0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave0.jdbc-url=jdbc:mysql://localhost:3306/ds_0_slave?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false&serverTimezone=UTC
spring.shardingsphere.datasource.slave0.username=root
spring.shardingsphere.datasource.slave0.password=0490218292 #读写分离
spring.shardingsphere.masterslave.name=datasource
spring.shardingsphere.masterslave.master-data-source-name=master
spring.shardingsphere.masterslave.slave-data-source-names=slave0
#多个读库时的负载均衡策略
spring.shardingsphere.masterslave.load-balance-algorithm-type=ROUND_ROBIN
  1. 查询测试
    @Test
public void test(){
List<City> all = cityRepository.findAll();
all.forEach(x->System.out.println(x));
}

分库分表+读写分离的参数配置

#数据源
spring.shardingsphere.datasource.names=master0,slave0,slave1,master1,slave2,slave3 spring.shardingsphere.datasource.master0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.master0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.master0.jdbc-url=jdbc:mysql://localhost:3306/master0?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.shardingsphere.datasource.master0.username=root
spring.shardingsphere.datasource.master0.password=root spring.shardingsphere.datasource.slave0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.slave0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave0.jdbc-url=jdbc:mysql://localhost:3306/slave0?useSSL=false
spring.shardingsphere.datasource.slave0.username=root
spring.shardingsphere.datasource.slave0.password=root spring.shardingsphere.datasource.slave1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.slave1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave1.jdbc-url=jdbc:mysql://localhost:3306/slave1?useSSL=false
spring.shardingsphere.datasource.slave1.username=root
spring.shardingsphere.datasource.slave1.password=root spring.shardingsphere.datasource.master1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.master1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.master1.jdbc-url=jdbc:mysql://localhost:3306/master1?useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.shardingsphere.datasource.master1.username=root
spring.shardingsphere.datasource.master1.password=root spring.shardingsphere.datasource.slave2.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.slave2.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave2.jdbc-url=jdbc:mysql://localhost:3306/slave2?useSSL=false
spring.shardingsphere.datasource.slave2.username=root
spring.shardingsphere.datasource.slave2.password=root spring.shardingsphere.datasource.slave3.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.slave3.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.slave3.jdbc-url=jdbc:mysql://localhost:3306/slave3?useSSL=false
spring.shardingsphere.datasource.slave3.username=root
spring.shardingsphere.datasource.slave3.password=root #分库分表
spring.shardingsphere.sharding.tables.b_order.database-strategy.inline.sharding-column=company_id
spring.shardingsphere.sharding.tables.b_order.database-strategy.inline.algorithm-expression=master$->{company_id % 2}
spring.shardingsphere.sharding.tables.b_order.actual-data-nodes=master$->{0..1}.b_order$->{0..1}
spring.shardingsphere.sharding.tables.b_order.table-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.tables.b_order.table-strategy.inline.algorithm-expression=b_order$->{id % 2} #读写分离
spring.shardingsphere.sharding.master-slave-rules.master0.master-data-source-name=master0
spring.shardingsphere.sharding.master-slave-rules.master0.slave-data-source-names=slave0, slave1
spring.shardingsphere.sharding.master-slave-rules.master1.master-data-source-name=master1
spring.shardingsphere.sharding.master-slave-rules.master1.slave-data-source-names=slave2, slave3

强制路由

在一些应用场景中,分片条件并不存在于SQL,而存在于外部业务逻辑。因此需要提供一种通过在外部业务代码中指定路由配置的一种方式,在ShardingSphere中叫做Hint。如果使用Hint指定了强制分片路由,那么SQL将会无视原有的分片逻辑,直接路由至指定的数据节点操作。

使用场景:

  • 数据分片操作,如果分片键没有在SQL或者数据表中,而是在业务逻辑代码中
  • 读写分离操作,如果需要强制在主库进行某些操作
  1. 自定义Hint实现类
public class MyHintShardingAlgorithm implements HintShardingAlgorithm<String> {

    @Override
public Collection<String> doSharding(Collection<String> collection, HintShardingValue<String> hintShardingValue) {
Collection<String> result=new ArrayList<>();
if(hintShardingValue.getValues().contains("master")){
((ArrayList<String>) result).add("master");
}else {
((ArrayList<String>) result).add("slave0");
}
return result;
}
}
  1. 配置自定义的Hint类
spring.shardingsphere.sharding.tables.city.database-strategy.hint.algorithm-class-name=com.mmc.sharding.hint.MyHintShardingAlgorithm

  1. 测试
    @Test
public void testHint(){
HintManager hintManager = HintManager.getInstance();
hintManager.addDatabaseShardingValue("city","master");
// hintManager.setMasterRouteOnly();
List<City> all = cityRepository.findAll();
all.forEach(x->System.out.println(x));
}

还可以使用hintManager.setMasterRouteOnly()指定仅路由到主库。

测试过程中发现Hint的自定义策略和读写分离配置有冲突。配置了读写分离后自定义Hint类不生效了,仅hintManager.setMasterRouteOnly()还可以用。

数据加密

脱敏配置分为如下几个:数据源配置,加密器配置,脱敏表配置以及查询属性配置,其详情如下图所示:

  • 数据源配置:指Datasource的配置信息
  • 加密器配置:指使用什么加密策略进行加解密。目前ShardingSphere内置了两种加解密策略AES、MD5
  • 脱敏表配置:指定哪个列用于存储密文数据,哪个列存明文数据,以及在应用里用哪个列(应用层sql里使用的列名)
  • 查询属性配置:当数据库同时存了明文和密文的时候,该属性开关用于决定是直接查询数据库表里的明文,还是查密文然后通过解密后返回。
  1. 先创建个表
CREATE TABLE `c_user` (
`Id` bigint(11) NOT NULL AUTO_INCREMENT,
`name` varchar(256) DEFAULT NULL,
`pwd_plain` varchar(256) DEFAULT NULL,
`pwd_cipher` varchar(256) DEFAULT NULL,
PRIMARY KEY (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
  1. 创建实体类
@Entity
@Table(name = "c_user")
public class CUser implements Serializable {
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; @Column(name = "name")
private String name; /**
* 逻辑列名
*/
@Column(name = "password")
private String password;
}
  1. 参数配置
#定义数据表真实明文列
#spring.shardingsphere.encrypt.tables.c_user.columns.password.plain-column=pwd_plain
#定义数据表真实密文列
spring.shardingsphere.encrypt.tables.c_user.columns.password.cipher-column=pwd_cipher
#定义加密器,名称为lagou_pwd
spring.shardingsphere.encrypt.encryptors.lagou_pwd.type=aes
spring.shardingsphere.encrypt.encryptors.lagou_pwd.props.aes.key.value=1234
#指定加密器,password是逻辑列名,与实体类中的字段对应
spring.shardingsphere.encrypt.tables.c_user.columns.password.encryptor=lagou_pwd
  1. 测试
    @Test
public void testEncrypt(){
CUser cUser=new CUser();
cUser.setName("阿百川");
cUser.setPassword("123456");
cUserRepository.save(cUser);
} @Test
public void testQueryByPassword(){
List<CUser> byPassword = cUserRepository.findByPassword("123456");
System.out.println(byPassword);
}



数据库存放的已经是密文了,通过明文密码也可以查询到数据了。

分布式事务

仅仅需要在测试方法上加上两个注解:

    @Transactional(rollbackFor = Exception.class)
@ShardingTransactionType(TransactionType.XA)

TransactionType有XA、BASE、LOCAL三种

@Test
@Transactional(rollbackFor = Exception.class)
@ShardingTransactionType(TransactionType.XA)
public void testAddDetail(){
for (int i = 0; i <= 3; i++) {
Position position=new Position();
position.setName("lagou"+i);
position.setSalary("1000");
position.setCity("beijing");
positionRepository.save(position); if(i==3){
throw new RuntimeException();
}
PositionDetail positionDetail=new PositionDetail();
positionDetail.setPid(position.getId());
positionDetail.setDescription("详情");
positionDetailRepository.save(positionDetail);
}
}

Sharding JDBC案例实战的更多相关文章

  1. (升级版)Spark从入门到精通(Scala编程、案例实战、高级特性、Spark内核源码剖析、Hadoop高端)

    本课程主要讲解目前大数据领域最热门.最火爆.最有前景的技术——Spark.在本课程中,会从浅入深,基于大量案例实战,深度剖析和讲解Spark,并且会包含完全从企业真实复杂业务需求中抽取出的案例实战.课 ...

  2. Spark Streaming 进阶与案例实战

    Spark Streaming 进阶与案例实战 1.带状态的算子: UpdateStateByKey 2.实战:计算到目前位置累积出现的单词个数写入到MySql中 1.create table CRE ...

  3. 《图解Spark:核心技术与案例实战》作者经验谈

    1,看您有维护博客,还利用业余时间著书,在技术输出.自我提升以及本职工作的时间利用上您有没有什么心得和大家分享?(也可以包含一些您写书的小故事.)回答:在工作之余能够写博客.著书主要对技术的坚持和热爱 ...

  4. 图解CSS3核心技术与案例实战(1)

    前言: 我买了一本<图解CSS3核心技术与案例实战>大漠写的,为了提高自己的自觉性呢,抓紧看书,把读书笔记放在这上面,跟大家一起分享,也为督促自己完成读书计划. 文末有微信公众号,感谢你的 ...

  5. kafka关于修改副本数和分区的数的案例实战(也可用作leader节点均衡案例)

    kafka关于修改副本数和分区的数的案例实战(也可用作leader节点均衡案例) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.关于topic分区数的修改 1>.创建1分 ...

  6. Java------------JVM(Java虚拟机)优化大全和案例实战

    JVM(Java虚拟机)优化大全和案例实战 堆内存设置 原理 JVM堆内存分为2块:Permanent Space 和 Heap Space. Permanent 即 持久代(Permanent Ge ...

  7. 《图解CSS3:核心技术与案例实战》

    <图解CSS3:核心技术与案例实战> 基本信息 作者: 大漠 丛书名: Web开发技术丛书 出版社:机械工业出版社 ISBN:9787111469209 上架时间:2014-7-2 出版日 ...

  8. 我的第一个上线小程序,案例实战篇二——LayaAir游戏开始界面开发

    不知不觉我的第一个小程序已经上线一周了,uv也稳定的上升着. 很多人说我的小程序没啥用,我默默一笑,心里说:“它一直敦促我学习,敦促我进步”.我的以一个小程序初衷是经验分享,目前先把经验分享到博客园, ...

  9. Spring boot项目集成Sharding Jdbc

    环境 jdk:1.8 framework: spring boot, sharding jdbc database: MySQL 搭建步骤 在pom 中加入sharding 依赖 <depend ...

随机推荐

  1. 学习FastDfs(二)

    分布式文件系统(DFS) 指文件系统管理的物理存储资源不一定直接连接在本地节点上 而是通过计算机网络与节点相连 分布式文件系统的设计基于客户机/服务器模式 一个典型的网络可能包括多个多个用户访问的服务 ...

  2. 剑指Offer30——包含min函数的栈

    剑指Offer30--包含min函数的栈 1. 题目简述 定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的min函数在该栈中,调用min.push及pop的时间复杂度是O(1). 2. 题 ...

  3. 遇到的问题之“Dubbo 直连 Invoke remote method timeout 问题!”

    Dubbo 直连 Invoke remote method timeout 问题!   在测试环境消费者直连服务端进行测试时, 其中一个RPC接口抛出一个错误, 如下: Caused by: com. ...

  4. CommonCollection1反序列化学系

    CommonsCollection1 1.前置知识 1.1.反射基础知识 1.1.1. 对象与类的基础知识 类(class),对象(object) 对象是类的实例化,中华田园犬(object)是狗(c ...

  5. 12 Web Development Trends That Will Dominate 2022

    12 Web Development Trends That Will Dominate 2022 (mindinventory.com) Progressive Web Apps (PWAs) An ...

  6. java Web开发实现手机拍照上传到服务器

    第一步: 搭环境,基本jdk 1.6+apache tomcat6.0+myeclipse2014 1.我们要清楚自己的jdk版本.因为我们Apache Tomcat配置的成功的前提是版本相对应. 安 ...

  7. web项目中视频的上传和展示

    思路: 上传:<form>表单提交视频-->后台使用字节流保存到本地. 展示:<video>标签展示: src属性发送请求 --> 使用字节流将视频绑定到响应并返回 ...

  8. 手把手教你学vue-4(vuex)

    1.首先明白vuex是做什么用的. 管理统一组件状态state.每个应用将仅仅包含一个 store 实例.单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态 ...

  9. properties和XML配置文件内容的获取

    @ 目录 总结内容 1. Java中为什么要使用配置文件 2. Java中常用的配置文件类型有哪些以及它们的特点 Properties配置文件 XML配置文件 总结 总结内容 1. Java中为什么要 ...

  10. git-flow-avh的使用过程

    安装: 在mac上 brew install git-flow-avh 使用: 在仓库中 git flow init 一路回车就行,这个命令相当于写入一些默认命令流程到.git中, 此命令执行之后会增 ...