diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..82eca33 --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +/target/ +!.mvn/wrapper/maven-wrapper.jar + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/build/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ \ No newline at end of file diff --git a/AI/langchain.md b/AI/langchain.md new file mode 100644 index 0000000..e69de29 diff --git a/AI/pip.md b/AI/pip.md new file mode 100644 index 0000000..20a0db1 --- /dev/null +++ b/AI/pip.md @@ -0,0 +1,2 @@ +pip install -i https://pypi.tuna.tsinghua.edu.cn/simple langchain +pip install --upgrade langchain diff --git a/AI/qwen.md b/AI/qwen.md new file mode 100644 index 0000000..e69de29 diff --git a/AI/tensor.md b/AI/tensor.md new file mode 100644 index 0000000..e5610d7 --- /dev/null +++ b/AI/tensor.md @@ -0,0 +1,22 @@ +在机器学习中,我们经常会遇到以下几种量的概念:标量、矢量、向量以及张量。让我逐个解释它们的含义: +* 标量(Scalar): +含义:标量是一个单独的数值,没有方向,只有大小。在数学上,标量通常表示为一个普通的数字。 +示例:例如,温度、重量、身高等都是标量,因为它们只有一个数值,没有方向。 + + +* 矢量(Vector): +含义:矢量是具有大小和方向的量。它是由多个标量组成的一维数组。 +示例:在机器学习中,特征向量是常见的例子。例如,如果我们有一个包含房屋特征的矢量,可能包括房间数量、卧室数量和浴室数量等。 + + +* 向量(Array): +含义:在数学和计算机科学中,向量通常用来表示具有一定顺序的数值序列。它可以是一维的(矢量)、二维的、三维的,甚至是更高维度的。 +示例:在机器学习中,我们经常使用向量来表示特征、标签、权重等。例如,如果我们有一组包含多个样本的特征向量,那么可以将它们组合成一个特征矩阵,其中每一行代表一个样本的特征,每一列代表一个特征。 + + +* 张量(Tensor): +含义:张量是一个多维数组,它可以是标量、矢量或向量的泛化。在机器学习中,通常使用张量来表示多维数据,如图像、声音、视频等。 +示例:在深度学习中,神经网络的输入和输出通常是张量。例如,一个彩色图像可以表示为一个三维张量,其中包含了图像的高度、宽度和颜色通道。 + + +总的来说,标量表示单个数值,矢量表示有大小和方向的量,向量是多个数值按顺序排列的一维数组,而张量则是多维数组,可以是标量、矢量或向量的泛化。在机器学习中,我们经常使用这些不同类型的量来表示和处理数据。 diff --git a/AI/trainer.md b/AI/trainer.md new file mode 100644 index 0000000..e69de29 diff --git a/AI/transformer.md b/AI/transformer.md new file mode 100644 index 0000000..e69de29 diff --git a/DesignPattern/DesignPattern.md b/DesignPattern/DesignPattern.md new file mode 100644 index 0000000..4e46b74 --- /dev/null +++ b/DesignPattern/DesignPattern.md @@ -0,0 +1,16 @@ +设计模式 +=== +* 反应堆模式(reactor) +* 前摄器模式(proactor) +* [设计模式](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#设计模式) + * [设计模式的六大原则](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#设计模式的六大原则) + * [23种常见设计模式](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#23种常见设计模式) + * [应用场景](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#应用场景) + * [单例模式](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#单例模式) + * [责任链模式](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#责任链模式) + * [MVC](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#mvc) + * [IOC](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#ioc) + * [AOP](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#aop) + * [UML](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#uml) + * [微服务思想](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#微服务思想) + * [康威定律](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#康威定律) diff --git "a/MD\344\275\277\347\224\250.md" "b/MD\344\275\277\347\224\250.md" deleted file mode 100644 index 090835a..0000000 --- "a/MD\344\275\277\347\224\250.md" +++ /dev/null @@ -1,12 +0,0 @@ -Markdown教程 -== -副标题 --- -# 大纲标题 -## 大纲标题 -#### 大纲标题 ---- -_斜体_ 或 *斜体* -__粗体__ **粗体** -~~删除线~~ ---- diff --git a/README.md b/README.md index c6ec059..b2ecf90 100644 --- a/README.md +++ b/README.md @@ -1,86 +1,2 @@ -Java 知识点,继续完善中。 - -> 多数是一些 Java 基础知识、底层原理、算法详解。也有上层应用设计,其中不乏一些大厂面试真题。 - - -如果对你有帮助请点下 `Star`,有疑问欢迎提 [Issues](https://github.com/crossoverJie/Java-Interview/issues),有好的想法请提 [PR](https://github.com/crossoverJie/Java-Interview/pulls)。 - - -[常用集合](https://github.com/crossoverJie/Java-Interview/blob/master/README.md#%E5%B8%B8%E7%94%A8%E9%9B%86%E5%90%88) | [Java 多线程](https://github.com/crossoverJie/Java-Interview/blob/master/README.md#java-%E5%A4%9A%E7%BA%BF%E7%A8%8B) | [JVM](https://github.com/crossoverJie/Java-Interview/blob/master/README.md#jvm) | [分布式相关](https://github.com/crossoverJie/Java-Interview/blob/master/README.md#%E5%88%86%E5%B8%83%E5%BC%8F%E7%9B%B8%E5%85%B3) |[常用框架\第三方组件](https://github.com/crossoverJie/Java-Interview/blob/master/README.md#%E5%B8%B8%E7%94%A8%E6%A1%86%E6%9E%B6%E7%AC%AC%E4%B8%89%E6%96%B9%E7%BB%84%E4%BB%B6)|[架构设计](https://github.com/crossoverJie/Java-Interview/blob/master/README.md#%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1)|[DB 相关](https://github.com/crossoverJie/Java-Interview/blob/master/README.md#db-%E7%9B%B8%E5%85%B3)|[数据结构与算法](https://github.com/crossoverJie/Java-Interview/blob/master/README.md#%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95)|[Netty 相关](https://github.com/crossoverJie/Java-Interview#netty-%E7%9B%B8%E5%85%B3)|[附加技能](https://github.com/crossoverJie/Java-Interview/blob/master/README.md#%E9%99%84%E5%8A%A0%E6%8A%80%E8%83%BD)|[联系作者](https://github.com/crossoverJie/Java-Interview#%E8%81%94%E7%B3%BB%E4%BD%9C%E8%80%85) ----- | --- | --- | ---| ---| ---| ---| ---| ---|---|--- - - - -### 常用集合 -- [ArrayList/Vector](https://github.com/crossoverJie/Java-Interview/blob/master/MD/ArrayList.md) -- [LinkedList](https://github.com/crossoverJie/Java-Interview/blob/master/MD/LinkedList.md) -- [HashMap](https://github.com/crossoverJie/Java-Interview/blob/master/MD/HashMap.md) -- [HashSet](https://github.com/crossoverJie/Java-Interview/blob/master/MD/collection/HashSet.md) -- [LinkedHashMap](https://github.com/crossoverJie/Java-Interview/blob/master/MD/collection/LinkedHashMap.md) - -### Java 多线程 -- [多线程中的常见问题](https://github.com/crossoverJie/Java-Interview/blob/master/MD/Thread-common-problem.md) -- [synchronize 关键字原理](https://github.com/crossoverJie/Java-Interview/blob/master/MD/Synchronize.md) -- [多线程的三大核心](https://github.com/crossoverJie/Java-Interview/blob/master/MD/Threadcore.md) -- [对锁的一些认知](https://github.com/crossoverJie/Java-Interview/blob/master/MD/Java-lock.md) -- [ReentrantLock 实现原理 ](https://github.com/crossoverJie/Java-Interview/blob/master/MD/ReentrantLock.md) -- [ConcurrentHashMap 的实现原理](https://github.com/crossoverJie/Java-Interview/blob/master/MD/ConcurrentHashMap.md) -- [线程池原理](https://github.com/crossoverJie/Java-Interview/blob/master/MD/ThreadPoolExecutor.md) -- [深入理解线程通信](https://github.com/crossoverJie/Java-Interview/blob/master/MD/concurrent/thread-communication.md) -- [交替打印奇偶数](https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/actual/TwoThread.java) - -### JVM -- [Java 运行时内存划分](https://github.com/crossoverJie/Java-Interview/blob/master/MD/MemoryAllocation.md) -- [类加载机制](https://github.com/crossoverJie/Java-Interview/blob/master/MD/ClassLoad.md) -- [OOM 分析](https://github.com/crossoverJie/Java-Interview/blob/master/MD/OOM-analysis.md) -- [垃圾回收](https://github.com/crossoverJie/Java-Interview/blob/master/MD/GarbageCollection.md) -- [对象的创建与内存分配](https://github.com/crossoverJie/Java-Interview/blob/master/MD/newObject.md) -- [你应该知道的 volatile 关键字](https://github.com/crossoverJie/Java-Interview/blob/master/MD/concurrent/volatile.md) - -### 分布式相关 - -- [分布式限流](http://crossoverjie.top/2018/04/28/sbc/sbc7-Distributed-Limit/) -- [基于 Redis 的分布式锁](http://crossoverjie.top/2018/03/29/distributed-lock/distributed-lock-redis/) -- [分布式缓存设计](https://github.com/crossoverJie/Java-Interview/blob/master/MD/Cache-design.md) -- [分布式 ID 生成器](https://github.com/crossoverJie/Java-Interview/blob/master/MD/ID-generator.md) - -### 常用框架\第三方组件 - -- [Spring Bean 生命周期](https://github.com/crossoverJie/Java-Interview/blob/master/MD/spring/spring-bean-lifecycle.md) -- [Spring AOP 的实现原理](https://github.com/crossoverJie/Java-Interview/blob/master/MD/SpringAOP.md) -- [Guava 源码分析(Cache 原理)](https://crossoverjie.top/2018/06/13/guava/guava-cache/) -- SpringBoot 启动过程 -- Tomcat 类加载机制 - - -### 架构设计 -- [秒杀系统设计](https://github.com/crossoverJie/Java-Interview/blob/master/MD/Spike.md) -- [秒杀架构实践](http://crossoverjie.top/2018/05/07/ssm/SSM18-seconds-kill/) - -### DB 相关 - -- [MySQL 索引原理](https://github.com/crossoverJie/Java-Interview/blob/master/MD/MySQL-Index.md) -- [SQL 优化](https://github.com/crossoverJie/Java-Interview/blob/master/MD/SQL-optimization.md) -- [数据库水平垂直拆分](https://github.com/crossoverJie/Java-Interview/blob/master/MD/DB-split.md) - -### 数据结构与算法 -- [红包算法](https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/red/RedPacket.java) -- [二叉树中序遍历](https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/algorithm/BinaryNode.java#L76-L101) -- [是否为快乐数字](https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/algorithm/HappyNum.java#L38-L55) -- [链表是否有环](https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/algorithm/LinkLoop.java#L32-L59) -- [从一个数组中返回两个值相加等于目标值的下标](https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/algorithm/TwoSum.java#L38-L59) -- [一致性 Hash 算法](https://github.com/crossoverJie/Java-Interview/blob/master/MD/Consistent-Hash.md) -- [限流算法](https://github.com/crossoverJie/Java-Interview/blob/master/MD/Limiting.md) -- [三种方式反向打印单向链表](https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/algorithm/ReverseNode.java) -- [合并两个排好序的链表](https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/algorithm/MergeTwoSortedLists.java) -- [两个栈实现队列](https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/algorithm/TwoStackQueue.java) -- [动手实现一个 LRU cache](http://crossoverjie.top/2018/04/07/algorithm/LRU-cache/) - -### Netty 相关 -- [SpringBoot 整合长连接心跳机制](https://crossoverjie.top/2018/05/24/netty/Netty(1)TCP-Heartbeat/) - -### 附加技能 - -- [TCP/IP 协议](https://github.com/crossoverJie/Java-Interview/blob/master/MD/TCP-IP.md) -- [一个学渣的阿里之路](https://crossoverjie.top/2018/06/21/personal/Interview-experience/) - +* [docker](docker/docker-command.md) +* [git](git/git-usage.md) \ No newline at end of file diff --git a/algorithm/Limiting.md b/algorithm/Limiting.md new file mode 100644 index 0000000..383ac6d --- /dev/null +++ b/algorithm/Limiting.md @@ -0,0 +1,56 @@ +# 限流算法 +限流是解决高并发大流量的一种方案,至少是可以保证应用的可用性。 + +通常有以下两种限流方案: + +- 漏桶算法 +- 令牌桶算法 + +## 漏桶算法 +![漏桶算法,来自网络.png](https://i.loli.net/2017/08/11/598c905caa8cb.png) + +漏桶算法非常简单,就是将流量放入桶中并按照一定的速率流出。如果流量过大时候并不会提高流出效率,而溢出的流量也只能是抛弃掉了。 + +这种算法很简单,但也非常粗暴,无法应对突发的大流量。 +这时可以考虑令牌桶算法。 + +## 令牌桶算法 +![令牌桶算法-来自网络.gif](https://i.loli.net/2017/08/11/598c91f2a33af.gif) + +令牌桶算法是按照恒定的速率向桶中放入令牌,每当请求经过时则消耗一个或多个令牌。当桶中的令牌为 0 时,请求则会被阻塞。 + +> note: +令牌桶算法支持先消费后付款,比如一个请求可以获取多个甚至全部的令牌,但是需要后面的请求付费。也就是说后面的请求需要等到桶中的令牌补齐之后才能继续获取。 + +实例: +```java_holder_method_tree + @Override + public BaseResponse getUserByFeignBatch(@RequestBody UserReqVO userReqVO) { + //调用远程服务 + OrderNoReqVO vo = new OrderNoReqVO() ; + vo.setReqNo(userReqVO.getReqNo()); + + RateLimiter limiter = RateLimiter.create(2.0) ; + //批量调用 + for (int i = 0 ;i< 10 ; i++){ + double acquire = limiter.acquire(); + logger.debug("获取令牌成功!,消耗=" + acquire); + BaseResponse orderNo = orderServiceClient.getOrderNo(vo); + logger.debug("远程返回:"+JSON.toJSONString(orderNo)); + } + + UserRes userRes = new UserRes() ; + userRes.setUserId(123); + userRes.setUserName("张三"); + + userRes.setReqNo(userReqVO.getReqNo()); + userRes.setCode(StatusEnum.SUCCESS.getCode()); + userRes.setMessage("成功"); + + return userRes ; + } +``` + + +1. [单 JVM 限流](http://crossoverjie.top/2017/08/11/sbc4/) +2. [分布式限流](http://crossoverjie.top/2018/04/28/sbc/sbc7-Distributed-Limit/) diff --git a/algorithm/algorithm.md b/algorithm/algorithm.md new file mode 100644 index 0000000..4a99fbd --- /dev/null +++ b/algorithm/algorithm.md @@ -0,0 +1,26 @@ +* [常用算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#常用算法) + * [排序、查找算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#排序查找算法) + * [选择排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#选择排序) + * [冒泡排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#冒泡排序) + * [插入排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#插入排序) + * [快速排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#快速排序) + * [归并排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#归并排序) + * [希尔排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#希尔排序) + * [堆排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#堆排序) + * [计数排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#计数排序) + * [桶排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#桶排序) + * [基数排序](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#基数排序) + * [二分查找](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#二分查找) + * [Java 中的排序工具](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#java-中的排序工具) + * [布隆过滤器](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#布隆过滤器) + * [字符串比较](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#字符串比较) + * [KMP 算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#kmp-算法) + * [深度优先、广度优先](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#深度优先广度优先) + * [贪心算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#贪心算法) + * [回溯算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#回溯算法) + * [剪枝算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#剪枝算法) + * [动态规划](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#动态规划) + * [朴素贝叶斯](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#朴素贝叶斯) + * [推荐算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#推荐算法) + * [最小生成树算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#最小生成树算法) + * [最短路径算法](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#最短路径算法) diff --git a/architecture-design/ID-generator.md b/architecture-design/ID-generator.md new file mode 100644 index 0000000..563c681 --- /dev/null +++ b/architecture-design/ID-generator.md @@ -0,0 +1,36 @@ +# 分布式 ID 生成器 +一个唯一 ID 在一个分布式系统中是非常重要的一个业务属性,其中包括一些如订单 ID,消息 ID ,会话 ID,他们都有一些共有的特性: +- 全局唯一。 +- 趋势递增。 + +全局唯一很好理解,目的就是唯一标识某个次请求,某个业务。 + +通常有以下几种方案: + +## 基于数据库 +可以利用 `MySQL` 中的自增属性 `auto_increment` 来生成全局唯一 ID,也能保证趋势递增。 +但这种方式太依赖 DB,如果数据库挂了那就非常容易出问题。 + +### 水平扩展改进 +但也有改进空间,可以将数据库水平拆分,如果拆为了两个库 A 库和 B 库。 +A 库的递增方式可以是 `0 ,2 ,4 ,6`。B 库则是 `1 ,3 ,5 ,7`。这样的方式可以提高系统可用性,并且 ID 也是趋势递增的。 + +但也有如下一下问题: +- 想要扩容增加性能变的困难,之前已经定义好了 A B 库递增的步数,新加的数据库不好加入进来,水平扩展困难。 +- 也是强依赖与数据库,并且如果其中一台挂掉了那就不是绝对递增了。 + +## 本地 UUID 生成 +还可以采用 `UUID` 的方式生成唯一 ID,由于是在本地生成没有了网络之类的消耗,所有效率非常高。 + +但也有以下几个问题: +- 生成的 ID 是无序性的,不能做到趋势递增。 +- 由于是字符串并且不是递增,所以不太适合用作主键。 + +## 采用本地时间 +这种做法非常简单,可以利用本地的毫秒数加上一些业务 ID 来生成唯一ID,这样可以做到趋势递增,并且是在本地生成效率也很高。 + +但有一个致命的缺点:当并发量足够高的时候**唯一性**就不能保证了。 + +## Twitter 雪花算法 + +可以基于 `Twitter` 的 `Snowflake` 算法来实现。它主要是一种划分命名空间的算法,将生成的 ID 按照机器、时间等来进行标志。 \ No newline at end of file diff --git a/architecture-design/Spike.md b/architecture-design/Spike.md new file mode 100644 index 0000000..8d85d68 --- /dev/null +++ b/architecture-design/Spike.md @@ -0,0 +1,32 @@ +# 设计一个秒杀系统 + +**具体实现参考 [秒杀架构实践](https://crossoverjie.top/2018/05/07/ssm/SSM18-seconds-kill/)** + +主要做到以下两点: + +- 尽量将请求过滤在上游。 +- 尽可能的利用缓存(大多数场景下都是**查多于写**)。 + +常用的系统分层结构: + +

+ +针对于浏览器端,可以使用 JS 进行请求过滤,比如五秒钟之类只能点一次抢购按钮,五秒钟只能允许请求一次后端服务。(APP 同理) + +这样其实就可以过滤掉大部分普通用户。 + +但是防不住直接抓包循环调用。这种情况可以最简单的处理:在`Web层`通过限制一个 UID 五秒之类的请求服务层的次数(可利用 Redis 实现)。 + +但如果是真的有 10W 个不同的 UID 来请求,比如黑客抓肉鸡的方式。 + +这种情况可以在`服务层` 针对于写请求使用请求队列,再通过限流算法([限流算法](https://github.com/crossoverJie/Java-Interview/blob/master/MD/Limiting.md))每秒钟放一部分请求到队列。 + +对于读请求则尽量使用缓存,可以提前将数据准备好,不管是 `Redis` 还是其他缓存中间件效率都是非常高的。 + +> ps : 刷新缓存情况,比如库存扣除成功这种情况不用马上刷新缓存,如果库存扣到了 0 再刷新缓存。因为大多数用户都只关心是否有货,并不关心现在还剩余多少。 + +## 总结 + +- 如果流量巨大,导致各个层的压力都很大可以适当的加机器横向扩容。如果加不了机器那就只有放弃流量直接返回失败。快速失败非常重要,至少可以保证系统的可用性。 +- 业务分批执行:对于下单、付款等操作可以异步执行提高吞吐率。 +- 主要目的就是尽量少的请求直接访问到 `DB`。 diff --git a/architecture-design/million-sms-push.md b/architecture-design/million-sms-push.md new file mode 100644 index 0000000..e02a022 --- /dev/null +++ b/architecture-design/million-sms-push.md @@ -0,0 +1,349 @@ +# 设计一个百万级的消息推送系统 + +# 前言 + +先简单说下本次的主题,由于我最近做的是物联网相关的开发工作,其中就不免会遇到和设备的交互。 + +最主要的工作就是要有一个系统来支持设备的接入、向设备推送消息;同时还得满足大量设备接入的需求。 + +所以本次分享的内容不但可以满足物联网领域同时还支持以下场景: + +- 基于 `WEB` 的聊天系统(点对点、群聊)。 +- `WEB` 应用中需求服务端推送的场景。 +- 基于 SDK 的消息推送平台。 + +# 技术选型 + +要满足大量的连接数、同时支持双全工通信,并且性能也得有保障。 + +在 Java 技术栈中进行选型首先自然是排除掉了传统 `IO`。 + +那就只有选 NIO 了,在这个层面其实选择也不多,考虑到社区、资料维护等方面最终选择了 Netty。 + +最终的架构图如下: + +![](https://ws1.sinaimg.cn/mw690/72fbb941gy1fvjz1teappj20rg0humy1.jpg) + + +现在看着蒙没关系,下文一一介绍。 + +# 协议解析 + +既然是一个消息系统,那自然得和客户端定义好双方的协议格式。 + +常见和简单的是 HTTP 协议,但我们的需求中有一项需要是双全工的交互方式,同时 HTTP 更多的是服务于浏览器。我们需要的是一个更加精简的协议,减少许多不必要的数据传输。 + +因此我觉得最好是在满足业务需求的情况下定制自己的私有协议,在我这个场景下其实有标准的物联网协议。 + +如果是其他场景可以借鉴现在流行的 `RPC` 框架定制私有协议,使得双方通信更加高效。 + +不过根据这段时间的经验来看,不管是哪种方式都得在协议中预留安全相关的位置。 + +协议相关的内容就不过讨论了,更多介绍具体的应用。 + +# 简单实现 + +首先考虑如何实现功能,再来思考百万连接的情况。 + +## 注册鉴权 + +在做真正的消息上、下行之前首先要考虑的就是鉴权问题。 + +就像你使用微信一样,第一步怎么也得是登录吧,不能无论是谁都可以直接连接到平台。 + +所以第一步得是注册才行。 + +如上面架构图中的 `注册/鉴权` 模块。通常来说都需要客户端通过 `HTTP` 请求传递一个唯一标识,后台鉴权通过之后会响应一个 `token`,并将这个 `token` 和客户端的关系维护到 `Redis` 或者是 DB 中。 + +客户端将这个 token 也保存到本地,今后的每一次请求都得带上这个 token。一旦这个 token 过期,客户端需要再次请求获取 token。 + +鉴权通过之后客户端会直接通过`TCP 长连接`到图中的 `push-server` 模块。 + +这个模块就是真正处理消息的上、下行。 + +## 保存通道关系 + +在连接接入之后,真正处理业务之前需要将当前的客户端和 Channel 的关系维护起来。 + +假设客户端的唯一标识是手机号码,那就需要把手机号码和当前的 Channel 维护到一个 Map 中。 + +这点和之前 [SpringBoot 整合长连接心跳机制](http://t.cn/EPcNHFZ) 类似。 + +![](https://ws2.sinaimg.cn/large/006tNbRwgy1fvkj6oe4rej30k104c0tg.jpg) + +同时为了可以通过 Channel 获取到客户端唯一标识(手机号码),还需要在 Channel 中设置对应的属性: + +```java_holder_method_tree +public static void putClientId(Channel channel, String clientId) { + channel.attr(CLIENT_ID).set(clientId); +} +``` + +获取时手机号码时: + +```java_holder_method_tree +public static String getClientId(Channel channel) { + return (String)getAttribute(channel, CLIENT_ID); +} +``` + +这样当我们客户端下线的时便可以记录相关日志: + +```java_holder_method_tree +String telNo = NettyAttrUtil.getClientId(ctx.channel()); +NettySocketHolder.remove(telNo); +log.info("客户端下线,TelNo=" + telNo); +``` + +> 这里有一点需要注意:存放客户端与 Channel 关系的 Map 最好是预设好大小(避免经常扩容),因为它将是使用最为频繁同时也是占用内存最大的一个对象。 + +## 消息上行 + +接下来则是真正的业务数据上传,通常来说第一步是需要判断上传消息输入什么业务类型。 + +在聊天场景中,有可能上传的是文本、图片、视频等内容。 + +所以我们得进行区分,来做不同的处理;这就和客户端协商的协议有关了。 + +- 可以利用消息头中的某个字段进行区分。 +- 更简单的就是一个 `JSON` 消息,拿出一个字段用于区分不同消息。 + +不管是哪种只有可以区分出来即可。 + +### 消息解析与业务解耦 + +消息可以解析之后便是处理业务,比如可以是写入数据库、调用其他接口等。 + +我们都知道在 Netty 中处理消息一般是在 `channelRead()` 方法中。 + +![](https://ws2.sinaimg.cn/large/006tNbRwgy1fvkkawymbkj30o6027mxf.jpg) + +在这里可以解析消息,区分类型。 + +但如果我们的业务逻辑也写在里面,那这里的内容将是巨多无比。 + +甚至我们分为好几个开发来处理不同的业务,这样将会出现许多冲突、难以维护等问题。 + +所以非常有必要将消息解析与业务处理完全分离开来。 + + +> 这时面向接口编程就发挥作用了。 + +这里的核心代码和 [「造个轮子」——cicada(轻量级 WEB 框架)](https://crossoverjie.top/2018/09/03/wheel/cicada1/#%E9%85%8D%E7%BD%AE%E4%B8%9A%E5%8A%A1-Action) 是一致的。 + +都是先定义一个接口用于处理业务逻辑,然后在解析消息之后通过反射创建具体的对象执行其中的`处理函数`即可。 + +这样不同的业务、不同的开发人员只需要实现这个接口同时实现自己的业务逻辑即可。 + +伪代码如下: + +![](https://ws1.sinaimg.cn/large/006tNbRwgy1fvkkhd8961j30n602kglr.jpg) + +![](https://ws2.sinaimg.cn/large/006tNbRwgy1fvkkhwsgkqj30nh0m0gpt.jpg) + +想要了解 cicada 的具体实现请点击这里: + +[https://github.com/TogetherOS/cicada](https://github.com/TogetherOS/cicada) + + +上行还有一点需要注意;由于是基于长连接,所以客户端需要定期发送心跳包用于维护本次连接。同时服务端也会有相应的检查,N 个时间间隔没有收到消息之后将会主动断开连接节省资源。 + +这点使用一个 `IdleStateHandler` 就可实现,更多内容可以查看 [Netty(一) SpringBoot 整合长连接心跳机制](https://crossoverjie.top/2018/05/24/netty/Netty(1)TCP-Heartbeat/#%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%BF%83%E8%B7%B3)。 + + + +## 消息下行 + +有了上行自然也有下行。比如在聊天的场景中,有两个客户端连上了 `push-server`,他们直接需要点对点通信。 + +这时的流程是: + +- A 将消息发送给服务器。 +- 服务器收到消息之后,得知消息是要发送给 B,需要在内存中找到 B 的 Channel。 +- 通过 B 的 Channel 将 A 的消息转发下去。 + +这就是一个下行的流程。 + +甚至管理员需要给所有在线用户发送系统通知也是类似: + +遍历保存通道关系的 Map,挨个发送消息即可。这也是之前需要存放到 Map 中的主要原因。 + +伪代码如下: + +![](https://ws3.sinaimg.cn/large/006tNbRwgy1fvkkpefci7j30w408h768.jpg) + +具体可以参考: + +[https://github.com/crossoverJie/netty-action/](https://github.com/crossoverJie/netty-action/) + + +# 分布式方案 + +单机版的实现了,现在着重讲讲如何实现百万连接。 + +百万连接其实只是一个形容词,更多的是想表达如何来实现一个分布式的方案,可以灵活的水平拓展从而能支持更多的连接。 + +再做这个事前首先得搞清楚我们单机版的能支持多少连接。影响这个的因素就比较多了。 + +- 服务器自身配置。内存、CPU、网卡、Linux 支持的最大文件打开数等。 +- 应用自身配置,因为 Netty 本身需要依赖于堆外内存,但是 JVM 本身也是需要占用一部分内存的,比如存放通道关系的大 `Map`。这点需要结合自身情况进行调整。 + +结合以上的情况可以测试出单个节点能支持的最大连接数。 + +单机无论怎么优化都是有上限的,这也是分布式主要解决的问题。 + +## 架构介绍 + +在将具体实现之前首先得讲讲上文贴出的整体架构图。 + +![](https://ws1.sinaimg.cn/mw690/72fbb941gy1fvjz1teappj20rg0humy1.jpg) + +先从左边开始。 + +上文提到的 `注册鉴权` 模块也是集群部署的,通过前置的 Nginx 进行负载。之前也提过了它主要的目的是来做鉴权并返回一个 token 给客户端。 + +但是 `push-server` 集群之后它又多了一个作用。那就是得返回一台可供当前客户端使用的 `push-server`。 + +右侧的 `平台` 一般指管理平台,它可以查看当前的实时在线数、给指定客户端推送消息等。 + +推送消息则需要经过一个推送路由(`push-server`)找到真正的推送节点。 + +其余的中间件如:Redis、Zookeeper、Kafka、MySQL 都是为了这些功能所准备的,具体看下面的实现。 + +## 注册发现 + +首先第一个问题则是 `注册发现`,`push-server` 变为多台之后如何给客户端选择一台可用的节点是第一个需要解决的。 + +这块的内容其实已经在 [分布式(一) 搞定服务注册与发现](https://crossoverjie.top/2018/08/27/distributed/distributed-discovery-zk/) 中详细讲过了。 + +所有的 `push-server` 在启动时候需要将自身的信息注册到 Zookeeper 中。 + +`注册鉴权` 模块会订阅 Zookeeper 中的节点,从而可以获取最新的服务列表。结构如下: + +![](https://ws2.sinaimg.cn/large/006tNbRwgy1fundatqf6uj30el06f0su.jpg) + +以下是一些伪代码: + +应用启动注册 Zookeeper。 + +![](https://ws2.sinaimg.cn/large/006tNbRwgy1fvkriuz7yrj30m304lq3r.jpg) + +![](https://ws4.sinaimg.cn/large/006tNbRwgy1fvkrj927rsj30od08ejst.jpg) + +对于`注册鉴权`模块来说只需要订阅这个 Zookeeper 节点: + +![](https://ws2.sinaimg.cn/large/006tNbRwgy1fvkrlfdgrkj30tb08j0uf.jpg) + +### 路由策略 + +既然能获取到所有的服务列表,那如何选择一台刚好合适的 `push-server` 给客户端使用呢? + +这个过程重点要考虑以下几点: + +- 尽量保证各个节点的连接均匀。 +- 增删节点是否要做 Rebalance。 + +首先保证均衡有以下几种算法: + +- 轮询。挨个将各个节点分配给客户端。但会出现新增节点分配不均匀的情况。 +- Hash 取模的方式。类似于 HashMap,但也会出现轮询的问题。当然也可以像 HashMap 那样做一次 Rebalance,让所有的客户端重新连接。不过这样会导致所有的连接出现中断重连,代价有点大。 +- 由于 Hash 取模方式的问题带来了[`一致性 Hash`算法](https://crossoverjie.top/%2F2018%2F01%2F08%2FConsistent-Hash%2F),但依然会有一部分的客户端需要 Rebalance。 +- 权重。可以手动调整各个节点的负载情况,甚至可以做成自动的,基于监控当某些节点负载较高就自动调低权重,负载较低的可以提高权重。 + +还有一个问题是: + +> 当我们在重启部分应用进行升级时,在该节点上的客户端怎么处理? + +由于我们有心跳机制,当心跳不通之后就可以认为该节点出现问题了。那就得重新请求`注册鉴权`模块获取一个可用的节点。在弱网情况下同样适用。 + +如果这时客户端正在发送消息,则需要将消息保存到本地等待获取到新的节点之后再次发送。 + +## 有状态连接 + +在这样的场景中不像是 HTTP 那样是无状态的,我们得明确的知道各个客户端和连接的关系。 + +在上文的单机版中我们将这个关系保存到本地的缓存中,但在分布式环境中显然行不通了。 + +比如在平台向客户端推送消息的时候,它得首先知道这个客户端的通道保存在哪台节点上。 + +借助我们以前的经验,这样的问题自然得引入一个第三方中间件用来存放这个关系。 + +也就是架构图中的存放`路由关系的 Redis`,在客户端接入 `push-server` 时需要将当前客户端唯一标识和服务节点的 `ip+port` 存进 `Redis`。 + +同时在客户端下线时候得在 Redis 中删掉这个连接关系。 + + +> 这样在理想情况下各个节点内存中的 map 关系加起来应该正好等于 Redis 中的数据。 + +伪代码如下: + +![](https://ws1.sinaimg.cn/large/006tNbRwgy1fvkt2ytdxoj30r109u40n.jpg) + +这里存放路由关系的时候会有并发问题,最好是换为一个 `lua` 脚本。 + +## 推送路由 + +设想这样一个场景:管理员需要给最近注册的客户端推送一个系统消息会怎么做? + +> 结合架构图 + +假设这批客户端有 10W 个,首先我们需要将这批号码通过`平台`下的 `Nginx` 下发到一个推送路由中。 + +为了提高效率甚至可以将这批号码再次分散到每个 `push-route` 中。 + +拿到具体号码之后再根据号码的数量启动多线程的方式去之前的路由 Redis 中获取客户端所对应的 `push-server`。 + +再通过 HTTP 的方式调用 `push-server` 进行真正的消息下发(Netty 也很好的支持 HTTP 协议)。 + +推送成功之后需要将结果更新到数据库中,不在线的客户端可以根据业务再次推送等。 + +## 消息流转 + +也许有些场景对于客户端上行的消息非常看重,需要做持久化,并且消息量非常大。 + +在 `push-sever` 做业务显然不合适,这时完全可以选择 Kafka 来解耦。 + +将所有上行的数据直接往 Kafka 里丢后就不管了。 + +再由消费程序将数据取出写入数据库中即可。 + +其实这块内容也很值得讨论,可以先看这篇了解下:[强如 Disruptor 也发生内存溢出?](https://crossoverjie.top/2018/08/29/java-senior/OOM-Disruptor/) + +后续谈到 Kafka 再做详细介绍。 + +# 分布式问题 + +分布式解决了性能问题但却带来了其他麻烦。 + +## 应用监控 + +比如如何知道线上几十个 `push-server` 节点的健康状况? + +这时就得监控系统发挥作用了,我们需要知道各个节点当前的内存使用情况、GC。 + +以及操作系统本身的内存使用,毕竟 Netty 大量使用了堆外内存。 + +同时需要监控各个节点当前的在线数,以及 Redis 中的在线数。理论上这两个数应该是相等的。 + +这样也可以知道系统的使用情况,可以灵活的维护这些节点数量。 + +## 日志处理 + +日志记录也变得异常重要了,比如哪天反馈有个客户端一直连不上,你得知道问题出在哪里。 + + +最好是给每次请求都加上一个 traceID 记录日志,这样就可以通过这个日志在各个节点中查看到底是卡在了哪里。 + +以及 ELK 这些工具都得用起来才行。 + +# 总结 + +本次是结合我日常经验得出的,有些坑可能在工作中并没有踩到,所有还会有一些遗漏的地方。 + +就目前来看想做一个稳定的推送系统其实是比较麻烦的,其中涉及到的点非常多,只有真正做过之后才会知道。 + +看完之后觉得有帮助的还请不吝转发分享。 + +**欢迎关注公众号一起交流:** + +![](https://ws4.sinaimg.cn/large/006tNbRwgy1fvkwiw9pwaj30760760t7.jpg) \ No newline at end of file diff --git a/cache/Cache-design.md b/cache/Cache-design.md new file mode 100644 index 0000000..3df7c2a --- /dev/null +++ b/cache/Cache-design.md @@ -0,0 +1,60 @@ +# 分布式缓存设计 + +## 缓存使用场景 +* 读多写少 + +## 缓存模式 +* Cache-Aside Pattern(旁路缓存模式) +* Read-Through/Write-through(读写穿透,跟Cache-Aside比会多出Cache-Provider层) +* Write-behind(异步缓存写入) + +## 缓存优点 +* 提升性能 +* 减少数据库压力 + +## 缓存使用的缺点 +* 数据库和缓存的数据不一致问题(强一致性、弱一致性、最终一致性) + +## 目前常见的缓存方案都是分层缓存,通常可以分为以下几层: + +- `NG` 本地缓存,命中的话直接返回。 +- `NG` 没有命中时则需要查询分布式缓存,如 `Redis` 。 +- 如果分布式缓存没有命中则需要回源到 `Tomcat` 在本地堆进行查询,命中之后异步写回 `Redis` 。 +- 以上都没有命中那就只有从 `DB` 或者是数据源进行查询,并写回到 Redis 中。 + + +## 缓存更新的原子性 + +在写回 Redis 的时候如果是 `Tomcat` 集群,多个进程同时写那很有可能出现脏数据,这时就会出现更新原子性的问题。 + +可以有以下解决方案: +- 可以将多个 Tomcat 中的数据写入到 MQ 队列中,由消费者进行单线程更新缓存。 +- 利用[分布式锁](https://github.com/crossoverJie/Java-Interview/blob/master/MD/Java-lock.md#%E5%9F%BA%E4%BA%8E%E6%95%B0%E6%8D%AE%E5%BA%93),只有获取到锁进程才能写数据。 + +## 如何写缓存 + +写缓存时也要注意,通常来说分为以下几步: + +- 开启事务 +- 写入DB +- 提交事务 +- 写入缓存 + +这里可能会存在数据库写入成功但是缓存写入失败的情况,但是也不建议将写入缓存加入到事务中。 +因为写缓存的时候可能会因为网络原因耗时较长,这样会阻塞数据库事务。 +如果对一致性要求不高并且数据量也不大的情况下,可以单独起一个服务来做DB和缓存之间的数据同步操作。 + +更新缓存时也建议做增量更新。 + +## 负载策略 + +缓存负载策略一般有以下两种: +- 轮询机制。 +- 一致哈希算法。 + +轮询的优点是负载到各个服务器的请求是均匀的,但是如果进行扩容则缓存命中率会下降。 + +一致哈希的优点是相同的请求会负载到同一台服务器上,命中率不会随着扩容而降低,但是当大流量过来时有可能把服务器拖垮。 + +所以建议两种方案都采用: +首先采用一致哈希算法,当流量达到一定的阈值的时候则切换为轮询,这样既能保证缓存命中率,也能提高系统的可用性。 \ No newline at end of file diff --git a/database/DB-split.md b/database/DB-split.md new file mode 100644 index 0000000..a486cd0 --- /dev/null +++ b/database/DB-split.md @@ -0,0 +1,34 @@ +# 数据库水平垂直拆分 + +当数据库量非常大的时候,DB 已经成为系统瓶颈时就可以考虑进行水平垂直拆分了。 + +## 水平拆分 +一般水平拆分是根据表中的某一字段(通常是主键 ID )取模处理,将一张表的数据拆分到多个表中。这样每张表的表结构是相同的但是数据不同。 + +不但可以通过 ID 取模分表还可以通过时间分表,比如每月生成一张表。 + +按照范围分表也是可行的:一张表只存储 `0~1000W`的数据,超过只就进行分表,这样分表的优点是扩展灵活,但是存在热点数据。 +按照取模分表拆分之后我们的查询、修改、删除也都是取模。比如新增一条数据的时候往往需要一张临时表来生成 ID,然后根据生成的 ID 取模计算出需要写入的是哪张表(也可以使用[分布式 ID 生成器](https://github.com/crossoverJie/Java-Interview/blob/master/MD/ID-generator.md)来生成 ID)。 + +分表之后不能避免的就是查询要比以前复杂,通常不建议 `join` ,一般的做法是做两次查询。 + +## 垂直拆分 + +当一张表的字段过多时则可以考虑垂直拆分。 +通常是将一张表的字段才分为主表以及扩展表,使用频次较高的字段在一张表,其余的在一张表。 + +这里的多表查询也不建议使用 `join` ,依然建议使用两次查询。 + +## 拆分之后带来的问题 + +拆分之后由一张表变为了多张表,一个库变为了多个库。最突出的一个问题就是事务如何保证。 + +### 两段提交 + +### 最终一致性 + +如果业务对强一致性要求不是那么高那么最终一致性则是一种比较好的方案。 + +通常的做法就是补偿,比如 一个业务是 A 调用 B,两个执行成功才算最终成功,当 A 成功之后,B 执行失败如何来通知 A 呢。 + +比较常见的做法是 失败时 B 通过 MQ 将消息告诉 A,A 再来进行回滚。这种的前提是 A 的回滚操作得是幂等的,不然 B 重复发消息就会出现问题。 \ No newline at end of file diff --git a/database/SQL-optimization.md b/database/SQL-optimization.md new file mode 100644 index 0000000..6e00913 --- /dev/null +++ b/database/SQL-optimization.md @@ -0,0 +1,75 @@ +# SQL 优化 + +## 负向查询不能使用索引 +```sql +select name from user where id not in (1,3,4); +``` +应该修改为: +``` +select name from user where id in (2,5,6); +``` + +## 前导模糊查询不能使用索引: +```sql +select name from user where name like '%zhangsan' +``` + +非前导则可以使用索引: +```sql +select name from user where name like 'zhangsan%' +``` +建议可以考虑使用 `Lucene` 等全文索引工具来代替频繁的模糊查询。 + +## 数据区分不明显的不建议创建索引 +如 user 表中的性别字段,可以明显区分的才建议创建索引,如身份证等字段。 + +## 字段的默认值不要为 null +这样会带来和预期不一致的查询结果。 + +## 在字段上进行计算不能命中索引 +```sql +select name from user where FROM_UNIXTIME(create_time) < CURDATE(); +``` + +应该修改为: +```sql +select name from user where create_time < FROM_UNIXTIME(CURDATE()); +``` + +## 最左前缀问题 + +如果给 user 表中的 username pwd 字段创建了复合索引那么使用以下SQL 都是可以命中索引: + +```sql +select username from user where username='zhangsan' and pwd ='axsedf1sd' + +select username from user where pwd ='axsedf1sd' and username='zhangsan' + +select username from user where username='zhangsan' +``` + +但是使用 +```sql +select username from user where pwd ='axsedf1sd' +``` +是不能命中索引的。 + +## 如果明确知道只有一条记录返回 +```sql +select name from user where username='zhangsan' limit 1 +``` +可以提高效率,可以让数据库停止游标移动。 + +## 不要让数据库帮我们做强制类型转换 + +```sql +select name from user where telno=18722222222 +``` +这样虽然可以查出数据,但是会导致全表扫描。需要修改为 +``` +select name from user where telno='18722222222' +``` + +## 如果需要进行 join 的字段两表的字段类型要相同 + +不然也不会命中索引。 \ No newline at end of file diff --git a/database/database.md b/database/database.md new file mode 100644 index 0000000..e5be4c4 --- /dev/null +++ b/database/database.md @@ -0,0 +1,15 @@ +* [数据库](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#数据库) + * [基础理论](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#基础理论) + * [数据库设计的三大范式](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#数据库设计的三大范式) + * [MySQL](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#mysql) + * [原理](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#原理) + * [InnoDB](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#innodb) + * [优化](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#优化) + * [索引](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#索引) + * [聚集索引, 非聚集索引](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#聚集索引-非聚集索引) + * [复合索引](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#复合索引) + * [自适应哈希索引(AHI)](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#自适应哈希索引ahi) + * [explain](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#explain) + * [NoSQL](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#nosql) + * [MongoDB](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#mongodb) + * [Hbase](https://github.com/xingshaocheng/architect-awesome/blob/master/README.md#hbase) diff --git a/database/mysql/MySQL-Index.md b/database/mysql/MySQL-Index.md new file mode 100644 index 0000000..579ba42 --- /dev/null +++ b/database/mysql/MySQL-Index.md @@ -0,0 +1,26 @@ +# MySQL 索引原理 + +现在互联网应用中对数据库的使用多数都是读较多,比例可以达到 `10:1`。并且数据库在做查询时 `IO` 消耗较大,所以如果能把一次查询的 `IO` 次数控制在常量级那对数据库的性能提升将是非常明显的,因此基于 `B+ Tree` 的索引结构出现了。 + + +## B+ Tree 的数据结构 + +![](https://ws2.sinaimg.cn/large/006tKfTcgy1fn10d6j9sij30hc08cab3.jpg) + +如图所示是 `B+ Tree` 的数据结构。是由一个一个的磁盘块组成的树形结构,每个磁盘块由数据项和指针组成。 + +> 所有的数据都是存放在叶子节点,非叶子节点不存放数据。 + +## 查找过程 + +以磁盘块1为例,指针 P1 表示小于17的磁盘块,P2 表示在 `17~35` 之间的磁盘块,P3 则表示大于35的磁盘块。 + +比如要查找数据项99,首先将磁盘块1 load 到内存中,发生 1 次 `IO`。接着通过二分查找发现 99 大于 35,所以找到了 P3 指针。通过P3 指针发生第二次 IO 将磁盘块4加载到内存。再通过二分查找发现大于87,通过 P3 指针发生了第三次 IO 将磁盘块11 加载到内存。最后再通过一次二分查找找到了数据项99。 + +由此可见,如果一个几百万的数据查询只需要进行三次 IO 即可找到数据,那么整个效率将是非常高的。 + +观察树的结构,发现查询需要经历几次 IO 是由树的高度来决定的,而树的高度又由磁盘块,数据项的大小决定的。 + +磁盘块越大,数据项越小那么树的高度就越低。这也就是为什么索引字段要尽可能小的原因。 + +> 索引使用的一些[原则](https://github.com/crossoverJie/Java-Interview/blob/master/MD/SQL-optimization.md)。 diff --git a/database/mysql/schedule.sql b/database/mysql/schedule.sql new file mode 100644 index 0000000..1039e75 --- /dev/null +++ b/database/mysql/schedule.sql @@ -0,0 +1,28 @@ +-- SET GLOBAL EVENt_scheduler=1;临时 + +-- show VARIABLES like '%event_sche%'; + +-- [mysqld] +-- event_scheduler=ON //这一行加入mysqld标签下--永久 + +/* +use winjean; +create procedure test_proce() +begin + update t_user set `password` = 'winjean'; +end +*/ + +/* +-- 定时任务 second,minute,hour,day,week(周),quarter(季度),month,year + +create event second_event +on schedule every 1 second +on completion preserve disable +do call test_proce(); +*/ + +-- alter event second_event on completion preserve enable; +-- alter event second_event on completion preserve disable; + +SELECT event_name,event_definition,interval_value,interval_field,status FROM information_schema.EVENTS; \ No newline at end of file diff --git a/database/postgres.md b/database/postgres.md new file mode 100644 index 0000000..4154c2b --- /dev/null +++ b/database/postgres.md @@ -0,0 +1,10 @@ + +ALTER table tb_name add COLUMN cl_name int4 null; + +ALTER table tb_name DROP COLUMN cl_name; + +ALTER table tb_name RENAME COLUMN cl_name1 TO cl_name2; + +ALTER table tb_name ALTER COLUMN cl_name set not null; + +ALTER TABLE tb_name ALTER COLUMN cl_name TYPE int USING cl_name::integer; \ No newline at end of file diff --git a/database/transaction.md b/database/transaction.md new file mode 100644 index 0000000..3a7dad6 --- /dev/null +++ b/database/transaction.md @@ -0,0 +1,64 @@ +# **PROPAGATION** +* PROPAGATION_REQUIRED -- default +* PROPAGATION_SUPPORTS +* PROPAGATION_MANDATORY +* PROPAGATION_REQUIRES_NEW +* PROPAGATION_NOT_SUPPORTED +* PROPAGATION_NEVER +* PROPAGATION_NESTED + +# **ISOLATION** +* TRANSACTION_READ_UNCOMMITTED -- dirty reads, non-repeatable reads and phantom reads +允许脏读取,但不允许更新丢失。如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。该隔离级别可以通过“排他写锁”实现。 +不可避免 脏读、不可重复读、虚读。 +* TRANSACTION_READ_COMMITTED -- non-repeatable reads and phantom reads +允许不可重复读取,但不允许脏读取。这可以通过“瞬间共享读锁”和“排他写锁”实现。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。 +可避免 脏读,不可避免 不可重复读、虚读。Oracle采用读已提交。 +* TRANSACTION_REPEATABLE_READ -- phantom reads +禁止不可重复读取和脏读取,但是有时可能出现幻读数据。这可以通过“共享读锁”和“排他写锁”实现。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。 +可避免 脏读、不可重复读, 不可避免 虚读。MySQL采用可重复读。 +* TRANSACTION_SERIALIZABLE +提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。仅仅通过“行级锁”是无法实现事务序列化的,必须通过其他机制保证新插入的数据不会被刚执行查询操作的事务访问到。 +可避免 脏读、不可重复读、幻读情况的发生。 + +# **TRANSACTION** +## 刚性事务 +刚性事务的理论基础-ACID原则 +* 原子性(Atomicity) +事务必须是原子工作单元;对于其数据修改,要么全都执行,要么全都不执行。 +比如转账,要么转账成功,账户余额增加(减少);要么转账失败,账户余额不变。 +* 一致性(Consistency) +事务在完成时,必须使所有的数据都保持一致状态。 +在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。事务结束时,所有的内部数据结构(如 B 树索引或双向链表)都必须是正确的。 +某些维护一致性的责任由应用程序开发人员承担,他们必须确保应用程序已强制所有已知的完整性约束。例如,当开发用于转帐的应用程序时,应避免在转帐过程中任意移动小数点。 +* 隔离性(Isolation) +由并发事务所作的修改必须与任何其它并发事务所作的修改隔离。 +事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。 +这称为隔离性,因为它能够重新装载起始数据,并且重播一系列事务,以使数据结束时的状态与原始事务执行的状态相同。 +当事务可序列化时将获得最高的隔离级别。在此级别上,从一组可并行执行的事务获得的结果与通过连续运行每个事务所获得的结果相同。 +由于高度隔离会限制可并行执行的事务数,所以一些应用程序降低隔离级别以换取更大的吞吐量。 +* 持久性(Durability) +事务完成之后,它对于系统的影响是永久性的。该修改即使出现致命的系统故障也将一直保持。 +例如我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务以及正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成,否则就会造成我们看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。 + +##刚性事务的实现方案 +* WAL(Write ahead logging) +* 影子分页(Shadow paging) +* 两阶段型 + +##分布式事务 +分布式事务的理论基础-CAP理论 +* 一致性(Consistency) +* 可用性(Availability) +* 分区容错性(Partition tolerance) + +##柔性事务 +柔性事务的理论基础-BASE理论 +* 基本可用(Basically Available) +* 软状态(Soft state) +* 最终一致性(Eventually consistent) + +##柔性事务的实现 +* 补偿型 +* 异步确保型 +* 最大努力型 \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..939be00 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,30 @@ +# base image +FROM centos + +# MAINTAINER +MAINTAINER 19548901@qq.com + +# put nginx-1.12.2.tar.gz into /usr/local/src and unpack nginx +ADD nginx-1.15.9.tar.gz /usr/local/src + +# running required command +RUN yum install -y gcc gcc-c++ glibc make autoconf openssl openssl-devel +RUN yum install -y libxslt-devel -y gd gd-devel GeoIP GeoIP-devel pcre pcre-devel +RUN useradd -M -s /sbin/nologin nginx + +# mount a dir to container +VOLUME ["/data"] + +# change dir to /usr/local/src/nginx-1.12.2 +WORKDIR /usr/local/src/nginx-1.15.9 + +# execute command to compile nginx +RUN ./configure --user=nginx --group=nginx --prefix=/usr/local/nginx --with-file-aio --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module --with-http_image_filter_module --with-http_geoip_module --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_auth_request_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_stub_status_module && make && make install + +ENV PATH /usr/local/nginx/sbin:$PATH + +EXPOSE 80 + +ENTRYPOINT ["nginx"] + +CMD ["-g","daemon on;"] diff --git a/docker/docker-command.md b/docker/docker-command.md new file mode 100644 index 0000000..a705a06 --- /dev/null +++ b/docker/docker-command.md @@ -0,0 +1,20 @@ +docker command +=== +* docker images: 列出images +* docker images -a :列出所有的images(包含历史) +* docker images --tree :显示镜像的所有层(layer) +* docker rmi : 删除一个或多个image + +* docker pull pull centos:latest 下载最新的centos + +* docker run -it -p80:80 --name=centos centos :运行centos i交互式 t临时终端 exit退出 +* docker start|stop|restart -ai 容器名 +-a 参数将容器的输出导出到终端,同时使用 -i 参数进行交互式的操作。这条命令可以让我们继续运行容器 +* docker ps -a 列出正在运行的容器 +* docker commit -a "" -m "" 容器名 镜像名:版本 +* docker rm 容器名 + +* docker logs -f 容器id --tail 200 查看tomcat日志 + +### 进入容器 +* docker exec -it nginx7 /bin/bash diff --git a/docker/docker-compose-install.md b/docker/docker-compose-install.md new file mode 100644 index 0000000..339938d --- /dev/null +++ b/docker/docker-compose-install.md @@ -0,0 +1,11 @@ +1、安装 Docker Compose 可以通过下面命令自动下载适应版本的 Compose +sudo curl -L https://github.com/docker/compose/releases/download/1.21.2/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose + or +sudo curl -L https://github.com/docker/compose/releases/download/1.24.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose + + +2、为安装脚本添加执行权限 +sudo chmod +x /usr/local/bin/docker-compose + +3、查看docker-compose版本 +docker-compose -v \ No newline at end of file diff --git a/docker/docker-hub.md b/docker/docker-hub.md new file mode 100644 index 0000000..3a7a16d --- /dev/null +++ b/docker/docker-hub.md @@ -0,0 +1,15 @@ +docker run -d -p 5000:5000 --restart=always --name registry2 registry:2 + +docker tag repo:tag localhost:5000/repo:tag + +docker push localhost:5000/repo:tag + +docker pull localhost:5000/repo:tag + +curl http://localhost:5000/v2/_catalog + +curl http://localhost:5000/v2/repo/tags/list + +curl --header "Accept: application/vnd.docker.distribution.manifest.v2+json" -I -X HEAD http://localhost:5000/v2/registry/manifests/2 + +curl -X DELETE localhost:5000/v2/java/mainfests/sha256:b1165286043f2745f45ea637873d61939bff6d9a59f76539d6228abf79f87774 \ No newline at end of file diff --git a/docker/docker-install.md b/docker/docker-install.md new file mode 100644 index 0000000..918155e --- /dev/null +++ b/docker/docker-install.md @@ -0,0 +1,38 @@ +1、Docker 要求 CentOS 系统的内核版本高于 3.10 ,查看本页面的前提条件来验证你的CentOS 版本是否支持 Docker 。 +通过 uname -r 命令查看你当前的内核版本 +$ uname -r + +2、使用 root 权限登录 Centos。确保 yum 包更新到最新。 +$ sudo yum update + +3、卸载旧版本(如果安装过旧版本的话) +$ sudo yum remove docker docker-common docker-selinux docker-engine + +4、安装需要的软件包, yum-util 提供yum-config-manager功能,另外两个是devicemapper驱动依赖的 +$ sudo yum install -y yum-utils device-mapper-persistent-data lvm2 + +5、设置yum源 +$ sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo + + +6、可以查看所有仓库中所有docker版本,并选择特定版本安装 +$ yum list docker-ce --showduplicates | sort -r + + +7、安装docker +$ sudo yum install docker-ce #由于repo中默认只开启stable仓库,故这里安装的是最新稳定版17.12.0 +$ sudo yum install # 例如:sudo yum install docker-ce-17.12.0.ce + + +8、启动并加入开机启动 +$ sudo systemctl start docker +$ sudo systemctl enable docker + +9、验证安装是否成功(有client和service两部分表示docker安装启动都成功了) + +$ docker version + + +开启remote api +vi /usr/lib/systemd/system/docker.service +在 ExecStart=/usr/bin/dockerd-current 后 增加 -H tcp://0.0.0.0:2375 \ No newline at end of file diff --git a/docker/docker-machine.md b/docker/docker-machine.md new file mode 100644 index 0000000..dbd912b --- /dev/null +++ b/docker/docker-machine.md @@ -0,0 +1,18 @@ +## https://www.jianshu.com/p/a40c07f9f456 +yum -y install epel-release +cd /etc/yum.repos.d/ +wget https://download.virtualbox.org/virtualbox/rpm/el/virtualbox.repo +yum search virtualbox +yum -y install VirtualBox-5.2 + +base=https://github.com/docker/machine/releases/download/v0.15.0 && +curl -L $base/docker-machine-$(uname -s)-$(uname -m) >/tmp/docker-machine && +install /tmp/docker-machine /usr/local/bin/docker-machine + +base=https://raw.githubusercontent.com/docker/machine/v0.15.0 +for i in docker-machine-prompt.bash docker-machine-wrapper.bash docker-machine.bash +do + wget "$base/contrib/completion/bash/${i}" -P /etc/bash_completion.d +done + +docker-machine create --driver virtualbox default diff --git a/docker/docker-registry.md b/docker/docker-registry.md new file mode 100644 index 0000000..247d7fa --- /dev/null +++ b/docker/docker-registry.md @@ -0,0 +1,30 @@ +https://blog.csdn.net/qq_35904833/article/details/80807592 +https://www.cnblogs.com/Tempted/p/7768694.html + +#查询镜像 +docker search registry + +#拉取镜像 +docker pull registry + +#启动镜像查库 +docker run -d -p 5000:5000 -v /opt/data/registry:/tmp/registry registry + + +docker tag busybox 192.168.0.153:5000/busybox + +#上传镜像 +docker push 192.168.0.153:5000/busybox + +#查看镜像 +curl -XGET http://registry:5000/v2/_catalog + +#查看镜像标签 +curl -XGET http://registry:5000/v2/image_name/tags/list + +#查看镜像详情 +curl -XGET http://localhost:5000/v2/image_name/manifests/tag + +#删除镜像 +默认是删除是关闭的,须开启 +curl -I -X DELETE http://192.168.0.153:5000/v2/image_name/manifests/sha256:6a67ba482a8dd4f8143ac96b1dcffa5e45af95b8d3e37aeba72401a5afd7ab8e \ No newline at end of file diff --git a/docker/docker-swarm.md b/docker/docker-swarm.md new file mode 100644 index 0000000..a449b7f --- /dev/null +++ b/docker/docker-swarm.md @@ -0,0 +1,44 @@ +## https://blog.csdn.net/wanglei_storage/article/details/77508620 +## https://blog.csdn.net/u011781521/article/details/80468985 + +hostnamectl set-hostname swarm01 + +## swarm集群管理 +docker swarm init --advertise-addr 192.168.71.129 + +查看swarm集群中的节点 +docker node list + +查看节点的任务数 +docker node ps + +查看集群节点的详细信息 +docker node inspect [--pretty] [nodeName] + +docker swarm leave --force + +将manager角色降级为worker +docker node demote hostname + +将worker角色升级为manage +docker node promote hostname + + +docker swarm join-token manager + +docker swarm join-token [-q] worker + +## swarm service manage +docker service ls + +docker service ps redis + +docker network create --driver overlay --subnet 10.0.0.1/24 euraka-network + +docker network ls + +docker stack deploy -c docker-compose.yml hellowork + +docker stack rm hellowork + +docker stack ls \ No newline at end of file diff --git a/docker/jenkins.txt b/docker/jenkins.txt new file mode 100644 index 0000000..74fc536 --- /dev/null +++ b/docker/jenkins.txt @@ -0,0 +1 @@ +docker pull jenkins/jenkins \ No newline at end of file diff --git a/git/git-usage.md b/git/git-usage.md index 15ed9a4..66df5ea 100644 --- a/git/git-usage.md +++ b/git/git-usage.md @@ -10,6 +10,9 @@ git 使用 -C 来指定所指定的注释,可以方便用户标识这个密钥,指出密钥的用途或其他有用的信息。所以在这里输入自己的邮箱或者其他都行 生成id_rsa、id_rsa.pub文件 +### git clone +* git clone http://username:password@remote_git + ### Git branch 一般用于分支的操作,比如创建分支,查看分支等等, @@ -123,4 +126,24 @@ repo start是对git checkout -b这个命令的封装,将所有仓库的分支 * gitk dev 查看push的结果 +### git tag +* git tag -a <版本号> -m "<备注信息>" +新增标签 +* git tag -d <版本号> +删除标签 +* git push origin --tags +推送所有标签 +* git push origin <版本号> +推送指定版本的标签 +* git push origin --delete <版本号> +删除远程仓库的标签 +* git tag +打印所有标签 +* git tag -l 1.\*.\* +打印符合检索条件的标签 + +### git commit +git commit --amend -m "YOUR-NEW-COMMIT-MESSAGE" +amend commit message + --- 未完,待续 \ No newline at end of file diff --git a/git/how-to-use-git-efficiently.md b/git/how-to-use-git-efficiently.md new file mode 100644 index 0000000..38b0734 --- /dev/null +++ b/git/how-to-use-git-efficiently.md @@ -0,0 +1,120 @@ + +# 【译】如何高效的使用 Git + +[原文链接](https://medium.freecodecamp.org/how-to-use-git-efficiently-54320a236369) + +![](https://ws1.sinaimg.cn/large/0069RVTdly1fuz415uvavj318g0tmh0f.jpg) + +> 代码昨天还是运行好好的今天就不行了。 + +> 代码被删了。 + +> 突然出现了一个奇怪的 bug,但是没人知道怎么回事。 + + +如果你出现过上面的任何一种情况,那本篇文章就是为你准备的。 + +除了知道 `git add`, `git commit` , `git push` 之外,Git 中还需要其他重要的技术需要掌握。长远来看对我们是有帮助的。这里我将向你展示 Git 的最佳实践。 + + +# Git 工作流 + +当有多个开发者同时涉及到一个项目时那么就非常有必要正确使用 Git 工作流。 + +这里我将介绍一种工作流,它在一个多人大型项目中将非常有用。 + +![](https://ws1.sinaimg.cn/large/0069RVTdly1fuz4imimuuj313111zq6q.jpg) + + +# 前言 + +突然有一天,你成为了一个项目的技术 Leader 并计划做出下一个 Facebook。在这个项目中你有三个开发人员。 + +1. Alice:一个开发小白。 +2. Bob:拥有一年工作经验,了解基本开发。 +3. John:三年开发经验,熟练开发技能。 +4. 你:该项目的技术负责人。 + +# Git 开发流程 + +## Master 分支 + +1. Master 分支应该始终和生产环境保持一致。 +2. 由于 master 和生产代码是一致的,所以没有人包括技术负责人能在 master 上直接开发。 +3. 真正的开发代码应当写在其他分支上。 + +## Release(发布) 分支 + +1. 当项目开始时,第一件事情就是创建发布分支。发布分支是基于 master 分支创建而来。 +2. 所有与本项目相关的代码都在发布分支中,这个分支也是一个以 `release/` 开头的普通分支。 +3. 比如这次的发布分支名为 `release/fb`。 +4. 可能有多个项目都基于同一份代码运行,因此对于每一个项目来说都需要创建一个独立的发布分支。假设现在还有一个项目正在并行运行,那就得为这个项目创建一个单独的发布分支比如 `release/messenger`。 +5. 需要单独的发布分支的原因是:多个并行项目是基于同一份代码运行的,但是项目之间不能有冲突。 + +## Feature(功能分支) branch + +1. 对于应用中的每一个功能都应该创建一个独立的功能分支,这会确保这些功能能被单独构建。 +2. 功能分支也和其他分支一样,只是以 `feature/` 开头。 +3. 现在作为技术 Leader,你要求 Alice 去做 Facebook 的登录页面。因此他创建了一个新的功能分支。把他命名为 `feature/login`。Alice 将会在这个分支上编写所有的登录代码。 +4. 这个功能分支通常是基于 Release(发布) 分支 创建而来。 +5. Bob 的任务为创建添加好友页面,因此他创建了一个名为 `feature/friendrequest` 的功能分支。 +6. John 则被安排构建消息流,因此创建了一个 `feature/newsfeed` 的功能分支。 +7. 所有的开发人员都在自己的分支上进行开发,目前为止都很正常。 +8. 现在当 Alice 完成了他的登录开发,他需要将他的功能分支 `feature/login` 发送给 Release(发布) 分支。这个过程是通过发起一个 `pull request` 完成的。 + + +## Pull request + +首先 `pull request` 不能和 `git pull` 搞混了。 + +开发人员不能直接向 Release(发布) 分支推送代码,技术 Leader 需要在功能分支合并到 Release(发布) 分支之前做好代码审查。这也是通过 `pull request` 完成的。 + +Alice 能够按照如下 GitHub 方式提交 `pull request`。 + +![](https://ws1.sinaimg.cn/large/0069RVTdgy1fv03386jcoj30ig05swet.jpg) + +在分支名字的旁边有一个 “New pull request” 按钮,点击之后将会显示如下界面: + +![](https://ws4.sinaimg.cn/large/0069RVTdgy1fv03etb1afj30no078gmn.jpg) + +- 比较分支是 Alice 的功能分支 `feature/login`。 +- base 分支则应该是发布分支 `release/fb`。 + +点击之后 Alice 需要为这个 `pull request` 输入名称和描述,最后再点击 “Create Pull Request” 按钮。 + +同时 Alice 需要为这个 `pull request` 指定一个 reviewer。作为技术 Leader 的你被选为本次 `pull request` 的 reviewer。 + +你完成代码审查之后就需要把这个功能分支合并到 Release(发布) 分支。 + +现在你已经把 `feature/login` 分支合并到 `release/fb`,并且 Alice 非常高兴他的代码被合并了。 + +## 代码冲突 😠 + +1. Bob 完成了他的编码工作,同时向 `release/fb` 分支发起了一个 `pull request`。 +2. 因为发布分支已经合并了登录的代码,这时代码冲突发生了。解决冲突和合并代码是 reviewer 的责任。在这样的情况下,作为技术 Leader 就需要解决冲突和合并代码了。 +3. 现在 John 也已经完成了他的开发,同时也想把代码合并到发布分支。但 John 非常擅长于解决代码冲突。他将 `release/fb` 上最新的代码合并到他自己的功能分支 `feature/newsfeed` (通过 git pull 或 git merge 命令)。同时他解决了所有存在的冲突,现在 `feature/newsfeed` 已经有了所有发布分支 `release/fb` 的代码。 +4. 最后 John 创建了一个 `pull request`,由于 John 已经解决了所有问题,所以本次 `pull request` 不会再有冲突了。 + +因此通常有两种方式来解决代码冲突: + +- `pull request` 的 reviewer 需要解决所有的代码冲突。 +- 开发人员需要确保将发布分支的最新代码合并到功能分支,并且解决所有的冲突。 + + +# 还是 Master 分支 + + +一旦项目完成,发布分支的代码需要合并回 master 分支,同时需要发布到生产环境。 + +因此生产环境中的代码总是和 master 分支保持一致。同时对于今后的任何项目来说都是要确保 master 代码是最新的。 + + + + + +> 我们现在团队就是按照这样的方式进行开发,确实可以尽可能的减少代码管理上的问题。 + + + + +**你的点赞与转发是最大的支持。** \ No newline at end of file diff --git a/hadoop/hadoop.md b/hadoop/hadoop.md new file mode 100644 index 0000000..180a709 --- /dev/null +++ b/hadoop/hadoop.md @@ -0,0 +1,74 @@ +name:root - winjean +name:winjean - winjean + +vsftpd home:/etc/vsftpd +vsftpd user + ftpuser1 - ftpuser1 ???/var/ftp/user1 + ftpuser2 - ftpuser2 ???/var/ftp/user2 + + rpm -ivh jdk-6u11-linux-i586-rpm + + +???????java +rpm -qa |grep jdk +rpm -e --nodeps jdk-1.7.0_79-fcs.x86_64 + +???java +rpm -ivh jdk-7u79-linux-x64.rpm + +????java???? +vi /etc/profile +export JAVA_HOME=/usr/java/jdk1.7.0_79 +export PATH=$JAVA_HOME/bin:$PATH +export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar + +?????????? +~/hadoop/etc/hadoop/hadoop-env.sh +~/hadoop/etc/hadoop/yarn-env.sh +~/hadoop/etc/hadoop/slaves +~/hadoop/etc/hadoop/core-site.xml +~/hadoop/etc/hadoop/hdfs-site.xml +~/hadoop/etc/hadoop/mapred-site.xml +~/hadoop/etc/hadoop/yarn-site.xml + +????hdfs +./sbin/start-dfs.sh + +????yarn +./sbin/start-yarn.sh + +????datanode +./sbin/hadoop-daemon.sh start datanode + +????????? + ./bin/hdfs dfsadmin -report + +??hadoop +http://192.168.186.128:50070/ + +http://192.168.186.128:8088/ + +????wordcount???? +1?????? input????[spark@S1PA11 hadoop-2.6.0]$ mkdir input +2????input????f1??f2??????? + cat input/f1 +Hello world bye jj +cat input/f2 +Hello Hadoop bye Hadoop +3????hdfs????/tmp/input?? + ./bin/hadoop fs -mkdir /tmp + ./bin/hadoop fs -mkdir /tmp/input +4????f1??f2???copy??hdfs /tmp/input?? + ./bin/hadoop fs -put input/ /tmp +5????hdfs???????f1??f2??? + ./bin/hadoop fs -ls /tmp/input/ +Found 2 items +-rw-r--r-- 3 spark supergroup 20 2015-01-04 19:09 /tmp/input/f1 +-rw-r--r-- 3 spark supergroup 25 2015-01-04 19:09 /tmp/input/f2 +6?????wordcount???? + ./bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.6.1.jar wordcount /tmp/input /output +7???????wordcount??? + ./bin/hdfs dfs -cat /output/* + + eclipse Map/Reduce Location + MR Master??DFS Master????????mapred-site.xml??core-site.xml???????????? \ No newline at end of file diff --git a/java/classloader/ClassLoad.md b/java/classloader/ClassLoad.md new file mode 100644 index 0000000..e2e7969 --- /dev/null +++ b/java/classloader/ClassLoad.md @@ -0,0 +1,17 @@ +# 类加载机制 + +## 双亲委派模型 + +模型如下图: + +![](https://ws3.sinaimg.cn/large/006tNc79ly1fmjwua3iv4j30ic0f0mxq.jpg) + +双亲委派模型中除了启动类加载器之外其余都需要有自己的父类加载器 + +当一个类收到了类加载请求时: 自己不会首先加载,而是委派给父加载器进行加载,每个层次的加载器都是这样。 + +所以最终每个加载请求都会经过启动类加载器。只有当父类加载返回不能加载时子加载器才会进行加载。 + +双亲委派的好处 : 由于每个类加载都会经过最顶层的启动类加载器,比如 `java.lang.Object`这样的类在各个类加载器下都是同一个类(只有当两个类是由同一个类加载器加载的才有意义,这两个类才相等。) + +如果没有双亲委派模型,由各个类加载器自行加载的话。当用户自己编写了一个 `java.lang.Object`类,那样系统中就会出现多个 `Object`,这样 Java 程序中最基本的行为都无法保证,程序会变的非常混乱。 \ No newline at end of file diff --git a/java/collection/ArrayList.md b/java/collection/ArrayList.md new file mode 100644 index 0000000..11bb2ae --- /dev/null +++ b/java/collection/ArrayList.md @@ -0,0 +1,151 @@ +# ArrayList/Vector 的底层分析 + +## ArrayList + +`ArrayList` 实现于 `List`、`RandomAccess` 接口。可以插入空数据,也支持随机访问。 + +`ArrayList `相当于动态数据,其中最重要的两个属性分别是: +`elementData` 数组,以及 `size` 大小。 +在调用 `add()` 方法的时候: +```java + public boolean add(E e) { + ensureCapacityInternal(size + 1); // Increments modCount!! + elementData[size++] = e; + return true; + } +``` + +- 首先进行扩容校验。 +- 将插入的值放到尾部,并将 size + 1 。 + +如果是调用 `add(index,e)` 在指定位置添加的话: +```java + public void add(int index, E element) { + rangeCheckForAdd(index); + + ensureCapacityInternal(size + 1); // Increments modCount!! + //复制,向后移动 + System.arraycopy(elementData, index, elementData, index + 1, + size - index); + elementData[index] = element; + size++; + } +``` + + +- 也是首先扩容校验。 +- 接着对数据进行复制,目的是把 index 位置空出来放本次插入的数据,并将后面的数据向后移动一个位置。 + +其实扩容最终调用的代码: +```java + private void grow(int minCapacity) { + // overflow-conscious code + int oldCapacity = elementData.length; + int newCapacity = oldCapacity + (oldCapacity >> 1); + if (newCapacity - minCapacity < 0) + newCapacity = minCapacity; + if (newCapacity - MAX_ARRAY_SIZE > 0) + newCapacity = hugeCapacity(minCapacity); + // minCapacity is usually close to size, so this is a win: + elementData = Arrays.copyOf(elementData, newCapacity); + } +``` + +也是一个数组复制的过程。 + +由此可见 `ArrayList` 的主要消耗是数组扩容以及在指定位置添加数据,在日常使用时最好是指定大小,尽量减少扩容。更要减少在指定位置插入数据的操作。 + +### 序列化 + +由于 ArrayList 是基于动态数组实现的,所以并不是所有的空间都被使用。因此使用了 `transient` 修饰,可以防止被自动序列化。 + +```java +transient Object[] elementData; +``` + +因此 ArrayList 自定义了序列化与反序列化: + +```java + private void writeObject(java.io.ObjectOutputStream s) + throws java.io.IOException{ + // Write out element count, and any hidden stuff + int expectedModCount = modCount; + s.defaultWriteObject(); + + // Write out size as capacity for behavioural compatibility with clone() + s.writeInt(size); + + // Write out all elements in the proper order. + //只序列化了被使用的数据 + for (int i=0; i