├── docs
├── 数据库
│ ├── MySQL的锁.md
│ ├── Redis
│ │ ├── Redlock分布式锁.md
│ │ ├── 如何做可靠的分布式锁,Redlock真的可行么.md
│ │ └── Redis持久化.md
│ ├── 面试必备之乐观锁与悲观锁.md
│ ├── 事务隔离级别(图文详解).md
│ ├── 一条sql语句在mysql中如何执行的.md
│ ├── MySQL事务及其ACID特性.md
│ └── MySQL索引.md
├── img
│ ├── 640.webp
│ ├── 642.webp
│ ├── 643.webp
│ ├── 644.webp
│ ├── 645.webp
│ ├── 646.webp
│ ├── 650.webp
│ ├── 651.webp
│ ├── 652.webp
│ ├── 653.webp
│ ├── 654.webp
│ ├── 655.webp
│ ├── 656.webp
│ ├── 657.webp
│ ├── 658.webp
│ ├── 659.webp
│ ├── 660.webp
│ ├── 661.webp
│ ├── 662.webp
│ ├── 663.webp
│ ├── 664.webp
│ ├── 665.webp
│ ├── 666.webp
│ ├── 667.webp
│ ├── 668.webp
│ ├── 669.webp
│ ├── 670.webp
│ ├── 671.webp
│ ├── 672.webp
│ ├── 700.webp
│ ├── 701.webp
│ ├── 702.png
│ ├── 705.jpg
│ ├── cs-2.png
│ ├── cs-bit.png
│ ├── cs-dp.png
│ ├── cs-gdyx.png
│ ├── cs-hash.png
│ ├── cs-heap.png
│ ├── cs-hs.png
│ ├── cs-link.png
│ ├── cs-sdyx.png
│ ├── cs-sj.png
│ ├── cs-str.png
│ ├── cs-sx.png
│ ├── cs-tx.png
│ ├── img_1.jpg
│ ├── img_10.png
│ ├── img_11.png
│ ├── img_12.png
│ ├── img_13.png
│ ├── img_14.jpg
│ ├── img_14.png
│ ├── img_15.jpg
│ ├── img_15.png
│ ├── img_16.jpg
│ ├── img_17.jpg
│ ├── img_18.jpg
│ ├── img_2.jpg
│ ├── img_3.png
│ ├── img_4.jpg
│ ├── img_5.png
│ ├── img_6.jpg
│ ├── img_7.jpg
│ ├── img_8.gif
│ ├── img_9.png
│ ├── tcp+udp.jpg
│ ├── 665webp.webp
│ ├── cs-array.png
│ ├── cs-2search.png
│ ├── 16b44d429a3051ac.webp
│ ├── 16b44d429a580edc.webp
│ ├── 16b44d429c540956.webp
│ ├── 16b44d429d15079d.webp
│ ├── 16b44d429dc9de16.webp
│ ├── 16b44d429e563f79.webp
│ ├── 16b44d431d021bc8.webp
│ ├── 16b44d435181da78.webp
│ ├── 16b44d4362c33e1b.webp
│ ├── 16b44d436380e5e7.webp
│ ├── 16b44d4367801b29.webp
│ ├── 16b44d436b021664.webp
│ ├── 16b44d4392909e0f.webp
│ ├── 16b44d43957d7cd9.webp
│ ├── 16b44d43967a3ee7.webp
│ ├── 16b44d43af436644.webp
│ ├── 16b44d43afc607f8.webp
│ ├── 16b44d43b87632d3.webp
│ ├── 16b44d43cbc31b5a.webp
│ ├── 16b44d43cc3e7792.webp
│ ├── 16b44d43d3d4dc49.webp
│ ├── 1454031-20190410222131829-793976539.png
│ ├── 1454031-20190410222744803-621774735.png
│ ├── 1454031-20190416205427730-1477237969.png
│ ├── 1454031-20190416213935585-1678554014.png
│ ├── 1454031-20190421223327003-2058879216.png
│ ├── 1454031-20190421224640224-1042940039.png
│ └── 1454031-20190421224720018-809378807.png
├── 简历与面经
│ ├── BATJrealInterviewExperience
│ │ ├── 深信服Python开发2018-8.md
│ │ ├── 字节跳动python后台开发面经2018-12.md
│ │ ├── 5面阿里,终获offer.md
│ │ └── 蚂蚁金服实习生面经总结(已拿口头offer).md
│ ├── 简历模板.md
│ ├── PreparingForInterview
│ │ ├── 如果面试官问你“你有什么问题问我吗?”时,你该如何回答.md
│ │ ├── interviewPrepare.md
│ │ ├── JavaInterviewLibrary.md
│ │ ├── JavaProgrammerNeedKnow.md
│ │ └── 程序员的简历之道.md
│ └── 手把手教你用Markdown写一份高质量的简历.md
├── 数据结构与算法
│ ├── LeetCode高频率题目.md
│ ├── 算法学习资源推荐.md
│ ├── 面试官问你什么B树和B+树,把这篇文章丢给他.md
│ ├── 动态规划.md
│ ├── 数据结构.md
│ ├── Backtracking-NQueens.md
│ └── 公司真题.md
├── 系统设计
│ ├── 设计模式简述.md
│ ├── website-architecture
│ │ ├── 8 张图读懂大型网站技术架构.md
│ │ └── 分布式.md
│ ├── data-communication
│ │ ├── summary.md
│ │ ├── message-queue.md
│ │ ├── RocketMQ-Questions.md
│ │ └── dubbo.md
│ ├── 设计模式.md
│ ├── ZooKeeper
│ │ └── ZooKeeper数据模型和常见命令.md
│ └── authority-certification
│ │ └── basis-of-authority-certification.md
├── Python
│ ├── Python实现常见排序算法.py
│ ├── Flask请求流程.md
│ └── Python进程通信.md
├── 计算机网络
│ └── HTTPS中的TLS.md
└── tools
│ ├── Git.md
│ └── Docker.md
├── .idea
└── vcs.xml
├── README.md
└── test.py
/docs/数据库/MySQL的锁.md:
--------------------------------------------------------------------------------
1 | https://blog.csdn.net/weixin_42358062/article/details/80731501
2 |
--------------------------------------------------------------------------------
/docs/img/640.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/640.webp
--------------------------------------------------------------------------------
/docs/img/642.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/642.webp
--------------------------------------------------------------------------------
/docs/img/643.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/643.webp
--------------------------------------------------------------------------------
/docs/img/644.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/644.webp
--------------------------------------------------------------------------------
/docs/img/645.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/645.webp
--------------------------------------------------------------------------------
/docs/img/646.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/646.webp
--------------------------------------------------------------------------------
/docs/img/650.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/650.webp
--------------------------------------------------------------------------------
/docs/img/651.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/651.webp
--------------------------------------------------------------------------------
/docs/img/652.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/652.webp
--------------------------------------------------------------------------------
/docs/img/653.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/653.webp
--------------------------------------------------------------------------------
/docs/img/654.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/654.webp
--------------------------------------------------------------------------------
/docs/img/655.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/655.webp
--------------------------------------------------------------------------------
/docs/img/656.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/656.webp
--------------------------------------------------------------------------------
/docs/img/657.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/657.webp
--------------------------------------------------------------------------------
/docs/img/658.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/658.webp
--------------------------------------------------------------------------------
/docs/img/659.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/659.webp
--------------------------------------------------------------------------------
/docs/img/660.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/660.webp
--------------------------------------------------------------------------------
/docs/img/661.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/661.webp
--------------------------------------------------------------------------------
/docs/img/662.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/662.webp
--------------------------------------------------------------------------------
/docs/img/663.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/663.webp
--------------------------------------------------------------------------------
/docs/img/664.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/664.webp
--------------------------------------------------------------------------------
/docs/img/665.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/665.webp
--------------------------------------------------------------------------------
/docs/img/666.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/666.webp
--------------------------------------------------------------------------------
/docs/img/667.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/667.webp
--------------------------------------------------------------------------------
/docs/img/668.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/668.webp
--------------------------------------------------------------------------------
/docs/img/669.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/669.webp
--------------------------------------------------------------------------------
/docs/img/670.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/670.webp
--------------------------------------------------------------------------------
/docs/img/671.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/671.webp
--------------------------------------------------------------------------------
/docs/img/672.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/672.webp
--------------------------------------------------------------------------------
/docs/img/700.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/700.webp
--------------------------------------------------------------------------------
/docs/img/701.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/701.webp
--------------------------------------------------------------------------------
/docs/img/702.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/702.png
--------------------------------------------------------------------------------
/docs/img/705.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/705.jpg
--------------------------------------------------------------------------------
/docs/img/cs-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/cs-2.png
--------------------------------------------------------------------------------
/docs/img/cs-bit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/cs-bit.png
--------------------------------------------------------------------------------
/docs/img/cs-dp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/cs-dp.png
--------------------------------------------------------------------------------
/docs/img/cs-gdyx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/cs-gdyx.png
--------------------------------------------------------------------------------
/docs/img/cs-hash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/cs-hash.png
--------------------------------------------------------------------------------
/docs/img/cs-heap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/cs-heap.png
--------------------------------------------------------------------------------
/docs/img/cs-hs.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/cs-hs.png
--------------------------------------------------------------------------------
/docs/img/cs-link.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/cs-link.png
--------------------------------------------------------------------------------
/docs/img/cs-sdyx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/cs-sdyx.png
--------------------------------------------------------------------------------
/docs/img/cs-sj.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/cs-sj.png
--------------------------------------------------------------------------------
/docs/img/cs-str.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/cs-str.png
--------------------------------------------------------------------------------
/docs/img/cs-sx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/cs-sx.png
--------------------------------------------------------------------------------
/docs/img/cs-tx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/cs-tx.png
--------------------------------------------------------------------------------
/docs/img/img_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_1.jpg
--------------------------------------------------------------------------------
/docs/img/img_10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_10.png
--------------------------------------------------------------------------------
/docs/img/img_11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_11.png
--------------------------------------------------------------------------------
/docs/img/img_12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_12.png
--------------------------------------------------------------------------------
/docs/img/img_13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_13.png
--------------------------------------------------------------------------------
/docs/img/img_14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_14.jpg
--------------------------------------------------------------------------------
/docs/img/img_14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_14.png
--------------------------------------------------------------------------------
/docs/img/img_15.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_15.jpg
--------------------------------------------------------------------------------
/docs/img/img_15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_15.png
--------------------------------------------------------------------------------
/docs/img/img_16.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_16.jpg
--------------------------------------------------------------------------------
/docs/img/img_17.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_17.jpg
--------------------------------------------------------------------------------
/docs/img/img_18.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_18.jpg
--------------------------------------------------------------------------------
/docs/img/img_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_2.jpg
--------------------------------------------------------------------------------
/docs/img/img_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_3.png
--------------------------------------------------------------------------------
/docs/img/img_4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_4.jpg
--------------------------------------------------------------------------------
/docs/img/img_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_5.png
--------------------------------------------------------------------------------
/docs/img/img_6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_6.jpg
--------------------------------------------------------------------------------
/docs/img/img_7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_7.jpg
--------------------------------------------------------------------------------
/docs/img/img_8.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_8.gif
--------------------------------------------------------------------------------
/docs/img/img_9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/img_9.png
--------------------------------------------------------------------------------
/docs/img/tcp+udp.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/tcp+udp.jpg
--------------------------------------------------------------------------------
/docs/img/665webp.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/665webp.webp
--------------------------------------------------------------------------------
/docs/img/cs-array.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/cs-array.png
--------------------------------------------------------------------------------
/docs/img/cs-2search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/cs-2search.png
--------------------------------------------------------------------------------
/docs/img/16b44d429a3051ac.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d429a3051ac.webp
--------------------------------------------------------------------------------
/docs/img/16b44d429a580edc.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d429a580edc.webp
--------------------------------------------------------------------------------
/docs/img/16b44d429c540956.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d429c540956.webp
--------------------------------------------------------------------------------
/docs/img/16b44d429d15079d.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d429d15079d.webp
--------------------------------------------------------------------------------
/docs/img/16b44d429dc9de16.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d429dc9de16.webp
--------------------------------------------------------------------------------
/docs/img/16b44d429e563f79.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d429e563f79.webp
--------------------------------------------------------------------------------
/docs/img/16b44d431d021bc8.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d431d021bc8.webp
--------------------------------------------------------------------------------
/docs/img/16b44d435181da78.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d435181da78.webp
--------------------------------------------------------------------------------
/docs/img/16b44d4362c33e1b.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d4362c33e1b.webp
--------------------------------------------------------------------------------
/docs/img/16b44d436380e5e7.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d436380e5e7.webp
--------------------------------------------------------------------------------
/docs/img/16b44d4367801b29.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d4367801b29.webp
--------------------------------------------------------------------------------
/docs/img/16b44d436b021664.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d436b021664.webp
--------------------------------------------------------------------------------
/docs/img/16b44d4392909e0f.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d4392909e0f.webp
--------------------------------------------------------------------------------
/docs/img/16b44d43957d7cd9.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d43957d7cd9.webp
--------------------------------------------------------------------------------
/docs/img/16b44d43967a3ee7.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d43967a3ee7.webp
--------------------------------------------------------------------------------
/docs/img/16b44d43af436644.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d43af436644.webp
--------------------------------------------------------------------------------
/docs/img/16b44d43afc607f8.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d43afc607f8.webp
--------------------------------------------------------------------------------
/docs/img/16b44d43b87632d3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d43b87632d3.webp
--------------------------------------------------------------------------------
/docs/img/16b44d43cbc31b5a.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d43cbc31b5a.webp
--------------------------------------------------------------------------------
/docs/img/16b44d43cc3e7792.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d43cc3e7792.webp
--------------------------------------------------------------------------------
/docs/img/16b44d43d3d4dc49.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/16b44d43d3d4dc49.webp
--------------------------------------------------------------------------------
/docs/img/1454031-20190410222131829-793976539.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/1454031-20190410222131829-793976539.png
--------------------------------------------------------------------------------
/docs/img/1454031-20190410222744803-621774735.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/1454031-20190410222744803-621774735.png
--------------------------------------------------------------------------------
/docs/img/1454031-20190416205427730-1477237969.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/1454031-20190416205427730-1477237969.png
--------------------------------------------------------------------------------
/docs/img/1454031-20190416213935585-1678554014.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/1454031-20190416213935585-1678554014.png
--------------------------------------------------------------------------------
/docs/img/1454031-20190421223327003-2058879216.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/1454031-20190421223327003-2058879216.png
--------------------------------------------------------------------------------
/docs/img/1454031-20190421224640224-1042940039.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/1454031-20190421224640224-1042940039.png
--------------------------------------------------------------------------------
/docs/img/1454031-20190421224720018-809378807.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ChangAn223/Python-Interview/HEAD/docs/img/1454031-20190421224720018-809378807.png
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/docs/简历与面经/BATJrealInterviewExperience/深信服Python开发2018-8.md:
--------------------------------------------------------------------------------
1 | 【一面】
2 |
3 | 1、进程、线程、协程的定义及区别。
4 |
5 | 2、装饰器
6 |
7 | 3、Python进程通信
8 |
9 | 4、Django登陆流程
10 |
11 | 5、了解的数据结构有哪些?
12 |
13 | 6、哈希?
14 |
15 | 7、从文件查找除出现次数最多的前十的个字符。
--------------------------------------------------------------------------------
/docs/数据结构与算法/LeetCode高频率题目.md:
--------------------------------------------------------------------------------
1 |
2 | 来自:https://www.nowcoder.com/discuss/292850
3 |
4 |
5 | ### 动态规划
6 | 
7 |
8 | ### 堆
9 | 
10 |
11 | ### 二叉树
12 | 
13 |
14 | ### 二分查找
15 | 
16 |
17 | ### 广度优先搜索
18 | 
19 |
20 | ### 哈希表
21 | 
22 |
23 | ### 回溯算法
24 | 
25 |
26 | ### 链表
27 | 
28 |
29 | ### 设计
30 | 
31 |
32 | ### 深度优先搜索
33 | 
34 |
35 | ### 数学
36 | 
37 |
38 |
39 | ### 数组
40 | 
41 |
42 | ### 贪心算法
43 | 
44 |
45 | ### 位运算
46 | 
47 |
48 | ### 字符串
49 | 
50 |
51 |
--------------------------------------------------------------------------------
/docs/系统设计/设计模式简述.md:
--------------------------------------------------------------------------------
1 | ## 设计模式
2 |
3 | 三大设计模式:创建型模式、结构型模式、行为模式
4 |
5 | ### 1.创建型模式
6 |
7 | #### 1.1单例模式
8 | 一个类只能创建一个实例,多次创建则返回同一个对象。Python中的import导入就是单例的,因为只会导入一次。
9 |
10 | #### 1.2工厂模式
11 | 解决对象创建问题,
12 |
13 | #### 1.3构造模式
14 | 控制复杂对象的创建
15 |
16 | **工厂模式与构造模式的区别:**
17 | 工厂模式时直接返回一个对象,构造模式允许定义一些复杂的属性之后再返回这个对象。
18 |
19 | #### 1.4原型模式
20 | 通过原型的克隆创建新的对象,类似Linux中fork创建进程。
21 | 可以使用相同的原型,通过修改部分属性来创建实例,适用于于一些创建实例开销大的地方
22 |
23 | #### 1.5对象池模式
24 | 预先分配同一类型的一组实例
25 |
26 | #### 1.6惰性计算模式
27 | 延迟计算(Python的property),直到访问该属性时才做相应操作。
28 |
29 | ### 2.结构型设计模式
30 |
31 | #### 2.1装饰器模式
32 | 不必通过子类化来扩展功能,Python中的装饰器就是装饰器模式。
33 |
34 | #### 2.2代理模式
35 | 把一个对象的操作代理到另一个对象中。通过代理对象间接去操作该对象。(作用:代码分层,解决一些安全问题如不暴露原接口)
36 |
37 | #### 2.3适配器模式
38 | 通过一个间接层适配统一的接口,把不同的对象的接口适配到同一个接口上。
39 |
40 | #### 2.4外观模式
41 | 简化复杂对象的访问问题
42 |
43 | #### 2.5享元模式
44 | 通过对象复用改善资源利用(如对象池,数据库连接池)
45 |
46 | #### 2.6MVC模式
47 | 解耦展示逻辑和业务逻辑,Model-View-Controller,基本Web框架都是这种模式。
48 |
49 | ### 3.行为型模式
50 |
51 | #### 3.1迭代器模式
52 | 通过统一的接口迭代对象。(如Python中的for循环迭代遍历可迭代对象)
53 |
54 | #### 3.2观察者模式
55 | 对象发生改变时,观察者执行相应的动作。(最常用的实现方式:发布/订阅方式。)也可通过回调等方式实现,事件发生则调用相应的回调函数。
56 |
57 | #### 3.3策略模式
58 | 针对不同的输入采用不同的策略。对外采用统一的接口,对内根据输入采用不同的执行策略。
--------------------------------------------------------------------------------
/docs/Python/Python实现常见排序算法.py:
--------------------------------------------------------------------------------
1 | def bubblesort(list):
2 | length = len(list)
3 | for i in range(1, length):
4 | flag = True
5 | for j in range(length - i):
6 | if list[j] > list[j + 1]:
7 | list[j], list[j + 1] = list[j + 1], list[j]
8 | flag = False
9 | if flag:
10 | return list
11 | return list
12 |
13 |
14 | def selectionsort(list):
15 | length = len(list)
16 | for i in range(length):
17 | minindex = i
18 | for j in range(i + 1, length):
19 | if list[j] < list[minindex]:
20 | minindex = j
21 | if i != minindex:
22 | list[i], list[minindex] = list[minindex], list[i]
23 | return list
24 |
25 |
26 | def insertsort(list):
27 | length = len(list)
28 | for i in range(1, length):
29 | for j in range(i, 0, -1):
30 | if list[j] < list[j - 1]:
31 | list[j], list[j - 1] = list[j - 1], list[j]
32 | return list
33 |
34 |
35 | def quicksort(list, start, end):
36 | if start < end:
37 | mid = start
38 | left, right = start, end
39 | while left < right:
40 | while list[left] < list[mid] and left < right:
41 | left += 1
42 | while list[right] >= list[mid] and left < right:
43 | right -= 1
44 | if left < right:
45 | list[left], list[right] = list[right], list[left]
46 | list[mid], list[left] = list[left], list[mid]
47 | quicksort(list, start, mid - 1)
48 | quicksort(list, mid + 1, end)
49 |
50 |
51 | def shellsort(list):
52 | pass
--------------------------------------------------------------------------------
/docs/简历与面经/BATJrealInterviewExperience/字节跳动python后台开发面经2018-12.md:
--------------------------------------------------------------------------------
1 | 抖音/火山 后台开发三面
2 | 作者:甘梅地瓜
3 | 链接:https://www.nowcoder.com/discuss/148961
4 | 来源:牛客网
5 |
6 | 【一面】
7 |
8 | (一面小哥蛮年轻的,看起来不太爱说话,不过人特温柔,问的问题也都很友好)
9 | 0、自我介绍 (之前有过实习经历,主要写python和go balabala
10 | 1、get post的区别 put delete 知道吗 put和post (安全 幂等 长度 状态balabala)
11 | 2、innodb为什么用b+树 ?多路树的好处?(说了下比较好控制高度 查询稳定 又说了一下对比b树的优势balabala)
12 | 为什么控制高度?(连续读磁盘 效率高)
13 | 详细描述b+?(以innodb索引为例给他画了下)
14 | 3、tcp为什么三次连接 二次 四次?
15 | 4、innodb 数据隔离级别 (四个隔离级别说了下 顺带把脏读 幻读 不可重复度说了下)
16 | 5、设计题 一个高并发定时执行任务:实现一个方法 接受一个任务以及它要开始执行的时间,定时执行这些任务,会有很多任务(设计了个数据结构用空间换时间,后来又问有没有别的方法,我当时脑袋一抽说要不就开goroutine,不过任务太多就爆了。后来回去想想 可以用优先队列)
17 | 6、redis (问了些基础
18 | 7、输入一个url 整个过程 返回结果和渲染是同时的吗 (我从七层分别说了下 又问我返回结果和渲染是同时的吗 ,我说不是 然后扯到http2.0服务端推送上
19 | 8、http 1.X 2.0区别 ( 帧 流 推送 头部压缩 安全性等等 ,答得有些乱
20 | 9、链表 奇位上升偶位下降 整合成升序链表 (非常友好的题了
21 | 10、gaplock mvcc mvcc 用处 (mvcc 结合之前的实习经验说了一下 ,gaplock听过 不过完全没了解)
22 | 一面结束 觉得自己说的还行 等二面的时候突然开始紧张
23 |
24 | 【二面】
25 |
26 | 1、LRU实现、插入操作、 描述数据结构如何变化 (说双向链表加哈希,在双向链表上做lru,加哈希表是为了快速定位要移动的节点)
27 | 2、实现哈希表 冲突过多的时候如何解决 (扩容
28 | 3、redis zset 数据结构描述 (之前看过黄建宏的redis设计与实现原理(强推,还有redis实战),答得还比较轻松,zset是一个字典加跳表 又详细描述(画)了下跳表
29 | 4、大数加法 链表 (也很友好了
30 | 5、zset 除了用跳表 还可以用什么实现 (想了想 可以红黑树,不过说完我就赶紧说 可是红黑树我不会实现,提前认怂,面试官就笑了,他说不用实现别怕)
31 | 如何用红黑树如何实现zrange by score (我第一反应是加个双向链表,但是没有想好到底怎么和红黑树连起来 。后来面试官提示,可以在红黑树里加索引)
32 | 二面面试官刚开始感觉很严肃 其实人很nice 。没让我问问题,直接让我等下一个面试官
33 |
34 | 【三面】
35 |
36 | 1、Tcp: 拔网线之后连接是否存在 为什么 (记得tcp的长连接是有一个类似心跳检测的机制,忘了叫啥了,面试官问我心跳检测是在传输层吗还是应用层 ,我说应用层有心跳检测,但tcp那层也有类似的,后来回来看了下tcp的保活,跟我当时说的差不多,就是名词没想起来)
37 | 2、联合索引:b+树是什么状态 (画了一下,面试官反复问我高度什么的,我说高度没有影响,说了最左前缀 )
38 | 3、寻找中位数 ( 堆
39 | 4、一棵树 寻找节点中最长路径 (动规
40 | 5、sql语句
41 | 6、 操作系统如何识别tcp连接
--------------------------------------------------------------------------------
/docs/数据结构与算法/算法学习资源推荐.md:
--------------------------------------------------------------------------------
1 | 我比较推荐大家可以刷一下 Leetcode ,我自己平时没事也会刷一下,我觉得刷 Leetcode 不仅是为了能让你更从容地面对面试中的手撕算法问题,更可以提高你的编程思维能力、解决问题的能力以及你对某门编程语言 API 的熟练度。当然牛客网也有一些算法题,我下面也整理了一些。
2 |
3 | ## LeetCode
4 |
5 | - [LeetCode(中国)官网](https://leetcode-cn.com/)
6 |
7 | - [如何高效地使用 LeetCode](https://leetcode-cn.com/articles/%E5%A6%82%E4%BD%95%E9%AB%98%E6%95%88%E5%9C%B0%E4%BD%BF%E7%94%A8-leetcode/)
8 |
9 |
10 | ## 牛客网
11 |
12 | - [牛客网官网](https://www.nowcoder.com)
13 | - [剑指offer编程题](https://www.nowcoder.com/ta/coding-interviews)
14 |
15 | - [2017校招真题](https://www.nowcoder.com/ta/2017test)
16 | - [华为机试题](https://www.nowcoder.com/ta/huawei)
17 |
18 |
19 | ## 公司真题
20 |
21 | - [ 网易2018校园招聘编程题真题集合](https://www.nowcoder.com/test/6910869/summary)
22 | - [ 网易2018校招内推编程题集合](https://www.nowcoder.com/test/6291726/summary)
23 | - [2017年校招全国统一模拟笔试(第五场)编程题集合](https://www.nowcoder.com/test/5986669/summary)
24 | - [2017年校招全国统一模拟笔试(第四场)编程题集合](https://www.nowcoder.com/test/5507925/summary)
25 | - [2017年校招全国统一模拟笔试(第三场)编程题集合](https://www.nowcoder.com/test/5217106/summary)
26 | - [2017年校招全国统一模拟笔试(第二场)编程题集合](https://www.nowcoder.com/test/4546329/summary)
27 | - [ 2017年校招全国统一模拟笔试(第一场)编程题集合](https://www.nowcoder.com/test/4236887/summary)
28 | - [百度2017春招笔试真题编程题集合](https://www.nowcoder.com/test/4998655/summary)
29 | - [网易2017春招笔试真题编程题集合](https://www.nowcoder.com/test/4575457/summary)
30 | - [网易2017秋招编程题集合](https://www.nowcoder.com/test/2811407/summary)
31 | - [网易有道2017内推编程题](https://www.nowcoder.com/test/2385858/summary)
32 | - [ 滴滴出行2017秋招笔试真题-编程题汇总](https://www.nowcoder.com/test/3701760/summary)
33 | - [腾讯2017暑期实习生编程题](https://www.nowcoder.com/test/1725829/summary)
34 | - [今日头条2017客户端工程师实习生笔试题](https://www.nowcoder.com/test/1649301/summary)
35 | - [今日头条2017后端工程师实习生笔试题](https://www.nowcoder.com/test/1649268/summary)
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/docs/简历与面经/简历模板.md:
--------------------------------------------------------------------------------
1 | # 联系方式
2 |
3 | - 手机:
4 | - Email:
5 | - 微信:
6 |
7 | # 个人信息
8 |
9 | - 姓名/性别/出生日期
10 | - 本科/xxx计算机系xxx专业/英语六级
11 | - 技术博客:[http://snailclimb.top/](http://snailclimb.top/)
12 | - 荣誉奖励:获得了什么奖(获奖时间)
13 | - Github:[https://github.com/Snailclimb ](https://github.com/Snailclimb)
14 | - Github Resume: [http://resume.github.io/?Snailclimb](http://resume.github.io/?Snailclimb)
15 | - 期望职位:Java 研发程序员/大数据工程师(Java后台开发为首选)
16 | - 期望城市:xxx城市
17 |
18 |
19 | # 项目经历
20 |
21 | ## xxx项目
22 |
23 | ### 项目描述
24 |
25 | 介绍该项目是做什么的、使用到了什么技术以及你对项目整体设计的一个感受
26 |
27 | ### 责任描述
28 |
29 | 主要可以从下面三点来写:
30 |
31 | 1. 在这个项目中你负责了什么、做了什么、担任了什么角色
32 | 2. 从这个项目中你学会了那些东西,使用到了那些技术,学会了那些新技术的使用
33 | 3. 另外项目描述中,最好可以体现自己的综合素质,比如你是如何协调项目组成员协同开发的或者在遇到某一个棘手的问题的时候你是如何解决的。
34 |
35 | # 开源项目和技术文章
36 |
37 | ## 开源项目
38 |
39 | - [Java-Guide](https://github.com/Snailclimb/Java-Guide) :一份涵盖大部分Java程序员所需要掌握的核心知识。Star:3.9K; Fork:0.9k。
40 |
41 |
42 | ## 技术文章推荐
43 |
44 | - [可能是把Java内存区域讲的最清楚的一篇文章](https://juejin.im/post/5b7d69e4e51d4538ca5730cb)
45 | - [搞定JVM垃圾回收就是这么简单](https://juejin.im/post/5b85ea54e51d4538dd08f601)
46 | - [前端&后端程序员必备的Linux基础知识](https://juejin.im/post/5b3b19856fb9a04fa42f8c71)
47 | - [可能是把Docker的概念讲的最清楚的一篇文章](https://juejin.im/post/5b260ec26fb9a00e8e4b031a)
48 |
49 |
50 | # 校园经历(可选)
51 |
52 | ## 2016-2017
53 |
54 | 担任学校社团-致深社副会长,主要负责团队每周活动的组建以及每周例会的主持。
55 |
56 | ## 2017-2018
57 | 担任学校传媒组织:“长江大学在线信息传媒”的副站长以及安卓组成员。主要负责每周例会主持、活动策划以及学校校园通APP的研发工作。
58 |
59 |
60 | # 技能清单
61 |
62 | 以下均为我熟练使用的技能
63 |
64 | - Web开发:PHP/Hack/Node
65 | - Web框架:ThinkPHP/Yaf/Yii/Lavarel/LazyPHP
66 | - 前端框架:Bootstrap/AngularJS/EmberJS/HTML5/Cocos2dJS/ionic
67 | - 前端工具:Bower/Gulp/SaSS/LeSS/PhoneGap
68 | - 数据库相关:MySQL/PgSQL/PDO/SQLite
69 | - 版本管理、文档和自动化部署工具:Svn/Git/PHPDoc/Phing/Composer
70 | - 单元测试:PHPUnit/SimpleTest/Qunit
71 | - 云和开放平台:SAE/BAE/AWS/微博开放平台/微信应用开发
72 |
73 | # 自我评价(可选)
74 |
75 | 自我发挥。切记不要过度自夸!!!
76 |
77 |
78 | ### 感谢您花时间阅读我的简历,期待能有机会和您共事。
79 |
80 |
--------------------------------------------------------------------------------
/docs/系统设计/website-architecture/8 张图读懂大型网站技术架构.md:
--------------------------------------------------------------------------------
1 | > 本文是作者读 《大型网站技术架构》所做的思维导图,在这里分享给各位,公众号(JavaGuide)后台回复:“架构”。即可获得下面图片的源文件以及思维导图源文件!
2 |
3 |
4 |
5 | - [1. 大型网站架构演化](#1-大型网站架构演化)
6 | - [2. 大型架构模式](#2-大型架构模式)
7 | - [3. 大型网站核心架构要素](#3-大型网站核心架构要素)
8 | - [4. 瞬时响应:网站的高性能架构](#4-瞬时响应网站的高性能架构)
9 | - [5. 万无一失:网站的高可用架构](#5-万无一失网站的高可用架构)
10 | - [6. 永无止境:网站的伸缩性架构](#6-永无止境网站的伸缩性架构)
11 | - [7. 随机应变:网站的可扩展性架构](#7-随机应变网站的可扩展性架构)
12 | - [8. 固若金汤:网站的安全机构](#8-固若金汤网站的安全机构)
13 |
14 |
15 |
16 |
17 | ### 1. 大型网站架构演化
18 |
19 | 
20 |
21 | ### 2. 大型架构模式
22 |
23 | 
24 |
25 | ### 3. 大型网站核心架构要素
26 |
27 | 
28 |
29 | ### 4. 瞬时响应:网站的高性能架构
30 |
31 | 
32 |
33 | ### 5. 万无一失:网站的高可用架构
34 |
35 | 
36 |
37 | ### 6. 永无止境:网站的伸缩性架构
38 |
39 | 
40 |
41 | ### 7. 随机应变:网站的可扩展性架构
42 |
43 | 
44 |
45 | ### 8. 固若金汤:网站的安全机构
46 |
47 | 
48 |
--------------------------------------------------------------------------------
/docs/数据库/Redis/Redlock分布式锁.md:
--------------------------------------------------------------------------------
1 | 这篇文章主要是对 Redis 官方网站刊登的 [Distributed locks with Redis](https://redis.io/topics/distlock) 部分内容的总结和翻译。
2 |
3 | ## 什么是 RedLock
4 |
5 | Redis 官方站这篇文章提出了一种权威的基于 Redis 实现分布式锁的方式名叫 *Redlock*,此种方式比原先的单节点的方法更安全。它可以保证以下特性:
6 |
7 | 1. 安全特性:互斥访问,即永远只有一个 client 能拿到锁
8 | 2. 避免死锁:最终 client 都可能拿到锁,不会出现死锁的情况,即使原本锁住某资源的 client crash 了或者出现了网络分区
9 | 3. 容错性:只要大部分 Redis 节点存活就可以正常提供服务
10 |
11 | ## 怎么在单节点上实现分布式锁
12 |
13 | > SET resource_name my_random_value NX PX 30000
14 |
15 | 主要依靠上述命令,该命令仅当 Key 不存在时(NX保证)set 值,并且设置过期时间 3000ms (PX保证),值 my_random_value 必须是所有 client 和所有锁请求发生期间唯一的,释放锁的逻辑是:
16 |
17 | ```lua
18 | if redis.call("get",KEYS[1]) == ARGV[1] then
19 | return redis.call("del",KEYS[1])
20 | else
21 | return 0
22 | end
23 | ```
24 |
25 | 上述实现可以避免释放另一个client创建的锁,如果只有 del 命令的话,那么如果 client1 拿到 lock1 之后因为某些操作阻塞了很长时间,此时 Redis 端 lock1 已经过期了并且已经被重新分配给了 client2,那么 client1 此时再去释放这把锁就会造成 client2 原本获取到的锁被 client1 无故释放了,但现在为每个 client 分配一个 unique 的 string 值可以避免这个问题。至于如何去生成这个 unique string,方法很多随意选择一种就行了。
26 |
27 | ## Redlock 算法
28 |
29 | 算法很易懂,起 5 个 master 节点,分布在不同的机房尽量保证可用性。为了获得锁,client 会进行如下操作:
30 |
31 | 1. 得到当前的时间,微秒单位
32 | 2. 尝试顺序地在 5 个实例上申请锁,当然需要使用相同的 key 和 random value,这里一个 client 需要合理设置与 master 节点沟通的 timeout 大小,避免长时间和一个 fail 了的节点浪费时间
33 | 3. 当 client 在大于等于 3 个 master 上成功申请到锁的时候,且它会计算申请锁消耗了多少时间,这部分消耗的时间采用获得锁的当下时间减去第一步获得的时间戳得到,如果锁的持续时长(lock validity time)比流逝的时间多的话,那么锁就真正获取到了。
34 | 4. 如果锁申请到了,那么锁真正的 lock validity time 应该是 origin(lock validity time) - 申请锁期间流逝的时间
35 | 5. 如果 client 申请锁失败了,那么它就会在少部分申请成功锁的 master 节点上执行释放锁的操作,重置状态
36 |
37 | ## 失败重试
38 |
39 | 如果一个 client 申请锁失败了,那么它需要稍等一会在重试避免多个 client 同时申请锁的情况,最好的情况是一个 client 需要几乎同时向 5 个 master 发起锁申请。另外就是如果 client 申请锁失败了它需要尽快在它曾经申请到锁的 master 上执行 unlock 操作,便于其他 client 获得这把锁,避免这些锁过期造成的时间浪费,当然如果这时候网络分区使得 client 无法联系上这些 master,那么这种浪费就是不得不付出的代价了。
40 |
41 | ## 放锁
42 |
43 | 放锁操作很简单,就是依次释放所有节点上的锁就行了
44 |
45 | ## 性能、崩溃恢复和 fsync
46 |
47 | 如果我们的节点没有持久化机制,client 从 5 个 master 中的 3 个处获得了锁,然后其中一个重启了,这是注意 **整个环境中又出现了 3 个 master 可供另一个 client 申请同一把锁!** 违反了互斥性。如果我们开启了 AOF 持久化那么情况会稍微好转一些,因为 Redis 的过期机制是语义层面实现的,所以在 server 挂了的时候时间依旧在流逝,重启之后锁状态不会受到污染。但是考虑断电之后呢,AOF部分命令没来得及刷回磁盘直接丢失了,除非我们配置刷回策略为 fsnyc = always,但这会损伤性能。解决这个问题的方法是,当一个节点重启之后,我们规定在 max TTL 期间它是不可用的,这样它就不会干扰原本已经申请到的锁,等到它 crash 前的那部分锁都过期了,环境不存在历史锁了,那么再把这个节点加进来正常工作。
48 |
--------------------------------------------------------------------------------
/docs/系统设计/website-architecture/分布式.md:
--------------------------------------------------------------------------------
1 | - ### 一 分布式系统的经典基础理论
2 |
3 | [分布式系统的经典基础理论](https://blog.csdn.net/qq_34337272/article/details/80444032)
4 |
5 | 本文主要是简单的介绍了三个常见的概念: **分布式系统设计理念** 、 **CAP定理** 、 **BASE理论** ,关于分布式系统的还有很多很多东西。
6 | 
7 |
8 | - ### 二 分布式事务
9 | 分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。以上是百度百科的解释,简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用,分布式事务需要保证这些小操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。
10 | * [深入理解分布式事务](http://www.codeceo.com/article/distributed-transaction.html)
11 | * [分布式事务?No, 最终一致性](https://zhuanlan.zhihu.com/p/25933039)
12 | * [聊聊分布式事务,再说说解决方案](https://www.cnblogs.com/savorboard/p/distributed-system-transaction-consistency.html)
13 |
14 |
15 | - ### 三 分布式系统一致性
16 | [分布式服务化系统一致性的“最佳实干”](https://www.jianshu.com/p/1156151e20c8)
17 |
18 | - ### 四 一致性协议/算法
19 | 早在1898年就诞生了著名的 **Paxos经典算法** (**Zookeeper就采用了Paxos算法的近亲兄弟Zab算法**),但由于Paxos算法非常难以理解、实现、排错。所以不断有人尝试简化这一算法,直到2013年才有了重大突破:斯坦福的Diego Ongaro、John Ousterhout以易懂性为目标设计了新的一致性算法—— **Raft算法** ,并发布了对应的论文《In Search of an Understandable Consensus Algorithm》,到现在有十多种语言实现的Raft算法框架,较为出名的有以Go语言实现的Etcd,它的功能类似于Zookeeper,但采用了更为主流的Rest接口。
20 | * [图解 Paxos 一致性协议](https://mp.weixin.qq.com/s?__biz=MzI0NDI0MTgyOA==&mid=2652037784&idx=1&sn=d8c4f31a9cfb49ee91d05bb374e5cdd5&chksm=f2868653c5f10f45fc4a64d15a5f4163c3e66c00ed2ad334fa93edb46671f42db6752001f6c0#rd)
21 | * [图解分布式协议-RAFT](http://ifeve.com/raft/)
22 | * [Zookeeper ZAB 协议分析](https://dbaplus.cn/news-141-1875-1.html)
23 |
24 | - ### 五 分布式存储
25 |
26 | **分布式存储系统将数据分散存储在多台独立的设备上**。传统的网络存储系统采用集中的存储服务器存放所有数据,存储服务器成为系统性能的瓶颈,也是可靠性和安全性的焦点,不能满足大规模存储应用的需要。分布式网络存储系统采用可扩展的系统结构,利用多台存储服务器分担存储负荷,利用位置服务器定位存储信息,它不但提高了系统的可靠性、可用性和存取效率,还易于扩展。
27 |
28 | * [分布式存储系统概要](http://witchiman.top/2017/05/05/distributed-system/)
29 |
30 | - ### 六 分布式计算
31 |
32 | **所谓分布式计算是一门计算机科学,它研究如何把一个需要非常巨大的计算能力才能解决的问题分成许多小的部分,然后把这些部分分配给许多计算机进行处理,最后把这些计算结果综合起来得到最终的结果。**
33 | 分布式网络存储技术是将数据分散的存储于多台独立的机器设备上。分布式网络存储系统采用可扩展的系统结构,利用多台存储服务器分担存储负荷,利用位置服务器定位存储信息,不但解决了传统集中式存储系统中单存储服务器的瓶颈问题,还提高了系统的可靠性、可用性和扩展性。
34 |
35 | * [关于分布式计算的一些概念](https://blog.csdn.net/qq_34337272/article/details/80549020)
36 |
37 |
--------------------------------------------------------------------------------
/docs/Python/Flask请求流程.md:
--------------------------------------------------------------------------------
1 | ## Flask请求流程
2 |
3 | http请求-->Ngix-->WSGI-->web框架
4 |
5 | 请求进入Flask,flask调用__call__方法:
6 | ```python
7 | def __call__(self, environ, start_response):
8 | """The WSGI server calls the Flask application object as the
9 | WSGI application. This calls :meth:`wsgi_app` which can be
10 | wrapped to applying middleware."""
11 | return self.wsgi_app(environ, start_response)
12 | ```
13 |
14 | 实际调用wsgi_app方法:
15 | ```python
16 | def wsgi_app(self, environ, start_response):
17 | """..."""
18 | ctx = self.request_context(environ)
19 | error = None
20 | try:
21 | try:
22 | ctx.push()
23 | response = self.full_dispatch_request()
24 | except Exception as e:
25 | error = e
26 | response = self.handle_exception(e)
27 | except: # noqa: B001
28 | error = sys.exc_info()[1]
29 | raise
30 | return response(environ, start_response)
31 | finally:
32 | if self.should_ignore_error(error):
33 | error = None
34 | ctx.auto_pop(error)
35 | ```
36 |
37 | 首先调用request_context方法创建请求上下文ctx。然后做相应的处理,最后请求上下文ctx出栈。
38 | 可任意看到其实request_contect方法实际上就是创建返回的请求上下文对象RequestContext。
39 |
40 | ```python
41 | def request_context(self, environ):
42 | """..."""
43 | return RequestContext(self, environ)
44 | ```
45 |
46 | 进入RequestContext去看,就是封装了一个Request对象,并提供了push,pop方法。
47 |
48 | ```python
49 | class RequestContext(object):
50 | def __init__(self, app, environ, request=None, session=None):...
51 |
52 | def push(self):...
53 |
54 | def pop(self, exc=_sentinel):...
55 | ```
56 |
57 | 创建了请求上下文对象,接下来调用 ctx.push()方法将它推入栈中,full_dispatch_request()方法就是对请求进行分发,更具请求调用相应的视图函数做处理,得到响应内容,再将响应返回。最后是调用ctx.auto_pop方法将这个请求上下文推出栈(auto_pop方法进行了异常处理然后调用pop方法出栈)。
58 |
59 | 再视图函数或其他地方需要用到当前请求相关内容时,是从flask导入request。用到session也是从flask导入,如下:
60 | ```python
61 | from flask import session, request
62 | ```
63 | 实际上是从flask框架的globals.py里导入的。这个模块里有如下全局变量:
64 | ```python
65 | # context locals
66 | _request_ctx_stack = LocalStack()
67 | _app_ctx_stack = LocalStack()
68 | current_app = LocalProxy(_find_app)
69 | request = LocalProxy(partial(_lookup_req_object, "request"))
70 | session = LocalProxy(partial(_lookup_req_object, "session"))
71 | g = LocalProxy(partial(_lookup_app_object, "g"))
72 | ```
73 |
74 | 导入request实际上是导入的一个代理对象,对request的操作都是通过这个代理对象去操作的。类似threading.Local(),这个代理对象也实现了线程隔离。
--------------------------------------------------------------------------------
/docs/简历与面经/PreparingForInterview/如果面试官问你“你有什么问题问我吗?”时,你该如何回答.md:
--------------------------------------------------------------------------------
1 | 我还记得当时我去参加面试的时候,几乎每一场面试,特别是HR面和高管面的时候,面试官总是会在结尾问我:“问了你这么多问题了,你有什么问题问我吗?”。这个时候很多人内心就会陷入短暂的纠结中:我该问吗?不问的话面试官会不会对我影响不好?问什么问题?问这个问题会不会让面试官对我的影响不好啊?
2 |
3 | 
4 |
5 | ### 这个问题对最终面试结果的影响到底大不大?
6 |
7 | 就技术面试而言,回答这个问题的时候,只要你不是触碰到你所面试的公司的雷区,那么我觉得这对你能不能拿到最终offer来说影响确实是不大的。我说这些并不代表你就可以直接对面试官说:“我没问题了。”,笔主当时面试的时候确实也说过挺多次“没问题要问了。”,最终也没有导致笔主被pass掉(可能是前面表现比较好,哈哈,自恋一下)。我现在回想起来,觉得自己当时做法其实挺不对的。面试本身就是一个双向选择的过程,你对这个问题的回答也会侧面反映出你对这次面试的上心程度,你的问题是否有价值,也影响了你最终的选择与公司是否选择你。
8 |
9 | 面试官在技术面试中主要考察的还是你这样个人到底有没有胜任这个工作的能力以及你是否适合公司未来的发展需要,很多公司还需要你认同它的文化,我觉得你只要不是太笨,应该不会栽在这里。除非你和另外一个人在能力上相同,但是只能在你们两个人中选一个,那么这个问题才对你能不能拿到offer至关重要。有准备总比没准备好,给面试官留一个好的影响总归是没错的。
10 |
11 | 但是,就非技术面试来说,我觉得好好回答这个问题对你最终的结果还是比较重要的。
12 |
13 | 总的来说不管是技术面试还是非技术面试,如果你想赢得公司的青睐和尊重,我觉得我们都应该重视这个问题。
14 |
15 | ### 真诚一点,不要问太 Low 的问题
16 |
17 | 回答这个问题很重要的一点就是你没有必要放低自己的姿态问一些很虚或者故意讨好面试官的问题,也不要把自己从面经上学到的东西照搬下来使用。面试官也不是傻子,特别是那种特别有经验的面试官,你是真心诚意的问问题,还是从别处照搬问题来讨好面试官,人家可能一听就听出来了。总的来说,还是要真诚。除此之外,不要问太 Low 的问题,会显得你整个人格局比较小或者说你根本没有准备(侧面反映你对这家公司不上心,既然你不上心,为什么要要你呢)。举例几个比较 Low 的问题,大家看看自己有没有问过其中的问题:
18 |
19 | - 贵公司的主要业务是什么?(面试之前自己不知道提前网上查一下吗?)
20 | - 贵公司的男女比例如何?(考虑脱单?记住你是来工作的!)
21 | - 贵公司一年搞几次外出旅游?(你是来工作的,这些娱乐活动先别放在心上!)
22 | - ......
23 |
24 | ### 有哪些有价值的问题值得问?
25 |
26 | 针对这个问题。笔主专门找了几个专门做HR工作的小哥哥小姐姐们询问并且查阅了挺多前辈们的回答,然后结合自己的实际经历,我概括了下面几个比较适合问的问题。
27 |
28 | #### 面对HR或者其他Level比较低的面试官时
29 |
30 | 1. **能不能谈谈你作为一个公司老员工对公司的感受?** (这个问题比较容易回答,不会让面试官陷入无话可说的尴尬境地。另外,从面试官的回答中你可以加深对这个公司的了解,让你更加清楚这个公司到底是不是你想的那样或者说你是否能适应这个公司的文化。除此之外,这样的问题在某种程度上还可以拉进你与面试官的距离。)
31 | 2. **能不能问一下,你当时因为什么原因选择加入这家公司的呢或者说这家公司有哪些地方吸引你?有什么地方你觉得还不太好或者可以继续完善吗?** (类似第一个问题,都是问面试官个人对于公司的看法。)
32 | 3. **我觉得我这次表现的不是太好,你有什么建议或者评价给我吗?**(这个是我常问的。我觉得说自己表现不好只是这个语境需要这样来说,这样可以显的你比较谦虚好学上进。)
33 | 4. **接下来我会有一段空档期,有什么值得注意或者建议学习的吗?** (体现出你对工作比较上心,自助学习意识比较强。)
34 | 5. **这个岗位为什么还在招人?** (岗位真实性和价值咨询)
35 | 6. **大概什么时候能给我回复呢?** (终面的时候,如果面试官没有说的话,可以问一下)
36 | 7. ......
37 |
38 |
39 |
40 | #### 面对部门领导
41 |
42 | 1. **部门的主要人员分配以及对应的主要工作能简单介绍一下吗?**
43 | 2. **未来如果我要加入这个团队,你对我的期望是什么?** (部门领导一般情况下是你的直属上级了,你以后和他打交道的机会应该是最多的。你问这个问题,会让他感觉你是一个对他的部门比较上心,比较有团体意识,并且愿意倾听的候选人。)
44 | 3. **公司对新入职的员工的培养机制是什么样的呢?** (正规的公司一般都有培养机制,提前问一下是对你自己的负责也会显的你比较上心)
45 | 4. **以您来看,这个岗位未来在公司内部的发展如何?** (在我看来,问这个问题也是对你自己的负责吧,谁不想发展前景更好的岗位呢?)
46 | 5. **团队现在面临的最大挑战是什么?** (这样的问题不会暴露你对公司的不了解,并且也能让你对未来工作的挑战或困难有一个提前的预期。)
47 |
48 |
49 |
50 | #### 面对Level比较高的(比如总裁,老板)
51 |
52 | 1. **贵公司的发展目标和方向是什么?** (看下公司的发展是否满足自己的期望)
53 | 2. **与同行业的竞争者相比,贵公司的核心竞争优势在什么地方?** (充分了解自己的优势和劣势)
54 | 3. **公司现在面临的最大挑战是什么?**
55 |
56 | ### 来个补充,顺便送个祝福给大家
57 |
58 | 薪酬待遇和相关福利问题一般在终面的时候(最好不要在前面几面的时候就问到这个问题),面试官会提出来或者在面试完之后以邮件的形式告知你。一般来说,如果面试官很愿意为你回答问题,对你的问题也比较上心的话,那他肯定是觉得你就是他们要招的人。
59 |
60 | 大家在面试的时候,可以根据自己对于公司或者岗位的了解程度,对上面提到的问题进行适当修饰或者修改。上面提到的一些问题只是给没有经验的朋友一个参考,如果你还有其他比较好的问题的话,那当然也更好啦!
61 |
62 | 金三银四。过了二月就到了面试高峰期或者说是黄金期。几份惊喜几份愁,愿各位能始终不忘初心!每个人都有每个人的难处。引用一句《阿甘正传》里面的台词:“生活就像一盒巧克力,你永远不知道下一块是什么味道“。
63 |
64 | 
--------------------------------------------------------------------------------
/docs/简历与面经/PreparingForInterview/interviewPrepare.md:
--------------------------------------------------------------------------------
1 | 不论是校招还是社招都避免不了各种面试、笔试,如何去准备这些东西就显得格外重要。不论是笔试还是面试都是有章可循的,我这个“有章可循”说的意思只是说应对技术面试是可以提前准备。 我其实特别不喜欢那种临近考试就提前背啊记啊各种题的行为,非常反对!我觉得这种方法特别极端,而且在稍有一点经验的面试官面前是根本没有用的。建议大家还是一步一个脚印踏踏实实地走。
2 |
3 |
4 |
5 | - [1 如何获取大厂面试机会?](#1-如何获取大厂面试机会)
6 | - [2 面试前的准备](#2--面试前的准备)
7 | - [2.1 准备自己的自我介绍](#21-准备自己的自我介绍)
8 | - [2.2 关于着装](#22-关于着装)
9 | - [2.3 随身带上自己的成绩单和简历](#23-随身带上自己的成绩单和简历)
10 | - [2.4 如果需要笔试就提前刷一些笔试题](#24-如果需要笔试就提前刷一些笔试题)
11 | - [2.5 花时间一些逻辑题](#25-花时间一些逻辑题)
12 | - [2.6 准备好自己的项目介绍](#26-准备好自己的项目介绍)
13 | - [2.7 提前准备技术面试](#27-提前准备技术面试)
14 | - [2.7 面试之前做好定向复习](#27-面试之前做好定向复习)
15 | - [3 面试之后复盘](#3-面试之后复盘)
16 |
17 |
18 |
19 | ## 1 如何获取大厂面试机会?
20 |
21 | **在讲如何获取大厂面试机会之前,先来给大家科普/对比一下两个校招非常常见的概念——春招和秋招。**
22 |
23 | 1. **招聘人数** :秋招多于春招 ;
24 | 2. **招聘时间** : 秋招一般7月左右开始,大概一直持续到10月底。但是大厂(如BAT)都会早开始早结束,所以一定要把握好时间。春招最佳时间为3月,次佳时间为4月,进入5月基本就不会再有春招了(金三银四)。
25 | 3. **应聘难度** :秋招略大于春招;
26 | 4. **招聘公司:** 秋招数量多,而春招数量较少,一般为秋招的补充。
27 |
28 | **综上,一般来说,秋招的含金量明显是高于春招的。**
29 |
30 | **下面我就说一下我自己知道的一些方法,不过应该也涵盖了大部分获取面试机会的方法。**
31 |
32 | 1. **关注大厂官网,随时投递简历(走流程的网申);**
33 | 2. **线下参加宣讲会,直接投递简历;**
34 | 3. **找到师兄师姐/认识的人,帮忙内推(能够让你避开网申简历筛选,笔试筛选,还是挺不错的,不过也还是需要你的简历够棒);**
35 | 4. **博客发文被看中/Github优秀开源项目作者,大厂内部人员邀请你面试;**
36 | 5. **求职类网站投递简历(不是太推荐,适合海投);**
37 |
38 |
39 | 除了这些方法,我也遇到过这样的经历:有些大公司的一些部门可能暂时没招够人,然后如果你的亲戚或者朋友刚好在这个公司,而你正好又在寻求offer,那么面试机会基本上是有了,而且这种面试的难度好像一般还普遍比其他正规面试低很多。
40 |
41 | ## 2 面试前的准备
42 |
43 | ### 2.1 准备自己的自我介绍
44 |
45 | 从HR面、技术面到高管面/部门主管面,面试官一般会让你先自我介绍一下,所以好好准备自己的自我介绍真的非常重要。网上一般建议的是准备好两份自我介绍:一份对hr说的,主要讲能突出自己的经历,会的编程技术一语带过;另一份对技术面试官说的,主要讲自己会的技术细节,项目经验,经历那些就一语带过。
46 |
47 | 我这里简单分享一下我自己的自我介绍的一个简单的模板吧:
48 |
49 | > 面试官,您好!我叫某某。大学时间我主要利用课外时间学习某某。在校期间参与过一个某某系统的开发,另外,自己学习过程中也写过很多系统比如某某系统。在学习之余,我比较喜欢通过博客整理分享自己所学知识。我现在是某某社区的认证作者,写过某某很不错的文章。另外,我获得过某某奖,我的Github上开源的某个项目已经有多少Star了。
50 |
51 | ### 2.2 关于着装
52 |
53 | 穿西装、打领带、小皮鞋?NO!NO!NO!这是互联网公司面试又不是去走红毯,所以你只需要穿的简单大方就好,不需要太正式。
54 |
55 | ### 2.3 随身带上自己的成绩单和简历
56 |
57 | 有的公司在面试前都会让你交一份成绩单和简历当做面试中的参考。
58 |
59 | ### 2.4 如果需要笔试就提前刷一些笔试题
60 |
61 | 平时空闲时间多的可以刷一下笔试题目(牛客网上有很多)。但是不要只刷面试题,不动手code,程序员不是为了考试而存在的。
62 |
63 | ### 2.5 花时间一些逻辑题
64 |
65 | 面试中发现有些公司都有逻辑题测试环节,并且都把逻辑笔试成绩作为很重要的一个参考。
66 |
67 | ### 2.6 准备好自己的项目介绍
68 |
69 | 如果有项目的话,技术面试第一步,面试官一般都是让你自己介绍一下你的项目。你可以从下面几个方向来考虑:
70 |
71 | 1. 对项目整体设计的一个感受(面试官可能会让你画系统的架构图)
72 | 2. 在这个项目中你负责了什么、做了什么、担任了什么角色
73 | 3. 从这个项目中你学会了那些东西,使用到了那些技术,学会了那些新技术的使用
74 | 4. 另外项目描述中,最好可以体现自己的综合素质,比如你是如何协调项目组成员协同开发的或者在遇到某一个棘手的问题的时候你是如何解决的又或者说你在这个项目用了什么技术实现了什么功能比如:用redis做缓存提高访问速度和并发量、使用消息队列削峰和降流等等。
75 |
76 | ### 2.7 提前准备技术面试
77 |
78 | 搞清楚自己面试中可能涉及哪些知识点、哪些知识点是重点。面试中哪些问题会被经常问到、自己该如何回答。(强烈不推荐背题,第一:通过背这种方式你能记住多少?能记住多久?第二:背题的方式的学习很难坚持下去!)
79 |
80 | ### 2.7 面试之前做好定向复习
81 |
82 | 所谓定向复习就是专门针对你要面试的公司来复习。比如你在面试之前可以在网上找找有没有你要面试的公司的面经。
83 |
84 | 举个栗子:在我面试 ThoughtWorks 的前几天我就在网上找了一些关于 ThoughtWorks 的技术面的一些文章。然后知道了 ThoughtWorks 的技术面会让我们在之前做的作业的基础上增加一个或两个功能,所以我提前一天就把我之前做的程序重新重构了一下。然后在技术面的时候,简单的改了几行代码之后写个测试就完事了。如果没有提前准备,我觉得 20 分钟我很大几率会完不成这项任务。
85 |
86 | ## 3 面试之后复盘
87 |
88 | 如果失败,不要灰心;如果通过,切勿狂喜。面试和工作实际上是两回事,可能很多面试未通过的人,工作能力比你强的多,反之亦然。我个人觉得面试也像是一场全新的征程,失败和胜利都是平常之事。所以,劝各位不要因为面试失败而灰心、丧失斗志。也不要因为面试通过而沾沾自喜,等待你的将是更美好的未来,继续加油!
89 |
--------------------------------------------------------------------------------
/docs/简历与面经/手把手教你用Markdown写一份高质量的简历.md:
--------------------------------------------------------------------------------
1 | ## Markdown 简历模板样式一览
2 | 
3 | **可以看到我把联系方式放在第一位,因为公司一般会与你联系,所以把联系方式放在第一位也是为了方便联系考虑。**
4 |
5 | ## 为什么要用 Markdown 写简历?
6 |
7 | Markdown 语法简单,易于上手。使用正确的 Markdown 语言写出来的简历不论是在排版还是格式上都比较干净,易于阅读。另外,使用 Markdown 写简历也会给面试官一种你比较专业的感觉。
8 |
9 | 除了这些,我觉得使用 Markdown 写简历可以很方便将其与PDF、HTML、PNG格式之间转换。后面我会介绍到转换方法,只需要一条命令你就可以实现 Markdown 到 PDF、HTML 与 PNG之间的无缝切换。
10 |
11 | > 下面的一些内容我在之前的一篇文章中已经提到过,这里再说一遍,最后会分享如何实现Markdown 到 PDF、HTML、PNG格式之间转换。
12 |
13 | ## 为什么说简历很重要?
14 |
15 | 假如你是网申,你的简历必然会经过HR的筛选,一张简历HR可能也就花费10秒钟看一下,然后HR就会决定你这一关是Fail还是Pass。
16 |
17 | 假如你是内推,如果你的简历没有什么优势的话,就算是内推你的人再用心,也无能为力。
18 |
19 | 另外,就算你通过了筛选,后面的面试中,面试官也会根据你的简历来判断你究竟是否值得他花费很多时间去面试。
20 |
21 | ## 写简历的两大法则
22 |
23 | 目前写简历的方式有两种普遍被认可,一种是 STAR, 一种是 FAB。
24 |
25 | **STAR法则(Situation Task Action Result):**
26 |
27 | - **Situation:** 事情是在什么情况下发生;
28 | - **Task::** 你是如何明确你的任务的;
29 | - **Action:** 针对这样的情况分析,你采用了什么行动方式;
30 | - **Result:** 结果怎样,在这样的情况下你学习到了什么。
31 |
32 | **FAB 法则(Feature Advantage Benefit):**
33 |
34 | - **Feature:** 是什么;
35 | - **Advantage:** 比别人好在哪些地方;
36 | - **Benefit:** 如果雇佣你,招聘方会得到什么好处。
37 |
38 | ## 项目经历怎么写?
39 | 简历上有一两个项目经历很正常,但是真正能把项目经历很好的展示给面试官的非常少。对于项目经历大家可以考虑从如下几点来写:
40 |
41 | 1. 对项目整体设计的一个感受
42 | 2. 在这个项目中你负责了什么、做了什么、担任了什么角色
43 | 3. 从这个项目中你学会了那些东西,使用到了那些技术,学会了那些新技术的使用
44 | 4. 另外项目描述中,最好可以体现自己的综合素质,比如你是如何协调项目组成员协同开发的或者在遇到某一个棘手的问题的时候你是如何解决的。
45 |
46 | ## 专业技能该怎么写?
47 | 先问一下你自己会什么,然后看看你意向的公司需要什么。一般HR可能并不太懂技术,所以他在筛选简历的时候可能就盯着你专业技能的关键词来看。对于公司有要求而你不会的技能,你可以花几天时间学习一下,然后在简历上可以写上自己了解这个技能。比如你可以这样写:
48 |
49 | - Dubbo:精通
50 | - Spring:精通
51 | - Docker:掌握
52 | - SOA分布式开发 :掌握
53 | - Spring Cloud:了解
54 |
55 | ## 简历模板分享
56 |
57 | **开源程序员简历模板**: [https://github.com/geekcompany/ResumeSample](https://github.com/geekcompany/ResumeSample)(包括PHP程序员简历模板、iOS程序员简历模板、Android程序员简历模板、Web前端程序员简历模板、Java程序员简历模板、C/C++程序员简历模板、NodeJS程序员简历模板、架构师简历模板以及通用程序员简历模板)
58 |
59 | **上述简历模板的改进版本:** [https://github.com/Snailclimb/Java-Guide/blob/master/面试必备/简历模板.md](https://github.com/Snailclimb/Java-Guide/blob/master/面试必备/简历模板.md)
60 |
61 | ## 其他的一些小tips
62 |
63 | 1. 尽量避免主观表述,少一点语义模糊的形容词,尽量要简洁明了,逻辑结构清晰。
64 | 2. 注意排版(不需要花花绿绿的),尽量使用Markdown语法。
65 | 3. 如果自己有博客或者个人技术栈点的话,写上去会为你加分很多。
66 | 4. 如果自己的Github比较活跃的话,写上去也会为你加分很多。
67 | 5. 注意简历真实性,一定不要写自己不会的东西,或者带有欺骗性的内容
68 | 6. 项目经历建议以时间倒序排序,另外项目经历不在于多,而在于有亮点。
69 | 7. 如果内容过多的话,不需要非把内容压缩到一页,保持排版干净整洁就可以了。
70 | 8. 简历最后最好能加上:“感谢您花时间阅读我的简历,期待能有机会和您共事。”这句话,显的你会很有礼貌。
71 |
72 |
73 | > 我们刚刚讲了很多关于如何写简历的内容并且分享了一份 Markdown 格式的简历文档。下面我们来看看如何实现 Markdown 到 HTML格式、PNG格式之间转换。
74 | ## Markdown 到 HTML格式、PNG格式之间转换
75 |
76 | 网上很难找到一个比较方便并且效果好的转换方法,最后我是通过 Visual Studio Code 的 Markdown PDF 插件完美解决了这个问题!
77 |
78 | ### 安装 Markdown PDF 插件
79 |
80 | **① 打开Visual Studio Code ,按快捷键 F1,选择安装扩展选项**
81 |
82 | 
83 |
84 | **② 搜索 “Markdown PDF” 插件并安装 ,然后重启**
85 |
86 | 
87 |
88 | ### 使用方法
89 |
90 | 随便打开一份 Markdown 文件 点击F1,然后输入export即可!
91 |
92 | 
93 |
94 |
--------------------------------------------------------------------------------
/docs/简历与面经/PreparingForInterview/JavaInterviewLibrary.md:
--------------------------------------------------------------------------------
1 | 昨天我整理了公众号历史所有和面试相关的我觉得还不错的文章:[整理了一些有助于你拿Offer的文章]() 。今天分享一下最近逛Github看到了一些我觉得对于Java面试以及学习有帮助的仓库,这些仓库涉及Java核心知识点整理、Java常见面试题、算法、基础知识点比如网络和操作系统等等。
2 |
3 | ## 知识点相关
4 |
5 | ### 1.JavaGuide
6 |
7 | - Github地址: [https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide)
8 | - star: 64.0k
9 | - 介绍: 【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识。
10 |
11 | ### 2.CS-Notes
12 |
13 | - Github 地址:
14 | - Star: 68.3k
15 | - 介绍: 技术面试必备基础知识、Leetcode 题解、后端面试、Java 面试、春招、秋招、操作系统、计算机网络、系统设计。
16 |
17 | ### 3. advanced-java
18 |
19 | - Github地址:[https://github.com/doocs/advanced-java](https://github.com/doocs/advanced-java)
20 | - star: 23.4k
21 | - 介绍: 互联网 Java 工程师进阶知识完全扫盲:涵盖高并发、分布式、高可用、微服务等领域知识,后端同学必看,前端同学也可学习。
22 |
23 | ### 4.JCSprout
24 |
25 | - Github地址:[https://github.com/crossoverJie/JCSprout](https://github.com/crossoverJie/JCSprout)
26 | - star: 21.2k
27 | - 介绍: Java Core Sprout:处于萌芽阶段的 Java 核心知识库。
28 |
29 | ### 5.toBeTopJavaer
30 |
31 | - Github地址:[https://github.com/hollischuang/toBeTopJavaer](https://github.com/hollischuang/toBeTopJavaer)
32 | - star: 4.0 k
33 | - 介绍: Java工程师成神之路。
34 |
35 | ### 6.architect-awesome
36 |
37 | - Github地址:[https://github.com/xingshaocheng/architect-awesome](https://github.com/xingshaocheng/architect-awesome)
38 | - star: 34.4 k
39 | - 介绍:后端架构师技术图谱。
40 |
41 | ### 7.technology-talk
42 |
43 | - Github地址: [https://github.com/aalansehaiyang/technology-talk](https://github.com/aalansehaiyang/technology-talk)
44 | - star: 6.1k
45 | - 介绍: 汇总java生态圈常用技术框架、开源中间件,系统架构、项目管理、经典架构案例、数据库、常用三方库、线上运维等知识。
46 |
47 | ### 8.fullstack-tutorial
48 |
49 | - Github地址: [https://github.com/frank-lam/fullstack-tutorial](https://github.com/frank-lam/fullstack-tutorial)
50 | - star: 4.0k
51 | - 介绍: fullstack tutorial 2019,后台技术栈/架构师之路/全栈开发社区,春招/秋招/校招/面试。
52 |
53 | ### 9.3y
54 |
55 | - Github地址:[https://github.com/ZhongFuCheng3y/3y](https://github.com/ZhongFuCheng3y/3y)
56 | - star: 1.9 k
57 | - 介绍: Java 知识整合。
58 |
59 | ### 10.java-bible
60 |
61 | - Github地址:[https://github.com/biezhi/java-bible](https://github.com/biezhi/java-bible)
62 | - star: 2.3k
63 | - 介绍: 这里记录了一些技术摘要,部分文章来自网络,本项目的目的力求分享精品技术干货,以Java为主。
64 |
65 | ### 11.interviews
66 |
67 | - Github地址: [https://github.com/kdn251/interviews/blob/master/README-zh-cn.md](https://github.com/kdn251/interviews/blob/master/README-zh-cn.md)
68 | - star: 35.3k
69 | - 介绍: 软件工程技术面试个人指南(国外的一个项目,虽然有翻译版,但是不太推荐,因为很多内容并不适用于国内)。
70 |
71 | ## 算法相关
72 |
73 | ### 1.LeetCodeAnimation
74 |
75 | - Github 地址:
76 | - Star: 33.4k
77 | - 介绍: Demonstrate all the questions on LeetCode in the form of animation.(用动画的形式呈现解LeetCode题目的思路)。
78 |
79 | ### 2.awesome-java-leetcode
80 |
81 | - Github地址:[https://github.com/Blankj/awesome-java-leetcode](https://github.com/Blankj/awesome-java-leetcode)
82 | - star: 6.1k
83 | - 介绍: LeetCode 上 Facebook 的面试题目。
84 |
85 | ### 3.leetcode
86 |
87 | - Github地址:[https://github.com/azl397985856/leetcode](https://github.com/azl397985856/leetcode)
88 | - star: 12.0k
89 | - 介绍: LeetCode Solutions: A Record of My Problem Solving Journey.( leetcode题解,记录自己的leetcode解题之路。)
--------------------------------------------------------------------------------
/docs/Python/Python进程通信.md:
--------------------------------------------------------------------------------
1 | ## Python进程通信
2 |
3 | 文章整理来源:https://blog.csdn.net/weixin_43790276/article/details/90906683
4 | 文章整理来源:https://www.cnblogs.com/guguobao/p/9398653.html
5 |
6 | ### 1. 第一种:Queue
7 | ```
8 | from multiprocessing import Queue
9 |
10 | Queue([maxsize])
11 |
12 | maxsize:指定队列的长度,即队列中消息的最大数量
13 |
14 | 初始化Queue对象时,若括号中没有指定最大可接收的消息数量,或数量为负值,那么就代表可接受的消息数量没有上限(直到内存的尽头);
15 |
16 | Queue的常用方法:
17 |
18 | 1.qsize():返回当前队列包含的消息数量,即当前队列中有多少条数据
19 |
20 | 2.empty():如果队列为空,返回True,反之False
21 |
22 | 3.full():如果队列满了,返回True,反之False
23 |
24 | 4.get([block[, timeout]]):获取队列中的一条消息,然后将其从列队中移除,block默认值为True
25 |
26 | 如果block使用默认值,且没有设置timeout(单位秒),列队为空,此时程序将被阻塞(停在读取状态),直到从列队读到消息为止。如果设置了timeout,列队为空,则会等待timeout秒,若还没读取到任何消息,抛出"Queue.Empty"异常。
27 |
28 | 如果block值为False,消息列队如果为空,则会立刻抛出"Queue.Empty"异常。
29 |
30 | 5.get_nowait():相当于Queue.get(False)
31 |
32 | 6.Queue.put(item,[block[, timeout]]):将item消息写入队列,block默认值为True
33 |
34 | 如果block使用默认值,且没有设置timeout(单位秒),列队已满,此时程序将被阻塞(停在写入状态),直到列队腾出空间为止,将数据写入。如果设置了timeout,列队已满,则会等待timeout秒,若还没空间,抛出"Queue.Full"异常。
35 |
36 | 如果block值为False,消息列队如果没有空间可写入,则会立刻抛出"Queue.Full"异常。
37 |
38 | 7.Queue.put_nowait(item):相当于Queue.put(item, False)
39 |
40 | ```
41 |
42 | 示例:
43 |
44 | ```
45 | from multiprocessing import Process, Queue
46 | import time
47 |
48 | def put_card(queue):
49 | """往队列中添加数据"""
50 | for card in ['A', 'K', 'Q', 'J', '10']:
51 | print('Put {} to queue...'.format(card))
52 | queue.put(card)
53 | time.sleep(1)
54 |
55 | def get_card(queue):
56 | """从队列中取出数据"""
57 | while True:
58 | if not queue.empty():
59 | card = queue.get(True)
60 | print('Get {} from queue.'.format(card))
61 | time.sleep(1)
62 | else:
63 | break
64 |
65 | if __name__ == "__main__":
66 | q = Queue()
67 | pp = Process(target=put_card, args=(q,))
68 | pg = Process(target=get_card, args=(q,))
69 | pp.start()
70 |
71 | pg.start()
72 | pg.join()
73 | print(pg.is_alive())
74 |
75 | ```
76 |
77 | **进程池中的Queue**
78 |
79 | 如果要使用Pool创建进程,需要使用multiprocessing.Manager()中的Queue()来传递消息。
80 |
81 | ### 2. 第二种:Pipe
82 |
83 | ```
84 | from multiprocessing import Pipe
85 |
86 | pipe = Pipe()
87 |
88 | Pipe常用于两个进程,两个进程分别位于管道的两端
89 | Pipe方法返回(conn1,conn2)代表一个管道的两个端,Pipe方法有duplex参数,默认为True,即全双工模式,若为FALSE,conn1只负责接收信息,conn2负责发送,
90 | send和recv方法分别为发送和接收信息。
91 | ```
92 |
93 | 示例:
94 |
95 | ```
96 | import multiprocessing
97 | import time, random
98 |
99 | # 写数据进程执行的代码
100 | def proc_send(pipe, urls):
101 | # print 'Process is write....'
102 | for url in urls:
103 | print('Process is send :%s' % url)
104 | pipe.send(url)
105 | time.sleep(random.random())
106 |
107 | # 读数据进程的代码
108 | def proc_recv(pipe):
109 | while True:
110 | print('Process rev:%s' % pipe.recv())
111 | time.sleep(random.random())
112 |
113 | if __name__ == '__main__':
114 | # 父进程创建pipe,并传给各个子进程
115 | pipe = multiprocessing.Pipe()
116 | p1 = multiprocessing.Process(target=proc_send, args=(pipe[0], ['url_' + str(i) for i in range(10)]))
117 | p2 = multiprocessing.Process(target=proc_recv, args=(pipe[1],))
118 | # 启动子进程,写入
119 | p1.start()
120 | p2.start()
121 |
122 | p1.join()
123 | p2.terminate()
124 | ```
125 |
126 | ### 3. 第三种:socket
127 |
128 | 见网络编程篇
129 |
130 |
131 |
132 |
133 |
--------------------------------------------------------------------------------
/docs/简历与面经/BATJrealInterviewExperience/5面阿里,终获offer.md:
--------------------------------------------------------------------------------
1 | > 作者:ppxyn。本文来自读者投稿,同时也欢迎各位投稿,**对于不错的原创文章我根据你的选择给予现金(50-200)、付费专栏或者任选书籍进行奖励!所以,快提 pr 或者邮件的方式(邮件地址在主页)给我投稿吧!** 当然,我觉得奖励是次要的,最重要的是你可以从自己整理知识点的过程中学习到很多知识。
2 |
3 | **目录**
4 |
5 |
6 |
7 | - [前言](#前言)
8 | - [一面\(技术面\)](#一面技术面)
9 | - [二面\(技术面\)](#二面技术面)
10 | - [三面\(技术面\)](#三面技术面)
11 | - [四面\(半个技术面\)](#四面半个技术面)
12 | - [五面\(HR面\)](#五面hr面)
13 | - [总结](#总结)
14 |
15 |
16 |
17 | ### 前言
18 |
19 | 在接触 Java 之前我接触的比较多的是硬件方面,用的比较多的语言就是C和C++。到了大三我才正式选择 Java 方向,到目前为止使用Java到现在大概有一年多的时间,所以Java算不上很好。刚开始投递的时候,实习刚辞职,也没准备笔试面试,很多东西都忘记了。所以,刚开始我并没有直接就投递阿里,毕竟心里还是有一点点小害怕的。于是,我就先投递了几个不算大的公司来练手,就是想着刷刷经验而已或者说是练练手(ps:还是挺对不起那些公司的)。面了一个月其他公司后,我找了我实验室的学长内推我,后面就有了这5次面试。
20 |
21 | 下面简单的说一下我的这5次面试:4次技术面+1次HR面,希望我的经历能对你有所帮助。
22 |
23 | ### 一面(技术面)
24 |
25 | 1. 自我介绍(主要讲自己会的技术细节,项目经验,经历那些就一语带过,后面面试官会问你的)。
26 | 2. 聊聊项目(就是一个很普通的分布式商城,自己做了一些改进),让我画了整个项目的架构图,然后针对项目抛了一系列的提高性能的问题,还问了我做项目的过程中遇到了那些问题,如何解决的,差不读就这些吧。
27 | 3. 可能是我前面说了我会数据库优化,然后面试官就开始问索引、事务隔离级别、悲观锁和乐观锁、索引、ACID、MVVC这些问题。
28 | 4. 浏览器输入URL发生了什么? TCP和UDP区别? TCP如何保证传输可靠性?
29 | 5. 讲下跳表怎么实现的?哈夫曼编码是怎么回事?非递归且不用额外空间(不用栈),如何遍历二叉树
30 | 6. 后面又问了很多JVM方面的问题,比如Java内存模型、常见的垃圾回收器、双亲委派模型这些
31 | 7. 你有什么问题要问吗?
32 |
33 | ### 二面(技术面)
34 |
35 | 1. 自我介绍(主要讲自己会的技术细节,项目经验,经历那些就一语带过,后面面试官会问你的)。
36 | 2. 操作系统的内存管理机制
37 | 3. 进程和线程的区别
38 | 4. 说下你对线程安全的理解
39 | 5. volatile 有什么作用 ,sychronized和lock有什么区别
40 | 6. ReentrantLock实现原理
41 | 7. 用过CountDownLatch么?什么场景下用的?
42 | 8. AQS底层原理。
43 | 9. 造成死锁的原因有哪些,如何预防?
44 | 10. 加锁会带来哪些性能问题。如何解决?
45 | 11. HashMap、ConcurrentHashMap源码。HashMap是线程安全的吗?Hashtable呢?ConcurrentHashMap有了解吗?
46 | 12. 是否可以实习?
47 | 13. 你有什么问题要问吗?
48 |
49 | ### 三面(技术面)
50 |
51 | 1. 有没有参加过 ACM 或者他竞赛,有没有拿过什么奖?( 我说我没参加过ACM,本科参加过数学建模竞赛,名次并不好,没拿过什么奖。面试官好像有点失望,然后我又赶紧补充说我和老师一起做过一个项目,目前已经投入使用。面试官还比较感兴趣,后面又和他聊了一下这个项目。)
52 | 2. 研究生期间,做过什么项目,发过论文吗?有什么成果吗?
53 | 3. 你觉得你有什么优点和缺点?你觉得你相比于那些比你更优秀的人欠缺什么?
54 | 4. 有读过什么源码吗?(我说我读过 Java 集合框架和 Netty 的,面试官说 Java 集合前几面一定问的差不多,就不问了,然后就问我 Netty的,我当时很慌啊!)
55 | 5. 介绍一下自己对 Netty 的认识,为什么要用。说说业务中,Netty 的使用场景。什么是TCP 粘包/拆包,解决办法。Netty线程模型。Dubbo 在使用 Netty 作为网络通讯时候是如何避免粘包与半包问题?讲讲Netty的零拷贝?巴拉巴拉问了好多,我记得有好几个我都没回答上来,心里想着凉凉了啊。
56 | 6. 用到了那些开源技术、在开源领域做过贡献吗?
57 | 7. 常见的排序算法及其复杂度,现场写了快排。
58 | 8. 红黑树,B树的一些问题。
59 | 9. 讲讲算法及数据结构在实习项目中的用处。
60 | 10. 自己的未来规划(就简单描述了一下自己未来的设想啊,说的还挺诚恳,面试官好像还挺满意的)
61 | 11. 你有什么问题要问吗?
62 |
63 | ### 四面(半个技术面)
64 |
65 | 三面面完当天,晚上9点接到面试电话,感觉像是部门或者项目主管。 这个和之前的面试不大相同,感觉面试官主要考察的是你解决问题的能力、学习能力和团队协作能力。
66 |
67 | 1. 让我讲一个自己觉得最不错的项目。然后就巴拉巴拉的聊,我记得主要是问了项目是如何进行协作的、遇到问题是如何解决的、与他人发生冲突是如何解决的这些。感觉聊了挺久。
68 | 2. 出现 OOM 后你会怎么排查问题?
69 | 3. 自己平时是如何学习新技术的?除了 Java 还回去了解其他技术吗?
70 | 4. 上一段实习经历的收获。
71 | 5. NginX如何做负载均衡、常见的负载均衡算法有哪些、一致性哈希的一致性是什么意思、一致性哈希是如何做哈希的
72 | 6. 你有什么问题问我吗?
73 | 7. 还有一些其他的,想不起来了,感觉这一面不是偏向技术来问。
74 |
75 | ## 五面(HR面)
76 |
77 | 1. 自我介绍(主要讲能突出自己的经历,会的编程技术一语带过)。
78 | 2. 你觉得你有什么优点和缺点?如何克服这些缺点?
79 | 3. 说一件大学里你自己比较有成就感的一件事情,为此付出了那些努力。
80 | 4. 你前面跟其他面试官讲过一些你做的项目吧?可以给我讲讲吗?你要考虑到我不是一个做技术的人,怎么让我也听得懂。项目中有什么问题,你怎么解决的?你最大的收获是什么?
81 | 5. 你目前有面试过其他公司吗?如果让你选,这些公司和阿里,你选哪个?(送分题,回答不好可能送命)
82 | 6. 你期望的工作地点是哪里?
83 | 7. 你有什么问题吗?
84 |
85 | ### 总结
86 |
87 | 1. 可以看出面试官问我的很多问题都是比较常见的问题,所以记得一定要提前准备,还要深入准备,不要回答的太皮毛。很多时候一个问题可能会牵扯出很多问题,遇到不会的问题不要慌,冷静分析,如果你真的回答不上来,也不要担心自己是不是就要挂了,很可能这个问题本身就比较难。
88 | 2. 表达能力和沟通能力太重要了,一定要提前练一下,我自身就是一个不太会说话的人,所以,面试前我对于自我介绍、项目介绍和一些常见问题都在脑子里练了好久,确保面试的时候能够很清晰和简洁的说出来。
89 | 3. 等待面试的过程和面试的过程真的好熬人,那段时间我压力也比较大,好在我私下找到学长聊了很多,心情也好了很多。
90 | 4. 面试之后及时总结,面的好的话,不要得意,尽快准备下一场面试吧!
91 |
92 | 我觉得我还算是比较幸运的,最后也祝大家都能获得心仪的Offer。
93 |
94 |
95 |
96 |
97 |
--------------------------------------------------------------------------------
/docs/系统设计/data-communication/summary.md:
--------------------------------------------------------------------------------
1 | > ## RPC
2 |
3 | **RPC(Remote Procedure Call)—远程过程调用** ,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发分布式程序就像开发本地程序一样简单。
4 |
5 | **RPC采用客户端(服务调用方)/服务器端(服务提供方)模式,** 都运行在自己的JVM中。客户端只需要引入要使用的接口,接口的实现和运行都在服务器端。RPC主要依赖的技术包括序列化、反序列化和数据传输协议,这是一种定义与实现相分离的设计。
6 |
7 | **目前Java使用比较多的RPC方案主要有RMI(JDK自带)、Hessian、Dubbo以及Thrift等。**
8 |
9 | **注意: RPC主要指内部服务之间的调用,RESTful也可以用于内部服务之间的调用,但其主要用途还在于外部系统提供服务,因此没有将其包含在本知识点内。**
10 |
11 | ### 常见RPC框架:
12 |
13 | - **RMI(JDK自带):** JDK自带的RPC
14 |
15 | 详细内容可以参考:[从懵逼到恍然大悟之Java中RMI的使用](https://blog.csdn.net/lmy86263/article/details/72594760)
16 |
17 | - **Dubbo:** Dubbo是 阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成。
18 |
19 | 详细内容可以参考:
20 |
21 | - [ 高性能优秀的服务框架-dubbo介绍](https://blog.csdn.net/qq_34337272/article/details/79862899)
22 |
23 | - [Dubbo是什么?能做什么?](https://blog.csdn.net/houshaolin/article/details/76408399)
24 |
25 |
26 | - **Hessian:** Hessian是一个轻量级的remotingonhttp工具,使用简单的方法提供了RMI的功能。 相比WebService,Hessian更简单、快捷。采用的是二进制RPC协议,因为采用的是二进制协议,所以它很适合于发送二进制数据。
27 |
28 | 详细内容可以参考: [Hessian的使用以及理解](https://blog.csdn.net/sunwei_pyw/article/details/74002351)
29 |
30 | - **Thrift:** Apache Thrift是Facebook开源的跨语言的RPC通信框架,目前已经捐献给Apache基金会管理,由于其跨语言特性和出色的性能,在很多互联网公司得到应用,有能力的公司甚至会基于thrift研发一套分布式服务框架,增加诸如服务注册、服务发现等功能。
31 |
32 |
33 | 详细内容可以参考: [【Java】分布式RPC通信框架Apache Thrift 使用总结](https://www.cnblogs.com/zeze/p/8628585.html)
34 |
35 | ### 如何进行选择:
36 |
37 | - **是否允许代码侵入:** 即需要依赖相应的代码生成器生成代码,比如Thrift。
38 | - **是否需要长连接获取高性能:** 如果对于性能需求较高的haul,那么可以果断选择基于TCP的Thrift、Dubbo。
39 | - **是否需要跨越网段、跨越防火墙:** 这种情况一般选择基于HTTP协议的Hessian和Thrift的HTTP Transport。
40 |
41 | 此外,Google推出的基于HTTP2.0的gRPC框架也开始得到应用,其序列化协议基于Protobuf,网络框架使用的是Netty4,但是其需要生成代码,可扩展性也比较差。
42 |
43 | > ## 消息中间件
44 |
45 | **消息中间件,也可以叫做中央消息队列或者是消息队列(区别于本地消息队列,本地消息队列指的是JVM内的队列实现)**,是一种独立的队列系统,消息中间件经常用来解决内部服务之间的 **异步调用问题** 。请求服务方把请求队列放到队列中即可返回,然后等待服务提供方去队列中获取请求进行处理,之后通过回调等机制把结果返回给请求服务方。
46 |
47 | 异步调用只是消息中间件一个非常常见的应用场景。此外,常用的消息队列应用场景还偷如下几个:
48 | - **解耦 :** 一个业务的非核心流程需要依赖其他系统,但结果并不重要,有通知即可。
49 | - **最终一致性 :** 指的是两个系统的状态保持一致,可以有一定的延迟,只要最终达到一致性即可。经常用在解决分布式事务上。
50 | - **广播 :** 消息队列最基本的功能。生产者只负责生产消息,订阅者接收消息。
51 | - **错峰和流控**
52 |
53 |
54 | 具体可以参考:
55 |
56 | [《消息队列深入解析》](https://blog.csdn.net/qq_34337272/article/details/80029918)
57 |
58 | 当前使用较多的消息队列有ActiveMQ(性能差,不推荐使用)、RabbitMQ、RocketMQ、Kafka等等,我们之前提到的redis数据库也可以实现消息队列,不过不推荐,redis本身设计就不是用来做消息队列的。
59 |
60 | - **ActiveMQ:** ActiveMQ是Apache出品,最流行的,能力强劲的开源消息总线。ActiveMQ是一个完全支持JMS1.1和J2EE 1.4规范的JMSProvider实现,尽管JMS规范出台已经是很久的事情了,但是JMS在当今的J2EE应用中间仍然扮演着特殊的地位。
61 |
62 | 具体可以参考:
63 |
64 | [《消息队列ActiveMQ的使用详解》](https://blog.csdn.net/qq_34337272/article/details/80031702)
65 |
66 | - **RabbitMQ:** RabbitMQ 是一个由 Erlang 语言开发的 AMQP 的开源实现。RabbitMQ 最初起源于金融系统,用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗
67 | > AMQP :Advanced Message Queue,高级消息队列协议。它是应用层协议的一个开放标准,为面向消息的中间件设计,基于此协议的客户端与消息中间件可传递消息,并不受产品、开发语言等条件的限制。
68 |
69 |
70 | 具体可以参考:
71 |
72 | [《消息队列之 RabbitMQ》](https://www.jianshu.com/p/79ca08116d57)
73 |
74 | - **RocketMQ:**
75 |
76 | 具体可以参考:
77 |
78 | [《RocketMQ 实战之快速入门》](https://www.jianshu.com/p/824066d70da8)
79 |
80 | [《十分钟入门RocketMQ》](http://jm.taobao.org/2017/01/12/rocketmq-quick-start-in-10-minutes/) (阿里中间件团队博客)
81 |
82 |
83 | - **Kafka**:Kafka是一个分布式的、可分区的、可复制的、基于发布/订阅的消息系统(现在官方的描述是“一个分布式流平台”),Kafka主要用于大数据领域,当然在分布式系统中也有应用。目前市面上流行的消息队列RocketMQ就是阿里借鉴Kafka的原理、用Java开发而得。
84 |
85 | 具体可以参考:
86 |
87 | [《Kafka应用场景》](http://book.51cto.com/art/201801/565244.htm)
88 |
89 | [《初谈Kafka》](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484106&idx=1&sn=aa1999895d009d91eb3692a3e6429d18&chksm=fd9854abcaefddbd1101ca5dc2c7c783d7171320d6300d9b2d8e68b7ef8abd2b02ea03e03600#rd)
90 |
91 | **推荐阅读:**
92 |
93 | [《Kafka、RabbitMQ、RocketMQ等消息中间件的对比 —— 消息发送性能和区别》](https://mp.weixin.qq.com/s?__biz=MzU5OTMyODAyNg==&mid=2247484721&idx=1&sn=11e4e29886e581dd328311d308ccc068&chksm=feb7d144c9c058529465b02a4e26a25ef76b60be8984ace9e4a0f5d3d98ca52e014ecb73b061&scene=21#wechat_redirect)
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/docs/简历与面经/PreparingForInterview/JavaProgrammerNeedKnow.md:
--------------------------------------------------------------------------------
1 | 身边的朋友或者公众号的粉丝很多人都向我询问过:“我是双非/三本/专科学校的,我有机会进入大厂吗?”、“非计算机专业的学生能学好吗?”、“如何学习Java?”、“Java学习该学哪些东西?”、“我该如何准备Java面试?”......这些方面的问题。我会根据自己的一点经验对大部分人关心的这些问题进行答疑解惑。现在又刚好赶上考研结束,这篇文章也算是给考研结束准备往Java后端方向发展的朋友们指明一条学习之路。道理懂了如果没有实际行动,那这篇文章对你或许没有任何意义。
2 |
3 | ### Question1:我是双非/三本/专科学校的,我有机会进入大厂吗?
4 |
5 | 我自己也是非985非211学校的,结合自己的经历以及一些朋友的经历,我觉得让我回答这个问题再好不过。
6 |
7 | 首先,我觉得学校歧视很正常,真的太正常了,如果要抱怨的话,你只能抱怨自己没有进入名校。但是,千万不要动不动说自己学校差,动不动拿自己学校当做自己进不了大厂的借口,学历只是筛选简历的很多标准中的一个而已,如果你够优秀,简历够丰富,你也一样可以和名校同学一起同台竞争。
8 |
9 | 企业HR肯定是更喜欢高学历的人,毕竟985、211优秀人才比例肯定比普通学校高很多,HR团队肯定会优先在这些学校里选。这就好比相亲,你是愿意在很多优秀的人中选一个优秀的,还是愿意在很多普通的人中选一个优秀的呢?
10 |
11 | 双非本科甚至是二本、三本甚至是专科的同学也有很多进入大厂的,不过比率相比于名校的低很多而已。从大厂招聘的结果上看,高学历人才的数量占据大头,那些成功进入BAT、美团,京东,网易等大厂的双非本科甚至是二本、三本甚至是专科的同学往往是因为具备丰富的项目经历或者在某个含金量比较高的竞赛比如ACM中取得了不错的成绩。**一部分学历不突出但能力出众的面试者能够进入大厂并不是说明学历不重要,而是学历的软肋能够通过其他的优势来弥补。** 所以,如果你的学校不够好而你自己又想去大厂的话,建议你可以从这几点来做:**①尽量在面试前最好有一个可以拿的出手的项目;②有实习条件的话,尽早出去实习,实习经历也会是你的简历的一个亮点(有能力在大厂实习最佳!);③参加一些含金量比较高的比赛,拿不拿得到名次没关系,重在锻炼。**
12 |
13 |
14 | ### Question2:非计算机专业的学生能学好Java后台吗?我能进大厂吗?
15 |
16 | 当然可以!现在非科班的程序员很多,很大一部分原因是互联网行业的工资比较高。我们学校外面的培训班里面90%都是非科班,我觉得他们很多人学的都还不错。另外,我的一个朋友本科是机械专业,大一开始自学安卓,技术贼溜,在我看来他比大部分本科是计算机的同学学的还要好。参考Question1的回答,即使你是非科班程序员,如果你想进入大厂的话,你也可以通过自己的其他优势来弥补。
17 |
18 | 我觉得我们不应该因为自己的专业给自己划界限或者贴标签,说实话,很多科班的同学可能并不如你,你以为科班的同学就会认真听讲吗?还不是几乎全靠自己课下自学!不过如果你是非科班的话,你想要学好,那么注定就要舍弃自己本专业的一些学习时间,这是无可厚非的。
19 |
20 | 建议非科班的同学,首先要打好计算机基础知识基础:①计算机网络、②操作系统、③数据机构与算法,我个人觉得这3个对你最重要。这些东西就像是内功,对你以后的长远发展非常有用。当然,如果你想要进大厂的话,这些知识也是一定会被问到的。另外,“一定学好数据结构与算法!一定学好数据结构与算法!一定学好数据结构与算法!”,重要的东西说3遍。
21 |
22 |
23 |
24 | ### Question3: 我没有实习经历的话找工作是不是特别艰难?
25 |
26 | 没有实习经历没关系,只要你有拿得出手的项目或者大赛经历的话,你依然有可能拿到大厂的 offer 。笔主当时找工作的时候就没有实习经历以及大赛获奖经历,单纯就是凭借自己的项目经验撑起了整个面试。
27 |
28 | 如果你既没有实习经历,又没有拿得出手的项目或者大赛经历的话,我觉得在简历关,除非你有其他特别的亮点,不然,你应该就会被刷。
29 |
30 | ### Question4: 我该如何准备面试呢?面试的注意事项有哪些呢?
31 |
32 | 下面是我总结的一些准备面试的Tips以及面试必备的注意事项:
33 |
34 | 1. **准备一份自己的自我介绍,面试的时候根据面试对象适当进行修改**(突出重点,突出自己的优势在哪里,切忌流水账);
35 | 2. **注意随身带上自己的成绩单和简历复印件;** (有的公司在面试前都会让你交一份成绩单和简历当做面试中的参考。)
36 | 3. **如果需要笔试就提前刷一些笔试题,大部分在线笔试的类型是选择题+编程题,有的还会有简答题。**(平时空闲时间多的可以刷一下笔试题目(牛客网上有很多),但是不要只刷面试题,不动手code,程序员不是为了考试而存在的。)另外,注意抓重点,因为题目太多了,但是有很多题目几乎次次遇到,像这样的题目一定要搞定。
37 | 4. **提前准备技术面试。** 搞清楚自己面试中可能涉及哪些知识点、哪些知识点是重点。面试中哪些问题会被经常问到、自己该如何回答。(强烈不推荐背题,第一:通过背这种方式你能记住多少?能记住多久?第二:背题的方式的学习很难坚持下去!)
38 | 5. **面试之前做好定向复习。** 也就是专门针对你要面试的公司来复习。比如你在面试之前可以在网上找找有没有你要面试的公司的面经。
39 | 6. **准备好自己的项目介绍。** 如果有项目的话,技术面试第一步,面试官一般都是让你自己介绍一下你的项目。你可以从下面几个方向来考虑:①对项目整体设计的一个感受(面试官可能会让你画系统的架构图);②在这个项目中你负责了什么、做了什么、担任了什么角色;③ 从这个项目中你学会了那些东西,使用到了那些技术,学会了那些新技术的使用;④项目描述中,最好可以体现自己的综合素质,比如你是如何协调项目组成员协同开发的或者在遇到某一个棘手的问题的时候你是如何解决的又或者说你在这个项目用了什么技术实现了什么功能比如:用 redis 做缓存提高访问速度和并发量、使用消息队列削峰和降流等等。
40 | 7. **面试之后记得复盘。** 面试遭遇失败是很正常的事情,所以善于总结自己的失败原因才是最重要的。如果失败,不要灰心;如果通过,切勿狂喜。
41 |
42 |
43 | **一些还算不错的 Java面试/学习相关的仓库,相信对大家准备面试一定有帮助:**[盘点一下Github上开源的Java面试/学习相关的仓库,看完弄懂薪资至少增加10k](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484817&idx=1&sn=12f0c254a240c40c2ccab8314653216b&chksm=fd9853f0caefdae6d191e6bf085d44ab9c73f165e3323aa0362d830e420ccbfad93aa5901021&token=766994974&lang=zh_CN#rd)
44 |
45 | ### Question5: 我该自学还是报培训班呢?
46 |
47 | 我本人更加赞同自学(你要知道去了公司可没人手把手教你了,而且几乎所有的公司都对培训班出生的有偏见。为什么有偏见,你学个东西还要去培训班,说明什么,同等水平下,你的自学能力以及自律能力一定是比不上自学的人的)。但是如果,你连每天在寝室坚持学上8个小时以上都坚持不了,或者总是容易半途而废的话,我还是推荐你去培训班。观望身边同学去培训班的,大多是非计算机专业或者是没有自律能力以及自学能力非常差的人。
48 |
49 | 另外,如果自律能力不行,你也可以通过结伴学习、参加老师的项目等方式来督促自己学习。
50 |
51 | 总结:去不去培训班主要还是看自己,如果自己能坚持自学就自学,坚持不下来就去培训班。
52 |
53 | ### Question6: 没有项目经历/博客/Github开源项目怎么办?
54 |
55 | 从现在开始做!
56 |
57 | 网上有很多非常不错的项目视频,你就跟着一步一步做,不光要做,还要改进,改善。另外,如果你的老师有相关 Java 后台项目的话,你也可以主动申请参与进来。
58 |
59 | 如果有自己的博客,也算是简历上的一个亮点。建议可以在掘金、Segmentfault、CSDN等技术交流社区写博客,当然,你也可以自己搭建一个博客(采用 Hexo+Githu Pages 搭建非常简单)。写一些什么?学习笔记、实战内容、读书笔记等等都可以。
60 |
61 | 多用 Github,用好 Github,上传自己不错的项目,写好 readme 文档,在其他技术社区做好宣传。相信你也会收获一个不错的开源项目!
62 |
63 |
64 | ### Question7: 大厂到底青睐什么样的应届生?
65 |
66 | 从阿里、腾讯等大厂招聘官网对于Java后端方向/后端方向的应届实习生的要求,我们大概可以总结归纳出下面这 4 点能给简历增加很多分数:
67 |
68 | - 参加过竞赛(含金量超高的是ACM);
69 | - 对数据结构与算法非常熟练;
70 | - 参与过实际项目(比如学校网站);
71 | - 参与过某个知名的开源项目或者自己的某个开源项目很不错;
72 |
73 | 除了我上面说的这三点,在面试Java工程师的时候,下面几点也提升你的个人竞争力:
74 |
75 | - 熟悉Python、Shell、Perl等脚本语言;
76 | - 熟悉 Java 优化,JVM调优;
77 | - 熟悉 SOA 模式;
78 | - 熟悉自己所用框架的底层知识比如Spring;
79 | - 了解分布式一些常见的理论;
80 | - 具备高并发开发经验;大数据开发经验等等。
81 |
82 |
--------------------------------------------------------------------------------
/docs/数据结构与算法/面试官问你什么B树和B+树,把这篇文章丢给他.md:
--------------------------------------------------------------------------------
1 | ## 1、 B树
2 |
3 | 在介绍B+树之前, 先简单的介绍一下B树,这两种数据结构既有相似之处,也有他们的区别,最后,我们也会对比一下这两种数据结构的区别。
4 |
5 | ### 1.1 B树概念
6 |
7 | B树也称B-树,它是一颗多路平衡查找树。二叉树我想大家都不陌生,其实,B树和后面讲到的B+树也是从最简单的二叉树变换而来的,并没有什么神秘的地方,下面我们来看看B树的定义。
8 |
9 | (1)每个节点最多有m-1个关键字(可以存有的键值对)。
10 |
11 | (2)根节点最少可以只有1个关键字。
12 |
13 | (3)非根节点至少有m/2个关键字。
14 |
15 | (4)每个节点中的关键字都按照从小到大的顺序排列,每个关键字的左子树中的所有关键字都小于它,而右子树中的所有关键字都大于它。
16 |
17 | (5)所有叶子节点都位于同一层,或者说根节点到每个叶子节点的长度都相同。
18 |
19 | (6)每个节点都存有索引和数据,也就是对应的key和value。
20 |
21 | 所以,根节点的关键字数量范围:1 <= k <= m-1,非根节点的关键字数量范围:m/2 <= k <= m-1。
22 | 另外,我们需要注意一个概念,描述一颗B树时需要指定它的阶数,阶数表示了一个节点最多有多少个孩子节点,一般用字母m表示阶数。
23 |
24 | 我们再举个例子来说明一下上面的概念,比如这里有一个5阶的B树,根节点数量范围:1 <= k <= 4,非根节点数量范围:2 <= k <= 4。
25 | 下面,我们通过一个插入的例子,讲解一下B树的插入过程,接着,再讲解一下删除关键字的过程。
26 |
27 |
28 | ### 1.2 B树插入
29 |
30 | 插入的时候,我们需要记住一个规则:判断当前结点key的个数是否小于等于m-1,如果满足,直接插入即可,如果不满足,将节点的中间的key将这个节点分为左右两部分,中间的节点放到父节点中即可。
31 |
32 | 例子:在5阶B树中,结点最多有4个key,最少有2个key(注意:下面的节点统一用一个节点表示key和value)。
33 |
34 | 插入18,70,50,40
35 |
36 | 
37 |
38 | 插入22
39 |
40 | 
41 |
42 | 插入22时,发现这个节点的关键字已经大于4了,所以需要进行分裂,分裂的规则在上面已经讲了,分裂之后,如下。
43 |
44 | 
45 |
46 | 接着插入23,25,39
47 |
48 | 
49 |
50 | 分裂,得到下面的。
51 |
52 | 
53 |
54 | 更过的插入的过程就不多介绍了,相信有这个例子你已经知道怎么进行插入操作了。
55 |
56 | ### 1.3 B树的删除操作
57 |
58 | B树的删除操作相对于插入操作是相对复杂一些的,但是,你知道记住几种情况,一样可以很轻松的掌握的。
59 |
60 | 现在有一个初始状态是下面这样的B树,然后进行删除操作。
61 |
62 | 
63 |
64 | 删除15,这种情况是删除叶子节点的元素,如果删除之后,节点数还是大于m/2,这种情况只要直接删除即可。
65 |
66 | 
67 |
68 | 
69 |
70 | 接着,我们把22删除,这种情况的规则:22是非叶子节点,对于非叶子节点的删除,我们需要用后继key(元素)覆盖要删除的key,然后在后继key所在的子支中删除该后继key。对于删除22,需要将后继元素24移到被删除的22所在的节点。
71 |
72 | 
73 |
74 | 
75 |
76 | 此时发现26所在的节点只有一个元素,小于2个(m/2),这个节点不符合要求,这时候的规则(向兄弟节点借元素):如果删除叶子节点,如果删除元素后元素个数少于(m/2),并且它的兄弟节点的元素大于(m/2),也就是说兄弟节点的元素比最少值m/2还多,将先将父节点的元素移到该节点,然后将兄弟节点的元素再移动到父节点。这样就满足要求了。
77 |
78 | 我们看看操作过程就更加明白了。
79 |
80 | 
81 |
82 | 
83 |
84 | 接着删除28,删除叶子节点,删除后不满足要求,所以,我们需要考虑向兄弟节点借元素,但是,兄弟节点也没有多的节点(2个),借不了,怎么办呢?如果遇到这种情况,首先,还是将先将父节点的元素移到该节点,然后,将当前节点及它的兄弟节点中的key合并,形成一个新的节点。
85 |
86 | 
87 |
88 | 移动之后,跟兄弟节点合并。
89 |
90 | 
91 |
92 | 删除就只有上面的几种情况,根据不同的情况进行删除即可。
93 |
94 | 上面的这些介绍,相信对于B树已经有一定的了解了,接下来的一部分,我们接着讲解B+树,我相信加上B+树的对比,就更加清晰明了了。
95 |
96 |
97 | ## 2、 B+树
98 |
99 |
100 | ### 2.1 B+树概述
101 |
102 | B+树其实和B树是非常相似的,我们首先看看相同点。
103 |
104 | (1)根节点至少一个元素
105 |
106 | (2)非根节点元素范围:m/2 <= k <= m-1
107 |
108 | 不同点:
109 |
110 | (1)B+树有两种类型的节点:内部结点(也称索引结点)和叶子结点。内部节点就是非叶子节点,内部节点不存储数据,只存储索引,数据都存储在叶子节点。
111 |
112 | (2)内部结点中的key都按照从小到大的顺序排列,对于内部结点中的一个key,左树中的所有key都小于它,右子树中的key都大于等于它。叶子结点中的记录也按照key的大小排列。
113 |
114 | (3)每个叶子结点都存有相邻叶子结点的指针,叶子结点本身依关键字的大小自小而大顺序链接。
115 |
116 | (4)父节点存有右孩子的第一个元素的索引。
117 |
118 | 下面我们看一个B+树的例子,感受感受它吧!
119 |
120 | 
121 |
122 | ### 2.2 插入操作
123 |
124 | 对于插入操作很简单,只需要记住一个技巧即可:当节点元素数量大于m-1的时候,按中间元素分裂成左右两部分,中间元素分裂到父节点当做索引存储,但是,本身中间元素还是分裂右边这一部分的。
125 |
126 | 下面以一颗5阶B+树的插入过程为例,5阶B+树的节点最少2个元素,最多4个元素。
127 |
128 | 插入5,10,15,20
129 |
130 | 
131 |
132 | 插入25,此时元素数量大于4个了,分裂
133 |
134 | 
135 |
136 | 接着插入26,30,继续分裂
137 |
138 | 
139 |
140 | 
141 |
142 | 有了这几个例子,相信插入操作没什么问题了,下面接着看看删除操作。
143 |
144 |
145 | ### 2.3 删除操作
146 |
147 | 对于删除操作是比B树简单一些的,因为叶子节点有指针的存在,向兄弟节点借元素时,不需要通过父节点了,而是可以直接通过兄弟节移动即可(前提是兄弟节点的元素大于m/2),然后更新父节点的索引;如果兄弟节点的元素不大于m/2(兄弟节点也没有多余的元素),则将当前节点和兄弟节点合并,并且删除父节点中的key,下面我们看看具体的实例。
148 |
149 | 初始状态
150 |
151 | 
152 |
153 | 删除10,删除后,不满足要求,发现左边兄弟节点有多余的元素,所以去借元素,最后,修改父节点索引
154 |
155 | 
156 |
157 | 删除元素5,发现不满足要求,并且发现左右兄弟节点都没有多余的元素,所以,可以选择和兄弟节点合并,最后修改父节点索引
158 |
159 | 
160 |
161 | 发现父节点索引也不满足条件,所以,需要做跟上面一步一样的操作
162 |
163 | 
164 |
165 | 这样,B+树的删除操作也就完成了,是不是看完之后,觉得非常简单!
166 |
167 |
168 | ## 3 B树和B+树总结
169 |
170 | B+树相对于B树有一些自己的优势,可以归结为下面几点:
171 |
172 | (1)单一节点存储的元素更多,使得查询的IO次数更少,所以也就使得它更适合做为数据库MySQL的底层数据结构了。
173 |
174 | (2)所有的查询都要查找到叶子节点,查询性能是稳定的,而B树,每个节点都可以查找到数据,所以不稳定。
175 |
176 | (3)所有的叶子节点形成了一个有序链表,更加便于查找。
--------------------------------------------------------------------------------
/docs/简历与面经/PreparingForInterview/程序员的简历之道.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | - [程序员简历就该这样写](#程序员简历就该这样写)
4 | - [为什么说简历很重要?](#为什么说简历很重要)
5 | - [先从面试前来说](#先从面试前来说)
6 | - [再从面试中来说](#再从面试中来说)
7 | - [下面这几点你必须知道](#下面这几点你必须知道)
8 | - [必须了解的两大法则](#必须了解的两大法则)
9 | - [STAR法则(Situation Task Action Result)](#star法则situation-task-action-result)
10 | - [FAB 法则(Feature Advantage Benefit)](#fab-法则feature-advantage-benefit)
11 | - [项目经历怎么写?](#项目经历怎么写)
12 | - [专业技能该怎么写?](#专业技能该怎么写)
13 | - [排版注意事项](#排版注意事项)
14 | - [其他的一些小tips](#其他的一些小tips)
15 | - [推荐的工具/网站](#推荐的工具网站)
16 |
17 |
18 |
19 | # 程序员简历就该这样写
20 |
21 | 本篇文章除了教大家用Markdown如何写一份程序员专属的简历,后面还会给大家推荐一些不错的用来写Markdown简历的软件或者网站,以及如何优雅的将Markdown格式转变为PDF格式或者其他格式。
22 |
23 | 推荐大家使用Markdown语法写简历,然后再将Markdown格式转换为PDF格式后进行简历投递。
24 |
25 | 如果你对Markdown语法不太了解的话,可以花半个小时简单看一下Markdown语法说明: http://www.markdown.cn 。
26 |
27 | ## 为什么说简历很重要?
28 |
29 | 一份好的简历可以在整个申请面试以及面试过程中起到非常好的作用。 在不夸大自己能力的情况下,写出一份好的简历也是一项很棒的能力。为什么说简历很重要呢?
30 |
31 | ### 先从面试前来说
32 |
33 | - 假如你是网申,你的简历必然会经过HR的筛选,一张简历HR可能也就花费10秒钟看一下,然后HR就会决定你这一关是Fail还是Pass。
34 | - 假如你是内推,如果你的简历没有什么优势的话,就算是内推你的人再用心,也无能为力。
35 |
36 | 另外,就算你通过了筛选,后面的面试中,面试官也会根据你的简历来判断你究竟是否值得他花费很多时间去面试。
37 |
38 | 所以,简历就像是我们的一个门面一样,它在很大程度上决定了你能否进入到下一轮的面试中。
39 |
40 | ### 再从面试中来说
41 |
42 | 我发现大家比较喜欢看面经 ,这点无可厚非,但是大部分面经都没告诉你很多问题都是在特定条件下才问的。举个简单的例子:一般情况下你的简历上注明你会的东西才会被问到(Java、数据结构、网络、算法这些基础是每个人必问的),比如写了你会 redis,那面试官就很大概率会问你 redis 的一些问题。比如:redis的常见数据类型及应用场景、redis是单线程为什么还这么快、 redis 和 memcached 的区别、redis 内存淘汰机制等等。
43 |
44 | 所以,首先,你要明确的一点是:**你不会的东西就不要写在简历上**。另外,**你要考虑你该如何才能让你的亮点在简历中凸显出来**,比如:你在某某项目做了什么事情解决了什么问题(只要有项目就一定有要解决的问题)、你的某一个项目里使用了什么技术后整体性能和并发量提升了很多等等。
45 |
46 | 面试和工作是两回事,聪明的人会把面试官往自己擅长的领域领,其他人则被面试官牵着鼻子走。虽说面试和工作是两回事,但是你要想要获得自己满意的 offer ,你自身的实力必须要强。
47 |
48 | ## 下面这几点你必须知道
49 |
50 | 1. 大部分公司的HR都说我们不看重学历(骗你的!),但是如果你的学校不出众的话,很难在一堆简历中脱颖而出,除非你的简历上有特别的亮点,比如:某某大厂的实习经历、获得了某某大赛的奖等等。
51 | 2. **大部分应届生找工作的硬伤是没有工作经验或实习经历,所以如果你是应届生就不要错过秋招和春招。一旦错过,你后面就极大可能会面临社招,这个时候没有工作经验的你可能就会面临各种碰壁,导致找不到一个好的工作**
52 | 3. **写在简历上的东西一定要慎重,这是面试官大量提问的地方;**
53 | 4. **将自己的项目经历完美的展示出来非常重要。**
54 |
55 | ## 必须了解的两大法则
56 |
57 | ### STAR法则(Situation Task Action Result)
58 |
59 | - **Situation:** 事情是在什么情况下发生;
60 | - **Task::** 你是如何明确你的任务的;
61 | - **Action:** 针对这样的情况分析,你采用了什么行动方式;
62 | - **Result:** 结果怎样,在这样的情况下你学习到了什么。
63 |
64 | 简而言之,STAR法则,就是一种讲述自己故事的方式,或者说,是一个清晰、条理的作文模板。不管是什么,合理熟练运用此法则,可以轻松的对面试官描述事物的逻辑方式,表现出自己分析阐述问题的清晰性、条理性和逻辑性。
65 |
66 | ### FAB 法则(Feature Advantage Benefit)
67 |
68 | - **Feature:** 是什么;
69 | - **Advantage:** 比别人好在哪些地方;
70 | - **Benefit:** 如果雇佣你,招聘方会得到什么好处。
71 |
72 | 简单来说,这个法则主要是让你的面试官知道你的优势、招了你之后对公司有什么帮助。
73 |
74 | ## 项目经历怎么写?
75 |
76 | 简历上有一两个项目经历很正常,但是真正能把项目经历很好的展示给面试官的非常少。对于项目经历大家可以考虑从如下几点来写:
77 |
78 | 1. 对项目整体设计的一个感受
79 | 2. 在这个项目中你负责了什么、做了什么、担任了什么角色
80 | 3. 从这个项目中你学会了那些东西,使用到了那些技术,学会了那些新技术的使用
81 | 4. 另外项目描述中,最好可以体现自己的综合素质,比如你是如何协调项目组成员协同开发的或者在遇到某一个棘手的问题的时候你是如何解决的又或者说你在这个项目用了什么技术实现了什么功能比如:用redis做缓存提高访问速度和并发量、使用消息队列削峰和降流等等。
82 |
83 | ## 专业技能该怎么写?
84 |
85 | 先问一下你自己会什么,然后看看你意向的公司需要什么。一般HR可能并不太懂技术,所以他在筛选简历的时候可能就盯着你专业技能的关键词来看。对于公司有要求而你不会的技能,你可以花几天时间学习一下,然后在简历上可以写上自己了解这个技能。比如你可以这样写(下面这部分内容摘自我的简历,大家可以根据自己的情况做一些修改和完善):
86 |
87 | - 计算机网络、数据结构、算法、操作系统等课内基础知识:掌握
88 | - Java 基础知识:掌握
89 | - JVM 虚拟机(Java内存区域、虚拟机垃圾算法、虚拟垃圾收集器、JVM内存管理):掌握
90 | - 高并发、高可用、高性能系统开发:掌握
91 | - Struts2、Spring、Hibernate、Ajax、Mybatis、JQuery :掌握
92 | - SSH 整合、SSM 整合、 SOA 架构:掌握
93 | - Dubbo: 掌握
94 | - Zookeeper: 掌握
95 | - 常见消息队列: 掌握
96 | - Linux:掌握
97 | - MySQL常见优化手段:掌握
98 | - Spring Boot +Spring Cloud +Docker:了解
99 | - Hadoop 生态相关技术中的 HDFS、Storm、MapReduce、Hive、Hbase :了解
100 | - Python 基础、一些常见第三方库比如OpenCV、wxpy、wordcloud、matplotlib:熟悉
101 |
102 | ## 排版注意事项
103 |
104 | 1. 尽量简洁,不要太花里胡哨;
105 | 2. 一些技术名词不要弄错了大小写比如MySQL不要写成mysql,Java不要写成java。这个在我看来还是比较忌讳的,所以一定要注意这个细节;
106 | 3. 中文和数字英文之间加上空格的话看起来会舒服一点;
107 |
108 | ## 其他的一些小tips
109 |
110 | 1. 尽量避免主观表述,少一点语义模糊的形容词,尽量要简洁明了,逻辑结构清晰。
111 | 2. 如果自己有博客或者个人技术栈点的话,写上去会为你加分很多。
112 | 3. 如果自己的Github比较活跃的话,写上去也会为你加分很多。
113 | 4. 注意简历真实性,一定不要写自己不会的东西,或者带有欺骗性的内容
114 | 5. 项目经历建议以时间倒序排序,另外项目经历不在于多,而在于有亮点。
115 | 6. 如果内容过多的话,不需要非把内容压缩到一页,保持排版干净整洁就可以了。
116 | 7. 简历最后最好能加上:“感谢您花时间阅读我的简历,期待能有机会和您共事。”这句话,显的你会很有礼貌。
117 |
118 | ## 推荐的工具/网站
119 |
120 | - 冷熊简历(MarkDown在线简历工具,可在线预览、编辑和生成PDF):
121 | - Typora+[Java程序员简历模板](https://github.com/geekcompany/ResumeSample/blob/master/java.md)
122 |
--------------------------------------------------------------------------------
/docs/系统设计/设计模式.md:
--------------------------------------------------------------------------------
1 | # Java 设计模式
2 |
3 | 下面是自己学习设计模式的时候做的总结,有些是自己的原创文章,有些是网上写的比较好的文章,保存下来细细消化吧!
4 |
5 | **系列文章推荐:**
6 |
7 | ## 创建型模式
8 |
9 | ### 创建型模式概述
10 |
11 | - 创建型模式(Creational Pattern)对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。为了使软件的结构更加清晰,外界对于这些对象只需要知道它们共同的接口,而不清楚其具体的实现细节,使整个系统的设计更加符合单一职责原则。
12 | - 创建型模式在创建什么(What),由谁创建(Who),何时创建(When)等方面都为软件设计者提供了尽可能大的灵活性。创建型模式隐藏了类的实例的创建细节,通过隐藏对象如何被创建和组合在一起达到使整个系统独立的目的。
13 |
14 | 
15 |
16 | ### 常见创建型模式详解
17 |
18 | - **单例模式:** [深入理解单例模式——只有一个实例](https://blog.csdn.net/qq_34337272/article/details/80455972)
19 | - **工厂模式:** [深入理解工厂模式——由对象工厂生成对象](https://blog.csdn.net/qq_34337272/article/details/80472071)
20 | - **建造者模式:** [深入理解建造者模式 ——组装复杂的实例](http://blog.csdn.net/qq_34337272/article/details/80540059)
21 | - **原型模式:** [深入理解原型模式 ——通过复制生成实例](https://blog.csdn.net/qq_34337272/article/details/80706444)
22 |
23 | ## 结构型模式
24 |
25 | ### 结构型模式概述
26 |
27 | - **结构型模式(Structural Pattern):** 描述如何将类或者对象结合在一起形成更大的结构,就像搭积木,可以通过简单积木的组合形成复杂的、功能更为强大的结构
28 | 
29 | - **结构型模式可以分为类结构型模式和对象结构型模式:**
30 | - 类结构型模式关心类的组合,由多个类可以组合成一个更大的系统,在类结构型模式中一般只存在继承关系和实现关系。
31 | - 对象结构型模式关心类与对象的组合,通过关联关系使得在一个类中定义另一个类的实例对象,然后通过该对象调用其方法。根据“合成复用原则”,在系统中尽量使用关联关系来替代继承关系,因此大部分结构型模式都是对象结构型模式。
32 |
33 | 
34 |
35 | ### 常见结构型模式详解
36 |
37 | - **适配器模式:**
38 | - [深入理解适配器模式——加个“适配器”以便于复用](https://segmentfault.com/a/1190000011856448)
39 | - [适配器模式原理及实例介绍-IBM](https://www.ibm.com/developerworks/cn/java/j-lo-adapter-pattern/index.html)
40 | - **桥接模式:** [设计模式笔记16:桥接模式(Bridge Pattern)](https://blog.csdn.net/yangzl2008/article/details/7670996)
41 | - **组合模式:** [大话设计模式—组合模式](https://blog.csdn.net/lmb55/article/details/51039781)
42 | - **装饰模式:** [java模式—装饰者模式](https://www.cnblogs.com/chenxing818/p/4705919.html)、[Java设计模式-装饰者模式](https://blog.csdn.net/cauchyweierstrass/article/details/48240147)
43 | - **外观模式:** [java设计模式之外观模式(门面模式)](https://www.cnblogs.com/lthIU/p/5860607.html)
44 | - **享元模式:** [享元模式](http://www.jasongj.com/design_pattern/flyweight/)
45 | - **代理模式:**
46 | - [代理模式原理及实例讲解 (IBM出品,很不错)](https://www.ibm.com/developerworks/cn/java/j-lo-proxy-pattern/index.html)
47 | - [轻松学,Java 中的代理模式及动态代理](https://blog.csdn.net/briblue/article/details/73928350)
48 | - [Java代理模式及其应用](https://blog.csdn.net/justloveyou_/article/details/74203025)
49 |
50 |
51 | ## 行为型模式
52 |
53 | ### 行为型模式概述
54 |
55 | - 行为型模式(Behavioral Pattern)是对在不同的对象之间划分责任和算法的抽象化。
56 | - 行为型模式不仅仅关注类和对象的结构,而且重点关注它们之间的相互作用。
57 | - 通过行为型模式,可以更加清晰地划分类与对象的职责,并研究系统在运行时实例对象之间的交互。在系统运行时,对象并不是孤立的,它们可以通过相互通信与协作完成某些复杂功能,一个对象在运行时也将影响到其他对象的运行。
58 |
59 | **行为型模式分为类行为型模式和对象行为型模式两种:**
60 |
61 | - **类行为型模式:** 类的行为型模式使用继承关系在几个类之间分配行为,类行为型模式主要通过多态等方式来分配父类与子类的职责。
62 | - **对象行为型模式:** 对象的行为型模式则使用对象的聚合关联关系来分配行为,对象行为型模式主要是通过对象关联等方式来分配两个或多个类的职责。根据“合成复用原则”,系统中要尽量使用关联关系来取代继承关系,因此大部分行为型设计模式都属于对象行为型设计模式。
63 |
64 | 
65 |
66 | - **职责链模式:**
67 | - [Java设计模式之责任链模式、职责链模式](https://blog.csdn.net/jason0539/article/details/45091639)
68 | - [责任链模式实现的三种方式](https://www.cnblogs.com/lizo/p/7503862.html)
69 | - **命令模式:** 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计,使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活。命令模式可以对发送者和接收者完全解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。这就是命令模式的模式动机。
70 | - **解释器模式:**
71 | - **迭代器模式:**
72 | - **中介者模式:**
73 | - **备忘录模式:**
74 | - **观察者模式:** 、
75 | - **状态模式:**
76 | - **策略模式:**
77 |
78 | 策略模式作为设计原则中开闭原则最典型的体现,也是经常使用的。下面这篇博客介绍了策略模式一般的组成部分和概念,并用了一个小demo去说明了策略模式的应用。
79 |
80 | [java设计模式之策略模式](https://blog.csdn.net/zlj1217/article/details/81230077)
81 |
82 | - **模板方法模式:**
83 | - **访问者模式:**
84 |
85 |
--------------------------------------------------------------------------------
/docs/数据库/Redis/如何做可靠的分布式锁,Redlock真的可行么.md:
--------------------------------------------------------------------------------
1 | 本文是对 [Martin Kleppmann](https://martin.kleppmann.com/) 的文章 [How to do distributed locking](https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html) 部分内容的翻译和总结,上次写 Redlock 的原因就是看到了 Martin 的这篇文章,写得很好,特此翻译和总结。感兴趣的同学可以翻看原文,相信会收获良多。
2 |
3 | 开篇作者认为现在 Redis 逐渐被使用到数据管理领域,这个领域需要更强的数据一致性和耐久性,这使得他感到担心,因为这不是 Redis 最初设计的初衷(事实上这也是很多业界程序员的误区,越来越把 Redis 当成数据库在使用),其中基于 Redis 的分布式锁就是令人担心的其一。
4 |
5 | Martin 指出首先你要明确你为什么使用分布式锁,为了性能还是正确性?为了帮你区分这二者,在这把锁 fail 了的时候你可以询问自己以下问题:
6 | 1. **要性能的:** 拥有这把锁使得你不会重复劳动(例如一个 job 做了两次),如果这把锁 fail 了,两个节点同时做了这个 Job,那么这个 Job 增加了你的成本。
7 | 2. **要正确性的:** 拥有锁可以防止并发操作污染你的系统或者数据,如果这把锁 fail 了两个节点同时操作了一份数据,结果可能是数据不一致、数据丢失、file 冲突等,会导致严重的后果。
8 |
9 | 上述二者都是需求锁的正确场景,但是你必须清楚自己是因为什么原因需要分布式锁。
10 |
11 | 如果你只是为了性能,那没必要用 Redlock,它成本高且复杂,你只用一个 Redis 实例也够了,最多加个从防止主挂了。当然,你使用单节点的 Redis 那么断电或者一些情况下,你会丢失锁,但是你的目的只是加速性能且断电这种事情不会经常发生,这并不是什么大问题。并且如果你使用了单节点 Redis,那么很显然你这个应用需要的锁粒度是很模糊粗糙的,也不会是什么重要的服务。
12 |
13 | 那么是否 Redlock 对于要求正确性的场景就合适呢?Martin 列举了若干场景证明 Redlock 这种算法是不可靠的。
14 |
15 | ## 用锁保护资源
16 | 这节里 Martin 先将 Redlock 放在了一边而是仅讨论总体上一个分布式锁是怎么工作的。在分布式环境下,锁比 mutex 这类复杂,因为涉及到不同节点、网络通信并且他们随时可能无征兆的 fail 。
17 | Martin 假设了一个场景,一个 client 要修改一个文件,它先申请得到锁,然后修改文件写回,放锁。另一个 client 再申请锁 ... 代码流程如下:
18 |
19 | ```java
20 | // THIS CODE IS BROKEN
21 | function writeData(filename, data) {
22 | var lock = lockService.acquireLock(filename);
23 | if (!lock) {
24 | throw 'Failed to acquire lock';
25 | }
26 |
27 | try {
28 | var file = storage.readFile(filename);
29 | var updated = updateContents(file, data);
30 | storage.writeFile(filename, updated);
31 | } finally {
32 | lock.release();
33 | }
34 | }
35 | ```
36 |
37 | 可惜即使你的锁服务非常完美,上述代码还是可能跪,下面的流程图会告诉你为什么:
38 |
39 | 
40 |
41 | 上述图中,得到锁的 client1 在持有锁的期间 pause 了一段时间,例如 GC 停顿。锁有过期时间(一般叫租约,为了防止某个 client 崩溃之后一直占有锁),但是如果 GC 停顿太长超过了锁租约时间,此时锁已经被另一个 client2 所得到,原先的 client1 还没有感知到锁过期,那么奇怪的结果就会发生,曾经 HBase 就发生过这种 Bug。即使你在 client1 写回之前检查一下锁是否过期也无助于解决这个问题,因为 GC 可能在任何时候发生,即使是你非常不便的时候(在最后的检查与写操作期间)。
42 | 如果你认为自己的程序不会有长时间的 GC 停顿,还有其他原因会导致你的进程 pause。例如进程可能读取尚未进入内存的数据,所以它得到一个 page fault 并且等待 page 被加载进缓存;还有可能你依赖于网络服务;或者其他进程占用 CPU;或者其他人意外发生 SIGSTOP 等。
43 |
44 | ... .... 这里 Martin 又增加了一节列举各种进程 pause 的例子,为了证明上面的代码是不安全的,无论你的锁服务多完美。
45 |
46 | ## 使用 Fencing (栅栏)使得锁变安全
47 | 修复问题的方法也很简单:你需要在每次写操作时加入一个 fencing token。这个场景下,fencing token 可以是一个递增的数字(lock service 可以做到),每次有 client 申请锁就递增一次:
48 |
49 | 
50 |
51 | client1 申请锁同时拿到 token33,然后它进入长时间的停顿锁也过期了。client2 得到锁和 token34 写入数据,紧接着 client1 活过来之后尝试写入数据,自身 token33 比 34 小因此写入操作被拒绝。注意这需要存储层来检查 token,但这并不难实现。如果你使用 Zookeeper 作为 lock service 的话那么你可以使用 zxid 作为递增数字。
52 | 但是对于 Redlock 你要知道,没什么生成 fencing token 的方式,并且怎么修改 Redlock 算法使其能产生 fencing token 呢?好像并不那么显而易见。因为产生 token 需要单调递增,除非在单节点 Redis 上完成但是这又没有高可靠性,你好像需要引进一致性协议来让 Redlock 产生可靠的 fencing token。
53 |
54 | ## 使用时间来解决一致性
55 | Redlock 无法产生 fencing token 早该成为在需求正确性的场景下弃用它的理由,但还有一些值得讨论的地方。
56 |
57 | 学术界有个说法,算法对时间不做假设:因为进程可能pause一段时间、数据包可能因为网络延迟延后到达、时钟可能根本就是错的。而可靠的算法依旧要在上述假设下做正确的事情。
58 |
59 | 对于 failure detector 来说,timeout 只能作为猜测某个节点 fail 的依据,因为网络延迟、本地时钟不正确等其他原因的限制。考虑到 Redis 使用 gettimeofday,而不是单调的时钟,会受到系统时间的影响,可能会突然前进或者后退一段时间,这会导致一个 key 更快或更慢地过期。
60 |
61 | 可见,Redlock 依赖于许多时间假设,它假设所有 Redis 节点都能对同一个 Key 在其过期前持有差不多的时间、跟过期时间相比网络延迟很小、跟过期时间相比进程 pause 很短。
62 |
63 | ## 用不可靠的时间打破 Redlock
64 | 这节 Martin 举了个因为时间问题,Redlock 不可靠的例子。
65 |
66 | 1. client1 从 ABC 三个节点处申请到锁,DE由于网络原因请求没有到达
67 | 2. C节点的时钟往前推了,导致 lock 过期
68 | 3. client2 在CDE处获得了锁,AB由于网络原因请求未到达
69 | 4. 此时 client1 和 client2 都获得了锁
70 |
71 | **在 Redlock 官方文档中也提到了这个情况,不过是C崩溃的时候,Redlock 官方本身也是知道 Redlock 算法不是完全可靠的,官方为了解决这种问题建议使用延时启动,相关内容可以看之前的[这篇文章](https://zhuanlan.zhihu.com/p/40915772)。但是 Martin 这里分析得更加全面,指出延时启动不也是依赖于时钟的正确性的么?**
72 |
73 | 接下来 Martin 又列举了进程 Pause 时而不是时钟不可靠时会发生的问题:
74 |
75 | 1. client1 从 ABCDE 处获得了锁
76 | 2. 当获得锁的 response 还没到达 client1 时 client1 进入 GC 停顿
77 | 3. 停顿期间锁已经过期了
78 | 4. client2 在 ABCDE 处获得了锁
79 | 5. client1 GC 完成收到了获得锁的 response,此时两个 client 又拿到了同一把锁
80 |
81 | **同时长时间的网络延迟也有可能导致同样的问题。**
82 |
83 | ## Redlock 的同步性假设
84 | 这些例子说明了,仅有在你假设了一个同步性系统模型的基础上,Redlock 才能正常工作,也就是系统能满足以下属性:
85 |
86 | 1. 网络延时边界,即假设数据包一定能在某个最大延时之内到达
87 | 2. 进程停顿边界,即进程停顿一定在某个最大时间之内
88 | 3. 时钟错误边界,即不会从一个坏的 NTP 服务器处取得时间
89 |
90 | ## 结论
91 | Martin 认为 Redlock 实在不是一个好的选择,对于需求性能的分布式锁应用它太重了且成本高;对于需求正确性的应用来说它不够安全。因为它对高危的时钟或者说其他上述列举的情况进行了不可靠的假设,如果你的应用只需要高性能的分布式锁不要求多高的正确性,那么单节点 Redis 够了;如果你的应用想要保住正确性,那么不建议 Redlock,建议使用一个合适的一致性协调系统,例如 Zookeeper,且保证存在 fencing token。
92 |
--------------------------------------------------------------------------------
/docs/数据结构与算法/动态规划.md:
--------------------------------------------------------------------------------
1 |
2 | ### 动态规划
3 |
4 | #### 1. 使用条件:
5 |
6 | **最优子结构性质:** 如果问题的最优解所包含的子问题的解也是最优的,就称该问题具有最优子结构
7 |
8 | **无后效性:** 即某阶段状态一旦确定,就不受这个状态以后决策的影响。也就是说,某状态以后的过程不会影响以前的状态,只与当前状态有关。
9 |
10 | **子问题重叠性:** 即子问题之间是不独立的,一个子问题在下一阶段决策中可能被多次使用到。(该性质并不是动态规划适用的必要条件,但是如果没有这条性质,动态规划算法同其他算法相比就不具备优势)
11 |
12 |
13 | #### 2. 动规解题的一般思路
14 |
15 | 动态规划所处理的问题是一个多阶段决策问题,一般由初始状态开始,通过对中间阶段决策的选择,达到结束状态。这些决策形成了一个决策序列,同时确定了完成整个过程的一条活动路线(通常是求最优的活动路线)。
16 | 动态规划的设计都有着一定的模式,一般要经历以下几个步骤。
17 |
18 | 初始状态→│决策1│→│决策2│→…→│决策n│→结束状态
19 |
20 | **解题步骤:**
21 |
22 | (1)划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。在划分阶段时,注意划分后的阶段一定要是有序的或者是可排序的,否则问题就无法求解。
23 |
24 | (2)确定状态和状态变量:将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。
25 |
26 | (3)确定决策并写出状态转移方程:因为决策和状态转移有着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以如果确定了决策,状态转移方程也就可写出。但事实上常常是反过来做,根据相邻两个阶段的状态之间的关系来确定决策方法和状态转移方程。
27 |
28 | (4)寻找边界条件:给出的状态转移方程是一个递推式,需要一个递推的终止条件或边界条件。
29 |
30 | 一般,只要解决问题的阶段、状态和状态转移决策确定了,就可以写出状态转移方程(包括边界条件)。
31 |
32 | **最简化步骤:**
33 |
34 | (1)确定状态转移方程
35 |
36 | (2)找到边界条件
37 |
38 | (3)递归的定义最优解。
39 |
40 | (4)以自底向上或自顶向下的记忆化方式(备忘录法)计算出最优值。根据计算最优值时得到的信息,构造问题的最优解
41 |
42 | #### 最大子数组问题
43 |
44 | 一个数组,求他的一个子数组,这个子数组中的所有值的和是最大。
45 |
46 | 例子:数组 arr = arr = [6, -1, 8, -4, -6, 3, 2, -2, 5],最大连续子序列和为13。即为:6,-1,8
47 |
48 | **状态转移方程:**
49 |
50 | 创建一个数组res,长度为原数组长度,不同位置数字a[i]代表0...i上最大连续子序列和,a[0]=arr[0]设置一个最大值max,初始值为数组中的第一个数字。当进来一个新的数字arr[i+1]时,判断到他前面数字子序列和a[i]+arr[i+1]跟arr[i+1]哪个大,前者大就保留前者,后者大就说明前面连续数字加起来都不如后者一个新进来的数字大,前面数字就可以舍弃,从arr[i+1]开始,每次比较完都跟max比较一下,最后的max就是最大值。
51 |
52 | |—> opt(i-1) + arr[i] (选择当前元素)
53 | opt(i)= |
54 | |—> arr[i] (当前元素比前几个元素和还大,所以放弃前面的)
55 |
56 | **边界条件:**
57 |
58 | (1) i = 0 此时数组从尾到头走完了
59 |
60 | 完整代码:
61 | ```python
62 | arr = [6, -1, 8, -4, -6, 3, 2, -2, 5]
63 |
64 | def dp_opt(arr):
65 | # 记录数组
66 | res = [0 for i in range(len(arr))]
67 | # 初始化数组
68 | res[0] = arr[0]
69 | # 记录最大值的变量
70 | max_sum = res[0]
71 | # 开始循环
72 | for i in range(1, len(arr)):
73 | a = res[i - 1] + arr[i]
74 | b = arr[i]
75 | res[i] = max(a, b)
76 | max_sum = max_sum if max_sum >res[i] else res[i]
77 | return max_sum
78 |
79 | print(dp_opt(arr))
80 | ```
81 | 结果为13,说明正确。
82 |
83 |
84 | #### 0 1 背包问题
85 |
86 | 在N件物品取出若干件放在容量为W的背包里,每件物品的体积为W1,W2……Wn(Wi为整数),与之相对应的价值为V1,V2……Vn(Vi为整数),求背包能够容纳的最大价值。
87 |
88 | 例子:物品体积数组:w = [3,5,2,7,4],物品价值数组:v = [2,4,1,6,5],背包容量:s = 10
89 |
90 | **1.找出状态转移方程:**
91 |
92 | |—> opt(i-1, s - w[i]) + v[i] (选择当前物品放入背包)
93 | opt(i, s) = |
94 | |—> opt(i-1, s - w[i]) (放弃当前物品,不放入背包)
95 |
96 | **2.找出边界条件:**
97 |
98 | (1) i < 0 (物品选择完了)
99 | (2) s == w[i] (当前物品放入背包,背包容量剩余刚好为0)
100 | (3)一种特殊情况:s < c[i] (背包余量不足以放下第i件物品了,此时只好放弃当前物品了,状态转移方程必定选第2个转移状态)
101 |
102 | **3.递归解决**
103 |
104 | ```python
105 |
106 | def rec_opt(w, v, i, s):
107 | if i < 0:
108 | return 0
109 | elif w[i] == s:
110 | return v[i]
111 | elif s < w[i]:
112 | return rec_opt(w, v, i-1, s)
113 | else:
114 | a = rec_opt(w, v, i-1, s - w[i]) + v[i]
115 | b = rec_opt(w, v, i-1, s)
116 | return max(a, b)
117 |
118 | ```
119 |
120 | 完整代码:
121 | ```python
122 |
123 | w = [3, 5, 2, 7, 4]
124 | v = [2, 4, 1, 6, 5]
125 | s = 10
126 |
127 | def rec_opt(w, v, i, s):
128 | if i < 0:
129 | return 0
130 | elif w[i] == s:
131 | return v[i]
132 | elif s < w[i]:
133 | return rec_opt(w, v, i - 1, s)
134 | else:
135 | a = rec_opt(w, v, i - 1, s - w[i]) + v[i]
136 | b = rec_opt(w, v, i - 1, s)
137 | return max(a, b)
138 |
139 | print(rec_opt(w, v, 4, s))
140 | ```
141 |
142 | 运算结果为9,说明我们能装的物品最大价值为9
143 |
144 | **4.变为非递归**
145 |
146 | ```python
147 | w = [3, 5, 2, 7, 4]
148 | v = [2, 4, 1, 6, 5]
149 | s = 10
150 |
151 | def dp_opt(w, v, s):
152 | opt = [[0 for i in range(s+1)] for j in range(len(w))]
153 | # 初始化
154 | for n in range(w[0], s+1):
155 | opt[0][n] = v[0]
156 | # 开始循环
157 | for i in range(1, len(w)):
158 | for k in range(1, s+1):
159 | if k == w[i]:
160 | opt[i][k] = opt[i-1][k-w[i]] + v[i]
161 | elif k < w[i]:
162 | opt[i][k] = opt[i-1][k]
163 | else:
164 | a = opt[i - 1][k - w[i]] + v[i]
165 | b = opt[i - 1][k]
166 | opt[i][k] = max(a, b)
167 | return opt[len(w)-1][s]
168 |
169 | print(dp_opt(w, v, s))
170 | ```
171 |
172 | 非递归运算结果也是9。结果正确。
--------------------------------------------------------------------------------
/docs/数据结构与算法/数据结构.md:
--------------------------------------------------------------------------------
1 | 下面只是简单地总结,给了一些参考文章,后面会对这部分内容进行重构。
2 |
3 |
4 | - [Python内数据结构](#Python内数据结构)
5 | - [树](#树)
6 | - [Python内常见算法](#Python内常见算法)
7 |
8 |
9 |
10 | ## Python内数据结构
11 |
12 | ### 1.Python数据结构
13 |
14 | |数据结构|内置结构|内置模块|
15 | |----------------|--------------|----------------------------------|
16 | |线性结构|list,tuple|array/collections.namedtuple|
17 | |链式结构|.|collections.deque(双端队列)|
18 | |字典结构|dict|collections.Counter(计数器)/OrderedDict(有序字典)|
19 | |集合结构|set,frozenset(有序集合)|.|
20 |
21 | dict底层使用哈希表,Cpython解释器使用二次探查法解决哈希冲突
22 | set底层也使用了哈希表
23 | list底层使用顺序表
24 |
25 | **如何解决哈希冲突?哈希如何扩容?**
26 | 冲突:二次探查法,再哈希法,
27 | 扩容:重哈希(哈希表重新开辟空间并拷贝原有元素)
28 |
29 | ### 2. collections模块
30 |
31 |
32 |
33 |
34 | ## 树
35 | * ### 1 二叉树
36 |
37 | [二叉树](https://baike.baidu.com/item/%E4%BA%8C%E5%8F%89%E6%A0%91)(百度百科)
38 |
39 | (1)[完全二叉树](https://baike.baidu.com/item/%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91)——若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子结点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。
40 |
41 | (2)[满二叉树](https://baike.baidu.com/item/%E6%BB%A1%E4%BA%8C%E5%8F%89%E6%A0%91)——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。
42 |
43 | (3)[平衡二叉树](https://baike.baidu.com/item/%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91/10421057)——平衡二叉树又被称为AVL树(区别于AVL算法),它是一棵二叉排序树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
44 |
45 | * ### 2 完全二叉树
46 |
47 | [完全二叉树](https://baike.baidu.com/item/%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%8F%89%E6%A0%91)(百度百科)
48 |
49 | 完全二叉树:叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树。
50 | * ### 3 满二叉树
51 |
52 | [满二叉树](https://baike.baidu.com/item/%E6%BB%A1%E4%BA%8C%E5%8F%89%E6%A0%91)(百度百科,国内外的定义不同)
53 |
54 | 国内教程定义:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是(2^k) -1 ,则它就是满二叉树。
55 | * ### 堆
56 |
57 | [数据结构之堆的定义](https://blog.csdn.net/qq_33186366/article/details/51876191)
58 |
59 | 堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
60 | * ### 4 二叉查找树(BST)
61 |
62 | [浅谈算法和数据结构: 七 二叉查找树](http://www.cnblogs.com/yangecnu/p/Introduce-Binary-Search-Tree.html)
63 |
64 | 二叉查找树的特点:
65 |
66 | 1. 若任意节点的左子树不空,则左子树上所有结点的 值均小于它的根结点的值;
67 | 2. 若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
68 | 3. 任意节点的左、右子树也分别为二叉查找树;
69 | 4. 没有键值相等的节点(no duplicate nodes)。
70 |
71 | * ### 5 平衡二叉树(Self-balancing binary search tree)
72 |
73 | [ 平衡二叉树](https://baike.baidu.com/item/%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91)(百度百科,平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等)
74 | * ### 6 红黑树
75 |
76 | - 红黑树特点:
77 | 1. 每个节点非红即黑;
78 | 2. 根节点总是黑色的;
79 | 3. 每个叶子节点都是黑色的空节点(NIL节点);
80 | 4. 如果节点是红色的,则它的子节点必须是黑色的(反之不一定);
81 | 5. 从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)。
82 |
83 | - 红黑树的应用:
84 |
85 | TreeMap、TreeSet以及JDK1.8之后的HashMap底层都用到了红黑树。
86 |
87 | - 为什么要用红黑树
88 |
89 | 简单来说红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。详细了解可以查看 [漫画:什么是红黑树?](https://juejin.im/post/5a27c6946fb9a04509096248#comment)(也介绍到了二叉查找树,非常推荐)
90 |
91 | - 推荐文章:
92 | - [漫画:什么是红黑树?](https://juejin.im/post/5a27c6946fb9a04509096248#comment)(也介绍到了二叉查找树,非常推荐)
93 | - [寻找红黑树的操作手册](http://dandanlove.com/2018/03/18/red-black-tree/)(文章排版以及思路真的不错)
94 | - [红黑树深入剖析及Java实现](https://zhuanlan.zhihu.com/p/24367771)(美团点评技术团队)
95 | * ### 7 B-,B+,B*树
96 |
97 | [二叉树学习笔记之B树、B+树、B*树 ](https://yq.aliyun.com/articles/38345)
98 |
99 | [《B-树,B+树,B*树详解》](https://blog.csdn.net/aqzwss/article/details/53074186)
100 |
101 | [《B-树,B+树与B*树的优缺点比较》](https://blog.csdn.net/bigtree_3721/article/details/73632405)
102 |
103 | B-树(或B树)是一种平衡的多路查找(又称排序)树,在文件系统中有所应用。主要用作文件的索引。其中的B就表示平衡(Balance)
104 | 1. B+ 树的叶子节点链表结构相比于 B- 树便于扫库,和范围检索。
105 | 2. B+树支持range-query(区间查询)非常方便,而B树不支持。这是数据库选用B+树的最主要原因。
106 | 3. B\*树 是B+树的变体,B\*树分配新结点的概率比B+树要低,空间使用率更高;
107 | * ### 8 LSM 树
108 |
109 | [[HBase] LSM树 VS B+树](https://blog.csdn.net/dbanote/article/details/8897599)
110 |
111 | B+树最大的性能问题是会产生大量的随机IO
112 |
113 | 为了克服B+树的弱点,HBase引入了LSM树的概念,即Log-Structured Merge-Trees。
114 |
115 | [LSM树由来、设计思想以及应用到HBase的索引](http://www.cnblogs.com/yanghuahui/p/3483754.html)
116 |
117 |
118 | ## 图
119 |
120 |
121 |
122 |
123 | ## BFS及DFS
124 |
125 | - [《使用BFS及DFS遍历树和图的思路及实现》](https://blog.csdn.net/Gene1994/article/details/85097507)
126 |
127 |
128 |
129 | ## Python内常见算法
130 |
131 | 排序:sorted()函数,list.sort()区别
132 |
133 | 二分:bisect模块
134 |
135 | 堆:heapq模块
136 |
137 | 缓存算法:functolls.lru_cache(Python3中存在)
138 |
139 |
--------------------------------------------------------------------------------
/docs/数据库/面试必备之乐观锁与悲观锁.md:
--------------------------------------------------------------------------------
1 | 点击关注[公众号](#公众号)及时获取笔主最新更新文章,并可免费领取本文档配套的《Java面试突击》以及Java工程师必备学习资源。
2 |
3 |
4 |
5 | - [何谓悲观锁与乐观锁](#何谓悲观锁与乐观锁)
6 | - [悲观锁](#悲观锁)
7 | - [乐观锁](#乐观锁)
8 | - [两种锁的使用场景](#两种锁的使用场景)
9 | - [乐观锁常见的两种实现方式](#乐观锁常见的两种实现方式)
10 | - [1. 版本号机制](#1-版本号机制)
11 | - [2. CAS算法](#2-cas算法)
12 | - [乐观锁的缺点](#乐观锁的缺点)
13 | - [1 ABA 问题](#1-aba-问题)
14 | - [2 循环时间长开销大](#2-循环时间长开销大)
15 | - [3 只能保证一个共享变量的原子操作](#3-只能保证一个共享变量的原子操作)
16 | - [CAS与synchronized的使用情景](#cas与synchronized的使用情景)
17 |
18 |
19 |
20 | ### 何谓悲观锁与乐观锁
21 |
22 | > 乐观锁对应于生活中乐观的人总是想着事情往好的方向发展,悲观锁对应于生活中悲观的人总是想着事情往坏的方向发展。这两种人各有优缺点,不能不以场景而定说一种人好于另外一种人。
23 |
24 | #### 悲观锁
25 |
26 | 总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(**共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程**)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中`synchronized`和`ReentrantLock`等独占锁就是悲观锁思想的实现。
27 |
28 |
29 | #### 乐观锁
30 |
31 | 总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。**乐观锁适用于多读的应用类型,这样可以提高吞吐量**,像数据库提供的类似于**write_condition机制**,其实都是提供的乐观锁。在Java中`java.util.concurrent.atomic`包下面的原子变量类就是使用了乐观锁的一种实现方式**CAS**实现的。
32 |
33 | #### 两种锁的使用场景
34 |
35 | 从上面对两种锁的介绍,我们知道两种锁各有优缺点,不可认为一种好于另一种,像**乐观锁适用于写比较少的情况下(多读场景)**,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果是多写的情况,一般会经常产生冲突,这就会导致上层应用会不断的进行retry,这样反倒是降低了性能,所以**一般多写的场景下用悲观锁就比较合适。**
36 |
37 |
38 | ### 乐观锁常见的两种实现方式
39 |
40 | > **乐观锁一般会使用版本号机制或CAS算法实现。**
41 |
42 | #### 1. 版本号机制
43 |
44 | 一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。
45 |
46 | **举一个简单的例子:**
47 | 假设数据库中帐户信息表中有一个 version 字段,当前值为 1 ;而当前帐户余额字段( balance )为 $100 。
48 |
49 | 1. 操作员 A 此时将其读出( version=1 ),并从其帐户余额中扣除 $50( $100-$50 )。
50 | 2. 在操作员 A 操作的过程中,操作员B 也读入此用户信息( version=1 ),并从其帐户余额中扣除 $20 ( $100-$20 )。
51 | 3. 操作员 A 完成了修改工作,将数据版本号加一( version=2 ),连同帐户扣除后余额( balance=$50 ),提交至数据库更新,此时由于提交数据版本大于数据库记录当前版本,数据被更新,数据库记录 version 更新为 2 。
52 | 4. 操作员 B 完成了操作,也将版本号加一( version=2 )试图向数据库提交数据( balance=$80 ),但此时比对数据库记录版本时发现,操作员 B 提交的数据版本号为 2 ,数据库记录当前版本也为 2 ,不满足 “ 提交版本必须大于记录当前版本才能执行更新 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。
53 |
54 | 这样,就避免了操作员 B 用基于 version=1 的旧数据修改的结果覆盖操作员A 的操作结果的可能。
55 |
56 | #### 2. CAS算法
57 |
58 | 即**compare and swap(比较与交换)**,是一种有名的**无锁算法**。无锁编程,即不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步(Non-blocking Synchronization)。**CAS算法**涉及到三个操作数
59 |
60 | - 需要读写的内存值 V
61 | - 进行比较的值 A
62 | - 拟写入的新值 B
63 |
64 | 当且仅当 V 的值等于 A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个**自旋操作**,即**不断的重试**。
65 |
66 | 关于自旋锁,大家可以看一下这篇文章,非常不错:[《
67 | 面试必备之深入理解自旋锁》](https://blog.csdn.net/qq_34337272/article/details/81252853)
68 |
69 | ### 乐观锁的缺点
70 |
71 | > ABA 问题是乐观锁一个常见的问题
72 |
73 | #### 1 ABA 问题
74 |
75 | 如果一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,那我们就能说明它的值没有被其他线程修改过了吗?很明显是不能的,因为在这段时间它的值可能被改为其他值,然后又改回A,那CAS操作就会误认为它从来没有被修改过。这个问题被称为CAS操作的 **"ABA"问题。**
76 |
77 | JDK 1.5 以后的 `AtomicStampedReference 类`就提供了此种能力,其中的 `compareAndSet 方法`就是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
78 |
79 | #### 2 循环时间长开销大
80 | **自旋CAS(也就是不成功就一直循环执行直到成功)如果长时间不成功,会给CPU带来非常大的执行开销。** 如果JVM能支持处理器提供的pause指令那么效率会有一定的提升,pause指令有两个作用,第一它可以延迟流水线执行指令(de-pipeline),使CPU不会消耗过多的执行资源,延迟的时间取决于具体实现的版本,在一些处理器上延迟时间是零。第二它可以避免在退出循环的时候因内存顺序冲突(memory order violation)而引起CPU流水线被清空(CPU pipeline flush),从而提高CPU的执行效率。
81 |
82 | #### 3 只能保证一个共享变量的原子操作
83 |
84 | CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效。但是从 JDK 1.5开始,提供了`AtomicReference类`来保证引用对象之间的原子性,你可以把多个变量放在一个对象里来进行 CAS 操作.所以我们可以使用锁或者利用`AtomicReference类`把多个共享变量合并成一个共享变量来操作。
85 |
86 | ### CAS与synchronized的使用情景
87 |
88 | > **简单的来说CAS适用于写比较少的情况下(多读场景,冲突一般较少),synchronized适用于写比较多的情况下(多写场景,冲突一般较多)**
89 |
90 | 1. 对于资源竞争较少(线程冲突较轻)的情况,使用synchronized同步锁进行线程阻塞和唤醒切换以及用户态内核态间的切换操作额外浪费消耗cpu资源;而CAS基于硬件实现,不需要进入内核,不需要切换线程,操作自旋几率较少,因此可以获得更高的性能。
91 | 2. 对于资源竞争严重(线程冲突严重)的情况,CAS自旋的概率会比较大,从而浪费更多的CPU资源,效率低于synchronized。
92 |
93 | 补充: Java并发编程这个领域中synchronized关键字一直都是元老级的角色,很久之前很多人都会称它为 **“重量级锁”** 。但是,在JavaSE 1.6之后进行了主要包括为了减少获得锁和释放锁带来的性能消耗而引入的 **偏向锁** 和 **轻量级锁** 以及其它**各种优化**之后变得在某些情况下并不是那么重了。synchronized的底层实现主要依靠 **Lock-Free** 的队列,基本思路是 **自旋后阻塞**,**竞争切换后继续竞争锁**,**稍微牺牲了公平性,但获得了高吞吐量**。在线程冲突较少的情况下,可以获得和CAS类似的性能;而线程冲突严重的情况下,性能远高于CAS。
94 |
95 | ## 公众号
96 |
97 | 如果大家想要实时关注我更新的文章以及分享的干货的话,可以关注我的公众号。
98 |
99 | **《Java面试突击》:** 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本[公众号](#公众号)后台回复 **"面试突击"** 即可免费领取!
100 |
101 | **Java工程师必备学习资源:** 一些Java工程师常用学习资源公众号后台回复关键字 **“1”** 即可免费无套路获取。
102 |
103 | 
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
--------------------------------------------------------------------------------
/docs/数据库/Redis/Redis持久化.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 非常感谢《redis实战》真本书,本文大多内容也参考了书中的内容。非常推荐大家看一下《redis实战》这本书,感觉书中的很多理论性东西还是很不错的。
4 |
5 | 为什么本文的名字要加上春夏秋冬又一春,哈哈 ,这是一部韩国的电影,我感觉电影不错,所以就用在文章名字上了,没有什么特别的含义,然后下面的有些配图也是电影相关镜头。
6 |
7 | 
8 |
9 | **很多时候我们需要持久化数据也就是将内存中的数据写入到硬盘里面,大部分原因是为了之后重用数据(比如重启机器、机器故障之后回复数据),或者是为了防止系统故障而将数据备份到一个远程位置。**
10 |
11 | Redis不同于Memcached的很重一点就是,**Redis支持持久化**,而且支持两种不同的持久化操作。Redis的一种持久化方式叫**快照(snapshotting,RDB)**,另一种方式是**只追加文件(append-only file,AOF)**.这两种方法各有千秋,下面我会详细这两种持久化方法是什么,怎么用,如何选择适合自己的持久化方法。
12 |
13 |
14 | ## 快照(snapshotting)持久化
15 |
16 | Redis可以通过创建快照来获得存储在内存里面的数据在某个时间点上的副本。Redis创建快照之后,可以对快照进行备份,可以将快照复制到其他服务器从而创建具有相同数据的服务器副本(Redis主从结构,主要用来提高Redis性能),还可以将快照留在原地以便重启服务器的时候使用。
17 |
18 |
19 | 
20 |
21 | **快照持久化是Redis默认采用的持久化方式**,在redis.conf配置文件中默认有此下配置:
22 | ```
23 |
24 | save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
25 |
26 | save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
27 |
28 | save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
29 | ```
30 |
31 | 根据配置,快照将被写入dbfilename选项指定的文件里面,并存储在dir选项指定的路径上面。如果在新的快照文件创建完毕之前,Redis、系统或者硬件这三者中的任意一个崩溃了,那么Redis将丢失最近一次创建快照写入的所有数据。
32 |
33 | 举个例子:假设Redis的上一个快照是2:35开始创建的,并且已经创建成功。下午3:06时,Redis又开始创建新的快照,并且在下午3:08快照创建完毕之前,有35个键进行了更新。如果在下午3:06到3:08期间,系统发生了崩溃,导致Redis无法完成新快照的创建工作,那么Redis将丢失下午2:35之后写入的所有数据。另一方面,如果系统恰好在新的快照文件创建完毕之后崩溃,那么Redis将丢失35个键的更新数据。
34 |
35 | **创建快照的办法有如下几种:**
36 |
37 | - **BGSAVE命令:** 客户端向Redis发送 **BGSAVE命令** 来创建一个快照。对于支持BGSAVE命令的平台来说(基本上所有平台支持,除了Windows平台),Redis会调用fork来创建一个子进程,然后子进程负责将快照写入硬盘,而父进程则继续处理命令请求。
38 | - **SAVE命令:** 客户端还可以向Redis发送 **SAVE命令** 来创建一个快照,接到SAVE命令的Redis服务器在快照创建完毕之前不会再响应任何其他命令。SAVE命令不常用,我们通常只会在没有足够内存去执行BGSAVE命令的情况下,又或者即使等待持久化操作执行完毕也无所谓的情况下,才会使用这个命令。
39 | - **save选项:** 如果用户设置了save选项(一般会默认设置),比如 **save 60 10000**,那么从Redis最近一次创建快照之后开始算起,当“60秒之内有10000次写入”这个条件被满足时,Redis就会自动触发BGSAVE命令。
40 | - **SHUTDOWN命令:** 当Redis通过SHUTDOWN命令接收到关闭服务器的请求时,或者接收到标准TERM信号时,会执行一个SAVE命令,阻塞所有客户端,不再执行客户端发送的任何命令,并在SAVE命令执行完毕之后关闭服务器。
41 | - **一个Redis服务器连接到另一个Redis服务器:** 当一个Redis服务器连接到另一个Redis服务器,并向对方发送SYNC命令来开始一次复制操作的时候,如果主服务器目前没有执行BGSAVE操作,或者主服务器并非刚刚执行完BGSAVE操作,那么主服务器就会执行BGSAVE命令
42 |
43 | 如果系统真的发生崩溃,用户将丢失最近一次生成快照之后更改的所有数据。因此,快照持久化只适用于即使丢失一部分数据也不会造成一些大问题的应用程序。不能接受这个缺点的话,可以考虑AOF持久化。
44 |
45 |
46 |
47 | ## **AOF(append-only file)持久化**
48 | 与快照持久化相比,AOF持久化 的实时性更好,因此已成为主流的持久化方案。默认情况下Redis没有开启AOF(append only file)方式的持久化,可以通过appendonly参数开启:
49 | ```
50 | appendonly yes
51 | ```
52 |
53 | 开启AOF持久化后每执行一条会更改Redis中的数据的命令,Redis就会将该命令写入硬盘中的AOF文件。AOF文件的保存位置和RDB文件的位置相同,都是通过dir参数设置的,默认的文件名是appendonly.aof。
54 |
55 | 
56 |
57 | **在Redis的配置文件中存在三种同步方式,它们分别是:**
58 |
59 | ```
60 |
61 | appendfsync always #每次有数据修改发生时都会写入AOF文件,这样会严重降低Redis的速度
62 | appendfsync everysec #每秒钟同步一次,显示地将多个写命令同步到硬盘
63 | appendfsync no #让操作系统决定何时进行同步
64 | ```
65 |
66 | **appendfsync always** 可以实现将数据丢失减到最少,不过这种方式需要对硬盘进行大量的写入而且每次只写入一个命令,十分影响Redis的速度。另外使用固态硬盘的用户谨慎使用appendfsync always选项,因为这会明显降低固态硬盘的使用寿命。
67 |
68 | 为了兼顾数据和写入性能,用户可以考虑 **appendfsync everysec选项** ,让Redis每秒同步一次AOF文件,Redis性能几乎没受到任何影响。而且这样即使出现系统崩溃,用户最多只会丢失一秒之内产生的数据。当硬盘忙于执行写入操作的时候,Redis还会优雅的放慢自己的速度以便适应硬盘的最大写入速度。
69 |
70 |
71 | **appendfsync no** 选项一般不推荐,这种方案会使Redis丢失不定量的数据而且如果用户的硬盘处理写入操作的速度不够的话,那么当缓冲区被等待写入的数据填满时,Redis的写入操作将被阻塞,这会导致Redis的请求速度变慢。
72 |
73 | **虽然AOF持久化非常灵活地提供了多种不同的选项来满足不同应用程序对数据安全的不同要求,但AOF持久化也有缺陷——AOF文件的体积太大。**
74 |
75 | ## 重写/压缩AOF
76 |
77 | AOF虽然在某个角度可以将数据丢失降低到最小而且对性能影响也很小,但是极端的情况下,体积不断增大的AOF文件很可能会用完硬盘空间。另外,如果AOF体积过大,那么还原操作执行时间就可能会非常长。
78 |
79 | 为了解决AOF体积过大的问题,用户可以向Redis发送 **BGREWRITEAOF命令** ,这个命令会通过移除AOF文件中的冗余命令来重写(rewrite)AOF文件来减小AOF文件的体积。BGREWRITEAOF命令和BGSAVE创建快照原理十分相似,所以AOF文件重写也需要用到子进程,这样会导致性能问题和内存占用问题,和快照持久化一样。更糟糕的是,如果不加以控制的话,AOF文件的体积可能会比快照文件大好几倍。
80 |
81 | **文件重写流程:**
82 |
83 | 
84 | 和快照持久化可以通过设置save选项来自动执行BGSAVE一样,AOF持久化也可以通过设置
85 |
86 | ```
87 | auto-aof-rewrite-percentage
88 | ```
89 |
90 | 选项和
91 |
92 | ```
93 | auto-aof-rewrite-min-size
94 | ```
95 |
96 | 选项自动执行BGREWRITEAOF命令。举例:假设用户对Redis设置了如下配置选项并且启用了AOF持久化。那么当AOF文件体积大于64mb,并且AOF的体积比上一次重写之后的体积大了至少一倍(100%)的时候,Redis将执行BGREWRITEAOF命令。
97 |
98 | ```
99 | auto-aof-rewrite-percentage 100
100 | auto-aof-rewrite-min-size 64mb
101 | ```
102 |
103 | 无论是AOF持久化还是快照持久化,将数据持久化到硬盘上都是非常有必要的,但除了进行持久化外,用户还必须对持久化得到的文件进行备份(最好是备份到不同的地方),这样才能尽量避免数据丢失事故发生。如果条件允许的话,最好能将快照文件和重新重写的AOF文件备份到不同的服务器上面。
104 |
105 | 随着负载量的上升,或者数据的完整性变得 越来越重要时,用户可能需要使用到复制特性。
106 |
107 | ## Redis 4.0 对于持久化机制的优化
108 | Redis 4.0 开始支持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 `aof-use-rdb-preamble` 开启)。
109 |
110 | 如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF 文件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的, AOF 里面的 RDB 部分就是压缩格式不再是 AOF 格式,可读性较差。
111 |
112 | 参考:
113 |
114 | 《Redis实战》
115 |
116 | [深入学习Redis(2):持久化](https://www.cnblogs.com/kismetv/p/9137897.html)
117 |
118 |
119 |
--------------------------------------------------------------------------------
/docs/数据结构与算法/Backtracking-NQueens.md:
--------------------------------------------------------------------------------
1 | # N皇后
2 | [51. N皇后](https://leetcode-cn.com/problems/n-queens/)
3 |
4 | **python代码见文章最后**
5 |
6 |
7 | ### 题目描述
8 | > n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
9 | >
10 | 
11 | >
12 | 上图为 8 皇后问题的一种解法。
13 | >
14 | 给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
15 | >
16 | 每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。
17 |
18 | 示例:
19 |
20 | ```
21 | 输入: 4
22 | 输出: [
23 | [".Q..", // 解法 1
24 | "...Q",
25 | "Q...",
26 | "..Q."],
27 |
28 | ["..Q.", // 解法 2
29 | "Q...",
30 | "...Q",
31 | ".Q.."]
32 | ]
33 | 解释: 4 皇后问题存在两个不同的解法。
34 | ```
35 |
36 | ### 问题分析
37 | 约束条件为每个棋子所在的行、列、对角线都不能有另一个棋子。
38 |
39 | 使用一维数组表示一种解法,下标(index)表示行,值(value)表示该行的Q(皇后)在哪一列。
40 | 每行只存储一个元素,然后递归到下一行,这样就不用判断行了,只需要判断列和对角线。
41 | ### Solution1
42 | 当result[row] = column时,即row行的棋子在column列。
43 |
44 | 对于[0, row-1]的任意一行(i 行),若 row 行的棋子和 i 行的棋子在同一列,则有result[i] == column;
45 | 若 row 行的棋子和 i 行的棋子在同一对角线,等腰直角三角形两直角边相等,即 row - i == Math.abs(result[i] - column)
46 |
47 | 布尔类型变量 isValid 的作用是剪枝,减少不必要的递归。
48 | ```
49 | public List> solveNQueens(int n) {
50 | // 下标代表行,值代表列。如result[0] = 3 表示第1行的Q在第3列
51 | int[] result = new int[n];
52 | List> resultList = new LinkedList<>();
53 | dfs(resultList, result, 0, n);
54 | return resultList;
55 | }
56 |
57 | void dfs(List> resultList, int[] result, int row, int n) {
58 | // 递归终止条件
59 | if (row == n) {
60 | List list = new LinkedList<>();
61 | for (int x = 0; x < n; ++x) {
62 | StringBuilder sb = new StringBuilder();
63 | for (int y = 0; y < n; ++y)
64 | sb.append(result[x] == y ? "Q" : ".");
65 | list.add(sb.toString());
66 | }
67 | resultList.add(list);
68 | return;
69 | }
70 | for (int column = 0; column < n; ++column) {
71 | boolean isValid = true;
72 | result[row] = column;
73 | /*
74 | * 逐行往下考察每一行。同列,result[i] == column
75 | * 同对角线,row - i == Math.abs(result[i] - column)
76 | */
77 | for (int i = row - 1; i >= 0; --i) {
78 | if (result[i] == column || row - i == Math.abs(result[i] - column)) {
79 | isValid = false;
80 | break;
81 | }
82 | }
83 | if (isValid) dfs(resultList, result, row + 1, n);
84 | }
85 | }
86 | ```
87 | ### Solution2
88 | 使用LinkedList表示一种解法,下标(index)表示行,值(value)表示该行的Q(皇后)在哪一列。
89 |
90 | 解法二和解法一的不同在于,相同列以及相同对角线的校验。
91 | 将对角线抽象成【一次函数】这个简单的数学模型,根据一次函数的截距是常量这一特性进行校验。
92 |
93 | 这里,我将右上-左下对角线,简称为“\”对角线;左上-右下对角线简称为“/”对角线。
94 |
95 | “/”对角线斜率为1,对应方程为y = x + b,其中b为截距。
96 | 对于线上任意一点,均有y - x = b,即row - i = b;
97 | 定义一个布尔类型数组anti_diag,将b作为下标,当anti_diag[b] = true时,表示相应对角线上已经放置棋子。
98 | 但row - i有可能为负数,负数不能作为数组下标,row - i 的最小值为-n(当row = 0,i = n时),可以加上n作为数组下标,即将row -i + n 作为数组下标。
99 | row - i + n 的最大值为 2n(当row = n,i = 0时),故anti_diag的容量设置为 2n 即可。
100 |
101 | 
102 |
103 | “\”对角线斜率为-1,对应方程为y = -x + b,其中b为截距。
104 | 对于线上任意一点,均有y + x = b,即row + i = b;
105 | 同理,定义数组main_diag,将b作为下标,当main_diag[row + i] = true时,表示相应对角线上已经放置棋子。
106 |
107 | 有了两个校验对角线的数组,再来定义一个用于校验列的数组cols,这个太简单啦,不解释。
108 |
109 | **解法二时间复杂度为O(n!),在校验相同列和相同对角线时,引入三个布尔类型数组进行判断。相比解法一,少了一层循环,用空间换时间。**
110 |
111 | ```
112 | List> resultList = new LinkedList<>();
113 |
114 | public List> solveNQueens(int n) {
115 | boolean[] cols = new boolean[n];
116 | boolean[] main_diag = new boolean[2 * n];
117 | boolean[] anti_diag = new boolean[2 * n];
118 | LinkedList result = new LinkedList<>();
119 | dfs(result, 0, cols, main_diag, anti_diag, n);
120 | return resultList;
121 | }
122 |
123 | void dfs(LinkedList result, int row, boolean[] cols, boolean[] main_diag, boolean[] anti_diag, int n) {
124 | if (row == n) {
125 | List list = new LinkedList<>();
126 | for (int x = 0; x < n; ++x) {
127 | StringBuilder sb = new StringBuilder();
128 | for (int y = 0; y < n; ++y)
129 | sb.append(result.get(x) == y ? "Q" : ".");
130 | list.add(sb.toString());
131 | }
132 | resultList.add(list);
133 | return;
134 | }
135 | for (int i = 0; i < n; ++i) {
136 | if (cols[i] || main_diag[row + i] || anti_diag[row - i + n])
137 | continue;
138 | result.add(i);
139 | cols[i] = true;
140 | main_diag[row + i] = true;
141 | anti_diag[row - i + n] = true;
142 | dfs(result, row + 1, cols, main_diag, anti_diag, n);
143 | result.removeLast();
144 | cols[i] = false;
145 | main_diag[row + i] = false;
146 | anti_diag[row - i + n] = false;
147 | }
148 | }
149 | ```
150 |
151 |
152 | **Python解决N皇后问题代码:**
153 |
154 | ```python
155 | # 检测(x,y)这个位置是否合法(不会被其他皇后攻击到)
156 | def is_attack(queue, x, y):
157 | for i in range(x):
158 | if queue[i] == y or abs(x - i) == abs(queue[i] - y):
159 | return True
160 | return False
161 |
162 |
163 | # 按列来摆放皇后
164 | def put_position(n, queue, col):
165 | for i in range(n):
166 | if not is_attack(queue, col, i):
167 | queue[col] = i
168 | if col == n - 1: # 此时最后一个皇后摆放好了,打印结果。
169 | print(queue)
170 | else:
171 | put_position(n, queue, col + 1)
172 |
173 |
174 | n = 4 # 这里是n 就是n皇后
175 | queue = [None for i in range(n)] # 存储皇后位置的一维数组,数组下标表示皇后所在的列,下标对应的值为皇后所在的行。
176 | put_position(n, queue, 0)
177 | ```
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | ## 说明
3 |
4 | 这个项目是从github项目[JavaGuide](https://github.com/Snailclimb/JavaGuide)复制过来的,有心做一个与Python相关的项目,主要是为了面试复习所用。正在更改与完善中。
5 |
6 |
7 | ## 目录
8 |
9 | - [Python](#Python)
10 | - [网络](#网络)
11 | - [操作系统](#操作系统)
12 | - [Linux相关](#linux相关)
13 | - [数据结构与算法](#数据结构与算法)
14 | - [数据结构](#数据结构)
15 | - [算法](#算法)
16 | - [数据库](#数据库)
17 | - [MySQL](#mysql)
18 | - [Redis](#redis)
19 | - [系统设计](#系统设计)
20 | - [常用框架(Flask/Django、Zookeeper ... )](#常用框架)
21 | - [权限认证](#权限认证)
22 | - [设计模式(工厂模式、单例模式 ... )](#设计模式)
23 | - [数据通信(消息队列、Dubbo、Kafka ... )](#数据通信)
24 | - [网站架构](#网站架构)
25 | - [面试指南](#面试指南)
26 | - [备战面试](#备战面试)
27 | - [常见面试题总结](#常见面试题总结)
28 | - [面经](#面经)
29 | - [工具](#工具)
30 | - [Git](#git)
31 | - [Docker](#Docker)
32 | - [资源](#资源)
33 | - [书单](#书单)
34 | - [视频](#视频)
35 | - [Github榜单](#Github榜单)
36 |
37 |
38 | ## Python
39 |
40 |
41 | ### 基础
42 |
43 | * **[Python 基础知识回顾](docs/Python)**
44 |
45 | ### 进阶
46 |
47 | 待更新...
48 |
49 |
50 |
51 | ## 网络
52 |
53 | * [计算机网络常见面试题](docs/计算机网络/计算机网络.md)
54 | * [计算机网络基础知识总结](docs/计算机网络/干货:计算机网络知识总结.md)
55 | * [计算机网络知识点](docs/计算机网络/计算机网络知识点.md)
56 | * [HTTPS中的TLS](docs/计算机网络/HTTPS中的TLS.md)
57 |
58 | ## 操作系统
59 |
60 | * [操作系统知识复习](docs/操作系统/操作系统知识复习.md)
61 |
62 | ### Linux相关
63 |
64 | * [后端程序员必备的 Linux 基础知识](docs/操作系统/Linux基础知识.md)
65 | * [Shell 编程入门](docs/操作系统/Shell.md)
66 |
67 | ## 数据结构与算法
68 |
69 | ### 数据结构
70 |
71 | - [数据结构知识学习与面试](docs/数据结构与算法/数据结构.md)
72 | - [面试官问你什么B树和B+树,把这篇文章丢给他](docs/数据结构与算法/面试官问你什么B树和B+树,把这篇文章丢给他.md)
73 |
74 | ### 算法
75 |
76 | - [动态规划](docs/数据结构与算法/动态规划.md)
77 | - [算法学习资源推荐](docs/数据结构与算法/算法学习资源推荐.md)
78 | - [几道常见的字符串算法题总结 ](docs/数据结构与算法/几道常见的子符串算法题.md)
79 | - [几道常见的链表算法题总结 ](docs/数据结构与算法/几道常见的链表算法题.md)
80 | - [《剑指Offer》面试题Python实现](https://github.com/ChangAn223/Python-Offer)
81 | - [公司真题](docs/数据结构与算法/公司真题.md)
82 | - [请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径](https://www.cnblogs.com/ChangAn223/p/10828238.html)
83 | - [回溯算法经典案例之N皇后问题](https://www.cnblogs.com/ChangAn223/p/10940455.html)
84 | - [详解N皇后问题:用java](docs/数据结构与算法/Backtracking-NQueens.md)
85 |
86 |
87 | ## 数据库
88 |
89 | ### MySQL
90 |
91 | * **[MySQL 学习与面试](docs/数据库/MySQL.md)**
92 | * **[一千行MySQL学习笔记](docs/数据库/一千行MySQL命令.md)**
93 | * [MySQL高性能优化规范建议](docs/数据库/MySQL高性能优化规范建议.md)
94 | * [数据库索引总结](docs/数据库/MySQL%20Index.md)
95 | * [事务隔离级别(图文详解)](docs/数据库/事务隔离级别(图文详解).md)
96 | * [一条SQL语句在MySQL中如何执行的](docs/数据库/一条sql语句在mysql中如何执行的.md)
97 |
98 | ### Redis
99 |
100 | * [Redis 总结](docs/数据库/Redis/Redis.md)
101 | * [Redlock分布式锁](docs/数据库/Redis/Redlock分布式锁.md)
102 | * [如何做可靠的分布式锁,Redlock真的可行么](docs/数据库/Redis/如何做可靠的分布式锁,Redlock真的可行么.md)
103 |
104 | ## 系统设计
105 |
106 | ### 常用框架
107 |
108 | #### Flask
109 |
110 | - [Flask请求流程](docs/Python/Flask请求流程.md)
111 |
112 | #### Django
113 |
114 | - 暂无
115 |
116 |
117 | #### ZooKeeper
118 |
119 | - [ZooKeeper 相关概念总结](docs/系统设计/ZooKeeper/ZooKeeper.md)
120 | - [ZooKeeper 数据模型和常见命令](docs/系统设计/ZooKeeper/ZooKeeper数据模型和常见命令.md)
121 |
122 | ### 权限认证
123 |
124 | - **[权限认证基础:区分Authentication,Authorization以及Cookie、Session、Token](docs/系统设计/authority-certification/basis-of-authority-certification.md)**
125 | - **[适合初学者入门 Spring Security With JWT 的 Demo](https://github.com/Snailclimb/spring-security-jwt-guide)**
126 |
127 | ### 设计模式
128 |
129 | - [设计模式系列文章](docs/系统设计)
130 |
131 | ### 数据通信
132 |
133 | - [数据通信(RESTful、RPC、消息队列)相关知识点总结](docs/系统设计/data-communication/summary.md)
134 | - [Dubbo 总结:关于 Dubbo 的重要知识点](docs/系统设计/data-communication/dubbo.md)
135 | - [消息队列总结](docs/系统设计/data-communication/message-queue.md)
136 | - [RabbitMQ 入门](docs/系统设计/data-communication/rabbitmq.md)
137 | - [RocketMQ的几个简单问题与答案](docs/系统设计/data-communication/RocketMQ-Questions.md)
138 | - [Kafka系统设计开篇-面试看这篇就够了](docs/系统设计/data-communication/Kafka系统设计开篇-面试看这篇就够了.md)
139 |
140 | ### 网站架构
141 |
142 | - [一文读懂分布式应该学什么](docs/系统设计/website-architecture/分布式.md)
143 | - [8 张图读懂大型网站技术架构](docs/系统设计/website-architecture/8%20张图读懂大型网站技术架构.md)
144 | - [【面试精选】关于大型网站系统架构你不得不懂的10个问题](docs/系统设计/website-architecture/【面试精选】关于大型网站系统架构你不得不懂的10个问题.md)
145 |
146 | ## 面试指南
147 |
148 | ### 备战面试
149 |
150 | * **[【备战面试1】程序员的简历就该这样写](docs/简历与面经/PreparingForInterview/程序员的简历之道.md)**
151 | * **[【备战面试2】初出茅庐的程序员该如何准备面试?](docs/简历与面经/PreparingForInterview/interviewPrepare.md)**
152 | * **[【备战面试3】7个大部分程序员在面试前很关心的问题](docs/简历与面经/PreparingForInterview/JavaProgrammerNeedKnow.md)**
153 | * **[【备战面试5】如果面试官问你“你有什么问题问我吗?”时,你该如何回答](docs/简历与面经/PreparingForInterview/如果面试官问你"你有什么问题问我吗?"时,你该如何回答.md)**
154 | * **[【备战面试6】美团面试常见问题总结(附详解答案)](docs/简历与面经/PreparingForInterview/美团面试常见问题总结.md)**
155 |
156 |
157 | ### 面经
158 |
159 | * [常见面试题总结一](docs/简历与面经)
160 |
161 | - [5面阿里,终获offer(2018年秋招)](docs/简历与面经/BATJrealInterviewExperience/5面阿里,终获offer.md)
162 | - [蚂蚁金服2019实习生面经总结(已拿口头offer)](docs/简历与面经/BATJrealInterviewExperience/蚂蚁金服实习生面经总结(已拿口头offer).md)
163 | - [2019年蚂蚁金服、头条、拼多多的面试总结](docs/简历与面经/BATJrealInterviewExperience/2019alipay-pinduoduo-toutiao.md)
164 |
165 | ## 工具
166 |
167 | ### Git
168 |
169 | * [Git入门](docs/tools/Git.md)
170 |
171 | ### Docker
172 |
173 | * [Docker 入门](docs/tools/Docker.md)
174 | * [一文搞懂 Docker 镜像的常用操作!](docs/tools/Docker-Image.md)
175 |
176 |
177 | ## 资源
178 |
179 | ### 书单
180 |
181 |
182 | ### 视频
183 |
184 |
185 |
186 | ### Github榜单
187 |
188 | * [热门Python开源项目排行榜(月榜)](https://github.com/trending/python?since=monthly)
--------------------------------------------------------------------------------
/docs/计算机网络/HTTPS中的TLS.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | - [1. SSL 与 TLS](#1-ssl-%E4%B8%8E-tls)
4 | - [2. 从网络协议的角度理解 HTTPS](#2-%E4%BB%8E%E7%BD%91%E7%BB%9C%E5%8D%8F%E8%AE%AE%E7%9A%84%E8%A7%92%E5%BA%A6%E7%90%86%E8%A7%A3-https)
5 | - [3. 从密码学的角度理解 HTTPS](#3-%E4%BB%8E%E5%AF%86%E7%A0%81%E5%AD%A6%E7%9A%84%E8%A7%92%E5%BA%A6%E7%90%86%E8%A7%A3-https)
6 | - [3.1. TLS 工作流程](#31-tls-%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B)
7 | - [3.2. 密码基础](#32-%E5%AF%86%E7%A0%81%E5%9F%BA%E7%A1%80)
8 | - [3.2.1. 伪随机数生成器](#321-%E4%BC%AA%E9%9A%8F%E6%9C%BA%E6%95%B0%E7%94%9F%E6%88%90%E5%99%A8)
9 | - [3.2.2. 消息认证码](#322-%E6%B6%88%E6%81%AF%E8%AE%A4%E8%AF%81%E7%A0%81)
10 | - [3.2.3. 数字签名](#323-%E6%95%B0%E5%AD%97%E7%AD%BE%E5%90%8D)
11 | - [3.2.4. 公钥密码](#324-%E5%85%AC%E9%92%A5%E5%AF%86%E7%A0%81)
12 | - [3.2.5. 证书](#325-%E8%AF%81%E4%B9%A6)
13 | - [3.2.6. 密码小结](#326-%E5%AF%86%E7%A0%81%E5%B0%8F%E7%BB%93)
14 | - [3.3. TLS 使用的密码技术](#33-tls-%E4%BD%BF%E7%94%A8%E7%9A%84%E5%AF%86%E7%A0%81%E6%8A%80%E6%9C%AF)
15 | - [3.4. TLS 总结](#34-tls-%E6%80%BB%E7%BB%93)
16 | - [4. RSA 简单示例](#4-rsa-%E7%AE%80%E5%8D%95%E7%A4%BA%E4%BE%8B)
17 | - [5. 参考](#5-%E5%8F%82%E8%80%83)
18 |
19 |
20 |
21 | # 1. SSL 与 TLS
22 |
23 | SSL:(Secure Socket Layer) 安全套接层,于 1994 年由网景公司设计,并于 1995 年发布了 3.0 版本
24 | TLS:(Transport Layer Security)传输层安全性协议,是 IETF 在 SSL3.0 的基础上设计的协议
25 | 以下全部使用 TLS 来表示
26 |
27 | # 2. 从网络协议的角度理解 HTTPS
28 |
29 | ![此图并不准确][1]
30 | HTTP:HyperText Transfer Protocol 超文本传输协议
31 | HTTPS:Hypertext Transfer Protocol Secure 超文本传输安全协议
32 | TLS:位于 HTTP 和 TCP 之间的协议,其内部有 TLS握手协议、TLS记录协议
33 | HTTPS 经由 HTTP 进行通信,但利用 TLS 来保证安全,即 HTTPS = HTTP + TLS
34 |
35 | # 3. 从密码学的角度理解 HTTPS
36 |
37 | HTTPS 使用 TLS 保证安全,这里的“安全”分两部分,一是传输内容加密、二是服务端的身份认证
38 |
39 | ## 3.1. TLS 工作流程
40 |
41 | ![此图并不准确][2]
42 | 此为服务端单向认证,还有客户端/服务端双向认证,流程类似,只不过客户端也有自己的证书,并发送给服务器进行验证
43 |
44 | ## 3.2. 密码基础
45 |
46 | ### 3.2.1. 伪随机数生成器
47 |
48 | 为什么叫伪随机数,因为没有真正意义上的随机数,具体可以参考 Random/TheadLocalRandom
49 | 它的主要作用在于生成对称密码的秘钥、用于公钥密码生成秘钥对
50 |
51 | ### 3.2.2. 消息认证码
52 |
53 | 消息认证码主要用于验证消息的完整性与消息的认证,其中消息的认证指“消息来自正确的发送者”
54 |
55 | >消息认证码用于验证和认证,而不是加密
56 |
57 | ![消息认证码过程][3]
58 |
59 | 1. 发送者与接收者事先共享秘钥
60 | 2. 发送者根据发送消息计算 MAC 值
61 | 3. 发送者发送消息和 MAC 值
62 | 4. 接收者根据接收到的消息计算 MAC 值
63 | 5. 接收者根据自己计算的 MAC 值与收到的 MAC 对比
64 | 6. 如果对比成功,说明消息完整,并来自与正确的发送者
65 |
66 | ### 3.2.3. 数字签名
67 |
68 | 消息认证码的缺点在于**无法防止否认**,因为共享秘钥被 client、server 两端拥有,server 可以伪造 client 发送给自己的消息(自己给自己发送消息),为了解决这个问题,我们需要它们有各自的秘钥不被第二个知晓(这样也解决了共享秘钥的配送问题)
69 |
70 | ![数字签名过程][4]
71 |
72 | >数字签名和消息认证码都**不是为了加密**
73 | >可以将单向散列函数获取散列值的过程理解为使用 md5 摘要算法获取摘要的过程
74 |
75 | 使用自己的私钥对自己所认可的消息生成一个该消息专属的签名,这就是数字签名,表明我承认该消息来自自己
76 | 注意:**私钥用于加签,公钥用于解签,每个人都可以解签,查看消息的归属人**
77 |
78 | ### 3.2.4. 公钥密码
79 |
80 | 公钥密码也叫非对称密码,由公钥和私钥组成,它是最开始是为了解决秘钥的配送传输安全问题,即,我们不配送私钥,只配送公钥,私钥由本人保管
81 | 它与数字签名相反,公钥密码的私钥用于解密、公钥用于加密,每个人都可以用别人的公钥加密,但只有对应的私钥才能解开密文
82 | client:明文 + 公钥 = 密文
83 | server:密文 + 私钥 = 明文
84 | 注意:**公钥用于加密,私钥用于解密,只有私钥的归属者,才能查看消息的真正内容**
85 |
86 | ### 3.2.5. 证书
87 |
88 | 证书:全称公钥证书(Public-Key Certificate, PKC),里面保存着归属者的基本信息,以及证书过期时间、归属者的公钥,并由认证机构(Certification Authority, **CA**)施加数字签名,表明,某个认证机构认定该公钥的确属于此人
89 |
90 | >想象这个场景:你想在支付宝页面交易,你需要支付宝的公钥进行加密通信,于是你从百度上搜索关键字“支付宝公钥”,你获得了支什宝的公钥,这个时候,支什宝通过中间人攻击,让你访问到了他们支什宝的页面,最后你在这个支什宝页面完美的使用了支什宝的公钥完成了与支什宝的交易
91 | >![证书过程][5]
92 |
93 | 在上面的场景中,你可以理解支付宝证书就是由支付宝的公钥、和给支付宝颁发证书的企业的数字签名组成
94 | 任何人都可以给自己或别人的公钥添加自己的数字签名,表明:我拿我的尊严担保,我的公钥/别人的公钥是真的,至于信不信那是另一回事了
95 |
96 | ### 3.2.6. 密码小结
97 |
98 | | 密码 | 作用 | 组成 |
99 | | :-- | :-- | :-- |
100 | | 消息认证码 | 确认消息的完整、并对消息的来源认证 | 共享秘钥+消息的散列值 |
101 | | 数字签名 | 对消息的散列值签名 | 公钥+私钥+消息的散列值 |
102 | | 公钥密码 | 解决秘钥的配送问题 | 公钥+私钥+消息 |
103 | | 证书 | 解决公钥的归属问题 | 公钥密码中的公钥+数字签名 |
104 |
105 | ## 3.3. TLS 使用的密码技术
106 |
107 | 1. 伪随机数生成器:秘钥生成随机性,更难被猜测
108 | 2. 对称密码:对称密码使用的秘钥就是由伪随机数生成,相较于非对称密码,效率更高
109 | 3. 消息认证码:保证消息信息的完整性、以及验证消息信息的来源
110 | 4. 公钥密码:证书技术使用的就是公钥密码
111 | 5. 数字签名:验证证书的签名,确定由真实的某个 CA 颁发
112 | 6. 证书:解决公钥的真实归属问题,降低中间人攻击概率
113 |
114 | ## 3.4. TLS 总结
115 |
116 | TLS 是一系列密码工具的框架,作为框架,它也是非常的灵活,体现在每个工具套件它都可以替换,即:客户端与服务端之间协商密码套件,从而更难的被攻破,例如使用不同方式的对称密码,或者公钥密码、数字签名生成方式、单向散列函数技术的替换等
117 |
118 | # 4. RSA 简单示例
119 |
120 | RSA 是一种公钥密码算法,我们简单的走一遍它的加密解密过程
121 | 加密算法:密文 = (明文^E) mod N,其中公钥为{E,N},即”求明文的E次方的对 N 的余数“
122 | 解密算法:明文 = (密文^D) mod N,其中秘钥为{D,N},即”求密文的D次方的对 N 的余数“
123 | 例:我们已知公钥为{5,323},私钥为{29,323},明文为300,请写出加密和解密的过程:
124 | >加密:密文 = 123 ^ 5 mod 323 = 225
125 | >解密:明文 = 225 ^ 29 mod 323 = [[(225 ^ 5) mod 323] * [(225 ^ 5) mod 323] * [(225 ^ 5) mod 323] * [(225 ^ 5) mod 323] * [(225 ^ 5) mod 323] * [(225 ^ 4) mod 323]] mod 323 = (4 * 4 * 4 * 4 * 4 * 290) mod 323 = 123
126 |
127 | # 5. 参考
128 |
129 | 1. SSL加密发生在哪里:
130 | 2. TLS工作流程:
131 | 3. 《图解密码技术》: 豆瓣评分 9.5
132 |
133 | [1]: https://leran2deeplearnjavawebtech.oss-cn-beijing.aliyuncs.com/somephoto/%E4%B8%83%E5%B1%82.png
134 | [2]: https://leran2deeplearnjavawebtech.oss-cn-beijing.aliyuncs.com/somephoto/tls%E6%B5%81%E7%A8%8B.png
135 | [3]: https://leran2deeplearnjavawebtech.oss-cn-beijing.aliyuncs.com/somephoto/%E6%B6%88%E6%81%AF%E8%AE%A4%E8%AF%81%E7%A0%81%E8%BF%87%E7%A8%8B.png
136 | [4]: https://leran2deeplearnjavawebtech.oss-cn-beijing.aliyuncs.com/somephoto/%E6%95%B0%E5%AD%97%E7%AD%BE%E5%90%8D%E8%BF%87%E7%A8%8B.png
137 | [5]: https://leran2deeplearnjavawebtech.oss-cn-beijing.aliyuncs.com/somephoto/dns%E4%B8%AD%E9%97%B4%E4%BA%BA%E6%94%BB%E5%87%BB.png
--------------------------------------------------------------------------------
/docs/数据库/事务隔离级别(图文详解).md:
--------------------------------------------------------------------------------
1 | > 本文由 [SnailClimb](https://github.com/Snailclimb) 和 [BugSpeak](https://github.com/BugSpeak) 共同完成。
2 |
3 |
4 | - [事务隔离级别(图文详解)](#事务隔离级别图文详解)
5 | - [什么是事务?](#什么是事务)
6 | - [事务的特性(ACID)](#事务的特性acid)
7 | - [并发事务带来的问题](#并发事务带来的问题)
8 | - [事务隔离级别](#事务隔离级别)
9 | - [实际情况演示](#实际情况演示)
10 | - [脏读(读未提交)](#脏读读未提交)
11 | - [避免脏读(读已提交)](#避免脏读读已提交)
12 | - [不可重复读](#不可重复读)
13 | - [可重复读](#可重复读)
14 | - [防止幻读(可重复读)](#防止幻读可重复读)
15 | - [参考](#参考)
16 |
17 |
18 |
19 | ## 事务隔离级别(图文详解)
20 |
21 | ### 什么是事务?
22 |
23 | 事务是逻辑上的一组操作,要么都执行,要么都不执行。
24 |
25 | 事务最经典也经常被拿出来说例子就是转账了。假如小明要给小红转账1000元,这个转账会涉及到两个关键操作就是:将小明的余额减少1000元,将小红的余额增加1000元。万一在这两个操作之间突然出现错误比如银行系统崩溃,导致小明余额减少而小红的余额没有增加,这样就不对了。事务就是保证这两个关键操作要么都成功,要么都要失败。
26 |
27 | ### 事务的特性(ACID)
28 |
29 | 
30 |
31 |
32 | 1. **原子性:** 事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
33 | 2. **一致性:** 执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
34 | 3. **隔离性:** 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
35 | 4. **持久性:** 一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
36 |
37 | ### 并发事务带来的问题
38 |
39 | 在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务(多个用户对统一数据进行操作)。并发虽然是必须的,但可能会导致以下的问题。
40 |
41 | - **脏读(Dirty read):** 当一个事务正在访问数据并且对数据进行了修改,而这种修改还没有提交到数据库中,这时另外一个事务也访问了这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。
42 | - **丢失修改(Lost to modify):** 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。 例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。
43 | - **不可重复读(Unrepeatableread):** 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
44 | - **幻读(Phantom read):** 幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
45 |
46 | **不可重复度和幻读区别:**
47 |
48 | 不可重复读的重点是修改,幻读的重点在于新增或者删除。
49 |
50 | 例1(同样的条件, 你读取过的数据, 再次读取出来发现值不一样了 ):事务1中的A先生读取自己的工资为 1000的操作还没完成,事务2中的B先生就修改了A的工资为2000,导 致A再读自己的工资时工资变为 2000;这就是不可重复读。
51 |
52 | 例2(同样的条件, 第1次和第2次读出来的记录数不一样 ):假某工资单表中工资大于3000的有4人,事务1读取了所有工资大于3000的人,共查到4条记录,这时事务2 又插入了一条工资大于3000的记录,事务1再次读取时查到的记录就变为了5条,这样就导致了幻读。
53 |
54 | ### 事务隔离级别
55 |
56 | **SQL 标准定义了四个隔离级别:**
57 |
58 | - **READ-UNCOMMITTED(读取未提交):** 最低的隔离级别,允许读取尚未提交的数据变更,**可能会导致脏读、幻读或不可重复读**。
59 | - **READ-COMMITTED(读取已提交):** 允许读取并发事务已经提交的数据,**可以阻止脏读,但是幻读或不可重复读仍有可能发生**。
60 | - **REPEATABLE-READ(可重复读):** 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,**可以阻止脏读和不可重复读,但幻读仍有可能发生**。
61 | - **SERIALIZABLE(可串行化):** 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,**该级别可以防止脏读、不可重复读以及幻读**。
62 |
63 | ----
64 |
65 | | 隔离级别 | 脏读 | 不可重复读 | 幻影读 |
66 | | :---: | :---: | :---:| :---: |
67 | | READ-UNCOMMITTED | √ | √ | √ |
68 | | READ-COMMITTED | × | √ | √ |
69 | | REPEATABLE-READ | × | × | √ |
70 | | SERIALIZABLE | × | × | × |
71 |
72 | MySQL InnoDB 存储引擎的默认支持的隔离级别是 **REPEATABLE-READ(可重读)**。我们可以通过`SELECT @@tx_isolation;`命令来查看,MySQL 8.0 该命令改为`SELECT @@transaction_isolation;`
73 |
74 | ```sql
75 | mysql> SELECT @@tx_isolation;
76 | +-----------------+
77 | | @@tx_isolation |
78 | +-----------------+
79 | | REPEATABLE-READ |
80 | +-----------------+
81 | ```
82 |
83 | 这里需要注意的是:与 SQL 标准不同的地方在于InnoDB 存储引擎在 **REPEATABLE-READ(可重读)**事务隔离级别下使用的是Next-Key Lock 锁算法,因此可以避免幻读的产生,这与其他数据库系统(如 SQL Server)是不同的。所以说InnoDB 存储引擎的默认支持的隔离级别是 **REPEATABLE-READ(可重读)** 已经可以完全保证事务的隔离性要求,即达到了 SQL标准的**SERIALIZABLE(可串行化)**隔离级别。
84 |
85 | 因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是**READ-COMMITTED(读取提交内容):**,但是你要知道的是InnoDB 存储引擎默认使用 **REPEATABLE-READ(可重读)**并不会有任何性能损失。
86 |
87 | InnoDB 存储引擎在 **分布式事务** 的情况下一般会用到**SERIALIZABLE(可串行化)**隔离级别。
88 |
89 | ### 实际情况演示
90 |
91 | 在下面我会使用 2 个命令行mysql ,模拟多线程(多事务)对同一份数据的脏读问题。
92 |
93 | MySQL 命令行的默认配置中事务都是自动提交的,即执行SQL语句后就会马上执行 COMMIT 操作。如果要显式地开启一个事务需要使用命令:`START TARNSACTION`。
94 |
95 | 我们可以通过下面的命令来设置隔离级别。
96 |
97 | ```sql
98 | SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE]
99 | ```
100 |
101 | 我们再来看一下我们在下面实际操作中使用到的一些并发控制语句:
102 |
103 | - `START TARNSACTION` |`BEGIN`:显式地开启一个事务。
104 | - `COMMIT`:提交事务,使得对数据库做的所有修改成为永久性。
105 | - `ROLLBACK`:回滚会结束用户的事务,并撤销正在进行的所有未提交的修改。
106 |
107 | #### 脏读(读未提交)
108 |
109 |
110 |
实例.jpg)
111 |
112 |
113 | #### 避免脏读(读已提交)
114 |
115 |
116 |

117 |
118 |
119 | #### 不可重复读
120 |
121 | 还是刚才上面的读已提交的图,虽然避免了读未提交,但是却出现了,一个事务还没有结束,就发生了 不可重复读问题。
122 |
123 |
124 |

125 |
126 |
127 | #### 可重复读
128 |
129 |
130 |

131 |
132 |
133 | #### 防止幻读(可重复读)
134 |
135 |
136 |
.jpg)
137 |
138 |
139 | 一个事务对数据库进行操作,这种操作的范围是数据库的全部行,然后第二个事务也在对这个数据库操作,这种操作可以是插入一行记录或删除一行记录,那么第一个是事务就会觉得自己出现了幻觉,怎么还有没有处理的记录呢? 或者 怎么多处理了一行记录呢?
140 |
141 | 幻读和不可重复读有些相似之处 ,但是不可重复读的重点是修改,幻读的重点在于新增或者删除。
142 |
143 | ### 参考
144 |
145 | - 《MySQL技术内幕:InnoDB存储引擎》
146 | -
147 | - [Mysql 锁:灵魂七拷问](https://tech.youzan.com/seven-questions-about-the-lock-of-mysql/)
148 | - [Innodb 中的事务隔离级别和锁的关系](https://tech.meituan.com/2014/08/20/innodb-lock.html)
149 |
--------------------------------------------------------------------------------
/docs/系统设计/ZooKeeper/ZooKeeper数据模型和常见命令.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | - [ZooKeeper 数据模型](#zookeeper-数据模型)
4 | - [ZNode\(数据节点\)的结构](#znode数据节点的结构)
5 | - [测试 ZooKeeper 中的常见操作](#测试-zookeeper-中的常见操作)
6 | - [连接 ZooKeeper 服务](#连接-zookeeper-服务)
7 | - [查看常用命令\(help 命令\)](#查看常用命令help-命令)
8 | - [创建节点\(create 命令\)](#创建节点create-命令)
9 | - [更新节点数据内容\(set 命令\)](#更新节点数据内容set-命令)
10 | - [获取节点的数据\(get 命令\)](#获取节点的数据get-命令)
11 | - [查看某个目录下的子节点\(ls 命令\)](#查看某个目录下的子节点ls-命令)
12 | - [查看节点状态\(stat 命令\)](#查看节点状态stat-命令)
13 | - [查看节点信息和状态\(ls2 命令\)](#查看节点信息和状态ls2-命令)
14 | - [删除节点\(delete 命令\)](#删除节点delete-命令)
15 | - [参考](#参考)
16 |
17 |
18 |
19 | > 看本文之前如果你没有安装 ZooKeeper 的话,可以参考这篇文章:[《使用 SpringBoot+Dubbo 搭建一个简单分布式服务》](https://github.com/Snailclimb/springboot-integration-examples/blob/master/md/springboot-dubbo.md) 的 “开始实战 1 :zookeeper 环境安装搭建” 这部分进行安装(Centos7.4 环境下)。如果你想对 ZooKeeper 有一个整体了解的话,可以参考这篇文章:[《可能是把 ZooKeeper 概念讲的最清楚的一篇文章》](https://github.com/Snailclimb/JavaGuide/blob/master/%E4%B8%BB%E6%B5%81%E6%A1%86%E6%9E%B6/ZooKeeper.md)
20 |
21 | ### ZooKeeper 数据模型
22 |
23 | ZNode(数据节点)是 ZooKeeper 中数据的最小单元,每个ZNode上都可以保存数据,同时还是可以有子节点(这就像树结构一样,如下图所示)。可以看出,节点路径标识方式和Unix文件
24 | 系统路径非常相似,都是由一系列使用斜杠"/"进行分割的路径表示,开发人员可以向这个节点中写人数据,也可以在节点下面创建子节点。这些操作我们后面都会介绍到。
25 | 
26 |
27 | 提到 ZooKeeper 数据模型,还有一个不得不得提的东西就是 **事务 ID** 。事务的ACID(Atomic:原子性;Consistency:一致性;Isolation:隔离性;Durability:持久性)四大特性我在这里就不多说了,相信大家也已经挺腻了。
28 |
29 | 在Zookeeper中,事务是指能够改变 ZooKeeper 服务器状态的操作,我们也称之为事务操作或更新操作,一般包括数据节点创建与删除、数据节点内容更新和客户端会话创建与失效等操作。**对于每一个事务请求,ZooKeeper 都会为其分配一个全局唯一的事务ID,用 ZXID 来表示**,通常是一个64位的数字。每一个ZXID对应一次更新操作,**从这些 ZXID 中可以间接地识别出Zookeeper处理这些更新操作请求的全局顺序**。
30 |
31 | ### ZNode(数据节点)的结构
32 |
33 | 每个 ZNode 由2部分组成:
34 |
35 | - stat:状态信息
36 | - data:数据内容
37 |
38 | 如下所示,我通过 get 命令来获取 根目录下的 dubbo 节点的内容。(get 命令在下面会介绍到)
39 |
40 | ```shell
41 | [zk: 127.0.0.1:2181(CONNECTED) 6] get /dubbo
42 | # 该数据节点关联的数据内容为空
43 | null
44 | # 下面是该数据节点的一些状态信息,其实就是 Stat 对象的格式化输出
45 | cZxid = 0x2
46 | ctime = Tue Nov 27 11:05:34 CST 2018
47 | mZxid = 0x2
48 | mtime = Tue Nov 27 11:05:34 CST 2018
49 | pZxid = 0x3
50 | cversion = 1
51 | dataVersion = 0
52 | aclVersion = 0
53 | ephemeralOwner = 0x0
54 | dataLength = 0
55 | numChildren = 1
56 |
57 | ```
58 | 这些状态信息其实就是 Stat 对象的格式化输出。Stat 类中包含了一个数据节点的所有状态信息的字段,包括事务ID、版本信息和子节点个数等,如下图所示(图源:《从Paxos到Zookeeper 分布式一致性原理与实践》,下面会介绍通过 stat 命令查看数据节点的状态)。
59 |
60 | **Stat 类:**
61 |
62 | 
63 |
64 | 关于数据节点的状态信息说明(也就是对Stat 类中的各字段进行说明),可以参考下图(图源:《从Paxos到Zookeeper 分布式一致性原理与实践》)。
65 |
66 | 
67 |
68 | ### 测试 ZooKeeper 中的常见操作
69 |
70 |
71 | #### 连接 ZooKeeper 服务
72 |
73 | 进入安装 ZooKeeper文件夹的 bin 目录下执行下面的命令连接 ZooKeeper 服务(Linux环境下)(连接之前首选要确定你的 ZooKeeper 服务已经启动成功)。
74 |
75 | ```shell
76 | ./zkCli.sh -server 127.0.0.1:2181
77 | ```
78 | 
79 |
80 | 从上图可以看出控制台打印出了很多信息,包括我们的主机名称、JDK 版本、操作系统等等。如果你成功看到这些信息,说明你成功连接到 ZooKeeper 服务。
81 |
82 | #### 查看常用命令(help 命令)
83 |
84 | help 命令查看 zookeeper 常用命令
85 |
86 | 
87 |
88 | #### 创建节点(create 命令)
89 |
90 | 通过 create 命令在根目录创建了node1节点,与它关联的字符串是"node1"
91 |
92 | ```shell
93 | [zk: 127.0.0.1:2181(CONNECTED) 34] create /node1 “node1”
94 | ```
95 | 通过 create 命令在根目录创建了node1节点,与它关联的内容是数字 123
96 |
97 | ```shell
98 | [zk: 127.0.0.1:2181(CONNECTED) 1] create /node1/node1.1 123
99 | Created /node1/node1.1
100 | ```
101 |
102 | #### 更新节点数据内容(set 命令)
103 |
104 | ```shell
105 | [zk: 127.0.0.1:2181(CONNECTED) 11] set /node1 "set node1"
106 | ```
107 |
108 | #### 获取节点的数据(get 命令)
109 |
110 | get 命令可以获取指定节点的数据内容和节点的状态,可以看出我们通过set 命令已经将节点数据内容改为 "set node1"。
111 |
112 | ```shell
113 | set node1
114 | cZxid = 0x47
115 | ctime = Sun Jan 20 10:22:59 CST 2019
116 | mZxid = 0x4b
117 | mtime = Sun Jan 20 10:41:10 CST 2019
118 | pZxid = 0x4a
119 | cversion = 1
120 | dataVersion = 1
121 | aclVersion = 0
122 | ephemeralOwner = 0x0
123 | dataLength = 9
124 | numChildren = 1
125 |
126 | ```
127 |
128 | #### 查看某个目录下的子节点(ls 命令)
129 |
130 | 通过 ls 命令查看根目录下的节点
131 |
132 | ```shell
133 | [zk: 127.0.0.1:2181(CONNECTED) 37] ls /
134 | [dubbo, zookeeper, node1]
135 | ```
136 | 通过 ls 命令查看 node1 目录下的节点
137 |
138 | ```shell
139 | [zk: 127.0.0.1:2181(CONNECTED) 5] ls /node1
140 | [node1.1]
141 | ```
142 | zookeeper 中的 ls 命令和 linux 命令中的 ls 类似, 这个命令将列出绝对路径path下的所有子节点信息(列出1级,并不递归)
143 |
144 | #### 查看节点状态(stat 命令)
145 |
146 | 通过 stat 命令查看节点状态
147 |
148 | ```shell
149 | [zk: 127.0.0.1:2181(CONNECTED) 10] stat /node1
150 | cZxid = 0x47
151 | ctime = Sun Jan 20 10:22:59 CST 2019
152 | mZxid = 0x47
153 | mtime = Sun Jan 20 10:22:59 CST 2019
154 | pZxid = 0x4a
155 | cversion = 1
156 | dataVersion = 0
157 | aclVersion = 0
158 | ephemeralOwner = 0x0
159 | dataLength = 11
160 | numChildren = 1
161 | ```
162 | 上面显示的一些信息比如cversion、aclVersion、numChildren等等,我在上面 “ZNode(数据节点)的结构” 这部分已经介绍到。
163 |
164 | #### 查看节点信息和状态(ls2 命令)
165 |
166 |
167 | ls2 命令更像是 ls 命令和 stat 命令的结合。ls2 命令返回的信息包括2部分:子节点列表 + 当前节点的stat信息。
168 |
169 | ```shell
170 | [zk: 127.0.0.1:2181(CONNECTED) 7] ls2 /node1
171 | [node1.1]
172 | cZxid = 0x47
173 | ctime = Sun Jan 20 10:22:59 CST 2019
174 | mZxid = 0x47
175 | mtime = Sun Jan 20 10:22:59 CST 2019
176 | pZxid = 0x4a
177 | cversion = 1
178 | dataVersion = 0
179 | aclVersion = 0
180 | ephemeralOwner = 0x0
181 | dataLength = 11
182 | numChildren = 1
183 |
184 | ```
185 |
186 | #### 删除节点(delete 命令)
187 |
188 | 这个命令很简单,但是需要注意的一点是如果你要删除某一个节点,那么这个节点必须无子节点才行。
189 |
190 | ```shell
191 | [zk: 127.0.0.1:2181(CONNECTED) 3] delete /node1/node1.1
192 | ```
193 |
194 | 在后面我会介绍到 Java 客户端 API的使用以及开源 Zookeeper 客户端 ZkClient 和 Curator 的使用。
195 |
196 |
197 | ### 参考
198 |
199 | - 《从Paxos到Zookeeper 分布式一致性原理与实践》
200 |
201 |
--------------------------------------------------------------------------------
/docs/数据结构与算法/公司真题.md:
--------------------------------------------------------------------------------
1 | # 网易 2018
2 |
3 | 下面三道编程题来自网易2018校招编程题,这三道应该来说是非常简单的编程题了,这些题目大家稍微有点编程和数学基础的话应该没什么问题。看答案之前一定要自己先想一下如果是自己做的话会怎么去做,然后再对照这我的答案看看,和你自己想的有什么区别?那一种方法更好?
4 |
5 | ## 问题
6 |
7 | ### 一 获得特定数量硬币问题
8 |
9 | 小易准备去魔法王国采购魔法神器,购买魔法神器需要使用魔法币,但是小易现在一枚魔法币都没有,但是小易有两台魔法机器可以通过投入x(x可以为0)个魔法币产生更多的魔法币。
10 |
11 | 魔法机器1:如果投入x个魔法币,魔法机器会将其变为2x+1个魔法币
12 |
13 | 魔法机器2:如果投入x个魔法币,魔法机器会将其变为2x+2个魔法币
14 |
15 | 小易采购魔法神器总共需要n个魔法币,所以小易只能通过两台魔法机器产生恰好n个魔法币,小易需要你帮他设计一个投入方案使他最后恰好拥有n个魔法币。
16 |
17 | **输入描述:** 输入包括一行,包括一个正整数n(1 ≤ n ≤ 10^9),表示小易需要的魔法币数量。
18 |
19 | **输出描述:** 输出一个字符串,每个字符表示该次小易选取投入的魔法机器。其中只包含字符'1'和'2'。
20 |
21 | **输入例子1:** 10
22 |
23 | **输出例子1:** 122
24 |
25 | ### 二 求“相反数”问题
26 |
27 | 为了得到一个数的"相反数",我们将这个数的数字顺序颠倒,然后再加上原先的数得到"相反数"。例如,为了得到1325的"相反数",首先我们将该数的数字顺序颠倒,我们得到5231,之后再加上原先的数,我们得到5231+1325=6556.如果颠倒之后的数字有前缀零,前缀零将会被忽略。例如n = 100, 颠倒之后是1.
28 |
29 | **输入描述:** 输入包括一个整数n,(1 ≤ n ≤ 10^5)
30 |
31 | **输出描述:** 输出一个整数,表示n的相反数
32 |
33 | **输入例子1:** 1325
34 |
35 | **输出例子1:** 6556
36 |
37 | ### 三 字符串碎片的平均长度
38 |
39 | 一个由小写字母组成的字符串可以看成一些同一字母的最大碎片组成的。例如,"aaabbaaac"是由下面碎片组成的:'aaa','bb','c'。牛牛现在给定一个字符串,请你帮助计算这个字符串的所有碎片的平均长度是多少。
40 |
41 | **输入描述:** 输入包括一个字符串s,字符串s的长度length(1 ≤ length ≤ 50),s只含小写字母('a'-'z')
42 |
43 | **输出描述:** 输出一个整数,表示所有碎片的平均长度,四舍五入保留两位小数。
44 |
45 | **如样例所示:** s = "aaabbaaac"
46 | 所有碎片的平均长度 = (3 + 2 + 3 + 1) / 4 = 2.25
47 |
48 | **输入例子1:** aaabbaaac
49 |
50 | **输出例子1:** 2.25
51 |
52 | ## 答案
53 |
54 | ### 一 获得特定数量硬币问题
55 |
56 | #### 分析:
57 |
58 | 作为该试卷的第一题,这道题应该只要思路正确就很简单了。
59 |
60 | 解题关键:明确魔法机器1只能产生奇数,魔法机器2只能产生偶数即可。我们从后往前一步一步推回去即可。
61 |
62 | #### 示例代码
63 |
64 | 注意:由于用户的输入不确定性,一般是为了程序高可用性使需要将捕获用户输入异常然后友好提示用户输入类型错误并重新输入的。所以下面我给了两个版本,这两个版本都是正确的。这里只是给大家演示如何捕获输入类型异常,后面的题目中我给的代码没有异常处理的部分,参照下面两个示例代码,应该很容易添加。(PS:企业面试中没有明确就不用添加异常处理,当然你有的话也更好)
65 |
66 | **不带输入异常处理判断的版本:**
67 |
68 | ```java
69 | import java.util.Scanner;
70 |
71 | public class Main2 {
72 | // 解题关键:明确魔法机器1只能产生奇数,魔法机器2只能产生偶数即可。我们从后往前一步一步推回去即可。
73 |
74 | public static void main(String[] args) {
75 | System.out.println("请输入要获得的硬币数量:");
76 | Scanner scanner = new Scanner(System.in);
77 | int coincount = scanner.nextInt();
78 | StringBuilder sb = new StringBuilder();
79 | while (coincount >= 1) {
80 | // 偶数的情况
81 | if (coincount % 2 == 0) {
82 | coincount = (coincount - 2) / 2;
83 | sb.append("2");
84 | // 奇数的情况
85 | } else {
86 | coincount = (coincount - 1) / 2;
87 | sb.append("1");
88 | }
89 | }
90 | // 输出反转后的字符串
91 | System.out.println(sb.reverse());
92 |
93 | }
94 | }
95 | ```
96 |
97 | **带输入异常处理判断的版本(当输入的不是整数的时候会提示重新输入):**
98 |
99 | ```java
100 | import java.util.InputMismatchException;
101 | import java.util.Scanner;
102 |
103 |
104 | public class Main {
105 | // 解题关键:明确魔法机器1只能产生奇数,魔法机器2只能产生偶数即可。我们从后往前一步一步推回去即可。
106 |
107 | public static void main(String[] args) {
108 | System.out.println("请输入要获得的硬币数量:");
109 | Scanner scanner = new Scanner(System.in);
110 | boolean flag = true;
111 | while (flag) {
112 | try {
113 | int coincount = scanner.nextInt();
114 | StringBuilder sb = new StringBuilder();
115 | while (coincount >= 1) {
116 | // 偶数的情况
117 | if (coincount % 2 == 0) {
118 | coincount = (coincount - 2) / 2;
119 | sb.append("2");
120 | // 奇数的情况
121 | } else {
122 | coincount = (coincount - 1) / 2;
123 | sb.append("1");
124 | }
125 | }
126 | // 输出反转后的字符串
127 | System.out.println(sb.reverse());
128 | flag=false;//程序结束
129 | } catch (InputMismatchException e) {
130 | System.out.println("输入数据类型不匹配,请您重新输入:");
131 | scanner.nextLine();
132 | continue;
133 | }
134 | }
135 |
136 | }
137 | }
138 |
139 | ```
140 |
141 | ### 二 求“相反数”问题
142 |
143 | #### 分析:
144 |
145 | 解决本道题有几种不同的方法,但是最快速的方法就是利用reverse()方法反转字符串然后再将字符串转换成int类型的整数,这个方法是快速解决本题关键。我们先来回顾一下下面两个知识点:
146 |
147 | **1)String转int;**
148 |
149 | 在 Java 中要将 String 类型转化为 int 类型时,需要使用 Integer 类中的 parseInt() 方法或者 valueOf() 方法进行转换.
150 |
151 | ```java
152 | String str = "123";
153 | int a = Integer.parseInt(str);
154 | ```
155 |
156 | 或
157 |
158 | ```java
159 | String str = "123";
160 | int a = Integer.valueOf(str).intValue();
161 | ```
162 |
163 | **2)next()和nextLine()的区别**
164 |
165 | 在Java中输入字符串有两种方法,就是next()和nextLine().两者的区别就是:nextLine()的输入是碰到回车就终止输入,而next()方法是碰到空格,回车,Tab键都会被视为终止符。所以next()不会得到带空格的字符串,而nextLine()可以得到带空格的字符串。
166 |
167 | #### 示例代码:
168 |
169 | ```java
170 | import java.util.Scanner;
171 |
172 | /**
173 | * 本题关键:①String转int;②next()和nextLine()的区别
174 | */
175 | public class Main {
176 |
177 | public static void main(String[] args) {
178 |
179 | System.out.println("请输入一个整数:");
180 | Scanner scanner = new Scanner(System.in);
181 | String s=scanner.next();
182 | //将字符串转换成数字
183 | int number1=Integer.parseInt(s);
184 | //将字符串倒序后转换成数字
185 | //因为Integer.parseInt()的参数类型必须是字符串所以必须加上toString()
186 | int number2=Integer.parseInt(new StringBuilder(s).reverse().toString());
187 | System.out.println(number1+number2);
188 |
189 | }
190 | }
191 | ```
192 |
193 | ### 三 字符串碎片的平均长度
194 |
195 | #### 分析:
196 |
197 | 这道题的意思也就是要求:(字符串的总长度)/(相同字母团构成的字符串的个数)。
198 |
199 | 这样就很简单了,就变成了字符串的字符之间的比较。如果需要比较字符串的字符的话,我们可以利用charAt(i)方法:取出特定位置的字符与后一个字符比较,或者利用toCharArray()方法将字符串转换成字符数组采用同样的方法做比较。
200 |
201 | #### 示例代码
202 |
203 | **利用charAt(i)方法:**
204 |
205 | ```java
206 | import java.util.Scanner;
207 |
208 | public class Main {
209 |
210 | public static void main(String[] args) {
211 |
212 | Scanner sc = new Scanner(System.in);
213 | while (sc.hasNext()) {
214 | String s = sc.next();
215 | //个数至少为一个
216 | float count = 1;
217 | for (int i = 0; i < s.length() - 1; i++) {
218 | if (s.charAt(i) != s.charAt(i + 1)) {
219 | count++;
220 | }
221 | }
222 | System.out.println(s.length() / count);
223 | }
224 | }
225 |
226 | }
227 | ```
228 |
229 | **利用toCharArray()方法:**
230 |
231 | ```java
232 | import java.util.Scanner;
233 |
234 | public class Main2 {
235 |
236 | public static void main(String[] args) {
237 |
238 | Scanner sc = new Scanner(System.in);
239 | while (sc.hasNext()) {
240 | String s = sc.next();
241 | //个数至少为一个
242 | float count = 1;
243 | char [] stringArr = s.toCharArray();
244 | for (int i = 0; i < stringArr.length - 1; i++) {
245 | if (stringArr[i] != stringArr[i + 1]) {
246 | count++;
247 | }
248 | }
249 | System.out.println(s.length() / count);
250 | }
251 | }
252 |
253 | }
254 | ```
--------------------------------------------------------------------------------
/docs/数据库/一条sql语句在mysql中如何执行的.md:
--------------------------------------------------------------------------------
1 | 本文来自[木木匠](https://github.com/kinglaw1204)投稿,有改动。
2 |
3 |
4 |
5 | - [一 MySQL 基础架构分析](#一、MySQL基础架构分析)
6 | - [1.1 MySQL 基本架构概览](#MySQL基本架构概览)
7 | - [1.2 Server 层基本组件介绍](#Server层基本组件介绍)
8 | - [1) 连接器](#1.连接器)
9 | - [2) 查询缓存(MySQL 8.0 版本后移除)](#2.查询缓存(MySQL8.0版本后移除))
10 | - [3) 分析器](#3.分析器)
11 | - [4) 优化器](#4.优化器)
12 | - [5) 执行器](#5.执行器)
13 | - [二 语句分析](#二-语句分析)
14 | - [2.1 查询语句](#2.1查询语句)
15 | - [2.2 更新语句](#2.2更新语句)
16 | - [三 总结](#三-总结)
17 | - [四 参考](#四-参考)
18 |
19 |
20 |
21 | 本篇文章会分析下一个 sql 语句在 MySQL 中的执行流程,包括 sql 的查询在 MySQL 内部会怎么流转,sql 语句的更新是怎么完成的。
22 |
23 | 在分析之前我会先带着你看看 MySQL 的基础架构,知道了 MySQL 由那些组件组成已经这些组件的作用是什么,可以帮助我们理解和解决这些问题。
24 |
25 | ## 一、MySQL基础架构分析
26 |
27 | ### MySQL基本架构概览
28 |
29 | 下图是MySQL逻辑架构图,从下图你可以很清晰的看到用户的 SQL 语句在 MySQL 内部是如何执行的。
30 |
31 | 
32 |
33 | **MySQL逻辑架构分四层**
34 |
35 | 1.连接层:主要完成一些类似连接处理,授权认证及相关的安全方案。
36 |
37 | 2.服务层:在 MySQL据库系统处理底层数据之前的所有工作都是在这一层完成的,包括权限判断,SQL接口,SQL解析,SQL分析优化, 缓存查询的处理以及部分内置函数执行(如日期,时间,数学运算,加密)等等。各个存储引擎提供的功能都集中在这一层,如存储过程,触发器,视图等。
38 |
39 | 3.引擎层:是底层数据存取操作实现部分,由多种存储引擎共同组成。真正负责MySQL中数据的存储和提取。就像Linux众多的文件系统 一样。每个存储引擎都有自己的优点和缺陷。服务器是通过存储引擎API来与它们交互的。这个接口隐藏 了各个存储引擎不同的地方。对于查询层尽可能的透明。这个API包含了很多底层的操作。如开始一个事物,或者取出有特定主键的行。存储引擎不能解析SQL,互相之间也不能通信。仅仅是简单的响应服务器 的请求。
40 |
41 | 4.存储层:将数据存储于裸设备的文件系统之上,完成与存储引擎的交互。
42 |
43 | 参考[张冲andy的文章:MySQL逻辑架构概述](https://www.cnblogs.com/andy6/p/5789254.html)
44 |
45 | **简单来说 MySQL 主要分为 Server 层和存储引擎层:**
46 |
47 | - Server 层:主要包括连接器、查询缓存、分析器、优化器、执行器等,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图,函数等,还有一个通用的日志模块 binglog 日志模块。
48 | - 存储引擎: 主要负责数据的存储和读取,采用可以替换的插件式架构,支持 InnoDB、MyISAM、Memory 等多个存储引擎,其中 InnoDB 引擎有自有的日志模块 redolog 模块。**现在最常用的存储引擎是 InnoDB,它从 MySQL 5.5.5 版本开始就被当做默认存储引擎了。**
49 |
50 | 查看MySQL中现在提供的存储引擎:
51 |
52 | show engines;
53 |
54 | 查看MySQL现在默认使用的存储引擎:
55 |
56 | show variables like '%storage_engine%';
57 |
58 | 查看某表使用的存储引擎:
59 |
60 | show create table 表名;
61 |
62 | 从上面MySQL逻辑架构图可以看到,MySQL有很多种存储引擎,他们以插件的形式存在,需要那个存储引擎则装上那个存储引擎。
63 | 其实在MySQL中主要使用MyISAM引擎和InnoDB引擎。
64 |
65 | **MyISAM引擎和InnoDB引擎简单对比:**
66 |
67 |
68 | ||MyISAM引擎|InnoDB引擎|
69 | |------------|-----------|------------|
70 | |主外键|不支持|支持|
71 | |事务|不支持|支持|
72 | |行表锁|表所.不适合高并发|行锁.适合高并发|
73 | |缓存|只缓存索引|缓存索引和真实数据|
74 | |表空间|小|大|
75 | |关注点|性能.偏读|事务|
76 | |默认安装|是|是|
77 |
78 |
79 | ### Server层基本组件介绍
80 |
81 | #### 1.连接器
82 |
83 | 连接器主要和身份认证和权限相关的功能相关,就好比一个级别很高的门卫一样。
84 |
85 | 主要负责用户登录数据库,进行用户的身份认证,包括校验账户密码,权限等操作,如果用户账户密码已通过,连接器会到权限表中查询该用户的所有权限,之后在这个连接里的权限逻辑判断都是会依赖此时读取到的权限数据,也就是说,后续只要这个连接不断开,即时管理员修改了该用户的权限,该用户也是不受影响的。
86 |
87 | #### 2.查询缓存(MySQL8.0版本后移除)
88 |
89 | 查询缓存主要用来缓存我们所执行的 SELECT 语句以及该语句的结果集。
90 |
91 | 连接建立后,执行查询语句的时候,会先查询缓存,MySQL 会先校验这个 sql 是否执行过,以 Key-Value 的形式缓存在内存中,Key 是查询预计,Value 是结果集。如果缓存 key 被命中,就会直接返回给客户端,如果没有命中,就会执行后续的操作,完成后也会把结果缓存起来,方便下一次调用。当然在真正执行缓存查询的时候还是会校验用户的权限,是否有该表的查询条件。
92 |
93 | MySQL 查询不建议使用缓存,因为查询缓存失效在实际业务场景中可能会非常频繁,假如你对一个表更新的话,这个表上的所有的查询缓存都会被清空。对于不经常更新的数据来说,使用缓存还是可以的。
94 |
95 | 所以,一般在大多数情况下我们都是不推荐去使用查询缓存的。
96 |
97 | MySQL 8.0 版本后删除了缓存的功能,官方也是认为该功能在实际的应用场景比较少,所以干脆直接删掉了。
98 |
99 | #### 3.分析器
100 |
101 | MySQL 没有命中缓存,那么就会进入分析器,分析器主要是用来分析 SQL 语句是来干嘛的,分析器也会分为几步:
102 |
103 | **第一步,词法分析**,一条 SQL 语句有多个字符串组成,首先要提取关键字,比如 select,提出查询的表,提出字段名,提出查询条件等等。做完这些操作后,就会进入第二步。
104 |
105 | **第二步,语法分析**,主要就是判断你输入的 sql 是否正确,是否符合 MySQL 的语法。
106 |
107 | 完成这 2 步之后,MySQL 就准备开始执行了,但是如何执行,怎么执行是最好的结果呢?这个时候就需要优化器上场了。
108 |
109 | #### 4.优化器
110 |
111 | 优化器的作用就是它认为的最优的执行方案去执行(有时候可能也不是最优,这篇文章涉及对这部分知识的深入讲解),比如多个索引的时候该如何选择索引,多表查询的时候如何选择关联顺序等。
112 |
113 | 可以说,经过了优化器之后可以说这个语句具体该如何执行就已经定下来。
114 |
115 | #### 5.执行器
116 |
117 | 当选择了执行方案后,MySQL 就准备开始执行了,首先执行前会校验该用户有没有权限,如果没有权限,就会返回错误信息,如果有权限,就会去调用引擎的接口,返回接口执行的结果。
118 |
119 | ## 二 语句分析
120 |
121 | **MySQL语句执行流程图**
122 |
123 | 
124 |
125 | ### 2.1查询语句
126 |
127 | 说了以上这么多,那么究竟一条 sql 语句是如何执行的呢?其实我们的 sql 可以分为两种,一种是查询,一种是更新(增加,更新,删除)。我们先分析下查询语句,语句如下:
128 |
129 | ```sql
130 | select * from tb_student A where A.age='18' and A.name=' 张三 ';
131 | ```
132 |
133 | 结合上面的说明,我们分析下这个语句的执行流程:
134 |
135 | * 先检查该语句是否有权限,如果没有权限,直接返回错误信息,如果有权限,在 MySQL8.0 版本以前,会先查询缓存,以这条 sql 语句为 key 在内存中查询是否有结果,如果有直接缓存,如果没有,执行下一步。
136 | * 通过分析器进行词法分析,提取 sql 语句的关键元素,比如提取上面这个语句是查询 select,提取需要查询的表名为 tb_student,需要查询所有的列,查询条件是这个表的 id='1'。然后判断这个 sql 语句是否有语法错误,比如关键词是否正确等等,如果检查没问题就执行下一步。
137 | * 接下来就是优化器进行确定执行方案,上面的 sql 语句,可以有两种执行方案:
138 |
139 | a.先查询学生表中姓名为“张三”的学生,然后判断是否年龄是 18。
140 | b.先找出学生中年龄 18 岁的学生,然后再查询姓名为“张三”的学生。
141 | 那么优化器根据自己的优化算法进行选择执行效率最好的一个方案(优化器认为,有时候不一定最好)。那么确认了执行计划后就准备开始执行了。
142 |
143 | * 进行权限校验,如果没有权限就会返回错误信息,如果有权限就会调用数据库引擎接口,返回引擎的执行结果。
144 |
145 | ### 2.2更新语句
146 |
147 | 以上就是一条查询 sql 的执行流程,那么接下来我们看看一条更新语句如何执行的呢?sql 语句如下:
148 |
149 | ```
150 | update tb_student A set A.age='19' where A.name=' 张三 ';
151 | ```
152 | 我们来给张三修改下年龄,在实际数据库肯定不会设置年龄这个字段的,不然要被技术负责人打的。其实条语句也基本上会沿着上一个查询的流程走,只不过执行更新的时候肯定要记录日志啦,这就会引入日志模块了,MySQL 自带的日志模块式 **binlog(归档日志)** ,所有的存储引擎都可以使用,我们常用的 InnoDB 引擎还自带了一个日志模块 **redo log(重做日志)**,我们就以 InnoDB 模式下来探讨这个语句的执行流程。流程如下:
153 |
154 | * 先查询到张三这一条数据,如果有缓存,也是会用到缓存。
155 | * 然后拿到查询的语句,把 age 改为 19,然后调用引擎 API 接口,写入这一行数据,InnoDB 引擎把数据保存在内存中,同时记录 redo log,此时 redo log 进入 prepare 状态,然后告诉执行器,执行完成了,随时可以提交。
156 | * 执行器收到通知后记录 binlog,然后调用引擎接口,提交 redo log 为提交状态。
157 | * 更新完成。
158 |
159 | **这里肯定有同学会问,为什么要用两个日志模块,用一个日志模块不行吗?**
160 |
161 | 这是因为最开始 MySQL 并没与 InnoDB 引擎( InnoDB 引擎是其他公司以插件形式插入 MySQL 的) ,MySQL 自带的引擎是 MyISAM,但是我们知道 redo log 是 InnoDB 引擎特有的,其他存储引擎都没有,这就导致会没有 crash-safe 的能力(crash-safe 的能力即使数据库发生异常重启,之前提交的记录都不会丢失),binlog 日志只能用来归档。
162 |
163 | 并不是说只用一个日志模块不可以,只是 InnoDB 引擎就是通过 redo log 来支持事务的。那么,又会有同学问,我用两个日志模块,但是不要这么复杂行不行,为什么 redo log 要引入 prepare 预提交状态?这里我们用反证法来说明下为什么要这么做?
164 |
165 | * **先写 redo log 直接提交,然后写 binlog**,假设写完 redo log 后,机器挂了,binlog 日志没有被写入,那么机器重启后,这台机器会通过 redo log 恢复数据,但是这个时候 bingog 并没有记录该数据,后续进行机器备份的时候,就会丢失这一条数据,同时主从同步也会丢失这一条数据。
166 | * **先写 binlog,然后写 redo log**,假设写完了 binlog,机器异常重启了,由于没有 redo log,本机是无法恢复这一条记录的,但是 binlog 又有记录,那么和上面同样的道理,就会产生数据不一致的情况。
167 |
168 | 如果采用 redo log 两阶段提交的方式就不一样了,写完 binglog 后,然后再提交 redo log 就会防止出现上述的问题,从而保证了数据的一致性。那么问题来了,有没有一个极端的情况呢?假设 redo log 处于预提交状态,binglog 也已经写完了,这个时候发生了异常重启会怎么样呢?
169 | 这个就要依赖于 MySQL 的处理机制了,MySQL 的处理过程如下:
170 |
171 | * 判断 redo log 是否完整,如果判断是完整的,就立即提交。
172 | * 如果 redo log 只是预提交但不是 commit 状态,这个时候就会去判断 binlog 是否完整,如果完整就提交 redo log, 不完整就回滚事务。
173 |
174 | 这样就解决了数据一致性的问题。
175 |
176 | ## 三 总结
177 |
178 | * MySQL 主要分为 Server 层和引擎层,Server 层主要包括连接器、查询缓存、分析器、优化器、执行器,同时还有一个日志模块(binlog),这个日志模块所有执行引擎都可以共用,redolog 只有 InnoDB 有。
179 | * 引擎层是插件式的,目前主要包括,MyISAM,InnoDB,Memory 等。
180 | * 查询语句的执行流程如下:权限校验(如果命中缓存)---》查询缓存---》分析器---》优化器---》权限校验---》执行器---》引擎
181 | * 更新语句执行流程如下:分析器----》权限校验----》执行器---》引擎---redo log(prepare 状态---》binlog---》redo log(commit状态)
182 |
183 | ## 四 参考
184 |
185 | * 《MySQL 实战45讲》
186 | * MySQL 5.6参考手册:
187 |
--------------------------------------------------------------------------------
/docs/数据库/MySQL事务及其ACID特性.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | - [事务](#一、事务)
4 | - [什么是事务?](#什么是事务?)
5 | - [事务的隔离性与隔离级别](#隔离性与隔离级别)
6 | - [事务的ACID特性及实现原理](#二、ACID特性及实现原理)
7 | - [1.原子性](#1.原子性)
8 | - [2.一致性](#2.一致性)
9 | - [3.隔离性](#3.隔离性)
10 | - [保证写-写事务的隔离性:锁机制](#①锁机制)
11 | - [保证写-读事物的隔离性:MVCC](#②MVCC(多版本并发控制协议))
12 | - [4.持久性](#4.持久性)
13 |
14 |
15 |
16 | ## 一、事务
17 |
18 | ### 什么是事务?
19 |
20 | 事务是访问和更新数据库的程序执行单元,事务中包含一条或者多条SQL语句,这些语句要么全部执行成功,要么都不执行。
21 |
22 |
23 | 在MySQL中,事务支持是在引擎层实现的,MySQL是一个支持多引擎的系统,但并不是所有的引擎都支持事务,比如MySQL原生的MyISAM引擎就不支持事务,而InnoDB很好的支持事务。
24 |
25 |
26 | 事务的ACID特性是原子性、一致性、隔离性、持久性。按照严格的标准,只有同时满足ACID特性的才是事务,但是在各大数据库厂商的实现中,真正满足ACID特性的事务极少。InnoDB引擎默认事务隔离级别是“可重复读”,未满足隔离性;Oracle默认的事务隔离级别是“读提交”,也不满足隔离性...
27 |
28 |
29 | ### 隔离性与隔离级别
30 |
31 | 隔离性是事务特性之一,当数据库上有多个事务同时执行的时候,就有可能出现“脏读”、“不可重复读”、“幻读”的问题。为了解决相应的这些问题,提出了“隔离级别”的概念。
32 |
33 |
34 | **SQL标准的隔离级别包括:读未提交,读提交,可重复读,串行化。**需要明确的是,隔离的越严实,效率就会越低,因此很多时候我们需要在二者之间找到一个平衡点。
35 |
36 |
37 | - **读未提交:**一个事务还未提交,它做的变更就能被别的事务看见。
38 | - **读提交:**一事务在提交之后,它做的变更才能被其他事务看见。
39 | - **可重复读:**一个事务在执行过程中看到的数据总是和这个事务在启动时看见的数据是一致的。
40 | - **串行化:**对于同一记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成才能继续执行。(类似操作系统中对资源的互斥访问)
41 |
42 |
43 | **实现上述隔离级别原理简述:**
44 |
45 | “读未提交”直接返回记录上的最新值;“读提交”和“可重复读”在不同的时候建立视图,访问的时候以视图的逻辑结果为准。“读提交”在SQL语句开始执行的时候建立视图,“可重复读”在事务启动的时候创建视图,在整个事务存在期间都使用的这个视图;“串行化”直接用“加锁”的方式避免并行访问。
46 |
47 |
48 | ## 二、ACID特性及实现原理
49 |
50 | ### 1.原子性
51 |
52 | 原子性是指一个事务是一个不可分割的工作单位,其中的操作要么都做,要么都不做。如果事务中的某一条SQL执行失败,则已经执行的语句必须回滚,数据库退回到执行事务之前的状态。
53 |
54 | 实现原理:undo log(回滚日志)
55 |
56 | InnoDB存储引擎提供了两种事务日志:
57 |
58 | - redo log(重做日志)
59 | - undo log(回滚日志)
60 |
61 | redo log用于保持事务持久性,undo log是事务原子性和隔离性的实现基础。
62 |
63 | - 当事务对数据库进行修改的时候,InnoDB会生成对应的 undo log
64 |
65 | - 事务执行失败或者调用了rollback,事务进行回滚,利用 undo log 中的信息将数据回滚到修改之前的样子。
66 |
67 | undo log 属于逻辑日志,它记录的是SQL执行相关的信息。当发生回滚的时候,InnoDB会根据 undo log 的内容做与之前相反的工作:
68 |
69 | - 对于每个 insert ,回滚的时候执行 delete
70 | - 对于每个 delete ,回滚的时候执行 insert
71 | - 对于每个 update ,回滚的时候会执行一个相反的 update 把数据该回去
72 |
73 | 以 update 为例,当事务执行 update 的时候,生成的 undo log 中会包含被修改行的主键(便于知道修改了哪些行)、修改了哪些列、及这些列在修改前后的值等信息,回滚的时候便可以利用这些信息将数据还原到 update 之前的状态。
74 |
75 | ### 2.一致性
76 |
77 | 一致性是指事务执行后,数据库的完整性约束没有被破坏,事务执行前后都是合法的数据状态。
78 |
79 | 数据库的完整性约束包括但不限于:
80 |
81 | - 实体完整性(如行的主键存在且唯一)
82 | - 列完整性(如字段类型、大小、长度符合要求)
83 | - 外键约束
84 | - 用户自定义完整性(如转账前后两个账户的余额和不变)
85 |
86 | 可以说,一致性是事务追求的最终目标。原子性、持久性、隔离性都是为了保证数据库状态的一致性。此外,除了数据库层面的保障,一致性的实现也需要应用层面进行保障。
87 |
88 | 实现:
89 |
90 | - 保证原子性、持久性、隔离性
91 | - 数据库本身提供保障,如不允许向整形列插入字符串值,字符串长度不超过列限制等
92 | - 应用层面的保证
93 |
94 |
95 | ### 3.隔离性
96 |
97 | 隔离性是指事务内部的操作与其他事务都是隔离的,并发执行的各个事务之间不能相互干扰
98 |
99 | 严格的隔离性,对应了事务隔离级别中的“可串行化”,但实际应用中出于性能方面的考虑很少会使用可串行化。
100 |
101 | 考虑简单的读写操作(不考虑带锁读等特殊操作),对隔离性的讨论分以下两个方面:
102 |
103 | **- (事务A)写操作对(事务B)写操作的影响:锁机制保证隔离性**
104 | **- (事务A)写操作对(事务B)读操作的影响:MVCC保证隔离性**
105 |
106 | #### ①锁机制
107 |
108 | 隔离性要求同一时刻只能有一个事务对数据进行写操作,InnoDB存储引擎即是通过锁机制来保证这一点。
109 |
110 | (1) 锁机制的基本原理概括:
111 |
112 | a.事务在修改数据之前,获得相应的锁。
113 | b.获得锁之后,事务方可以修改数据。
114 | c.在该事务操作期间,这部分数据是锁定的,其他事务想要修改数据,需要等待当前事务提交或者回滚释放锁。
115 |
116 | (2) 按照粒度,锁可以分为表锁、行锁以及在二者之间的其他锁。表锁在操作时会锁定整张表,并发性能较差;行锁只锁定待操作的数据,并发性能好。由于加锁本身会消耗资源(获取锁,检查锁,释放锁都要消耗资源),因此在锁定数据较多的情况下选择使用表锁可以节省大量资源。
117 |
118 | MySQL中不同的存储引擎支持的锁不一样,比如MyISAM只支持表锁,InnoDB同时支持表锁和行锁,且处于性能方面的考虑,绝大多数情况下都选择使用行锁。
119 |
120 | (3) 查看锁信息(基于InnoDB存储引擎)
121 |
122 | select * from information_schema.innodb_locks; #查看锁的概况
123 | show engine innodb status; #InnoDB整体状态,包括锁的情况
124 |
125 |
126 | #### ②MVCC(多版本并发控制协议)
127 |
128 | (1) 在介绍MVCC之前,先看在并发情况下,读操作存在的三种问题:
129 |
130 | - 脏读:当前事务(A)可以读到其他事务(B)未提交的数据(这种数据称 脏数据)
131 |
132 | - 不可重复读:在事务A中先后两次读取同一个数据,两次读取到的数据不一致
133 |
134 | - 幻读:在事务A中按照某个条件先后两次查询数据库,两次查询结果的记录总条数不一致
135 |
136 | 脏读与不可重复读的区别:前者读取到的是其他事务未提交的数据,后者读到的是其他事务已经提交的数据。
137 | 不可重复读与幻读的区别:前者是数据自身变了,后者是数据的行数变了。
138 |
139 | 隔离级别与读问题的关系:
140 |
141 | 
142 |
143 | InnoDB存储引擎默认的隔离级别是可重复读(RR)。在SQL标准中,RR是无法避免幻读的,但是InnoDB中实现的RR却避免了幻读。
144 |
145 | (2) RR解决上述三个问题,使用的是MVCC,即多版本的并发控制协议。
146 |
147 | MVCC特点是在同一时刻,不同的事务读取到的数据可能是不同的(即多版本)。MVCC最大的优点是不加锁,因此读写不冲突,并发性能好。InnoDB实现的MVCC多个版本的数据可以共存,主要依靠的是隐藏列(也可以称之为标记位)和 undo log。其中隐藏列包括该行数据的版本号、删除时间、指向undo log 的指针等。
148 |
149 | 在 MySQL 中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值。
150 |
151 | 假设一个值从 1 被按顺序改成了 2、3、4,在回滚日志里面就会有类似下面的记录。
152 |
153 | 
154 |
155 | 当前值是 4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的 read-view。如图中看到的,在视图 A、B、C 里面,这一个记录的值分别是 1、2、4,同一条记录在系统中可以存 在多个版本,就是数据库的多版本并发控制(MVCC)。对于 read-view A,要得到 1,就必须 将当前值依次执行图中所有的回滚操作得到。同时,即使现在有另外一个事务正在将 4 改成 5,这个事务跟 read-view A、B、C 对 应的事务是不会冲突的。
156 |
157 | **所以**解决脏读与不可重复读问题使用简单的MVCC即可,InnoDB 实现的 RR 还要使用 next-keylock 机制一起来避免了幻读现象。
158 |
159 | (3) 当没有事务再需要用到这些回滚日志时,回滚日志会被删除。什么时候才不需要了呢?就是当系统里没有比这个回滚日志更早的 read-view 的时候。
160 |
161 | 由此我们得到一点注意事项:长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数 据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。
162 |
163 |
164 | ### 4.持久性
165 |
166 | 持久性是指事务一旦提交,他对数据库的改变就是永久性的,接下来的其他操作或者故障不应该对其有任何影响。
167 |
168 | 实现原理:redo log
169 |
170 | (1) redo log 和 undo log 都属于 InnoDB 的事务日志。下面介绍redo log的由来:
171 |
172 | InnoDB 作为 MySQL 的存储引擎,数据是存放在磁盘中的,但如果每次读写数据都需要磁盘 IO,效率会很低。为此,InnoDB 提供了缓存(Buffer Pool),Buffer Pool 中包含了磁盘中部分数据页的映射,作为访问数据库的缓冲:
173 |
174 | - 当从数据库读取数据时,会首先从 Buffer Pool 中读取,如果 Buffer Pool 中没有,则从磁盘读取后放入 Buffer Pool。
175 | - 当向数据库写入数据时,会首先写入 Buffer Pool,Buffer Pool 中修改的数据会定期刷新到磁盘中(这一过程称为刷脏)。
176 |
177 | Buffer Pool 的使用大大提高了读写数据的效率,但是也带来了新的问题:如果 MySQL 宕机,而此时 Buffer Pool 中修改的数据还没有刷新到磁盘,就会导致数据的丢失,事务的持久性无法保证。
178 |
179 | 于是,redo log 被引入来解决这个问题:当数据修改时,除了修改 Buffer Pool 中的数据,还会在 redo log 记录这次操作;当事务提交时,会调用 fsync 接口对 redo log 进行刷盘。
180 |
181 | 如果 MySQL 宕机,重启时可以读取 redo log 中的数据,对数据库进行恢复:
182 |
183 | - redo log 采用的是 WAL(Write-ahead logging,预写式日志),所有修改先写入日志,再更新到 Buffer Pool,保证了数据不会因 MySQL 宕机而丢失,从而满足了持久性要求。
184 |
185 |
186 | (2) 问题:既然 redo log 也需要在事务提交时将日志写入磁盘,为什么它比直接将 Buffer Pool 中修改的数据写入磁盘(即刷脏)要快呢?
187 |
188 | 主要有以下两方面的原因:
189 |
190 | - 刷脏是随机 IO,因为每次修改的数据位置随机,但写 redo log 是追加操作,属于顺序 IO。
191 |
192 | - 刷脏是以数据页(Page)为单位的,MySQL 默认页大小是 16KB,一个 Page 上一个小修改都要整页写入;而 redo log 中只包含真正需要写入的部分,无效 IO 大大减少。
193 |
194 |
195 |
196 | (3) redo log 与 binlog
197 |
198 | 我们知道,在 MySQL 中还存在 binlog(二进制日志)也可以记录写操作并用于数据的恢复,但二者是有着根本的不同的。
199 |
200 | 作用不同:
201 |
202 | - redo log 是用于 crash recovery 的,保证 MySQL 宕机也不会影响持久性;
203 | - binlog 是用于 point-in-time recovery 的,保证服务器可以基于时间点恢复数据,此外 binlog 还用于主从复制。
204 |
205 | 层次不同:
206 |
207 | - redo log 是 InnoDB 存储引擎实现的,
208 | - binlog 是 MySQL 的服务器层实现的,同时支持 InnoDB 和其他存储引擎。
209 |
210 | 内容不同:
211 |
212 | - redo log 是物理日志,内容基于磁盘的 Page。
213 | - binlog 是逻辑日志,内容是一条条 sql。
214 |
215 | 写入时机不同:
216 |
217 | - redo log 的写入时机相对多元。前面曾提到,当事务提交时会调用 fsync 对 redo log 进行刷盘;这是默认情况下的策略,修改innodb_flush_log_at_trx_commit 参数可以改变该策略,但事务的持久性将无法保证。
218 | 除了事务提交时,还有其他刷盘时机:如 master thread 每秒刷盘一次 redo log 等,这样的好处是不一定要等到 commit 时刷盘,commit 速度大大加快。
219 | - binlog 在事务提交时写入。
--------------------------------------------------------------------------------
/docs/系统设计/data-communication/message-queue.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | - [消息队列其实很简单](#消息队列其实很简单)
4 | - [一 什么是消息队列](#一-什么是消息队列)
5 | - [二 为什么要用消息队列](#二-为什么要用消息队列)
6 | - [\(1\) 通过异步处理提高系统性能(削峰、减少响应所需时间)](#1-通过异步处理提高系统性能削峰减少响应所需时间)
7 | - [\(2\) 降低系统耦合性](#2-降低系统耦合性)
8 | - [三 使用消息队列带来的一些问题](#三-使用消息队列带来的一些问题)
9 | - [四 JMS VS AMQP](#四-jms-vs-amqp)
10 | - [4.1 JMS](#41-jms)
11 | - [4.1.1 JMS 简介](#411-jms-简介)
12 | - [4.1.2 JMS两种消息模型](#412-jms两种消息模型)
13 | - [4.1.3 JMS 五种不同的消息正文格式](#413-jms-五种不同的消息正文格式)
14 | - [4.2 AMQP](#42-amqp)
15 | - [4.3 JMS vs AMQP](#43-jms-vs-amqp)
16 | - [五 常见的消息队列对比](#五-常见的消息队列对比)
17 |
18 |
19 |
20 |
21 | # 消息队列其实很简单
22 |
23 | “RabbitMQ?”“Kafka?”“RocketMQ?”...在日常学习与开发过程中,我们常常听到消息队列这个关键词。我也在我的多篇文章中提到了这个概念。可能你是熟练使用消息队列的老手,又或者你是不懂消息队列的新手,不论你了不了解消息队列,本文都将带你搞懂消息队列的一些基本理论。如果你是老手,你可能从本文学到你之前不曾注意的一些关于消息队列的重要概念,如果你是新手,相信本文将是你打开消息队列大门的一板砖。
24 |
25 | ## 一 什么是消息队列
26 |
27 | 我们可以把消息队列比作是一个存放消息的容器,当我们需要使用消息的时候可以取出消息供自己使用。消息队列是分布式系统中重要的组件,使用消息队列主要是为了通过异步处理提高系统性能和削峰、降低系统耦合性。目前使用较多的消息队列有ActiveMQ,RabbitMQ,Kafka,RocketMQ,我们后面会一一对比这些消息队列。
28 |
29 | 另外,我们知道队列 Queue 是一种先进先出的数据结构,所以消费消息时也是按照顺序来消费的。比如生产者发送消息1,2,3...对于消费者就会按照1,2,3...的顺序来消费。但是偶尔也会出现消息被消费的顺序不对的情况,比如某个消息消费失败又或者一个 queue 多个consumer 也会导致消息被消费的顺序不对,我们一定要保证消息被消费的顺序正确。
30 |
31 | 除了上面说的消息消费顺序的问题,使用消息队列,我们还要考虑如何保证消息不被重复消费?如何保证消息的可靠性传输(如何处理消息丢失的问题)?......等等问题。所以说使用消息队列也不是十全十美的,使用它也会让系统可用性降低、复杂度提高,另外需要我们保障一致性等问题。
32 |
33 | ## 二 为什么要用消息队列
34 |
35 | 我觉得使用消息队列主要有两点好处:1.通过异步处理提高系统性能(削峰、减少响应所需时间);2.降低系统耦合性。如果在面试的时候你被面试官问到这个问题的话,一般情况是你在你的简历上涉及到消息队列这方面的内容,这个时候推荐你结合你自己的项目来回答。
36 |
37 |
38 | 《大型网站技术架构》第四章和第七章均有提到消息队列对应用性能及扩展性的提升。
39 |
40 | ### (1) 通过异步处理提高系统性能(削峰、减少响应所需时间)
41 |
42 | 
43 | 如上图,**在不使用消息队列服务器的时候,用户的请求数据直接写入数据库,在高并发的情况下数据库压力剧增,使得响应速度变慢。但是在使用消息队列之后,用户的请求数据发送给消息队列之后立即 返回,再由消息队列的消费者进程从消息队列中获取数据,异步写入数据库。由于消息队列服务器处理速度快于数据库(消息队列也比数据库有更好的伸缩性),因此响应速度得到大幅改善。**
44 |
45 | 通过以上分析我们可以得出**消息队列具有很好的削峰作用的功能**——即**通过异步处理,将短时间高并发产生的事务消息存储在消息队列中,从而削平高峰期的并发事务。** 举例:在电子商务一些秒杀、促销活动中,合理使用消息队列可以有效抵御促销活动刚开始大量订单涌入对系统的冲击。如下图所示:
46 | 
47 |
48 | 因为**用户请求数据写入消息队列之后就立即返回给用户了,但是请求数据在后续的业务校验、写数据库等操作中可能失败**。因此使用消息队列进行异步处理之后,需要**适当修改业务流程进行配合**,比如**用户在提交订单之后,订单数据写入消息队列,不能立即返回用户订单提交成功,需要在消息队列的订单消费者进程真正处理完该订单之后,甚至出库后,再通过电子邮件或短信通知用户订单成功**,以免交易纠纷。这就类似我们平时手机订火车票和电影票。
49 |
50 | ### (2) 降低系统耦合性
51 |
52 | 我们知道如果模块之间不存在直接调用,那么新增模块或者修改模块就对其他模块影响较小,这样系统的可扩展性无疑更好一些。
53 |
54 | 我们最常见的**事件驱动架构**类似生产者消费者模式,在大型网站中通常用利用消息队列实现事件驱动结构。如下图所示:
55 |
56 | 
57 |
58 | **消息队列使利用发布-订阅模式工作,消息发送者(生产者)发布消息,一个或多个消息接受者(消费者)订阅消息。** 从上图可以看到**消息发送者(生产者)和消息接受者(消费者)之间没有直接耦合**,消息发送者将消息发送至分布式消息队列即结束对消息的处理,消息接受者从分布式消息队列获取该消息后进行后续处理,并不需要知道该消息从何而来。**对新增业务,只要对该类消息感兴趣,即可订阅该消息,对原有系统和业务没有任何影响,从而实现网站业务的可扩展性设计**。
59 |
60 | 消息接受者对消息进行过滤、处理、包装后,构造成一个新的消息类型,将消息继续发送出去,等待其他消息接受者订阅该消息。因此基于事件(消息对象)驱动的业务架构可以是一系列流程。
61 |
62 | **另外为了避免消息队列服务器宕机造成消息丢失,会将成功发送到消息队列的消息存储在消息生产者服务器上,等消息真正被消费者服务器处理后才删除消息。在消息队列服务器宕机后,生产者服务器会选择分布式消息队列服务器集群中的其他服务器发布消息。**
63 |
64 | **备注:** 不要认为消息队列只能利用发布-订阅模式工作,只不过在解耦这个特定业务环境下是使用发布-订阅模式的。**除了发布-订阅模式,还有点对点订阅模式(一个消息只有一个消费者),我们比较常用的是发布-订阅模式。** 另外,这两种消息模型是 JMS 提供的,AMQP 协议还提供了 5 种消息模型。
65 |
66 | ## 三 使用消息队列带来的一些问题
67 |
68 | - **系统可用性降低:** 系统可用性在某种程度上降低,为什么这样说呢?在加入MQ之前,你不用考虑消息丢失或者说MQ挂掉等等的情况,但是,引入MQ之后你就需要去考虑了!
69 | - **系统复杂性提高:** 加入MQ之后,你需要保证消息没有被重复消费、处理消息丢失的情况、保证消息传递的顺序性等等问题!
70 | - **一致性问题:** 我上面讲了消息队列可以实现异步,消息队列带来的异步确实可以提高系统响应速度。但是,万一消息的真正消费者并没有正确消费消息怎么办?这样就会导致数据不一致的情况了!
71 |
72 | ## 四 JMS VS AMQP
73 |
74 | ### 4.1 JMS
75 |
76 | #### 4.1.1 JMS 简介
77 |
78 | JMS(JAVA Message Service,java消息服务)是java的消息服务,JMS的客户端之间可以通过JMS服务进行异步的消息传输。**JMS(JAVA Message Service,Java消息服务)API是一个消息服务的标准或者说是规范**,允许应用程序组件基于JavaEE平台创建、发送、接收和读取消息。它使分布式通信耦合度更低,消息服务更加可靠以及异步性。
79 |
80 | **ActiveMQ 就是基于 JMS 规范实现的。**
81 |
82 | #### 4.1.2 JMS两种消息模型
83 |
84 | ①点到点(P2P)模型
85 |
86 | 
87 |
88 |
89 | 使用**队列(Queue)**作为消息通信载体;满足**生产者与消费者模式**,一条消息只能被一个消费者使用,未被消费的消息在队列中保留直到被消费或超时。比如:我们生产者发送100条消息的话,两个消费者来消费一般情况下两个消费者会按照消息发送的顺序各自消费一半(也就是你一个我一个的消费。)
90 |
91 | ② 发布/订阅(Pub/Sub)模型
92 |
93 | 
94 |
95 |
96 | 发布订阅模型(Pub/Sub) 使用**主题(Topic)**作为消息通信载体,类似于**广播模式**;发布者发布一条消息,该消息通过主题传递给所有的订阅者,**在一条消息广播之后才订阅的用户则是收不到该条消息的**。
97 |
98 | #### 4.1.3 JMS 五种不同的消息正文格式
99 |
100 | JMS定义了五种不同的消息正文格式,以及调用的消息类型,允许你发送并接收以一些不同形式的数据,提供现有消息格式的一些级别的兼容性。
101 |
102 | - StreamMessage -- Java原始值的数据流
103 | - MapMessage--一套名称-值对
104 | - TextMessage--一个字符串对象
105 | - ObjectMessage--一个序列化的 Java对象
106 | - BytesMessage--一个字节的数据流
107 |
108 |
109 | ### 4.2 AMQP
110 |
111 | AMQP,即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准 **高级消息队列协议**(二进制应用层协议),是应用层协议的一个开放标准,为面向消息的中间件设计,兼容 JMS。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件同产品,不同的开发语言等条件的限制。
112 |
113 | **RabbitMQ 就是基于 AMQP 协议实现的。**
114 |
115 |
116 |
117 | ### 4.3 JMS vs AMQP
118 |
119 |
120 | |对比方向| JMS | AMQP |
121 | | :-------- | --------:| :--: |
122 | | 定义| Java API | 协议 |
123 | | 跨语言 | 否 | 是 |
124 | | 跨平台 | 否 | 是 |
125 | | 支持消息类型 | 提供两种消息模型:①Peer-2-Peer;②Pub/sub| 提供了五种消息模型:①direct exchange;②fanout exchange;③topic change;④headers exchange;⑤system exchange。本质来讲,后四种和JMS的pub/sub模型没有太大差别,仅是在路由机制上做了更详细的划分;|
126 | |支持消息类型| 支持多种消息类型 ,我们在上面提到过| byte[](二进制)|
127 |
128 | **总结:**
129 |
130 | - AMQP 为消息定义了线路层(wire-level protocol)的协议,而JMS所定义的是API规范。在 Java 体系中,多个client均可以通过JMS进行交互,不需要应用修改代码,但是其对跨平台的支持较差。而AMQP天然具有跨平台、跨语言特性。
131 | - JMS 支持TextMessage、MapMessage 等复杂的消息类型;而 AMQP 仅支持 byte[] 消息类型(复杂的类型可序列化后发送)。
132 | - 由于Exchange 提供的路由算法,AMQP可以提供多样化的路由方式来传递消息到消息队列,而 JMS 仅支持 队列 和 主题/订阅 方式两种。
133 |
134 |
135 | ## 五 常见的消息队列对比
136 |
137 |
138 |
139 | 对比方向 |概要
140 | -------- | ---
141 | 吞吐量| 万级的 ActiveMQ 和 RabbitMQ 的吞吐量(ActiveMQ 的性能最差)要比 十万级甚至是百万级的 RocketMQ 和 Kafka 低一个数量级。
142 | 可用性| 都可以实现高可用。ActiveMQ 和 RabbitMQ 都是基于主从架构实现高可用性。RocketMQ 基于分布式架构。 kafka 也是分布式的,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用
143 | 时效性| RabbitMQ 基于erlang开发,所以并发能力很强,性能极其好,延时很低,达到微秒级。其他三个都是 ms 级。
144 | 功能支持| 除了 Kafka,其他三个功能都较为完备。 Kafka 功能较为简单,主要支持简单的MQ功能,在大数据领域的实时计算以及日志采集被大规模使用,是事实上的标准
145 | 消息丢失| ActiveMQ 和 RabbitMQ 丢失的可能性非常低, RocketMQ 和 Kafka 理论上不会丢失。
146 |
147 |
148 | **总结:**
149 |
150 | - ActiveMQ 的社区算是比较成熟,但是较目前来说,ActiveMQ 的性能比较差,而且版本迭代很慢,不推荐使用。
151 | - RabbitMQ 在吞吐量方面虽然稍逊于 Kafka 和 RocketMQ ,但是由于它基于 erlang 开发,所以并发能力很强,性能极其好,延时很低,达到微秒级。但是也因为 RabbitMQ 基于 erlang 开发,所以国内很少有公司有实力做erlang源码级别的研究和定制。如果业务场景对并发量要求不是太高(十万级、百万级),那这四种消息队列中,RabbitMQ 一定是你的首选。如果是大数据领域的实时计算、日志采集等场景,用 Kafka 是业内标准的,绝对没问题,社区活跃度很高,绝对不会黄,何况几乎是全世界这个领域的事实性规范。
152 | - RocketMQ 阿里出品,Java 系开源项目,源代码我们可以直接阅读,然后可以定制自己公司的MQ,并且 RocketMQ 有阿里巴巴的实际业务场景的实战考验。RocketMQ 社区活跃度相对较为一般,不过也还可以,文档相对来说简单一些,然后接口这块不是按照标准 JMS 规范走的有些系统要迁移需要修改大量代码。还有就是阿里出台的技术,你得做好这个技术万一被抛弃,社区黄掉的风险,那如果你们公司有技术实力我觉得用RocketMQ 挺好的
153 | - kafka 的特点其实很明显,就是仅仅提供较少的核心功能,但是提供超高的吞吐量,ms 级的延迟,极高的可用性以及可靠性,而且分布式可以任意扩展。同时 kafka 最好是支撑较少的 topic 数量即可,保证其超高吞吐量。kafka 唯一的一点劣势是有可能消息重复消费,那么对数据准确性会造成极其轻微的影响,在大数据领域中以及日志采集中,这点轻微影响可以忽略这个特性天然适合大数据实时计算以及日志收集。
154 |
155 |
156 | 参考:《Java工程师面试突击第1季-中华石杉老师》
157 |
--------------------------------------------------------------------------------
/docs/系统设计/authority-certification/basis-of-authority-certification.md:
--------------------------------------------------------------------------------
1 | ## 1. 认证 (Authentication) 和授权 (Authorization)的区别是什么?
2 |
3 | 这是一个绝大多数人都会混淆的问题。首先先从读音上来认识这两个名词,很多人都会把它俩的读音搞混,所以我建议你先先去查一查这两个单词到底该怎么读,他们的具体含义是什么。
4 |
5 | 说简单点就是:
6 |
7 | - **认证 (Authentication):** 你是谁。
8 | - **授权 (Authorization):** 你有权限干什么。
9 |
10 | 稍微正式点(啰嗦点)的说法就是:
11 |
12 | - **Authentication(认证)** 是验证您的身份的凭据(例如用户名/用户ID和密码),通过这个凭据,系统得以知道你就是你,也就是说系统存在你这个用户。所以,Authentication 被称为身份/用户验证。
13 | - **Authorization(授权)** 发生在 **Authentication(认证)**之后。授权嘛,光看意思大家应该就明白,它主要掌管我们访问系统的权限。比如有些特定资源只能具有特定权限的人才能访问比如admin,有些对系统资源操作比如删除、添加、更新只能特定人才具有。
14 |
15 | 这两个一般在我们的系统中被结合在一起使用,目的就是为了保护我们系统的安全性。
16 |
17 | ## 2. 什么是Cookie ? Cookie的作用是什么?如何在服务端使用 Cookie ?
18 |
19 | ### 2.1 什么是Cookie ? Cookie的作用是什么?
20 |
21 | Cookie 和 Session都是用来跟踪浏览器用户身份的会话方式,但是两者的应用场景不太一样。
22 |
23 | 维基百科是这样定义 Cookie 的:Cookies是某些网站为了辨别用户身份而储存在用户本地终端上的数据(通常经过加密)。简单来说: **Cookie 存放在客户端,一般用来保存用户信息**。
24 |
25 | 下面是 Cookie 的一些应用案例:
26 |
27 | 1. 我们在 Cookie 中保存已经登录过得用户信息,下次访问网站的时候页面可以自动帮你登录的一些基本信息给填了。除此之外,Cookie 还能保存用户首选项,主题和其他设置信息。
28 | 2. 使用Cookie 保存 session 或者 token ,向后端发送请求的时候带上 Cookie,这样后端就能取到session或者token了。这样就能记录用户当前的状态了,因为 HTTP 协议是无状态的。
29 | 3. Cookie 还可以用来记录和分析用户行为。举个简单的例子你在网上购物的时候,因为HTTP协议是没有状态的,如果服务器想要获取你在某个页面的停留状态或者看了哪些商品,一种常用的实现方式就是将这些信息存放在Cookie
30 |
31 | ### 2.2 如何能在 服务端使用 Cookie 呢?
32 |
33 | 这部分内容参考:https://attacomsian.com/blog/cookies-spring-boot,更多如何在Spring Boot中使用Cookie 的内容可以查看这篇文章。
34 |
35 | **1)设置cookie返回给客户端**
36 |
37 | ```java
38 | @GetMapping("/change-username")
39 | public String setCookie(HttpServletResponse response) {
40 | // 创建一个 cookie
41 | Cookie cookie = new Cookie("username", "Jovan");
42 | //设置 cookie过期时间
43 | cookie.setMaxAge(7 * 24 * 60 * 60); // expires in 7 days
44 | //添加到 response 中
45 | response.addCookie(cookie);
46 |
47 | return "Username is changed!";
48 | }
49 | ```
50 |
51 | **2) 使用Spring框架提供的`@CookieValue`注解获取特定的 cookie的值**
52 |
53 | ```java
54 | @GetMapping("/")
55 | public String readCookie(@CookieValue(value = "username", defaultValue = "Atta") String username) {
56 | return "Hey! My username is " + username;
57 | }
58 | ```
59 |
60 | **3) 读取所有的 Cookie 值**
61 |
62 | ```java
63 | @GetMapping("/all-cookies")
64 | public String readAllCookies(HttpServletRequest request) {
65 |
66 | Cookie[] cookies = request.getCookies();
67 | if (cookies != null) {
68 | return Arrays.stream(cookies)
69 | .map(c -> c.getName() + "=" + c.getValue()).collect(Collectors.joining(", "));
70 | }
71 |
72 | return "No cookies";
73 | }
74 | ```
75 |
76 | ## 3. Cookie 和 Session 有什么区别?如何使用Session进行身份验证?
77 |
78 | **Session 的主要作用就是通过服务端记录用户的状态。** 典型的场景是购物车,当你要添加商品到购物车的时候,系统不知道是哪个用户操作的,因为 HTTP 协议是无状态的。服务端给特定的用户创建特定的 Session 之后就可以标识这个用户并且跟踪这个用户了。
79 |
80 | **Cookie 数据保存在客户端(浏览器端),Session 数据保存在服务器端。相对来说 Session 安全性更高。如果使用 Cookie 的一些敏感信息不要写入 Cookie 中,最好能将 Cookie 信息加密然后使用到的时候再去服务器端解密。**
81 |
82 | **那么,如何使用Session进行身份验证?**
83 |
84 | 很多时候我们都是通过 SessionID 来实现特定的用户,SessionID 一般会选择存放在 Redis 中。举个例子:用户成功登陆系统,然后返回给客户端具有 SessionID 的 Cookie,当用户向后端发起请求的时候会把 SessionID 带上,这样后端就知道你的身份状态了。关于这种认证方式更详细的过程如下:
85 |
86 | 
87 |
88 | 1. 用户向服务器发送用户名和密码用于登陆系统。
89 | 2. 服务器验证通过后,服务器为用户创建一个 Session,并将 Session信息存储 起来。
90 | 3. 服务器向用户返回一个 SessionID,写入用户的 Cookie。
91 | 4. 当用户保持登录状态时,Cookie 将与每个后续请求一起被发送出去。
92 | 5. 服务器可以将存储在 Cookie 上的 Session ID 与存储在内存中或者数据库中的 Session 信息进行比较,以验证用户的身份,返回给用户客户端响应信息的时候会附带用户当前的状态。
93 |
94 | 另外,Spring Session提供了一种跨多个应用程序或实例管理用户会话信息的机制。如果想详细了解可以查看下面几篇很不错的文章:
95 |
96 | - [Getting Started with Spring Session](https://codeboje.de/spring-session-tutorial/)
97 | - [Guide to Spring Session](https://www.baeldung.com/spring-session)
98 | - [Sticky Sessions with Spring Session & Redis](https://medium.com/@gvnix/sticky-sessions-with-spring-session-redis-bdc6f7438cc3)
99 |
100 | ## 4. 什么是 Token?什么是 JWT?如何基于Token进行身份验证?
101 |
102 | 我们在上一个问题中探讨了使用 Session 来鉴别用户的身份,并且给出了几个 Spring Session 的案例分享。 我们知道 Session 信息需要保存一份在服务器端。这种方式会带来一些麻烦,比如需要我们保证保存 Session 信息服务器的可用性、不适合移动端(依赖Cookie)等等。
103 |
104 | 有没有一种不需要自己存放 Session 信息就能实现身份验证的方式呢?使用 Token 即可!JWT (JSON Web Token) 就是这种方式的实现,通过这种方式服务器端就不需要保存 Session 数据了,只用在客户端保存服务端返回给客户的 Token 就可以了,扩展性得到提升。
105 |
106 | **JWT 本质上就一段签名的 JSON 格式的数据。由于它是带有签名的,因此接收者便可以验证它的真实性。**
107 |
108 | 下面是 [RFC 7519](https://link.juejin.im/?target=https%3A%2F%2Ftools.ietf.org%2Fhtml%2Frfc7519) 对 JWT 做的较为正式的定义。
109 |
110 | > JSON Web Token (JWT) is a compact, URL-safe means of representing claims to be transferred between two parties. The claims in a JWT are encoded as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the plaintext of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted. ——[JSON Web Token (JWT)](https://tools.ietf.org/html/rfc7519)
111 |
112 | JWT 由 3 部分构成:
113 |
114 | 1. Header :描述 JWT 的元数据。定义了生成签名的算法以及 Token 的类型。
115 | 2. Payload(负载):用来存放实际需要传递的数据
116 | 3. Signature(签名):服务器通过`Payload`、`Header`和一个密钥(`secret`)使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成。
117 |
118 | 在基于 Token 进行身份验证的的应用程序中,服务器通过`Payload`、`Header`和一个密钥(`secret`)创建令牌(`Token`)并将 `Token` 发送给客户端,客户端将 `Token` 保存在 Cookie 或者 localStorage 里面,以后客户端发出的所有请求都会携带这个令牌。你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP Header 的 Authorization字段中:` Authorization: Bearer Token`。
119 |
120 | 
121 |
122 | 1. 用户向服务器发送用户名和密码用于登陆系统。
123 | 2. 身份验证服务响应并返回了签名的 JWT,上面包含了用户是谁的内容。
124 | 3. 用户以后每次向后端发请求都在Header中带上 JWT。
125 | 4. 服务端检查 JWT 并从中获取用户相关信息。
126 |
127 |
128 | 推荐阅读:
129 |
130 | - [JWT (JSON Web Tokens) Are Better Than Session Cookies](https://dzone.com/articles/jwtjson-web-tokens-are-better-than-session-cookies)
131 | - [JSON Web Tokens (JWT) 与 Sessions](https://juejin.im/entry/577b7b56a3413100618c2938)
132 | - [JSON Web Token 入门教程](https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html)
133 | - [彻底理解Cookie,Session,Token](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485603&idx=1&sn=c8d324f44d6102e7b44554733da10bb7&chksm=cea24768f9d5ce7efe7291ddabce02b68db34073c7e7d9a7dc9a7f01c5a80cebe33ac75248df&token=844918801&lang=zh_CN#rd)
134 |
135 | ## 5 什么是OAuth 2.0?
136 |
137 | OAuth 是一个行业的标准授权协议,主要用来授权第三方应用获取有限的权限。而 OAuth 2.0是对 OAuth 1.0 的完全重新设计,OAuth 2.0更快,更容易实现,OAuth 1.0 已经被废弃。详情请见:[rfc6749](https://tools.ietf.org/html/rfc6749)。
138 |
139 | 实际上它就是一种授权机制,它的最终目的是为第三方应用颁发一个有时效性的令牌 token,使得第三方应用能够通过该令牌获取相关的资源。
140 |
141 | OAuth 2.0 比较常用的场景就是第三方登录,当你的网站接入了第三方登录的时候一般就是使用的 OAuth 2.0 协议。
142 |
143 | 推荐阅读:
144 |
145 | - [OAuth 2.0 的一个简单解释](http://www.ruanyifeng.com/blog/2019/04/oauth_design.html)
146 | - [10 分钟理解什么是 OAuth 2.0 协议](https://deepzz.com/post/what-is-oauth2-protocol.html)
147 | - [OAuth 2.0 的四种方式](http://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html)
148 | - [GitHub OAuth 第三方登录示例教程](http://www.ruanyifeng.com/blog/2019/04/github-oauth.html)
149 |
150 | ## 参考
151 |
152 | - https://medium.com/@sherryhsu/session-vs-token-based-authentication-11a6c5ac45e4
153 | - https://www.varonis.com/blog/what-is-oauth/
154 | - https://tools.ietf.org/html/rfc6749
--------------------------------------------------------------------------------
/docs/tools/Git.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | - [版本控制](#版本控制)
4 | - [什么是版本控制](#什么是版本控制)
5 | - [为什么要版本控制](#为什么要版本控制)
6 | - [本地版本控制系统](#本地版本控制系统)
7 | - [集中化的版本控制系统](#集中化的版本控制系统)
8 | - [分布式版本控制系统](#分布式版本控制系统)
9 | - [认识 Git](#认识-git)
10 | - [Git 简史](#git-简史)
11 | - [Git 与其他版本管理系统的主要区别](#git-与其他版本管理系统的主要区别)
12 | - [Git 的三种状态](#git-的三种状态)
13 | - [Git 使用快速入门](#git-使用快速入门)
14 | - [获取 Git 仓库](#获取-git-仓库)
15 | - [记录每次更新到仓库](#记录每次更新到仓库)
16 | - [推送改动到远程仓库](#推送改动到远程仓库)
17 | - [远程仓库的移除与重命名](#远程仓库的移除与重命名)
18 | - [查看提交历史](#查看提交历史)
19 | - [撤销操作](#撤销操作)
20 | - [分支](#分支)
21 | - [推荐阅读](#推荐阅读)
22 |
23 |
24 |
25 | ## 版本控制
26 |
27 | ### 什么是版本控制
28 |
29 | 版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。 除了项目源代码,你可以对任何类型的文件进行版本控制。
30 |
31 | ### 为什么要版本控制
32 |
33 | 有了它你就可以将某个文件回溯到之前的状态,甚至将整个项目都回退到过去某个时间点的状态,你可以比较文件的变化细节,查出最后是谁修改了哪个地方,从而找出导致怪异问题出现的原因,又是谁在何时报告了某个功能缺陷等等。
34 |
35 | ### 本地版本控制系统
36 |
37 | 许多人习惯用复制整个项目目录的方式来保存不同的版本,或许还会改名加上备份时间以示区别。 这么做唯一的好处就是简单,但是特别容易犯错。 有时候会混淆所在的工作目录,一不小心会写错文件或者覆盖意想外的文件。
38 |
39 | 为了解决这个问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异。
40 |
41 | 
42 |
43 | ### 集中化的版本控制系统
44 |
45 | 接下来人们又遇到一个问题,如何让在不同系统上的开发者协同工作? 于是,集中化的版本控制系统(Centralized Version Control Systems,简称 CVCS)应运而生。
46 |
47 | 集中化的版本控制系统都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。
48 |
49 | 
50 |
51 | 这么做虽然解决了本地版本控制系统无法让在不同系统上的开发者协同工作的诟病,但也还是存在下面的问题:
52 |
53 | - **单点故障:** 中央服务器宕机,则其他人无法使用;如果中心数据库磁盘损坏有没有进行备份,你将丢失所有数据。本地版本控制系统也存在类似问题,只要整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险。
54 | - **必须联网才能工作:** 受网络状况、带宽影响。
55 |
56 | ### 分布式版本控制系统
57 |
58 | 于是分布式版本控制系统(Distributed Version Control System,简称 DVCS)面世了。 Git 就是一个典型的分布式版本控制系统。
59 |
60 | 这类系统,客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。 这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。 因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。
61 |
62 | 
63 |
64 | 分布式版本控制系统可以不用联网就可以工作,因为每个人的电脑上都是完整的版本库,当你修改了某个文件后,你只需要将自己的修改推送给别人就可以了。但是,在实际使用分布式版本控制系统的时候,很少会直接进行推送修改,而是使用一台充当“中央服务器”的东西。这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已。
65 |
66 | 分布式版本控制系统的优势不单是不必联网这么简单,后面我们还会看到 Git 极其强大的分支管理等功能。
67 |
68 | ## 认识 Git
69 |
70 | ### Git 简史
71 |
72 | Linux 内核项目组当时使用分布式版本控制系统 BitKeeper 来管理和维护代码。但是,后来开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束,他们收回了 Linux 内核社区免费使用 BitKeeper 的权力。 Linux 开源社区(特别是 Linux 的缔造者 Linus Torvalds)基于使用 BitKeeper 时的经验教训,开发出自己的版本系统,而且对新的版本控制系统做了很多改进。
73 |
74 | ### Git 与其他版本管理系统的主要区别
75 |
76 | Git 在保存和对待各种信息的时候与其它版本控制系统有很大差异,尽管操作起来的命令形式非常相近,理解这些差异将有助于防止你使用中的困惑。
77 |
78 | 下面我们主要说一个关于 Git 其他版本管理系统的主要差别:**对待数据的方式**。
79 |
80 | **Git采用的是直接记录快照的方式,而非差异比较。我后面会详细介绍这两种方式的差别。**
81 |
82 | 大部分版本控制系统(CVS、Subversion、Perforce、Bazaar 等等)都是以文件变更列表的方式存储信息,这类系统**将它们保存的信息看作是一组基本文件和每个文件随时间逐步累积的差异。**
83 |
84 | 具体原理如下图所示,理解起来其实很简单,每个我们对提交更新一个文件之后,系统记录都会记录这个文件做了哪些更新,以增量符号Δ(Delta)表示。
85 |
86 |
87 |

88 |
89 |
90 |
91 | **我们怎样才能得到一个文件的最终版本呢?**
92 |
93 | 很简单,高中数学的基本知识,我们只需要将这些原文件和这些增加进行相加就行了。
94 |
95 | **这种方式有什么问题呢?**
96 |
97 | 比如我们的增量特别特别多的话,如果我们要得到最终的文件是不是会耗费时间和性能。
98 |
99 | Git 不按照以上方式对待或保存数据。 反之,Git 更像是把数据看作是对小型文件系统的一组快照。 每次你提交更新,或在 Git 中保存项目状态时,它主要对当时的全部文件制作一个快照并保存这个快照的索引。 为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个 **快照流**。
100 |
101 |
102 |

103 |
104 |
105 |
106 |
107 | ### Git 的三种状态
108 |
109 | Git 有三种状态,你的文件可能处于其中之一:
110 |
111 | 1. **已提交(committed)**:数据已经安全的保存在本地数据库中。
112 | 2. **已修改(modified)**:已修改表示修改了文件,但还没保存到数据库中。
113 | 3. **已暂存(staged)**:表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
114 |
115 | 由此引入 Git 项目的三个工作区域的概念:**Git 仓库(.git directoty)**、**工作目录(Working Directory)** 以及 **暂存区域(Staging Area)** 。
116 |
117 |
118 |

119 |
120 |
121 | **基本的 Git 工作流程如下:**
122 |
123 | 1. 在工作目录中修改文件。
124 | 2. 暂存文件,将文件的快照放入暂存区域。
125 | 3. 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。
126 |
127 | ## Git 使用快速入门
128 |
129 | ### 获取 Git 仓库
130 |
131 | 有两种取得 Git 项目仓库的方法。
132 |
133 | 1. 在现有目录中初始化仓库: 进入项目目录运行 `git init` 命令,该命令将创建一个名为 `.git` 的子目录。
134 | 2. 从一个服务器克隆一个现有的 Git 仓库: `git clone [url]` 自定义本地仓库的名字: `git clone [url]` directoryname
135 |
136 | ### 记录每次更新到仓库
137 |
138 | 1. **检测当前文件状态** : `git status`
139 | 2. **提出更改(把它们添加到暂存区**):`git add filename ` (针对特定文件)、`git add *`(所有文件)、`git add *.txt`(支持通配符,所有 .txt 文件)
140 | 3. **忽略文件**:`.gitignore` 文件
141 | 4. **提交更新:** `git commit -m "代码提交信息"` (每次准备提交前,先用 `git status` 看下,是不是都已暂存起来了, 然后再运行提交命令 `git commit`)
142 | 5. **跳过使用暂存区域更新的方式** : `git commit -a -m "代码提交信息"`。 `git commit` 加上 `-a` 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 `git add` 步骤。
143 | 6. **移除文件** :`git rm filename` (从暂存区域移除,然后提交。)
144 | 7. **对文件重命名** :`git mv README.md README`(这个命令相当于`mv README.md README`、`git rm README.md`、`git add README` 这三条命令的集合)
145 |
146 | ### 推送改动到远程仓库
147 |
148 | - 如果你还没有克隆现有仓库,并欲将你的仓库连接到某个远程服务器,你可以使用如下命令添加:·`git remote add origin ` ,比如我们要让本地的一个仓库和 Github 上创建的一个仓库关联可以这样`git remote add origin https://github.com/Snailclimb/test.git`
149 | - 将这些改动提交到远端仓库:`git push origin master` (可以把 *master* 换成你想要推送的任何分支)
150 |
151 | 如此你就能够将你的改动推送到所添加的服务器上去了。
152 |
153 | ### 远程仓库的移除与重命名
154 |
155 | - 将 test 重命名位 test1:`git remote rename test test1`
156 | - 移除远程仓库 test1:`git remote rm test1`
157 |
158 | ### 查看提交历史
159 |
160 | 在提交了若干更新,又或者克隆了某个项目之后,你也许想回顾下提交历史。 完成这个任务最简单而又有效的工具是 `git log` 命令。`git log` 会按提交时间列出所有的更新,最近的更新排在最上面。
161 |
162 | **可以添加一些参数来查看自己希望看到的内容:**
163 |
164 | 只看某个人的提交记录:
165 |
166 | ```shell
167 | git log --author=bob
168 | ```
169 |
170 | ### 撤销操作
171 |
172 | 有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。 此时,可以运行带有 `--amend` 选项的提交命令尝试重新提交:
173 |
174 | ```console
175 | git commit --amend
176 | ```
177 |
178 | 取消暂存的文件
179 |
180 | ```console
181 | git reset filename
182 | ```
183 |
184 | 撤消对文件的修改:
185 |
186 | ```
187 | git checkout -- filename
188 | ```
189 |
190 | 假如你想丢弃你在本地的所有改动与提交,可以到服务器上获取最新的版本历史,并将你本地主分支指向它:
191 |
192 | ```
193 | git fetch origin
194 | git reset --hard origin/master
195 | ```
196 |
197 |
198 |
199 | ### 分支
200 |
201 | 分支是用来将特性开发绝缘开来的。在你创建仓库的时候,*master* 是“默认的”分支。在其他分支上进行开发,完成后再将它们合并到主分支上。
202 |
203 | 我们通常在开发新功能、修复一个紧急 bug 等等时候会选择创建分支。单分支开发好还是多分支开发好,还是要看具体场景来说。
204 |
205 | 创建一个名字叫做 test 的分支
206 |
207 | ```console
208 | git branch test
209 | ```
210 |
211 | 切换当前分支到 test(当你切换分支的时候,Git 会重置你的工作目录,使其看起来像回到了你在那个分支上最后一次提交的样子。 Git 会自动添加、删除、修改文件以确保此时你的工作目录和这个分支最后一次提交时的样子一模一样)
212 |
213 | ```console
214 | git checkout test
215 | ```
216 |
217 |
218 |

219 |
220 |
221 | 你也可以直接这样创建分支并切换过去(上面两条命令的合写)
222 |
223 | ```console
224 | git checkout -b feature_x
225 | ```
226 |
227 | 切换到主分支
228 |
229 | ```
230 | git checkout master
231 | ```
232 |
233 | 合并分支(可能会有冲突)
234 |
235 | ```java
236 | git merge test
237 | ```
238 |
239 | 把新建的分支删掉
240 |
241 | ```
242 | git branch -d feature_x
243 | ```
244 |
245 | 将分支推送到远端仓库(推送成功后其他人可见):
246 |
247 | ```
248 | git push origin
249 | ```
250 |
251 |
252 |
253 | ## 推荐阅读
254 |
255 | - [Git - 简明指南](http://rogerdudler.github.io/git-guide/index.zh.html)
256 | - [图解Git](http://marklodato.github.io/visual-git-guide/index-zh-cn.html)
257 | - [猴子都能懂得Git入门](https://backlog.com/git-tutorial/cn/intro/intro1_1.html)
258 | - https://git-scm.com/book/en/v2
259 | - [Generating a new SSH key and adding it to the ssh-agent](https://help.github.com/en/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent)
260 |
--------------------------------------------------------------------------------
/test.py:
--------------------------------------------------------------------------------
1 | """
2 | m = input().split(' ')
3 | n = int(input())
4 |
5 | m = map(int, m)
6 | count = 0
7 | min = []
8 | for i in m:
9 | if i >= n:
10 | res = i // n
11 | count += res
12 | if i % n:
13 | min.append(i % n)
14 | else:
15 | min.append(i % n)
16 | min = sorted(min)
17 | left = 0
18 | right = len(min) - 1
19 | while left < right:
20 | t = min[left] + min[right]
21 | if t > n:
22 | count += 1
23 | right -= 1
24 | elif t == n:
25 | left += 1
26 | right -= 1
27 | count += 1
28 | else:
29 | min[right] = t
30 | left += 1
31 | if left == right:
32 | count += 1
33 | print(count)
34 | """
35 |
36 | """
37 | import heapq
38 |
39 |
40 | class MaxHeap():
41 | def __init__(self, n):
42 | self.n = n
43 | self.t = 0
44 | self.q = []
45 |
46 | def push(self, x):
47 | if self.t < self.n:
48 | heapq.heappush(self.q, x)
49 | self.t += 1
50 | else:
51 | res = self.q[0]
52 | if x > res:
53 | heapq.heapreplace(self.q, x)
54 |
55 |
56 | str = input().split(' ')
57 | n = int(input())
58 | set = set()
59 | heap = MaxHeap(n)
60 | for s in str:
61 | set.add(len(s))
62 | for i in set:
63 | heap.push(i)
64 | print(heap.q[0] if heap.t >= heap.n else heap.q[-1])
65 | """
66 |
67 | # import threading
68 | #
69 | # lock = threading.Lock()
70 | # l = []
71 | #
72 | #
73 | # def test1(n):
74 | # lock.acquire()
75 | # l.append(n)
76 | # print(l)
77 | # lock.release()
78 | #
79 | #
80 | # def test(n):
81 | # l.append(n)
82 | # print(l)
83 | #
84 | #
85 | # def main():
86 | # for i in range(0, 10):
87 | # th = threading.Thread(target=test, args=(i,))
88 | # th.start()
89 | #
90 | #
91 | # main()
92 |
93 | # def consumer():
94 | # r = 'i am coming'
95 | # while True:
96 | # message = yield r
97 | # if message:
98 | # print('consumer is :', message)
99 | # r = '200 ok'
100 | # else:
101 | # return
102 | #
103 | #
104 | # def producer(c):
105 | # r = c.send(None)
106 | # print(r)
107 | # for i in range(1, 4):
108 | # print('producer ', i)
109 | # r = c.send(i)
110 | # print(r)
111 | # c.close()
112 | #
113 | # c = consumer()
114 | # producer(c)
115 |
116 | # def sort(list):
117 | # left = 0
118 | # right = len(list) - 1
119 | # while left < right:
120 | # while list[left] % 2 != 0 and left < right:
121 | # left += 1
122 | # while list[right] % 2 == 0 and left < right:
123 | # right -= 1
124 | # list[left], list[right] = list[right], list[left]
125 | # left += 1
126 | # right -= 1
127 | # return list
128 | #
129 | # print(sort([2,2,2,2,8]))
130 |
131 | # def delete_n(root, n):
132 | # left = right = root
133 | # for i in range(n-1):
134 | # if right:
135 | # right = right.next
136 | # else:
137 | # return root
138 | # while right:
139 | # right = right.next
140 | # left = left.next
141 | # t = left.next
142 | # left.value = t.value
143 | # left.next = t.next
144 | # del t
145 | # return left
146 |
147 | #
148 | # def is_huiwen(s):
149 | # if s == s[::-1]:
150 | # return True
151 | # return False
152 | #
153 | # def all_str(length,s,n):
154 | # str_list=[]
155 | # for i in range(length-n):
156 | # str_list.append(s[i:i+n])
157 | # return str_list
158 | #
159 | # def solution(s):
160 | # length = len(s)
161 | # res = ''
162 | # for i in range(1,length+1):
163 | # str_list = all_str(length,s,i)
164 | # for str in str_list:
165 | # if is_huiwen(str):
166 | # res = str
167 | # return res
168 | #
169 | # print(solution('abacdea'))
170 |
171 |
172 | # class Node(object):
173 | # def __init__(self, name=None, value=None):
174 | # self.name = name
175 | # self.value = value
176 | # self.left = None
177 | # self.right = None
178 | #
179 | #
180 | # # 哈夫曼树类
181 | # class HuffmanTree(object):
182 | #
183 | # # 根据Huffman树的思想:以叶子节点为基础,反向建立Huffman树
184 | # def __init__(self, char_weights):
185 | # self.a = [Node(part[0], part[1]) for part in char_weights] # 根据输入的字符及其频数生成叶子节点
186 | # while len(self.a) != 1:
187 | # self.a.sort(key=lambda node: node.value, reverse=True)
188 | # c = Node(value=(self.a[-1].value + self.a[-2].value))
189 | # c.left = self.a.pop(-1)
190 | # c.right = self.a.pop(-1)
191 | # self.a.append(c)
192 | # self.root = self.a[0]
193 | # self.b = range(10) # self.b用于保存每个叶子节点的Haffuman编码,range的值只需要不小于树的深度就行
194 | #
195 | # # 用递归的思想生成编码
196 | # def pre(self, dict, tree, length):
197 | # node = tree
198 | # if not node:
199 | # return
200 | # elif node.name:
201 | # t = [self.b[i] for i in range(length)]
202 | # dict[node.name] = ''.join(map(str, t))
203 | # return
204 | # self.b = list(self.b)
205 | # self.b[length] = 0
206 | # self.pre(dict, node.left, length + 1)
207 | # self.b[length] = 1
208 | # self.pre(dict, node.right, length + 1)
209 | #
210 | # # 生成哈夫曼编码
211 | # def get_code(self):
212 | # dict = {}
213 | # self.pre(dict, self.root, 0)
214 | # return dict
215 | #
216 | #
217 | # if __name__ == '__main__':
218 | # # 输入的是字符及其频数
219 | # char_weights = [('a', 5), ('b', 4), ('c', 10), ('d', 8), ('f', 15), ('g', 2)]
220 | # tree = HuffmanTree(char_weights)
221 | # dict = tree.get_code()
222 |
223 |
224 | # def solution(s):
225 | # s = s.replace(' ', '') # 去空格
226 | # count = int(s, 16) # 一起转十进制
227 | # res_list = []
228 | # while count:
229 | # t = count & 7 # 与二进制 111 与运算得到每三位的值
230 | # res_list.append(t)
231 | # count = count >> 3 # 右移3位继续
232 | # t = len(res_list) % 3 # 是否需要高位补0
233 | # if t:
234 | # res_list.extend([0] * (3 - t)) # 高位补0
235 | # res = []
236 | # for i in range(len(res_list)//3): # 每3位十进制组成一个配置
237 | # if res_list[3*i] == res_list[3*i+1] and res_list[3*i+1]==res_list[3*i+2]: # 3位相等
238 | # if res_list[3*i] == 0: # 3位全0 不输出
239 | # continue
240 | # res.append(str(res_list[3*i])) # 3位相等切不为0,则过滤只保留一个
241 | # else:
242 | # res.append(str(res_list[3*i]) + str(res_list[3*i+1]) + str(res_list[3*i+2]))
243 | # return ' '.join(res)[::-1]
244 | #
245 | #
246 | # if __name__ == "__main__":
247 | # s = input()
248 | # print(solution(s))
249 |
250 | # def reOrderArray(array):
251 | # # write code here
252 | # left = -1
253 | # right = 0
254 | # while right < len(array):
255 | # if array[right] % 2 == 0:
256 | # if left == -1:
257 | # left = right
258 | # else:
259 | # array[left], array[right] = array[right], array[left]
260 | # elif array[right] % 2 == 1 and left != -1:
261 | # array[left], array[right] = array[right], array[left]
262 | # if right == len(array)-1:
263 | # return array
264 | # left += 1
265 | # right = left
266 | # right += 1
267 | # print(array)
268 | #
269 | # array = [1,2,3,4,5,6,7]
270 | # reOrderArray(array)
271 |
272 | arr = [6, -1, 8, -4, -6, 3, 2, -2, 5]
273 |
274 | def dp_opt(arr):
275 | res = [0 for i in range(len(arr))]
276 | # 初始化
277 | res[0] = arr[0]
278 | # 开始循环
279 | max_sum = res[0]
280 | for i in range(1, len(arr)):
281 | a = res[i - 1] + arr[i]
282 | b = arr[i]
283 | res[i] = max(a, b)
284 | max_sum = max_sum if max_sum >res[i] else res[i]
285 | return max_sum
286 |
287 | print(dp_opt(arr))
288 |
--------------------------------------------------------------------------------
/docs/简历与面经/BATJrealInterviewExperience/蚂蚁金服实习生面经总结(已拿口头offer).md:
--------------------------------------------------------------------------------
1 | 本文来自 Anonymous 的投稿 ,JavaGuide 对原文进行了重新排版和一点完善。
2 |
3 |
4 |
5 | - [一面 (37 分钟左右)](#一面-37-分钟左右)
6 | - [二面 (33 分钟左右)](#二面-33-分钟左右)
7 | - [三面 (46 分钟)](#三面-46-分钟)
8 | - [HR 面](#hr-面)
9 |
10 |
11 |
12 | ### 一面 (37 分钟左右)
13 |
14 | 一面是上海的小哥打来的,3.12 号中午确认的内推,下午就打来约时间了,也是唯一一个约时间的面试官。约的晚上八点。紧张的一比,人生第一次面试就献给了阿里。
15 |
16 | 幸运的是一面的小哥特温柔。好像是个海归?口语中夹杂着英文。废话不多说,上干货:
17 |
18 | **面试官:** 先自我介绍下吧!
19 |
20 | **我:** 巴拉巴拉...。
21 |
22 | > 关于自我介绍:从 HR 面、技术面到高管面/部门主管面,面试官一般会让你先自我介绍一下,所以好好准备自己的自我介绍真的非常重要。网上一般建议的是准备好两份自我介绍:一份对 HR 说的,主要讲能突出自己的经历,会的编程技术一语带过;另一份对技术面试官说的,主要讲自己会的技术细节,项目经验,经历那些就一语带过。
23 |
24 | **面试官:** 我看你简历上写你做了个秒杀系统?我们就从这个项目开始吧,先介绍下你的项目。
25 |
26 | > 关于项目介绍:如果有项目的话,技术面试第一步,面试官一般都是让你自己介绍一下你的项目。你可以从下面几个方向来考虑:
27 | >
28 | > 1. 对项目整体设计的一个感受(面试官可能会让你画系统的架构图)
29 | > 2. 在这个项目中你负责了什么、做了什么、担任了什么角色
30 | > 3. 从这个项目中你学会了那些东西,使用到了那些技术,学会了那些新技术的使用
31 | > 4. 另外项目描述中,最好可以体现自己的综合素质,比如你是如何协调项目组成员协同开发的或者在遇到某一个棘手的问题的时候你是如何解决的又或者说你在这个项目用了什么技术实现了什么功能比如:用 redis 做缓存提高访问速度和并发量、使用消息队列削峰和降流等等。
32 |
33 | **我:** 我说了我是如何考虑它的需求(秒杀地址隐藏,记录订单,减库存),一开始简单的用 synchronized 锁住方法,出现了问题,后来乐观锁改进,又有瓶颈,再上缓存,出现了缓存雪崩,于是缓存预热,错开缓存失效时间。最后,发现先记录订单再减库存会减少行级锁等待时间。
34 |
35 | > 一面面试官很耐心地听,并给了我一些指导,问了我乐观锁是怎么实现的,我说是基于 sql 语句,在减库存操作的 where 条件里加剩余库存数>0,他说这应该不算是一种乐观锁,应该先查库存,在减库存的时候判断当前库存是否与读到的库存一样(可这样不是多一次查询操作吗?不是很理解,不过我没有反驳,只是说理解您的意思。事实证明千万别怼面试官,即使你觉得他说的不对)
36 |
37 | **面试官:** 我缓存雪崩什么情况下会发生?如何避免?
38 |
39 | **我:** 当多个商品缓存同时失效时会雪崩,导致大量查询数据库。还有就是秒杀刚开始的时候缓存里没有数据。解决方案:缓存预热,错开缓存失效时间
40 |
41 | **面试官:** 问我更新数据库的同时为什么不马上更新缓存,而是删除缓存?
42 |
43 | **我:** 因为考虑到更新数据库后更新缓存可能会因为多线程下导致写入脏数据(比如线程 A 先更新数据库成功,接下来要取更新缓存,接着线程 B 更新数据库,但 B 又更新了缓存,接着 B 的时间片用完了,线程 A 更新了缓存)
44 |
45 | 逼逼了将近 30 分钟,面试官居然用周杰伦的语气对我说:
46 |
47 | 
48 |
49 | 我突然受宠若惊,连忙说谢谢,也正是因为第一次面试得到了面试官的肯定,才让我信心大增,二三面稳定发挥。
50 |
51 | **面试官又曰:** 我看你还懂数据库是吧,答:略懂略懂。。。那我问个简单的吧!
52 |
53 | **我:** 因为这个问题太简单了,所以我忘记它是什么了。
54 |
55 | **面试官:** 你还会啥数据库知识?
56 |
57 | **我:** 我一听,问的这么随意的吗。。。都让我选题了,我就说我了解索引,慢查询优化,巴拉巴拉
58 |
59 | **面试官:** 等等,你说索引是吧,那你能说下索引的存储数据结构吗?
60 |
61 | **我:** 我心想这简单啊,我就说 B+树,还说了为什么用 B+树
62 |
63 | **面试官:** 你简历上写的这个 J.U.C 包是什么啊?(他居然不知道 JUC)
64 |
65 | **我:** 就是 java 多线程的那个包啊。。。
66 |
67 | **面试官:** 那你都了解里面的哪些东西呢?
68 |
69 | **我:** 哈哈哈!这可是我的强项,从 ConcurrentHashMap,ConcurrentLinkedQueue 说到 CountDownLatch,CyclicBarrier,又说到线程池,分别说了底层实现和项目中的应用。
70 |
71 | **面试官:** 我觉得差不多了,那我再问个与技术无关的问题哈,虽然这个问题可能不应该我问,就是你是如何考虑你的项目架构的呢?
72 |
73 | **我:** 先用最简单的方式实现它,再去发掘系统的问题和瓶颈,于是查资料改进架构。。。
74 |
75 | **面试官:** 好,那我给你介绍下我这边的情况吧
76 |
77 | 
78 |
79 | **总结:** 一面可能是简历面吧,问的比较简单,我在讲项目中说出了我做项目时的学习历程和思考,赢得了面试官的好感,感觉他应该给我的评价很好。
80 |
81 | ### 二面 (33 分钟左右)
82 |
83 | 然而开心了没一会,内推人问我面的怎么样啊?看我流程已经到大大 boss 那了。我一听二面不是主管吗???怎么直接跳了一面。于是瞬间慌了,赶紧(下床)学习准备二面。
84 |
85 | 隔了一天,3.14 的早上 10:56 分,杭州的大大 boss 给我打来了电话,卧槽我当时在上毛概课,万恶的毛概课每节课都点名,我还在最后一排不敢跑出去。于是接起电话来怂怂地说不好意思我在上课,晚上可以面试吗?大大 boss 看来很忙啊,跟我说晚上没时间啊,再说吧!
86 |
87 | 于是又隔了一天,3.16 中午我收到了北京的电话,当时心里小失望,我的大大 boss 呢???接起电话来,就是一番狂轰乱炸。。。
88 |
89 | 第一步还是先自我介绍,这个就不多说了,提前准备好要说的重点就没问题!
90 |
91 | **面试官:** 我们还是从你的项目开始吧,说说你的秒杀系统。
92 |
93 | **我:** 一面时的套路。。。我考虑到秒杀地址在开始前不应暴露给用户。。。
94 |
95 | **面试官:** 等下啊,为什么要这样呢?暴露给用户会怎么样?
96 |
97 | **我:** 用户提前知道秒杀地址就可以写脚本来抢购了,这样不公平
98 |
99 | **面试官:** 那比如说啊,我现在是个黑客,我在秒杀开始时写好了脚本,运行一万个线程获取秒杀地址,这样是不是也不公平呢?
100 |
101 | **我:** 我考虑到了这方面,于是我自己写了个 LRU 缓存(划重点,这么多好用的缓存我为啥不用偏要自己写?就是为了让面试官上钩问我是怎么写的,这样我就可以逼逼准备好的内容了!),用这个缓存存储请求的 ip 和用户名,一个 ip 和用户名只能同时透过 3 个请求。
102 |
103 | **面试官:** 那我可不可以创建一个 ip 代理池和很多用户来抢购呢?假设我有很多手机号的账户。
104 |
105 | **我:** 这就是在为难我胖虎啊,我说这种情况跟真实用户操作太像了。。。我没法区别,不过我觉得可以通过地理位置信息或者机器学习算法来做吧。。。
106 |
107 | **面试官:** 好的这个问题就到这吧,你接着说
108 |
109 | **我:** 我把生成订单和减库存两条 sql 语句放在一个事务里,都操作成功了则认为秒杀成功。
110 |
111 | **面试官:** 等等,你这个订单表和商品库存表是在一个数据库的吧,那如果在不同的数据库中呢?
112 |
113 | **我:** 这面试官好变态啊,我只是个本科生?!?!我觉得应该要用分布式锁来实现吧。。。
114 |
115 | **面试官:** 有没有更轻量级的做法?
116 |
117 | **我:** 不知道了。后来查资料发现可以用消息队列来实现。使用消息队列主要能带来两个好处:(1) 通过异步处理提高系统性能(削峰、减少响应所需时间);(2) 降低系统耦合性。关于消息队列的更多内容可以查看这篇文章:
118 |
119 | 后来发现消息队列作用好大,于是现在在学手写一个消息队列。
120 |
121 | **面试官:** 好的你接着说项目吧。
122 |
123 | **我:** 我考虑到了缓存雪崩问题,于是。。。
124 |
125 | **面试官:** 等等,你有没有考虑到一种情况,假如说你的缓存刚刚失效,大量流量就来查缓存,你的数据库会不会炸?
126 |
127 | **我:** 我不知道数据库会不会炸,反正我快炸了。当时说没考虑这么高的并发量,后来发现也是可以用消息队列来解决,对流量削峰填谷。
128 |
129 | **面试官:** 好项目聊(怼)完了,我们来说说别的,操作系统了解吧,你能说说 NIO 吗?
130 |
131 | **我:** NIO 是。。。
132 |
133 | **面试官:** 那你知道 NIO 的系统调用有哪些吗,具体是怎么实现的?
134 |
135 | **我:** 当时复习 NIO 的时候就知道是咋回事,不知道咋实现。最近在补这方面的知识,可见 NIO 还是很重要的!
136 |
137 | **面试官:** 说说进程切换时操作系统都会发生什么?
138 |
139 | **我:** 不如杀了我,我最讨厌操作系统了。简单说了下,可能不对,需要答案自行百度。
140 |
141 | **面试官:** 说说线程池?
142 |
143 | **答:** 卧槽这我熟啊,把 Java 并发编程的艺术里讲的都说出来了,说了得有十分钟,自夸一波,毕竟这本书我看了五遍😂
144 |
145 | **面试官:** 好问问计网吧如果设计一个聊天系统,应该用 TCP 还是 UDP?为什么
146 |
147 | **我:** 当然是 TCP!原因如下:
148 |
149 | 
150 |
151 | **面试官:** 好的,你有什么要问我的吗?
152 |
153 | **我:** 我还有下一次面试吗?
154 |
155 | **面试官:** 应该。应该有的,一周内吧。还告诉我居然转正前要实习三个月?wtf,一个大三满课的本科生让我如何在八月底前实习三个月?
156 |
157 | **我:** 面试官再见
158 |
159 | 
160 |
161 | ### 三面 (46 分钟)
162 |
163 | 3.18 号,三面来了,这次又是那个大大 boss!
164 |
165 | 第一步还是先自我介绍,这个就不多说了,提前准备好要说的重点就没问题!
166 |
167 | **面试官:** 聊聊你的项目?
168 |
169 | **我:** 经过二面的教训,我迅速学习了一下分布式的理论知识,并应用到了我的项目(吹牛逼)中。
170 |
171 | **面试官:** 看你用到了 Spring 的事务机制,你能说下 Spring 的事务传播吗?
172 |
173 | **我:** 完了这个问题好像没准备,虽然之前刷知乎看到过。。。我就只说出来一条,面试官说其实这个有很多机制的,比如事务嵌套,内事务回滚外事务回滚都会有不同情况,你可以回去看看。
174 |
175 | **面试官:** 说说你的分布式事务解决方案?
176 |
177 | **我:** 我叭叭的照着资料查到的解决方案说了一通,面试官怎么好像没大听懂???
178 |
179 | > 阿里巴巴之前开源了一个分布式 Fescar(一种易于使用,高性能,基于 Java 的开源分布式事务解决方案),后来,Ant Financial 加入 Fescar,使其成为一个更加中立和开放的分布式交易社区,Fescar 重命名为 Seata。Github 地址:
180 |
181 | **面试官:** 好,我们聊聊其他项目,说说你这个 MapReduce 项目?MapReduce 原理了解过吗?
182 |
183 | **我:** 我叭叭地说了一通,面试官好像觉得这个项目太简单了。要不是没项目,我会把我的实验写上吗???
184 |
185 | **面试官:** 你这个手写 BP 神经网络是干了啥?
186 |
187 | **我:** 这是我选修机器学习课程时的一个作业,我又对它进行了扩展。
188 |
189 | **面试官:** 你能说说为什么调整权值时要沿着梯度下降的方向?
190 |
191 | **我:** 老大,你太厉害了,怎么什么都懂。我压根没准备这个项目。。。没想到会问,做过去好几个月了,加上当时一紧张就忘了,后来想起来大概是....。
192 |
193 | **面试官:** 好我们问问基础知识吧,说说什么叫 xisuo?
194 |
195 | **我:**???xisuo,您说什么,不好意思我没听清。(这面试官有点口音。。。)就是 xisuo 啊!xisuo 你不知道吗?。。。尴尬了十几秒后我终于意识到,他在说死锁!!!
196 |
197 | **面试官:** 假如 A 账户给 B 账户转钱,会发生 xisuo 吗?能具体说说吗?
198 |
199 | **我:** 当时答的不好,后来发现面试官又是想问分布式,具体答案参考这个:
200 |
201 | **面试官:** 为什么不考研?
202 |
203 | **我:** 不喜欢学术氛围,巴拉巴拉。
204 |
205 | **面试官:** 你有什么问题吗?
206 |
207 | **我:** 我还有下一面吗。。。面试官说让我等,一周内答复。
208 |
209 | ------
210 |
211 | 等了十天,一度以为我凉了,内推人说我流程到 HR 了,让我等着吧可能 HR 太忙了,3.28 号 HR 打来了电话,当时在教室,我直接飞了出去。
212 |
213 | ### HR 面
214 |
215 | **面试官:** 你好啊,先自我介绍下吧
216 |
217 | **我:** 巴拉巴拉....HR 面的技术面试和技术面的还是有所区别的!
218 |
219 | 面试官人特别好,一听就是很会说话的小姐姐!说我这里给你悄悄透露下,你的评级是 A 哦!
220 |
221 | 
222 |
223 | 接下来就是几个经典 HR 面挂人的问题,什么难给我来什么,我看别人的 HR 面怎么都是聊聊天。。。
224 |
225 | **面试官:** 你为什么选择支付宝呢,你怎么看待支付宝?
226 |
227 | **我:** 我从个人情怀,公司理念,环境氛围,市场价值,趋势导向分析了一波(说白了就是疯狂夸支付宝,不过说实话我说的那些一点都没撒谎,阿里确实做到了。比如我举了个雷军和格力打赌 5 年 2000 亿销售额,大部分企业家关注的是利益,而马云更关注的是真的为人类为世界做一些事情,利益不是第一位的。)
228 |
229 | **面试官:** 明白了解,那你的优点我们都很明了了,你能说说你的缺点吗?
230 |
231 | > 缺点肯定不能是目标岗位需要的关键能力!!!
232 | >
233 | > 总之,记住一点,面试官问你这个问题的话,你可以说一些不影响你这个职位工作需要的一些缺点。比如你面试后端工程师,面试官问你的缺点是什么的话,你可以这样说:自己比较内向,平时不太爱与人交流,但是考虑到以后可能要和客户沟通,自己正在努力改。
234 |
235 | **我:** 据说这是 HR 面最难的一个问题。。。我当时翻了好几天的知乎才找到一个合适的,也符合我的答案:我有时候会表现的不太自信,比如阿里的内推二月份就开始了,其实我当时已经复习了很久了,但是老是觉得自己还不行,不敢投简历,于是又把书看了一遍才投的,当时也是舍友怂恿一波才投的,面了之后发现其实自己也没有很差。(划重点,一定要把自己的缺点圆回来)。
236 |
237 | **面试官:** HR 好像不太满意我的答案,继续问我还有缺点吗?
238 |
239 | **我:** 我说比较容易紧张吧,举了自己大一面实验室因为紧张没进去的例子,后来不断调整心态,现在已经好很多了。
240 |
241 | 接下来又是个好难的问题。
242 |
243 | **面试官:** BAT 都给你 offer 了,你怎么选?
244 |
245 | 其实我当时好想说,BT 是什么?不好意思我只知道阿里。
246 |
247 | **我 :** 哈哈哈哈开玩笑,就说了阿里的文化,支付宝给我们带来很多便利,想加入支付宝为人类做贡献!
248 |
249 | 最后 HR 问了我实习时间,现在大几之类的问题,说肯定会给我发 offer 的,让我等着就好了,希望过两天能收到好的结果。
250 |
251 | 
252 |
--------------------------------------------------------------------------------
/docs/数据库/MySQL索引.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | ## 思维导图-索引篇
5 |
6 | > 系列思维导图源文件(数据库+架构)以及思维导图制作软件—XMind8 破解安装,公众号后台回复:**“思维导图”** 免费领取!(下面的图片不是很清楚,原图非常清晰,另外提供给大家源文件也是为了大家根据自己需要进行修改)
7 |
8 | 
9 |
10 |
11 |
12 | ## 索引的前世今生
13 |
14 | ### 什么是索引
15 |
16 | **索引是一种实现高效获取数据的数据结构。Mysql中的索引是用B+树实现的。**
17 |
18 | 索引可以帮助我们快速地定位到数据而不需要每次搜索的时候都遍历数据库中的每一行。
19 |
20 | 在数据库系统中它具有以下优缺点。
21 |
22 | **优点:**
23 | 1.大大加快数据的检索速度;
24 | 2.创建唯一性索引,保证数据库表中每一行数据的唯一性;
25 | 3.加速表和表之间的连接;
26 | 4.在使用order by、group by子句进行数据检索时,可以显著减少查询中分组和排序的时间。
27 |
28 | **缺点:**
29 | 1.索引需要占物理空间。
30 | 2.当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度。
31 |
32 | ### 查找结构进化史
33 |
34 | (1) 线性结构:数组,链表。实现简单,查找效率低。
35 | (2) 二分查找:要求有序,插入慢。
36 | (3) 哈希(hash):查询极快,占用太多额外空间,不适合大规模数据,无范围查找。
37 | (4) 二叉查找树:查询、插入较快。存在退化问题,树太高。
38 | (6) 平衡树(AVL):解决了二叉查找树退化问题,但依然存在树高度太高的问题。
39 | (7) 多路查找树:节点很多时,树不会太高,但存在退化问题。
40 | (8) 多路平衡查找树(BTree):平衡树 + 多路查找树。无法范围查找。
41 | (9) B+Tree:BTree的变种,可进行范围查找(双向链表链接所有叶子节点)。只有叶子节点存储数据(增大树的度,减小了树高度).
42 |
43 | **关于BTree和B+Tree可以看这篇文章:**[什么是B树和B+树](../数据结构与算法/面试官问你什么B树和B+树,把这篇文章丢给他.md)
44 |
45 |
46 | ### 常见实现方式有哪些?
47 |
48 | 常见的实现索引的数据结构有哈希表、有序数组、搜索树(BTree,B+Tree)
49 |
50 | #### 哈希表
51 |
52 | 使用哈希表实现的索引称为哈希索引。
53 |
54 | 
55 |
56 |
57 | 如上图所示,我们将一系列的最终的键值通过哈希函数转化为存储实际数据桶的地址数值。值本身存储的地址就是基于哈希函数的计算结果,而搜索的过程就是利用哈希函数从元数据中推导出桶的地址。
58 |
59 | **哈希冲突:**
60 | 不同的键值通过哈希函数换算得到了相同的值。
61 |
62 | **解决办法:**
63 |
64 | - 链地址法:将所有哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。链地址法适用于经常进行插入和删除的情况。
65 |
66 | - 再哈希法:这种方法是同时构造多个不同的哈希函数:Hi=RH1(key) i=1,2,…,k。当哈希地址Hi=RH1(key)发生冲突时,再计算Hi=RH2(key)……,直到冲突不再产生。这种方法不易产生聚集,但增加了计算时间。
67 |
68 | - 开放定址法:这种方法也称再散列法,其基本思想是:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi ,将相应元素存入其中。
69 |
70 | - 建立公共溢出区:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。
71 |
72 | **优缺点:**
73 | 哈希索引会在进行相等性测试(等或者不等)时候具有非常高的性能,但是在进行比较查询、Order By 等更为复杂的场景下就无能为力。
74 |
75 |
76 | #### 有序数组
77 |
78 | 按照给定的顺序排好序的数组
79 |
80 | **优缺点:**
81 | 等值查询和比较查询性能非常优秀,但更新数据很繁琐,因为可能需要挪动大量的数据。所以这种索引结构适合静态存储引擎,存储不再改变的数据,如某学期的学生成绩信息。
82 |
83 |
84 | #### 搜索树
85 |
86 | 最简单的是二叉搜索树,然后有平衡二叉树、B树(B-Tree/BTree)和B+树(B+Tree),甚至B*Tree。这些BTree、B+Tree、B*Tree都是一棵自平衡的搜索树,它类似普通的平衡二叉树,不同的一点是BTree允许每个节点有更多的子节点。BTree是专门为外部存储器设计的,如磁盘,它对于读取和写入大块数据有良好的性能,所以一般被用在文件系统及数据库中。
87 |
88 | 我们使用最多的两个存储引擎MyISAM和InnoDB都是 B+Tree 实现索引。而 BTree 索引是 MySQL 数据库中使用最为频繁的索引类型,除了 Archive 存储引擎之外的其他所有的存储引擎都支持 BTree 索引。Archive 引擎直到 MySQL 5.1 才支持索引,而且只支持索引单个 AUTO_INCREMENT 列。
89 |
90 | 不仅仅在 MySQL 中是如此,实际上在其他的很多数据库管理系统中BTree 索引也同样是作为最主要的索引类型,这主要是因为 B-Tree 索引的存储结构在数据库的数据检索中有非常优异的表现。
91 |
92 |
93 | ### 为何B树与B+树相比其他二叉搜索树更适合作为数据库索引的实现?
94 |
95 | 一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的话,索引查找过程中就要产生磁盘 I/O 消耗,相对于内存存取,I/O 存取的消耗要高几个数量级,所以评价一个数据结构作为索引的优劣最重要的指标就是在查找过程中磁盘 I/O 操作次数的渐进复杂度。换句话说,索引的结构组织要尽量减少查找过程中磁盘 I/O 的存取次数。
96 |
97 | 根据 B-Tree 的定义,可知检索一次最多需要访问 h 个节点。数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次 I/O 就可以完全载入。(操作系统中一页大小通常是4KB)
98 |
99 | 每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,加之计算机存储分配都是按页对齐的,就实现了一个节点只需一次 I/O。
100 |
101 | 而检索的时候,一次检索最多需要 h-1 次 I/O(根节点常驻内存),其渐进复杂度为
102 |
103 | 
104 |
105 | 实际应用中,出度 d 是非常大的数字,通常超过 100,因此 h 非常小(通常不超过 3)。而其他搜索二叉树,h 明显要深的多。由于逻辑上很近的节点(父子)物理上可能很远,无法利用局部性,所以红黑树的 I/O 渐进复杂度也为 O(h),效率明显比 B-Tree 差很多。
106 |
107 |
108 | #### BTree的特点简述:
109 | (1)所有键值分布在整个树中
110 | (2)任何关键字出现且只出现在一个节点中
111 | (3)搜索有可能在非叶子节点结束
112 | (4)在关键字全集内做一次查找,性能逼近二分查找算法
113 |
114 | 一棵高度为3的B树:
115 |
116 | 
117 |
118 |
119 | #### B+Tree 是BTree的变种,有着比 BTree 更高的查询性能,其相较于 BTree 有了如下的变化:
120 |
121 | - 有 m 个子树的节点包含有 m 个元素(B-Tree 中是 m-1)。
122 | - 根节点和分支节点中不保存数据,只用于索引,所有数据都保存在叶子节点中。
123 | - 所有分支节点和根节点都同时存在于子节点中,在子节点元素中是最大或者最小的元素。
124 | - 叶子节点会包含所有的关键字,以及指向数据记录的指针,并且叶子节点本身是根据关键字的大小从小到大顺序链接。
125 |
126 | **一般在数据库系统或文件系统中使用的 B+Tree 结构都在经典 B+Tree 的基础上进行了优化,增加了顺序访问指针。**
127 |
128 | 一棵高度为3的B+树:
129 |
130 | 
131 |
132 |
133 | 通常在B+Tree上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点,而且所有叶子节点(即数据节点)之间是一种链式环结构。因此可以对B+Tree进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找。
134 |
135 |
136 | ### InnoDB存储引擎中的索引
137 |
138 | InnoDB中索引的每个节点默认大小是16KB。
139 |
140 | InnoDB中的索引分为:主键索引(聚集索引)和非主键索引(二级索引)
141 |
142 | 主键索引中叶子节点存储的数据是整行的完整数据。二级索引中叶子节点存储的数据是主键值。表中没有主键则将唯一字段认作主键,若没有唯一字段则自己建一个主键列。
143 |
144 | 查询时根据若查询条件是根据主键查询,则直接通过主键索引查找得到值。如果是根据其他的查询条件查询则会先到二级索引中查询出对应的主键值,再根据主键值到主键索引中去查找。(这个也叫做回表)
145 |
146 | #### 索引维护
147 |
148 | (1)数据插入与删除
149 |
150 | 插入数据的时候可能是直接插入或需要将部分数据往后挪空出插入位置再插入。
151 |
152 | 特殊情况:如果插入慢歌已经满了的数据页,则需要新申请一个数据页,然后将满的数据页的部分数据挪过去,最后再进行插入。(这个过程被称为**页分裂**)
153 |
154 | 如果插入的数据是向某个满数据页的首或者尾插入,为了减少页分裂和数据移动,会先看与这个满数据页相邻的前后数据页是否也已经满了,如果未满则会将该待插入数据先放置到前或后的数据页,满才会进行页分裂。(这样做是为了增加空间利用率)
155 |
156 | 同样,由于相邻两页删除了部分数据,两页的空间利用率都变得很低,会将这两数据页进行合并。(这个歌过程被称为**页合并**)
157 |
158 | (2)最左前缀
159 |
160 | 联合索引的最左N个字段,或者字符串索引的前N个字符。
161 |
162 | (3)覆盖索引
163 |
164 | 如果查询条件是非主键索引字段(或联合索引的最左原则字段),查询结果是主键(或联合索引的字段),不用回表,直接返回结果,这样减少了磁盘读取次数。
165 |
166 | (4)联合索引
167 |
168 | 对多个字段一起建立一个索引,根据字段顺序以最左前缀原则进行检索。
169 |
170 | (5)索引下推
171 |
172 | 建立联合索引(name,age),查询语句如下:
173 |
174 | SELECT * FROM 表名 WHERE name like "helle" AND age>18;
175 |
176 | 这条语句在MySQL5.6之前对name匹配的数据直接进行回表,而5.6之后的版本,会先把name匹配的数据中age<=18的数据再进行过滤掉最后才进行回表。
177 |
178 | ----------
179 |
180 | > **下面是补充的一些内容**
181 |
182 | ## 为什么索引能提高查询速度
183 |
184 | > 以下内容整理自:
185 | > 地址: https://juejin.im/post/5b55b842f265da0f9e589e79
186 | > 作者 :Java3y
187 |
188 | ### 先从 MySQL 的基本存储结构说起
189 |
190 | MySQL的基本存储结构是页(记录都存在页里边):
191 |
192 | 
193 |
194 | 
195 |
196 | - **各个数据页可以组成一个双向链表**
197 | - **每个数据页中的记录又可以组成一个单向链表**
198 | - 每个数据页都会为存储在它里边儿的记录生成一个页目录,在通过主键查找某条记录的时候可以在页目录中使用二分法快速定位到对应的槽,然后再遍历该槽对应分组中的记录即可快速找到指定的记录
199 | - 以其他列(非主键)作为搜索条件:只能从最小记录开始依次遍历单链表中的每条记录。
200 |
201 | 所以说,如果我们写select * from user where indexname = 'xxx'这样没有进行任何优化的sql语句,默认会这样做:
202 |
203 | 1. **定位到记录所在的页:需要遍历双向链表,找到所在的页**
204 | 2. **从所在的页内中查找相应的记录:由于不是根据主键查询,只能遍历所在页的单链表了**
205 |
206 | 很明显,在数据量很大的情况下这样查找会很慢!这样的时间复杂度为O(n)。
207 |
208 |
209 | ### 使用索引之后
210 |
211 | 索引做了些什么可以让我们查询加快速度呢?其实就是将无序的数据变成有序(相对):
212 |
213 | 
214 |
215 | 要找到id为8的记录简要步骤:
216 |
217 | 
218 |
219 | 很明显的是:没有用索引我们是需要遍历双向链表来定位对应的页,现在通过 **“目录”** 就可以很快地定位到对应的页上了!(二分查找,时间复杂度近似为O(logn))
220 |
221 | 其实底层结构就是B+树,B+树作为树的一种实现,能够让我们很快地查找出对应的记录。
222 |
223 | ## 关于索引其他重要的内容补充
224 |
225 | > 以下内容整理自:《Java工程师修炼之道》
226 |
227 |
228 | ### 最左前缀原则
229 |
230 | MySQL中的索引可以以一定顺序引用多列,这种索引叫作联合索引。如User表的name和city加联合索引就是(name,city),而最左前缀原则指的是,如果查询的时候查询条件精确匹配索引的左边连续一列或几列,则此列就可以被用到。如下:
231 |
232 | ```
233 | select * from user where name=xx and city=xx ; //可以命中索引
234 | select * from user where name=xx ; // 可以命中索引
235 | select * from user where city=xx ; // 无法命中索引
236 | ```
237 | 这里需要注意的是,查询的时候如果两个条件都用上了,但是顺序不同,如 `city= xx and name =xx`,那么现在的查询引擎会自动优化为匹配联合索引的顺序,这样是能够命中索引的。
238 |
239 | 由于最左前缀原则,在创建联合索引时,索引字段的顺序需要考虑字段值去重之后的个数,较多的放前面。ORDER BY子句也遵循此规则。
240 |
241 | ### 注意避免冗余索引
242 |
243 | 冗余索引指的是索引的功能相同,能够命中 就肯定能命中 ,那么 就是冗余索引如(name,city )和(name )这两个索引就是冗余索引,能够命中后者的查询肯定是能够命中前者的 在大多数情况下,都应该尽量扩展已有的索引而不是创建新索引。
244 |
245 | MySQL 5.7 版本后,可以通过查询 sys 库的 `schema_redundant_indexes` 表来查看冗余索引
246 |
247 | ### Mysql如何为表字段添加索引???
248 |
249 | 1.添加PRIMARY KEY(主键索引)
250 |
251 | ```
252 | ALTER TABLE `table_name` ADD PRIMARY KEY ( `column` )
253 | ```
254 | 2.添加UNIQUE(唯一索引)
255 |
256 | ```
257 | ALTER TABLE `table_name` ADD UNIQUE ( `column` )
258 | ```
259 |
260 | 3.添加INDEX(普通索引)
261 |
262 | ```
263 | ALTER TABLE `table_name` ADD INDEX index_name ( `column` )
264 | ```
265 |
266 | 4.添加FULLTEXT(全文索引)
267 |
268 | ```
269 | ALTER TABLE `table_name` ADD FULLTEXT ( `column`)
270 | ```
271 |
272 | 5.添加多列索引
273 |
274 | ```
275 | ALTER TABLE `table_name` ADD INDEX index_name ( `column1`, `column2`, `column3` )
276 | ```
277 |
278 |
279 | ## 参考
280 |
281 | - 《Java工程师修炼之道》
282 | - 《MySQL高性能书籍_第3版》
283 | - https://juejin.im/post/5b55b842f265da0f9e589e79
284 |
285 |
--------------------------------------------------------------------------------
/docs/tools/Docker.md:
--------------------------------------------------------------------------------
1 | **本文只是对Docker的概念做了较为详细的介绍,并不涉及一些像Docker环境的安装以及Docker的一些常见操作和命令。**
2 |
3 |
4 |
5 | - [一 先从认识容器开始](#一-先从认识容器开始)
6 | - [1.1 什么是容器?](#11-什么是容器)
7 | - [先来看看容器较为官方的解释](#先来看看容器较为官方的解释)
8 | - [再来看看容器较为通俗的解释](#再来看看容器较为通俗的解释)
9 | - [1.2 图解物理机,虚拟机与容器](#12-图解物理机虚拟机与容器)
10 | - [二 再来谈谈 Docker 的一些概念](#二-再来谈谈-docker-的一些概念)
11 | - [2.1 什么是 Docker?](#21-什么是-docker)
12 | - [2.2 Docker 思想](#22-docker-思想)
13 | - [2.3 Docker 容器的特点](#23-docker-容器的特点)
14 | - [2.4 为什么要用 Docker ?](#24-为什么要用-docker-)
15 | - [三 容器 VS 虚拟机](#三-容器-vs-虚拟机)
16 | - [3.1 两者对比图](#31-两者对比图)
17 | - [3.2 容器与虚拟机总结](#32-容器与虚拟机总结)
18 | - [3.3 容器与虚拟机两者是可以共存的](#33-容器与虚拟机两者是可以共存的)
19 | - [四 Docker基本概念](#四-docker基本概念)
20 | - [4.1 镜像(Image):一个特殊的文件系统](#41-镜像image一个特殊的文件系统)
21 | - [4.2 容器(Container):镜像运行时的实体](#42-容器container镜像运行时的实体)
22 | - [4.3仓库(Repository):集中存放镜像文件的地方](#43仓库repository集中存放镜像文件的地方)
23 | - [五 最后谈谈:Build Ship and Run](#五-最后谈谈build-ship-and-run)
24 | - [六 总结](#六-总结)
25 |
26 |
27 |
28 | > **Docker 是世界领先的软件容器平台**,所以想要搞懂Docker的概念我们必须先从容器开始说起。
29 |
30 | ## 一 先从认识容器开始
31 |
32 | ### 1.1 什么是容器?
33 |
34 | #### 先来看看容器较为官方的解释
35 |
36 | **一句话概括容器:容器就是将软件打包成标准化单元,以用于开发、交付和部署。**
37 |
38 | - **容器镜像是轻量的、可执行的独立软件包** ,包含软件运行所需的所有内容:代码、运行时环境、系统工具、系统库和设置。
39 | - **容器化软件适用于基于Linux和Windows的应用,在任何环境中都能够始终如一地运行。**
40 | - **容器赋予了软件独立性** ,使其免受外在环境差异(例如,开发和预演环境的差异)的影响,从而有助于减少团队间在相同基础设施上运行不同软件时的冲突。
41 |
42 | #### 再来看看容器较为通俗的解释
43 |
44 | **如果需要通俗的描述容器的话,我觉得容器就是一个存放东西的地方,就像书包可以装各种文具、衣柜可以放各种衣服、鞋架可以放各种鞋子一样。我们现在所说的容器存放的东西可能更偏向于应用比如网站、程序甚至是系统环境。**
45 |
46 | 
47 |
48 | ### 1.2 图解物理机,虚拟机与容器
49 | 关于虚拟机与容器的对比在后面会详细介绍到,这里只是通过网上的图片加深大家对于物理机、虚拟机与容器这三者的理解。
50 |
51 | **物理机**
52 | 
53 |
54 | **虚拟机:**
55 |
56 | 
57 |
58 | **容器:**
59 |
60 | 
61 |
62 | 通过上面这三张抽象图,我们可以大概可以通过类比概括出: **容器虚拟化的是操作系统而不是硬件,容器之间是共享同一套操作系统资源的。虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统。因此容器的隔离级别会稍低一些。**
63 |
64 | ---
65 |
66 | > 相信通过上面的解释大家对于容器这个既陌生又熟悉的概念有了一个初步的认识,下面我们就来谈谈Docker的一些概念。
67 |
68 | ## 二 再来谈谈 Docker 的一些概念
69 |
70 | 
71 |
72 | ### 2.1 什么是 Docker?
73 |
74 | 说实话关于Docker是什么并太好说,下面我通过四点向你说明Docker到底是个什么东西。
75 |
76 | - **Docker 是世界领先的软件容器平台。**
77 | - **Docker** 使用 Google 公司推出的 **Go 语言** 进行开发实现,基于 **Linux 内核** 的cgroup,namespace,以及AUFS类的**UnionFS**等技术,**对进程进行封装隔离,属于操作系统层面的虚拟化技术。** 由于隔离的进程独立于宿主和其它的隔离的进
78 | 程,因此也称其为容器。**Docke最初实现是基于 LXC.**
79 | - **Docker 能够自动执行重复性任务,例如搭建和配置开发环境,从而解放了开发人员以便他们专注在真正重要的事情上:构建杰出的软件。**
80 | - **用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。**
81 |
82 | 
83 |
84 | ### 2.2 Docker 思想
85 |
86 | - **集装箱**
87 | - **标准化:** ①运输方式 ② 存储方式 ③ API接口
88 | - **隔离**
89 |
90 | ### 2.3 Docker 容器的特点
91 |
92 | - #### 轻量
93 |
94 | 在一台机器上运行的多个 Docker 容器可以共享这台机器的操作系统内核;它们能够迅速启动,只需占用很少的计算和内存资源。镜像是通过文件系统层进行构造的,并共享一些公共文件。这样就能尽量降低磁盘用量,并能更快地下载镜像。
95 | - #### 标准
96 |
97 | Docker 容器基于开放式标准,能够在所有主流 Linux 版本、Microsoft Windows 以及包括 VM、裸机服务器和云在内的任何基础设施上运行。
98 | - #### 安全
99 |
100 | Docker 赋予应用的隔离性不仅限于彼此隔离,还独立于底层的基础设施。Docker 默认提供最强的隔离,因此应用出现问题,也只是单个容器的问题,而不会波及到整台机器。
101 |
102 | ### 2.4 为什么要用 Docker ?
103 |
104 | - **Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 “这段代码在我机器上没问题啊” 这类问题;——一致的运行环境**
105 | - **可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。——更快速的启动时间**
106 | - **避免公用的服务器,资源会容易受到其他用户的影响。——隔离性**
107 | - **善于处理集中爆发的服务器使用压力;——弹性伸缩,快速扩展**
108 | - **可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。——迁移方便**
109 | - **使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。——持续交付和部署**
110 |
111 | ---
112 |
113 | > 每当说起容器,我们不得不将其与虚拟机做一个比较。就我而言,对于两者无所谓谁会取代谁,而是两者可以和谐共存。
114 |
115 | ## 三 容器 VS 虚拟机
116 |
117 | 简单来说: **容器和虚拟机具有相似的资源隔离和分配优势,但功能有所不同,因为容器虚拟化的是操作系统,而不是硬件,因此容器更容易移植,效率也更高。**
118 |
119 | ### 3.1 两者对比图
120 |
121 | 传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便.
122 |
123 | 
124 |
125 | ### 3.2 容器与虚拟机总结
126 |
127 | 
128 |
129 | - **容器是一个应用层抽象,用于将代码和依赖资源打包在一起。** **多个容器可以在同一台机器上运行,共享操作系统内核,但各自作为独立的进程在用户空间中运行** 。与虚拟机相比, **容器占用的空间较少**(容器镜像大小通常只有几十兆),**瞬间就能完成启动** 。
130 |
131 | - **虚拟机 (VM) 是一个物理硬件层抽象,用于将一台服务器变成多台服务器。** 管理程序允许多个 VM 在一台机器上运行。每个VM都包含一整套操作系统、一个或多个应用、必要的二进制文件和库资源,因此 **占用大量空间** 。而且 VM **启动也十分缓慢** 。
132 |
133 | 通过Docker官网,我们知道了这么多Docker的优势,但是大家也没有必要完全否定虚拟机技术,因为两者有不同的使用场景。**虚拟机更擅长于彻底隔离整个运行环境**。例如,云服务提供商通常采用虚拟机技术隔离不同的用户。而 **Docker通常用于隔离不同的应用** ,例如前端,后端以及数据库。
134 |
135 | ### 3.3 容器与虚拟机两者是可以共存的
136 |
137 | 就我而言,对于两者无所谓谁会取代谁,而是两者可以和谐共存。
138 |
139 | 
140 |
141 | ---
142 |
143 | > Docker中非常重要的三个基本概念,理解了这三个概念,就理解了 Docker 的整个生命周期。
144 |
145 | ## 四 Docker基本概念
146 |
147 | Docker 包括三个基本概念
148 |
149 | - **镜像(Image)**
150 | - **容器(Container)**
151 | - **仓库(Repository)**
152 |
153 | 理解了这三个概念,就理解了 Docker 的整个生命周期
154 |
155 | 
156 |
157 | ### 4.1 镜像(Image):一个特殊的文件系统
158 |
159 | **操作系统分为内核和用户空间**。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而Docker 镜像(Image),就相当于是一个 root 文件系统。
160 |
161 | **Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。** 镜像不包含任何动态数据,其内容在构建之后也不会被改变。
162 |
163 | Docker 设计时,就充分利用 **Union FS**的技术,将其设计为 **分层存储的架构** 。 镜像实际是由多层文件系统联合组成。
164 |
165 | **镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。** 比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。
166 |
167 | 分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。
168 |
169 | ### 4.2 容器(Container):镜像运行时的实体
170 |
171 | 镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜像是静态的定义,**容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等** 。
172 |
173 | **容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。前面讲过镜像使用的是分层存储,容器也是如此。**
174 |
175 | **容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。**
176 |
177 | 按照 Docker 最佳实践的要求,**容器不应该向其存储层内写入任何数据** ,容器存储层要保持无状态化。**所有的文件写入操作,都应该使用数据卷(Volume)、或者绑定宿主目录**,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此, **使用数据卷后,容器可以随意删除、重新 run ,数据却不会丢失。**
178 |
179 |
180 | ### 4.3仓库(Repository):集中存放镜像文件的地方
181 |
182 | 镜像构建完成后,可以很容易的在当前宿主上运行,但是, **如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry就是这样的服务。**
183 |
184 | 一个 Docker Registry中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。所以说:**镜像仓库是Docker用来集中存放镜像文件的地方类似于我们之前常用的代码仓库。**
185 |
186 | 通常,**一个仓库会包含同一个软件不同版本的镜像**,而**标签就常用于对应该软件的各个版本** 。我们可以通过```<仓库名>:<标签>```的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签.。
187 |
188 | **这里补充一下Docker Registry 公开服务和私有 Docker Registry的概念:**
189 |
190 | **Docker Registry 公开服务** 是开放给用户使用、允许用户管理镜像的 Registry 服务。一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。
191 |
192 | 最常使用的 Registry 公开服务是官方的 **Docker Hub** ,这也是默认的 Registry,并拥有大量的高质量的官方镜像,网址为:[https://hub.docker.com/](https://hub.docker.com/) 。在国内访问**Docker Hub** 可能会比较慢国内也有一些云服务商提供类似于 Docker Hub 的公开服务。比如 [时速云镜像库](https://hub.tenxcloud.com/)、[网易云镜像服务](https://www.163yun.com/product/repo)、[DaoCloud 镜像市场](https://www.daocloud.io/)、[阿里云镜像库](https://www.aliyun.com/product/containerservice?utm_content=se_1292836)等。
193 |
194 | 除了使用公开服务外,用户还可以在 **本地搭建私有 Docker Registry** 。Docker 官方提供了 Docker Registry 镜像,可以直接使用做为私有 Registry 服务。开源的 Docker Registry 镜像只提供了 Docker Registry API 的服务端实现,足以支持 docker 命令,不影响使用。但不包含图形界面,以及镜像维护、用户管理、访问控制等高级功能。
195 |
196 | ---
197 |
198 | > Docker的概念基本上已经讲完,最后我们谈谈:Build, Ship, and Run。
199 |
200 | ## 五 最后谈谈:Build Ship and Run
201 | 如果你搜索Docker官网,会发现如下的字样:**“Docker - Build, Ship, and Run Any App, Anywhere”**。那么Build, Ship, and Run到底是在干什么呢?
202 |
203 | 
204 |
205 | - **Build(构建镜像)** : 镜像就像是集装箱包括文件以及运行环境等等资源。
206 | - **Ship(运输镜像)** :主机和仓库间运输,这里的仓库就像是超级码头一样。
207 | - **Run (运行镜像)** :运行的镜像就是一个容器,容器就是运行程序的地方。
208 |
209 | **Docker 运行过程也就是去仓库把镜像拉到本地,然后用一条命令把镜像运行起来变成容器。所以,我们也常常将Docker称为码头工人或码头装卸工,这和Docker的中文翻译搬运工人如出一辙。**
210 |
211 | ## 六 总结
212 |
213 | 本文主要把Docker中的一些常见概念做了详细的阐述,但是并不涉及Docker的安装、镜像的使用、容器的操作等内容。这部分东西,希望读者自己可以通过阅读书籍与官方文档的形式掌握。如果觉得官方文档阅读起来很费力的话,这里推荐一本书籍《Docker技术入门与实战第二版》。
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
--------------------------------------------------------------------------------
/docs/系统设计/data-communication/RocketMQ-Questions.md:
--------------------------------------------------------------------------------
1 | 本文来自读者 [PR](https://github.com/Snailclimb/JavaGuide/pull/291)。
2 |
3 |
4 | - [1 单机版消息中心](#1-%E5%8D%95%E6%9C%BA%E7%89%88%E6%B6%88%E6%81%AF%E4%B8%AD%E5%BF%83)
5 | - [2 分布式消息中心](#2-%E5%88%86%E5%B8%83%E5%BC%8F%E6%B6%88%E6%81%AF%E4%B8%AD%E5%BF%83)
6 | - [2.1 问题与解决](#21-%E9%97%AE%E9%A2%98%E4%B8%8E%E8%A7%A3%E5%86%B3)
7 | - [2.1.1 消息丢失的问题](#211-%E6%B6%88%E6%81%AF%E4%B8%A2%E5%A4%B1%E7%9A%84%E9%97%AE%E9%A2%98)
8 | - [2.1.2 同步落盘怎么才能快](#212-%E5%90%8C%E6%AD%A5%E8%90%BD%E7%9B%98%E6%80%8E%E4%B9%88%E6%89%8D%E8%83%BD%E5%BF%AB)
9 | - [2.1.3 消息堆积的问题](#213-%E6%B6%88%E6%81%AF%E5%A0%86%E7%A7%AF%E7%9A%84%E9%97%AE%E9%A2%98)
10 | - [2.1.4 定时消息的实现](#214-%E5%AE%9A%E6%97%B6%E6%B6%88%E6%81%AF%E7%9A%84%E5%AE%9E%E7%8E%B0)
11 | - [2.1.5 顺序消息的实现](#215-%E9%A1%BA%E5%BA%8F%E6%B6%88%E6%81%AF%E7%9A%84%E5%AE%9E%E7%8E%B0)
12 | - [2.1.6 分布式消息的实现](#216-%E5%88%86%E5%B8%83%E5%BC%8F%E6%B6%88%E6%81%AF%E7%9A%84%E5%AE%9E%E7%8E%B0)
13 | - [2.1.7 消息的 push 实现](#217-%E6%B6%88%E6%81%AF%E7%9A%84-push-%E5%AE%9E%E7%8E%B0)
14 | - [2.1.8 消息重复发送的避免](#218-%E6%B6%88%E6%81%AF%E9%87%8D%E5%A4%8D%E5%8F%91%E9%80%81%E7%9A%84%E9%81%BF%E5%85%8D)
15 | - [2.1.9 广播消费与集群消费](#219-%E5%B9%BF%E6%92%AD%E6%B6%88%E8%B4%B9%E4%B8%8E%E9%9B%86%E7%BE%A4%E6%B6%88%E8%B4%B9)
16 | - [2.1.10 RocketMQ 不使用 ZooKeeper 作为注册中心的原因,以及自制的 NameServer 优缺点?](#2110-rocketmq-%E4%B8%8D%E4%BD%BF%E7%94%A8-zookeeper-%E4%BD%9C%E4%B8%BA%E6%B3%A8%E5%86%8C%E4%B8%AD%E5%BF%83%E7%9A%84%E5%8E%9F%E5%9B%A0%E4%BB%A5%E5%8F%8A%E8%87%AA%E5%88%B6%E7%9A%84-nameserver-%E4%BC%98%E7%BC%BA%E7%82%B9)
17 | - [2.1.11 其它](#2111-%E5%85%B6%E5%AE%83)
18 | - [3 参考](#3-%E5%8F%82%E8%80%83)
19 |
20 |
21 |
22 | # 1 单机版消息中心
23 |
24 | 一个消息中心,最基本的需要支持多生产者、多消费者,例如下:
25 |
26 | ```java
27 | class Scratch {
28 |
29 | public static void main(String[] args) {
30 | // 实际中会有 nameserver 服务来找到 broker 具体位置以及 broker 主从信息
31 | Broker broker = new Broker();
32 | Producer producer1 = new Producer();
33 | producer1.connectBroker(broker);
34 | Producer producer2 = new Producer();
35 | producer2.connectBroker(broker);
36 |
37 | Consumer consumer1 = new Consumer();
38 | consumer1.connectBroker(broker);
39 | Consumer consumer2 = new Consumer();
40 | consumer2.connectBroker(broker);
41 |
42 | for (int i = 0; i < 2; i++) {
43 | producer1.asyncSendMsg("producer1 send msg" + i);
44 | producer2.asyncSendMsg("producer2 send msg" + i);
45 | }
46 | System.out.println("broker has msg:" + broker.getAllMagByDisk());
47 |
48 | for (int i = 0; i < 1; i++) {
49 | System.out.println("consumer1 consume msg:" + consumer1.syncPullMsg());
50 | }
51 | for (int i = 0; i < 3; i++) {
52 | System.out.println("consumer2 consume msg:" + consumer2.syncPullMsg());
53 | }
54 | }
55 |
56 | }
57 |
58 | class Producer {
59 |
60 | private Broker broker;
61 |
62 | public void connectBroker(Broker broker) {
63 | this.broker = broker;
64 | }
65 |
66 | public void asyncSendMsg(String msg) {
67 | if (broker == null) {
68 | throw new RuntimeException("please connect broker first");
69 | }
70 | new Thread(() -> {
71 | broker.sendMsg(msg);
72 | }).start();
73 | }
74 | }
75 |
76 | class Consumer {
77 | private Broker broker;
78 |
79 | public void connectBroker(Broker broker) {
80 | this.broker = broker;
81 | }
82 |
83 | public String syncPullMsg() {
84 | return broker.getMsg();
85 | }
86 |
87 | }
88 |
89 | class Broker {
90 |
91 | // 对应 RocketMQ 中 MessageQueue,默认情况下 1 个 Topic 包含 4 个 MessageQueue
92 | private LinkedBlockingQueue messageQueue = new LinkedBlockingQueue(Integer.MAX_VALUE);
93 |
94 | // 实际发送消息到 broker 服务器使用 Netty 发送
95 | public void sendMsg(String msg) {
96 | try {
97 | messageQueue.put(msg);
98 | // 实际会同步或异步落盘,异步落盘使用的定时任务定时扫描落盘
99 | } catch (InterruptedException e) {
100 |
101 | }
102 | }
103 |
104 | public String getMsg() {
105 | try {
106 | return messageQueue.take();
107 | } catch (InterruptedException e) {
108 |
109 | }
110 | return null;
111 | }
112 |
113 | public String getAllMagByDisk() {
114 | StringBuilder sb = new StringBuilder("\n");
115 | messageQueue.iterator().forEachRemaining((msg) -> {
116 | sb.append(msg + "\n");
117 | });
118 | return sb.toString();
119 | }
120 | }
121 | ```
122 |
123 | 问题:
124 | 1. 没有实现真正执行消息存储落盘
125 | 2. 没有实现 NameServer 去作为注册中心,定位服务
126 | 3. 使用 LinkedBlockingQueue 作为消息队列,注意,参数是无限大,在真正 RocketMQ 也是如此是无限大,理论上不会出现对进来的数据进行抛弃,但是会有内存泄漏问题(阿里巴巴开发手册也因为这个问题,建议我们使用自制线程池)
127 | 4. 没有使用多个队列(即多个 LinkedBlockingQueue),RocketMQ 的顺序消息是通过生产者和消费者同时使用同一个 MessageQueue 来实现,但是如果我们只有一个 MessageQueue,那我们天然就支持顺序消息
128 | 5. 没有使用 MappedByteBuffer 来实现文件映射从而使消息数据落盘非常的快(实际 RocketMQ 使用的是 FileChannel+DirectBuffer)
129 |
130 | # 2 分布式消息中心
131 |
132 | ## 2.1 问题与解决
133 |
134 | ### 2.1.1 消息丢失的问题
135 |
136 | 1. 当你系统需要保证百分百消息不丢失,你可以使用生产者每发送一个消息,Broker 同步返回一个消息发送成功的反馈消息
137 | 2. 即每发送一个消息,同步落盘后才返回生产者消息发送成功,这样只要生产者得到了消息发送生成的返回,事后除了硬盘损坏,都可以保证不会消息丢失
138 | 3. 但是这同时引入了一个问题,同步落盘怎么才能快?
139 |
140 | ### 2.1.2 同步落盘怎么才能快
141 |
142 | 1. 使用 FileChannel + DirectBuffer 池,使用堆外内存,加快内存拷贝
143 | 2. 使用数据和索引分离,当消息需要写入时,使用 commitlog 文件顺序写,当需要定位某个消息时,查询index 文件来定位,从而减少文件IO随机读写的性能损耗
144 |
145 | ### 2.1.3 消息堆积的问题
146 |
147 | 1. 后台定时任务每隔72小时,删除旧的没有使用过的消息信息
148 | 2. 根据不同的业务实现不同的丢弃任务,具体参考线程池的 AbortPolicy,例如FIFO/LRU等(RocketMQ没有此策略)
149 | 3. 消息定时转移,或者对某些重要的 TAG 型(支付型)消息真正落库
150 |
151 | ### 2.1.4 定时消息的实现
152 |
153 | 1. 实际 RocketMQ 没有实现任意精度的定时消息,它只支持某些特定的时间精度的定时消息
154 | 2. 实现定时消息的原理是:创建特定时间精度的 MessageQueue,例如生产者需要定时1s之后被消费者消费,你只需要将此消息发送到特定的 Topic,例如:MessageQueue-1 表示这个 MessageQueue 里面的消息都会延迟一秒被消费,然后 Broker 会在 1s 后发送到消费者消费此消息,使用 newSingleThreadScheduledExecutor 实现
155 |
156 | ### 2.1.5 顺序消息的实现
157 |
158 | 1. 与定时消息同原理,生产者生产消息时指定特定的 MessageQueue ,消费者消费消息时,消费特定的 MessageQueue,其实单机版的消息中心在一个 MessageQueue 就天然支持了顺序消息
159 | 2. 注意:同一个 MessageQueue 保证里面的消息是顺序消费的前提是:消费者是串行的消费该 MessageQueue,因为就算 MessageQueue 是顺序的,但是当并行消费时,还是会有顺序问题,但是串行消费也同时引入了两个问题:
160 | >1. 引入锁来实现串行
161 | >2. 前一个消费阻塞时后面都会被阻塞
162 |
163 | ### 2.1.6 分布式消息的实现
164 |
165 | 1. 需要前置知识:2PC
166 | 2. RocketMQ4.3 起支持,原理为2PC,即两阶段提交,prepared->commit/rollback
167 | 3. 生产者发送事务消息,假设该事务消息 Topic 为 Topic1-Trans,Broker 得到后首先更改该消息的 Topic 为 Topic1-Prepared,该 Topic1-Prepared 对消费者不可见。然后定时回调生产者的本地事务A执行状态,根据本地事务A执行状态,来是否将该消息修改为 Topic1-Commit 或 Topic1-Rollback,消费者就可以正常找到该事务消息或者不执行等
168 |
169 | >注意,就算是事务消息最后回滚了也不会物理删除,只会逻辑删除该消息
170 |
171 | ### 2.1.7 消息的 push 实现
172 |
173 | 1. 注意,RocketMQ 已经说了自己会有低延迟问题,其中就包括这个消息的 push 延迟问题
174 | 2. 因为这并不是真正的将消息主动的推送到消费者,而是 Broker 定时任务每5s将消息推送到消费者
175 |
176 | ### 2.1.8 消息重复发送的避免
177 |
178 | 1. RocketMQ 会出现消息重复发送的问题,因为在网络延迟的情况下,这种问题不可避免的发生,如果非要实现消息不可重复发送,那基本太难,因为网络环境无法预知,还会使程序复杂度加大,因此默认允许消息重复发送
179 | 2. RocketMQ 让使用者在消费者端去解决该问题,即需要消费者端在消费消息时支持幂等性的去消费消息
180 | 3. 最简单的解决方案是每条消费记录有个消费状态字段,根据这个消费状态字段来是否消费或者使用一个集中式的表,来存储所有消息的消费状态,从而避免重复消费
181 | 4. 具体实现可以查询关于消息幂等消费的解决方案
182 |
183 | ### 2.1.9 广播消费与集群消费
184 |
185 | 1. 消息消费区别:广播消费,订阅该 Topic 的消息者们都会消费**每个**消息。集群消费,订阅该 Topic 的消息者们只会有一个去消费**某个**消息
186 | 2. 消息落盘区别:具体表现在消息消费进度的保存上。广播消费,由于每个消费者都独立的去消费每个消息,因此每个消费者各自保存自己的消息消费进度。而集群消费下,订阅了某个 Topic,而旗下又有多个 MessageQueue,每个消费者都可能会去消费不同的 MessageQueue,因此总体的消费进度保存在 Broker 上集中的管理
187 |
188 | ### 2.1.10 RocketMQ 不使用 ZooKeeper 作为注册中心的原因,以及自制的 NameServer 优缺点?
189 |
190 | 1. ZooKeeper 作为支持顺序一致性的中间件,在某些情况下,它为了满足一致性,会丢失一定时间内的可用性,RocketMQ 需要注册中心只是为了发现组件地址,在某些情况下,RocketMQ 的注册中心可以出现数据不一致性,这同时也是 NameServer 的缺点,因为 NameServer 集群间互不通信,它们之间的注册信息可能会不一致
191 | 2. 另外,当有新的服务器加入时,NameServer 并不会立马通知到 Produer,而是由 Produer 定时去请求 NameServer 获取最新的 Broker/Consumer 信息(这种情况是通过 Producer 发送消息时,负载均衡解决)
192 |
193 | ### 2.1.11 其它
194 |
195 | ![][1]
196 |
197 | 加分项咯
198 | 1. 包括组件通信间使用 Netty 的自定义协议
199 | 2. 消息重试负载均衡策略(具体参考 Dubbo 负载均衡策略)
200 | 3. 消息过滤器(Producer 发送消息到 Broker,Broker 存储消息信息,Consumer 消费时请求 Broker 端从磁盘文件查询消息文件时,在 Broker 端就使用过滤服务器进行过滤)
201 | 4. Broker 同步双写和异步双写中 Master 和 Slave 的交互
202 | 5. Broker 在 4.5.0 版本更新中引入了基于 Raft 协议的多副本选举,之前这是商业版才有的特性 [ISSUE-1046][2]
203 |
204 | # 3 参考
205 |
206 | 1. 《RocketMQ技术内幕》:https://blog.csdn.net/prestigeding/article/details/85233529
207 | 2. 关于 RocketMQ 对 MappedByteBuffer 的一点优化:https://lishoubo.github.io/2017/09/27/MappedByteBuffer%E7%9A%84%E4%B8%80%E7%82%B9%E4%BC%98%E5%8C%96/
208 | 3. 阿里中间件团队博客-十分钟入门RocketMQ:http://jm.taobao.org/2017/01/12/rocketmq-quick-start-in-10-minutes/
209 | 4. 分布式事务的种类以及 RocketMQ 支持的分布式消息:https://www.infoq.cn/article/2018/08/rocketmq-4.3-release
210 | 5. 滴滴出行基于RocketMQ构建企业级消息队列服务的实践:https://yq.aliyun.com/articles/664608
211 | 6. 基于《RocketMQ技术内幕》源码注释:https://github.com/LiWenGu/awesome-rocketmq
212 |
213 | [1]: https://leran2deeplearnjavawebtech.oss-cn-beijing.aliyuncs.com/somephoto/RocketMQ%E6%B5%81%E7%A8%8B.png
214 | [2]: http://rocketmq.apache.org/release_notes/release-notes-4.5.0/
215 |
--------------------------------------------------------------------------------
/docs/系统设计/data-communication/dubbo.md:
--------------------------------------------------------------------------------
1 | 本文是作者根据官方文档以及自己平时的使用情况,对 Dubbo 所做的一个总结。如果不懂 Dubbo 的使用的话,可以参考我的这篇文章[《超详细,新手都能看懂 !使用SpringBoot+Dubbo 搭建一个简单的分布式服务》](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484706&idx=1&sn=d413fc17023482f67ca17cb6756b9ff8&chksm=fd985343caefda555969568fdf4734536e0a1745f9de337d434a7dbd04e893bd2d75f3641aab&token=1902169190&lang=zh_CN#rd)
2 |
3 | Dubbo 官网:http://dubbo.apache.org/zh-cn/index.html
4 |
5 | Dubbo 中文文档: http://dubbo.apache.org/zh-cn/index.html
6 |
7 |
8 |
9 | - [一 重要的概念](#一-重要的概念)
10 | - [1.1 什么是 Dubbo?](#11-什么是-dubbo)
11 | - [1.2 什么是 RPC?RPC原理是什么?](#12-什么是-rpcrpc原理是什么)
12 | - [1.3 为什么要用 Dubbo?](#13-为什么要用-dubbo)
13 | - [1.4 什么是分布式?](#14-什么是分布式)
14 | - [1.5 为什么要分布式?](#15-为什么要分布式)
15 | - [二 Dubbo 的架构](#二-dubbo-的架构)
16 | - [2.1 Dubbo 的架构图解](#21-dubbo-的架构图解)
17 | - [2.2 Dubbo 工作原理](#22-dubbo-工作原理)
18 | - [三 Dubbo 的负载均衡策略](#三-dubbo-的负载均衡策略)
19 | - [3.1 先来解释一下什么是负载均衡](#31-先来解释一下什么是负载均衡)
20 | - [3.2 再来看看 Dubbo 提供的负载均衡策略](#32-再来看看-dubbo-提供的负载均衡策略)
21 | - [3.2.1 Random LoadBalance\(默认,基于权重的随机负载均衡机制\)](#321-random-loadbalance默认基于权重的随机负载均衡机制)
22 | - [3.2.2 RoundRobin LoadBalance\(不推荐,基于权重的轮询负载均衡机制\)](#322-roundrobin-loadbalance不推荐基于权重的轮询负载均衡机制)
23 | - [3.2.3 LeastActive LoadBalance](#323-leastactive-loadbalance)
24 | - [3.2.4 ConsistentHash LoadBalance](#324-consistenthash-loadbalance)
25 | - [3.3 配置方式](#33-配置方式)
26 | - [四 zookeeper宕机与dubbo直连的情况](#四-zookeeper宕机与dubbo直连的情况)
27 |
28 |
29 |
30 |
31 | ## 一 重要的概念
32 |
33 | ### 1.1 什么是 Dubbo?
34 |
35 | Apache Dubbo (incubating) |ˈdʌbəʊ| 是一款高性能、轻量级的开源Java RPC 框架,它提供了三大核心能力:面向接口的远程方法调用,智能容错和负载均衡,以及服务自动注册和发现。简单来说 Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。
36 |
37 | Dubbo 目前已经有接近 23k 的 Star ,Dubbo的Github 地址:[https://github.com/apache/incubator-dubbo](https://github.com/apache/incubator-dubbo) 。 另外,在开源中国举行的2018年度最受欢迎中国开源软件这个活动的评选中,Dubbo 更是凭借其超高人气仅次于 vue.js 和 ECharts 获得第三名的好成绩。
38 |
39 | Dubbo 是由阿里开源,后来加入了 Apache 。正式由于 Dubbo 的出现,才使得越来越多的公司开始使用以及接受分布式架构。
40 |
41 | **我们上面说了 Dubbo 实际上是 RPC 框架,那么什么是 RPC呢?**
42 |
43 | ### 1.2 什么是 RPC?RPC原理是什么?
44 |
45 | **什么是 RPC?**
46 |
47 | RPC(Remote Procedure Call)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。比如两个不同的服务 A、B 部署在两台不同的机器上,那么服务 A 如果想要调用服务 B 中的某个方法该怎么办呢?使用 HTTP请求 当然可以,但是可能会比较慢而且一些优化做的并不好。 RPC 的出现就是为了解决这个问题。
48 |
49 | **RPC原理是什么?**
50 |
51 | 我这里这是简单的提一下。详细内容可以查看下面这篇文章:
52 |
53 | [http://www.importnew.com/22003.html](http://www.importnew.com/22003.html)
54 |
55 | 
56 |
57 |
58 | 1. 服务消费方(client)调用以本地调用方式调用服务;
59 | 2. client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;
60 | 3. client stub找到服务地址,并将消息发送到服务端;
61 | 4. server stub收到消息后进行解码;
62 | 5. server stub根据解码结果调用本地的服务;
63 | 6. 本地服务执行并将结果返回给server stub;
64 | 7. server stub将返回结果打包成消息并发送至消费方;
65 | 8. client stub接收到消息,并进行解码;
66 | 9. 服务消费方得到最终结果。
67 |
68 | 下面再贴一个网上的时序图:
69 |
70 | 
71 |
72 | **说了这么多,我们为什么要用 Dubbo 呢?**
73 |
74 | ### 1.3 为什么要用 Dubbo?
75 |
76 | Dubbo 的诞生和 SOA 分布式架构的流行有着莫大的关系。SOA 面向服务的架构(Service Oriented Architecture),也就是把工程按照业务逻辑拆分成服务层、表现层两个工程。服务层中包含业务逻辑,只需要对外提供服务即可。表现层只需要处理和页面的交互,业务逻辑都是调用服务层的服务来实现。SOA架构中有两个主要角色:服务提供者(Provider)和服务使用者(Consumer)。
77 |
78 | 
79 |
80 | **如果你要开发分布式程序,你也可以直接基于 HTTP 接口进行通信,但是为什么要用 Dubbo呢?**
81 |
82 | 我觉得主要可以从 Dubbo 提供的下面四点特性来说为什么要用 Dubbo:
83 |
84 | 1. **负载均衡**——同一个服务部署在不同的机器时该调用那一台机器上的服务。
85 | 2. **服务调用链路生成**——随着系统的发展,服务越来越多,服务间依赖关系变得错踪复杂,甚至分不清哪个应用要在哪个应用之前启动,架构师都不能完整的描述应用的架构关系。Dubbo 可以为我们解决服务之间互相是如何调用的。
86 | 3. **服务访问压力以及时长统计、资源调度和治理**——基于访问压力实时管理集群容量,提高集群利用率。
87 | 4. **服务降级**——某个服务挂掉之后调用备用服务。
88 |
89 | 另外,Dubbo 除了能够应用在分布式系统中,也可以应用在现在比较火的微服务系统中。不过,由于 Spring Cloud 在微服务中应用更加广泛,所以,我觉得一般我们提 Dubbo 的话,大部分是分布式系统的情况。
90 |
91 | **我们刚刚提到了分布式这个概念,下面再给大家介绍一下什么是分布式?为什么要分布式?**
92 |
93 | ### 1.4 什么是分布式?
94 |
95 | 分布式或者说 SOA 分布式重要的就是面向服务,说简单的分布式就是我们把整个系统拆分成不同的服务然后将这些服务放在不同的服务器上减轻单体服务的压力提高并发量和性能。比如电商系统可以简单地拆分成订单系统、商品系统、登录系统等等,拆分之后的每个服务可以部署在不同的机器上,如果某一个服务的访问量比较大的话也可以将这个服务同时部署在多台机器上。
96 |
97 | ### 1.5 为什么要分布式?
98 |
99 | 从开发角度来讲单体应用的代码都集中在一起,而分布式系统的代码根据业务被拆分。所以,每个团队可以负责一个服务的开发,这样提升了开发效率。另外,代码根据业务拆分之后更加便于维护和扩展。
100 |
101 | 另外,我觉得将系统拆分成分布式之后不光便于系统扩展和维护,更能提高整个系统的性能。你想一想嘛?把整个系统拆分成不同的服务/系统,然后每个服务/系统 单独部署在一台服务器上,是不是很大程度上提高了系统性能呢?
102 |
103 | ## 二 Dubbo 的架构
104 |
105 | ### 2.1 Dubbo 的架构图解
106 |
107 | 
108 |
109 | **上述节点简单说明:**
110 |
111 | - **Provider:** 暴露服务的服务提供方
112 | - **Consumer:** 调用远程服务的服务消费方
113 | - **Registry:** 服务注册与发现的注册中心
114 | - **Monitor:** 统计服务的调用次数和调用时间的监控中心
115 | - **Container:** 服务运行容器
116 |
117 | **调用关系说明:**
118 |
119 | 1. 服务容器负责启动,加载,运行服务提供者。
120 | 2. 服务提供者在启动时,向注册中心注册自己提供的服务。
121 | 3. 服务消费者在启动时,向注册中心订阅自己所需的服务。
122 | 4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
123 | 5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
124 | 6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
125 |
126 | **重要知识点总结:**
127 |
128 | - **注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小**
129 | - **监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示**
130 | - **注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外**
131 | - **注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者**
132 | - **注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表**
133 | - **注册中心和监控中心都是可选的,服务消费者可以直连服务提供者**
134 | - **服务提供者无状态,任意一台宕掉后,不影响使用**
135 | - **服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复**
136 |
137 |
138 |
139 |
140 |
141 | ### 2.2 Dubbo 工作原理
142 |
143 |
144 | 
145 |
146 | 图中从下至上分为十层,各层均为单向依赖,右边的黑色箭头代表层之间的依赖关系,每一层都可以剥离上层被复用,其中,Service 和 Config 层为 API,其它各层均为 SPI。
147 |
148 | **各层说明**:
149 |
150 | - 第一层:**service层**,接口层,给服务提供者和消费者来实现的
151 | - 第二层:**config层**,配置层,主要是对dubbo进行各种配置的
152 | - 第三层:**proxy层**,服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton
153 | - 第四层:**registry层**,服务注册层,负责服务的注册与发现
154 | - 第五层:**cluster层**,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务
155 | - 第六层:**monitor层**,监控层,对rpc接口的调用次数和调用时间进行监控
156 | - 第七层:**protocol层**,远程调用层,封装rpc调用
157 | - 第八层:**exchange层**,信息交换层,封装请求响应模式,同步转异步
158 | - 第九层:**transport层**,网络传输层,抽象mina和netty为统一接口
159 | - 第十层:**serialize层**,数据序列化层,网络传输需要
160 |
161 |
162 | ## 三 Dubbo 的负载均衡策略
163 |
164 | ### 3.1 先来解释一下什么是负载均衡
165 |
166 | **先来个官方的解释。**
167 |
168 | > 维基百科对负载均衡的定义:负载均衡改善了跨多个计算资源(例如计算机,计算机集群,网络链接,中央处理单元或磁盘驱动的的工作负载分布。负载平衡旨在优化资源使用,最大化吞吐量,最小化响应时间,并避免任何单个资源的过载。使用具有负载平衡而不是单个组件的多个组件可以通过冗余提高可靠性和可用性。负载平衡通常涉及专用软件或硬件。
169 |
170 | **上面讲的大家可能不太好理解,再用通俗的话给大家说一下。**
171 |
172 | 比如我们的系统中的某个服务的访问量特别大,我们将这个服务部署在了多台服务器上,当客户端发起请求的时候,多台服务器都可以处理这个请求。那么,如何正确选择处理该请求的服务器就很关键。假如,你就要一台服务器来处理该服务的请求,那该服务部署在多台服务器的意义就不复存在了。负载均衡就是为了避免单个服务器响应同一请求,容易造成服务器宕机、崩溃等问题,我们从负载均衡的这四个字就能明显感受到它的意义。
173 |
174 | ### 3.2 再来看看 Dubbo 提供的负载均衡策略
175 |
176 | 在集群负载均衡时,Dubbo 提供了多种均衡策略,默认为 `random` 随机调用。可以自行扩展负载均衡策略,参见:[负载均衡扩展](https://dubbo.gitbooks.io/dubbo-dev-book/content/impls/load-balance.html)。
177 |
178 | 备注:下面的图片来自于:尚硅谷2018Dubbo 视频。
179 |
180 |
181 | #### 3.2.1 Random LoadBalance(默认,基于权重的随机负载均衡机制)
182 |
183 | - **随机,按权重设置随机概率。**
184 | - 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。
185 |
186 | 
187 |
188 |
189 |
190 | #### 3.2.2 RoundRobin LoadBalance(不推荐,基于权重的轮询负载均衡机制)
191 |
192 | - 轮循,按公约后的权重设置轮循比率。
193 | - 存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
194 |
195 | 
196 |
197 | #### 3.2.3 LeastActive LoadBalance
198 |
199 | - 最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
200 | - 使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
201 |
202 | #### 3.2.4 ConsistentHash LoadBalance
203 |
204 | - **一致性 Hash,相同参数的请求总是发到同一提供者。(如果你需要的不是随机负载均衡,是要一类请求都到一个节点,那就走这个一致性hash策略。)**
205 | - 当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
206 | - 算法参见:http://en.wikipedia.org/wiki/Consistent_hashing
207 | - 缺省只对第一个参数 Hash,如果要修改,请配置 ``
208 | - 缺省用 160 份虚拟节点,如果要修改,请配置 ``
209 |
210 | ### 3.3 配置方式
211 |
212 | **xml 配置方式**
213 |
214 | 服务端服务级别
215 |
216 | ```java
217 |
218 | ```
219 | 客户端服务级别
220 |
221 | ```java
222 |
223 | ```
224 |
225 | 服务端方法级别
226 |
227 | ```java
228 |
229 |
230 |
231 | ```
232 |
233 | 客户端方法级别
234 |
235 | ```java
236 |
237 |
238 |
239 | ```
240 |
241 | **注解配置方式:**
242 |
243 | 消费方基于基于注解的服务级别配置方式:
244 |
245 | ```java
246 | @Reference(loadbalance = "roundrobin")
247 | HelloService helloService;
248 | ```
249 |
250 | ## 四 zookeeper宕机与dubbo直连的情况
251 |
252 | zookeeper宕机与dubbo直连的情况在面试中可能会被经常问到,所以要引起重视。
253 |
254 | 在实际生产中,假如zookeeper注册中心宕掉,一段时间内服务消费方还是能够调用提供方的服务的,实际上它使用的本地缓存进行通讯,这只是dubbo健壮性的一种提现。
255 |
256 | **dubbo的健壮性表现:**
257 |
258 | 1. 监控中心宕掉不影响使用,只是丢失部分采样数据
259 | 2. 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务
260 | 3. 注册中心对等集群,任意一台宕掉后,将自动切换到另一台
261 | 4. 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯
262 | 5. 服务提供者无状态,任意一台宕掉后,不影响使用
263 | 5. 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复
264 |
265 | 我们前面提到过:注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小。所以,我们可以完全可以绕过注册中心——采用 **dubbo 直连** ,即在服务消费方配置服务提供方的位置信息。
266 |
267 |
268 | **xml配置方式:**
269 |
270 | ```xml
271 |
272 | ```
273 |
274 | **注解方式:**
275 |
276 | ```java
277 | @Reference(url = "127.0.0.1:20880")
278 | HelloService helloService;
279 | ```
280 |
281 |
282 |
283 |
--------------------------------------------------------------------------------