本文为分布式系列文章的集锦汇总,长期保持置顶及更新,读者可以在本文中更好的学习到某个具体的系列。

注: 转载本博客文章请注明出处,原创不易,洗文可耻。

我说分布式事务系列

文章链接
我说分布式事务之TCC
我说分布式事务之最大努力通知型事务
我说分布式事务之可靠消息最终一致性事务1-原理及实现
我说分布式事务之消息一致性事务2-rocketmq的实现
【汇总】我说分布式事务系列
分布式事务之聊聊TCC
分布式事务最终一致性常用方案
TCC-Transaction源码解析之事务执行
TCC-Transaction源码解析之事务补偿
自己写分布式事务框架之原理篇[详解本地消息表]

跟我学RocketMQ

文章链接
[1-1]安装RocketMQ
[1-2]安装RocketMQ-Console管理平台
[1-3]发送普通消息及封装DefaultMQProducer支持spring
[1-4]消费消息及封装DefaultMQPushConsumer支持spring
[1-5]发送事务消息实现分布式事务及封装TransactionMQProducer支持spring
[2-0]跟我学RocketMQ之消息重试
[2-1]跟我学RocketMQ之消息幂等
[2-2]跟我学RocketMQ之消息轨迹实战与源码分析
[2-3]跟我学RocketMQ之消息发送源码解析
[2-4]跟我学RocketMQ之批量消息发送源码解析
[2-5]跟我学RocketMQ之消息消费源码解析-p1
[2-6]跟我学RocketMQ之消息消费源码解析-p2
[2-7]跟我学RocketMQ之订阅关系一致性源码讨论
[2-8]跟我学RocketMQ之消息拉取源码解析
[2-9]跟我学RocketMQ之开源客户端混合云实践与案例解析
[2-10]跟我学RocketMQ之事务消息发送源码解析
[2-10]跟我学RocketMQ之事务消息存储源码解析
[2-11]跟我学RocektMQ之事务消息提交及回查源码解析
[2-12]跟我学RocketMQ之定时消息源码解析
[2-13]跟我学RocektMQ之理解长轮询机制
[2-14]跟我学RocketMQ之消息持久化原理与Mmap
[2-15]跟我学RocketMQ之拉模式消费的两种方式
[2-16]跟我学RocketMQ之聊聊死信队列

分库分表

文章链接
我说分布式之分库分表
跟我学shardingjdbc之shardingjdbc入门
跟我学shardingjdbc之使用jasypt加密数据库连接密码
跟我学shardingjdbc之分布式主键及其自定义
跟我学shardingjdbc之自定义分库分表策略-复合分片算法自定义实现

Read More

本文将主要记录在日常开发中遇到的各种问题。以技术类别进行章节划分,作为个人的编码备忘录随时进行查阅,并长期进行置顶。

问题排查

CPU异常飙高排查思路

cpu占用高如何排查

  • 查看占用cpu高的进程: 通过 top 命令找到 CPU 消耗最高的进程,并记住进程 ID {pid}。

    top -M -n 2 -d 3 >{pid}/top.txt 查看top

  • 再次通过 top -Hp {pid} 找到 CPU 消耗最高的线程 ID,并记住线程 ID(十进制).
  • 通过 JDK 提供的 jstack 工具 dump 线程堆栈信息到指定文件中。

    jstack {pid} >{pid}/jstack_1.txt 一次堆栈快照 备用

    jstack {pid} >{pid}/jstack_2.txt 两次堆栈快照 备用

  • 由于刚刚的线程 ID 是十进制的,而堆栈信息中的线程 ID 是16进制的,因此我们需要将10进制的转换成16进制的,并用这个线程 ID 在堆栈中查找。

    使用 printf “%x\n” [十进制数字] ,可以将10进制转换成16进制。

  • 通过刚刚转换的16进制数字从堆栈信息里找到对应的线程堆栈。就可以从该堆栈中看出端倪。

  • 通过top查看当前进程cpu占用情况,找到cpu使用最高的进程PID
  • 查看子进程情况

    top -p 4606 -H

  • 将 子进程id 转换成16进制

    printf “%x \n” 4648 结果为1228

  • 使用jstack查询具体出现问题的代码位置

    jstack 4606|grep 1228 -C 30

  • 根据打印结果定位到具体代码位置

JavaCore相关

该模块主要记录JavaCore相关的技术点

bigdecimal四舍五入

BigDecimal.ROUND_HALF_UP: 遇到.5的情况时往上近似,例: 1.5 ->;2
BigDecimal.ROUND_HALF_DOWN : 遇到.5的情况时往下近似,例: 1.5 ->;1

bigDecimal转换为百分比,保留若干小数

DecimalFormat decimalFormat = new DecimalFormat("0.00%");
BigDecimal decimal = new BigDecimal(count.intValue()).divide(new BigDecimal(allCount), 5, ROUND_HALF_UP);
String formatted = decimalFormat.format(sdPercent);

bigDecimal精确度

BigDecimal.setScale(5,  BigDecimal.ROUND_HALF_UP)  -->保留五位小数,最后一位遇到.5的情况时往上近似

Read More

项目中需要实现高效的IO操作,不仅支持查询、写入数据,还需要实现数据的持久化。选型最终选择了RocksDB,那么本文就来一睹RocksDB的芳容。

RocksDB是什么?

RocksDB是Facebook开发的一款高效的数据库软件,是采用C++编写而成的。

RocksDB是一款key-value型数据存储设施,具备四个特点,其具有四大特点。

高性能:RocksDB使用一套C++编写而成的高性能日志结构的数据库引擎。 它的Key和value支持任意大小的字节流。

可适配性:RocksDB适合于多种不同工作场景。从像MyRocks这样的数据存储引擎到应用数据缓存, 甚至是嵌入式工作场景,RocksDB都可以从容面对这些不同的数据工作需求。

为快速存储而优化:RocksDB为快速而又低延迟的存储设备(例如闪存或者高速硬盘)进行了特殊优化处理,将最大限度发挥闪存和RAM的高度率读写性能。

基础和高级的数据库操作:RocksDB提供了一些基础的操作,例如打开和关闭数据库。它对于合并、压缩、过滤等高级操作,也提供了支持。

如果对RocksDB感兴趣,可以去读它的源码,地址为:https://github.com/facebook/rocksdb

在Java工程中使用RocksDB

如何在Java工程中使用RocksDB呢?

首先建立一个maven工程,在pom.xml中引入RocksDB依赖:

<!-- https://mvnrepository.com/artifact/org.rocksdb/rocksdbjni -->
<dependency>
    <groupId>org.rocksdb</groupId>
    <artifactId>rocksdbjni</artifactId>
    <version>6.20.3</version>
</dependency>

接着我们通过一个demo尝试在Java中使用RocksDB。

初始化RocksDB

private RocksDB rocksDB;

private String path = "D:/rocksdb";

public RocksDBDemo() {
    RocksDB.loadLibrary();
    Options options = new Options();
    options.setCreateIfMissing(true);
    try {
        rocksDB = RocksDB.open(options, path);
    } catch (RocksDBException e) {
        e.printStackTrace();
    }
}

public RocksDB rocksDB() {
    return this.rocksDB;
}

首先指定一个路径用于创建RocksDB的log文件:rocksdb写入时,直接以append方式写到log文件以及memtable,随即返回,因此非常快速。

接着通过RocksDB.loadLibrary();导入库,然后设置Options,并打开rocksDB,此时成员变量rocksDB就可以拿来进行操作了。

RocksDB读写

RocksDBDemo rocksDBDemo = new RocksDBDemo();
RocksDB rocksDB = rocksDBDemo.rocksDB();
// 写入
rocksDB.put("name".getBytes(), "snowalker".getBytes());
// 读取
byte[] bytes = rocksDB.get(("name".getBytes()));
System.out.println("读取结果:" + new String(bytes));

// 遍历
RocksIterator iter = rocksDB.newIterator();
for (iter.seekToFirst();iter.isValid();iter.next()) {
    System.out.println("iter key: " + new String(iter.key()) + ",iter value: " +
            new String(iter.value()));
}

RocksDBDemo是我们的测试类名,首先获取rocksDB引用,然后调用put,get方法进行读写操作。

其中put方法签名为:

public void put(final byte[] key, final byte[] value)
    throws RocksDBException {
    put(nativeHandle_, key, 0, key.length, value, 0, value.length);
}

get方法签名为:

public byte[] get(final byte[] key) throws RocksDBException {
    return get(nativeHandle_, key, 0, key.length);
}

通过rocksDB.newIterator()可以获取迭代器,借助迭代器能够执行迭代操作,这里是读取了一下key与value。

rocksDB的write操作

rocksdb的一个WriteBatch是原子操作,要么全部成功,要么全部失败,

具体的实现原理是在整个log的写的过程中只会调用Write操作,最后会调用一次flush,所以如果中间发生机器crash,所有的都会失败,否则所有的都会成功。

看一段实际代码:

// write batch test
try (final WriteOptions writeOpt = new WriteOptions()) {
    for (int i = 0; i <= 10; ++i) {
        try (final WriteBatch batch = new WriteBatch()) {
            for (int j = 0; j <= 10; ++j) {
                batch.put(String.format("%d * %d%s", i, j, "--batch").getBytes(),
                        String.format("%d", i * j).getBytes());
            }
            rocksDB.write(writeOpt, batch);
        }
    }
}

这里实际上将乘法表写入了rocksDB。

运行代码,观察目录D:/rocksdb中出现了以下文件:

1.PNG

简单对这几种文件进行讲解:

我们可以从后缀看出:主要有这几种类型

  • sst文件
  • CURRENT文件
  • manifest文件
  • log文件
  • LOG文件和LOCK文件

其中

  1. sst文件存储的是落地的数据
  2. CURRENT文件存储的是当前最新的是哪个manifest文件
  3. manifest文件存储的是Version的变化
  4. log文件是rocksdb的write ahead log,就是在写db之前写的数据日志文件,类似binlog
  5. LOG文件是一些日志信息,是供调试用的
  6. LOCK是打开db锁,只允许同时有一个进程打开db

这里我们重点看一下log文件中的内容,由于写入的byte,可能有乱码。

2.PNG

从图中可以看到,实际上是顺序写入了我们在代码中设置的key-value。

解释一下rocksdb的flush操作

Flush是指将memtable的数据导入到sst中,变成持久化存储,从而不必担心数据丢失了。

1.首先在memtable的add的时候,
会检测是否memtable的大小达到了max write buffer,
如果是就将should_flush_置为true,
并会在WriteBatch的Handler里面调用CheckMemtableFull,
将当前column family加入flush_scheduler。

2.在Write的时候,调用ScheduleFlushes,
将需要flush的column family的memtable切换一个新的,
同时将原来的memtable加入cfd的imm中,
如果这个column family data的imm数量大于min_write_buffer_number_to_merge,
并启动一个新的线程调用BGWorkFlush

由于真正的Flush过程是在另一个线程完成的,所以这个地方并不会block写过程

rocksDB的WAL(write ahead log)

rocksdb的write ahead log(WAL)是指:
每次写操作,rocksdb会先写write ahead log,然后才会写db
write ahead log可以配置到单独的空间,并且可以配置WAL文件的单独的删除机制。

这种原因是为了保存WAL文件,达到特殊的目的,比如,其他sst文件放在不可靠存储里面,而WAL放到可靠存储里面。

对RocksDB 的写操作而言,每次都必写到两个地方:

  1. 基于内存的数据结构memtable(达到quota 后会flush 至SST file)。
  2. 预写日志-Write Ahead Log(WAL)。
    如果出现异常情况,WAL 可以用来完整恢复memtable 中的数据,恢复db 的原有的状态。

默认配置下,RocksDB 通过每次用户写之后flush WAL,来保证进程crash 后的一致性。

小结

本文我们主要对rocksDB做了一个了解和学习,一般来说,使用它的目的在于高性能的写入,实现数据的快速持久化。

业界不乏优秀的中间件底层依赖了rocksDB,如TiDB。

###



版权声明:

原创不易,洗文可耻。除非注明,本博文章均为原创,转载请以链接形式标明本文地址。

复杂软件开发之道系列进行到现在,主要讲的还是理论和思考,但是如何针对DDD编写代码这一问题想必是大家一直关心的问题。

本文我们就小试牛刀,展示一下通过DDD方式编写代码。

1.只有新项⽬才能考虑⽤DDD吗?

当然不是,DDD这套⽅法论不仅适合从零开始的新项⽬的建模,⽽且适 合复杂业务系统的重构。 当然如果能在新模型的建⽴的过程中就使⽤DDD作为指导,是最好不过的事情,原因在于你会省略对原 有业务系统代码逻辑的梳理和适配过程,众所周知,这不是⼀件容易的事情。

Read More

DDD领域驱动设计今年真的是大火了,这是好事,表明我们的软件开发领域是一直在向前发展的。

但是很多的文章或者培训,都是在说DDD如何如何优秀,简单的举一些例子就行了,有些甚至是完全错误的。

因此笔者决定将自己的实践心得以及与他人讨论的关于DDD的问题整理为一篇文章,通过问题驱动的方式,将领域驱动设计中的一些注意点进行总结,希望能够对读者有所帮助。

主要的问题如下:

  • 到底什么才是统⼀语⾔,它有那么重要么?
  • 领域驱动设计仅仅需要开发者参与吗?
  • 领域驱动设计的那些个概念到底是在说什么?实体和值对象有啥区别?
  • DDD四层架构的好处有哪些?
  • 限界上下文如何划分比较好呢?有没有工具推荐?
  • 聚合粒度如何控制呢?一个聚合根就够了,为什么还要细分各种子域?
  • ACL(防腐层)应该怎么用?它的作用和优势有哪些?它在DDD分层中处于哪个位置?

Read More

在笔者的公众号上发布了关于 对账 业务分析的一篇文章,, 该文也是笔者新书中的一节内容。

本文作为补充,我们从实战角度,从代码角度呈现一个对账框架的实现。

注意:本文中提供的对账框架为广义上的对账,也就是说不局限于支付、交易领域的对账场景,凡是需要通过数据比对进行数据校准、比对、核准的场景,均能够采用本文提供的思路进行实现。

Read More

事情的经过是这样的,群友四哥发来一个问题,问大家有什么看法,我看了下,刚好之前接触过类似的业务场景,因此斗胆就问题谈谈自己的看法,抛砖引玉。

问题如下:

A系统联机同步调用B系统(A和B不是同一公司系统,不能用分布式事务),

如何保证系统间数据准实时一致性(聊聊设计思路即可)?

提醒:需要考虑调用超时、并发、幂等、反交易先到等。

各种异常场景怎么处理要考虑更完善些,如事务隔离、并发、反交易先到调用方和服务方约定(前端客户不可能一直等着)

Read More

本次我们不分享技术,我们来聊聊软素质,具体点说,我们来聊聊新人程序员如何快速熟悉业务逻辑并付诸落地。

一个新人程序员,经历了面试的层层磨炼,终于尘埃落定入职心仪的公司。初来乍到,对周围的一切都不熟悉,业务不熟悉,同事也不熟悉。

这其中关系最大的就是对业务和代码的熟悉,如果能够掌握快速熟悉业务逻辑的方法,就能够很快进入到工作角色中,从容不迫的开展新的工作。

那么我们本次分享就从这几个方面展开:

  • 如何通过读代码建立对系统的全局观,cover链路上下游
  • 如何画图来辅助自己熟悉业务流程
  • 应当如何学习掌握领域知识
  • 对于方案设计有哪些技巧和注意点
  • 问题排查的大概思路

PS: 由于大部分的开发者都是面向业务,因此本次讨论对于中间件类的开发不做进一步展开,后面有机会再分享。

Read More

本文是笔者在一次技术直播过程中的文字大纲,基本上是即兴的,口语化严重。
因此对大纲进行整理,用文字方式进行展现。

既然是“漫谈分库分表”,那么我们需要确定我们要谈什么,不谈什么。

  1. 首先,我们不讨论具体的分库分表框架的实现和源码,这不是我们讨论的范围。

  2. 我们讨论的是思路,主要讨论如何分库分表的套路,有什么坑,有什么心得,不针对具体的细节进行展开式讨论。当然我自己的能力有限,只是希望能够抛砖引玉。

  3. 我们要明确,分库分表,并不是一个银弹,它只是我们针对MySQL单机性能不够的情况下,想要节约成本的一种方式。对于boss来说,既想要想要节约成本,又想要支撑业务,提供稳定持久度性能。

Read More

分布式系统开发过程中,我们常常会遇到同时访问多个远程接口,然后在自己的业务逻辑内聚合多个接口的调用结果。这个过程,类比到现实中很像“烧水泡茶”。

在正式讲技术实现之前,我们先以“烧水泡茶”为例,讨论一下并发任务执行在现实中是如何进行的。

烧水泡茶,往往分为如下几步:

  1. 清洗热水壶 (3min)
  2. 烧开水 (8min)
  3. 清洗茶壶 (2min)
  4. 清洗茶杯 (5min)
  5. 准备茶叶 (1min)
  6. 泡茶 (3min)

这个例子,源自著名数学家华罗庚的《统筹方法》一文,先生试图通过这样一个例子,为我们讲解如何进行统筹,组合不同的步骤,能够在最短的时间内喝到茶水。

那么我们就试着分析一下这个案例,如果我们不加优化,直接通过串行方式进行泡茶工序,那么具体的执行路径就如下图所示:

sync-tea.png

Read More

Fork me on GitHub