├── .gitattributes ├── .gitignore ├── README.md └── doc ├── TopK.md ├── 一文帮你理清面试知识点.md ├── 从春招惨败到秋招收获 BAT OFFER.md ├── 写好技术简历.md ├── 扫二维码登录过程.md ├── 海量数据判重.md └── 海量数据处理思路.md /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !doc/ 4 | !一文帮你理清面试知识点.md 5 | !从春招惨败到秋招收获 BAT OFFER.md 6 | !写好技术简历.md 7 | !海量数据处理思路.md 8 | !海量数据判重.md 9 | !TopK.md 10 | !扫二维码登录过程.md 11 | !README.md -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 后端面试进阶指南 2 | 3 | 📚 内容:1. 学习指导;2. 面试技巧;3. 核心基础知识;4. 大厂进阶知识。 4 | 5 | 👩🏻‍💻 面向人群:主要面向 Java、C++、Python 等后端研发岗位,以及 大数据、移动开发、测开 等和后端研发类似岗位的同学。 6 | 7 | - [一文帮你理清面试知识点](doc/一文帮你理清面试知识点.md) 8 | - [从春招惨败到秋招收获 BAT OFFER](doc/从春招惨败到秋招收获%20BAT%20OFFER.md) 9 | - [写好技术简历](doc/写好技术简历.md) 10 | - [海量数据处理思路](doc/海量数据处理思路.md) 11 | - [海量数据判重](doc/海量数据判重.md) 12 | - [TopK](doc/TopK.md) 13 | - [扫二维码登录过程](doc/扫二维码登录过程.md) 14 | 15 | 订阅 [专栏](https://xiaozhuanlan.com/CyC2018) 以获取全部内容。 16 | -------------------------------------------------------------------------------- /doc/TopK.md: -------------------------------------------------------------------------------- 1 | 2 | # 1. 问题描述 3 | 4 | TopK Elements 问题用于找出一组数中最大的 K 个的数。 5 | 6 | ![](https://diycode.b0.upaiyun.com/photo/2019/e96d0bc52e3a0b38fc5f3976ca78e6b3.png) 7 | 8 | 此外还有一种叫 Kth Element 问题,用于找出一组数中第 K 大的数。 9 | 10 | ![](https://diycode.b0.upaiyun.com/photo/2019/54a40d9e1d1d4e058d2e633de10523eb.png) 11 | 12 | 13 | 其实要求解 TopK Elements,可以先求解 Kth Element,因为找到 Kth Element 之后,再遍历一遍,大于等于 Kth Element 的数都是 TopK Elements。 14 | 15 | # 2. 一般解法 16 | 17 | 以 [Leetcode : 215. Kth Largest Element in an Array](https://leetcode.com/problems/kth-largest-element-in-an-array/description/) 为例,这是一道的 Kth Element 问题,不过这道题要找的是从后往前第 K 个元素,而不是从前往后。为了能和传统的 Kth Element 问题一样求解,可以先执行 `k = nums.length - k;`。 18 | 19 | ``` 20 | Input: [3,2,1,5,6,4] and k = 2 21 | Output: 5 22 | ``` 23 | 24 | ## 2.1 快速选择 25 | 26 | 快速排序的 partition() 方法,对于数组 nums 的 [l, h] 区间,会返回一个整数 k 使得 nums[l..k-1] 小于等于 nums[k],且 nums[k+1..h] 大于等于 nums[k],此时 nums[k] 就是数组的第 k 大元素。可以利用这个特性找出数组的 Kth Element,这种找 Kth Element 的算法称为快速选择算法。 27 | 28 | - 时间复杂度 O(N)、空间复杂度 O(1) 29 | - 只有当允许修改数组元素时才可以使用 30 | 31 | ![](https://diycode.b0.upaiyun.com/photo/2019/9b146e139d04105c8aeda8dce65dcf6f.png) 32 | 33 | ```java 34 | public int findKthElement(int[] nums, int k) { 35 | k = nums.length - k; 36 | int l = 0, h = nums.length - 1; 37 | while (l < h) { 38 | int j = partition(nums, l, h); 39 | if (j == k) { 40 | break; 41 | } else if (j < k) { 42 | l = j + 1; 43 | } else { 44 | h = j - 1; 45 | } 46 | } 47 | return nums[k]; 48 | } 49 | 50 | private int partition(int[] a, int l, int h) { 51 | int i = l, j = h + 1; 52 | while (true) { 53 | while (a[++i] < a[l] && i < h) ; 54 | while (a[--j] > a[l] && j > l) ; 55 | if (i >= j) { 56 | break; 57 | } 58 | swap(a, i, j); 59 | } 60 | swap(a, l, j); 61 | return j; 62 | } 63 | 64 | private void swap(int[] a, int i, int j) { 65 | int t = a[i]; 66 | a[i] = a[j]; 67 | a[j] = t; 68 | } 69 | ``` 70 | 71 | ## 2.2 堆 72 | 73 | 维护一个大小为 K 的最小堆,堆顶元素就是 Kth Element。 74 | 75 | 使用大顶堆来维护最小堆,而不能直接创建一个小顶堆并设置一个大小,企图让小顶堆中的元素都是最小元素。 76 | 77 | 维护一个大小为 K 的最小堆过程如下:在添加一个元素之后,如果大顶堆的大小大于 K,那么需要将大顶堆的堆顶元素去除。 78 | 79 | - 时间复杂度 O(NlogK) 、空间复杂度 O(K) 80 | - 特别适合处理海量数据 81 | 82 | ![](https://diycode.b0.upaiyun.com/photo/2019/0a94de372b0d665417844751e00c24c3.gif) 83 | 84 | ```java 85 | public int findKthLargest(int[] nums, int k) { 86 | k = nums.length - k + 1; 87 | PriorityQueue pq = new PriorityQueue<>(Comparator.reverseOrder()); // 大顶堆 88 | for (int val : nums) { 89 | pq.add(val); 90 | if (pq.size() > k) // 维护堆的大小为 K 91 | pq.poll(); 92 | } 93 | return pq.peek(); 94 | } 95 | ``` 96 | 97 | # 3. 海量数据 98 | 99 | 在这种场景下,单机通常不能存放下所有数据。 100 | 101 | - 拆分,可以按照哈希取模方式拆分到多台机器上,并在每个机器上维护最大堆; 102 | - 整合,将每台机器得到的最大堆合并成最终的最大堆。 103 | 104 | # 4. 频率统计 105 | 106 | Heavy Hitters 问题要求找出一个数据流的最频繁出现的 K 个数,比如热门搜索词汇等。 107 | 108 | ## 4.1 HashMap 109 | 110 | 使用 HashMap 进行频率统计,然后使用快速选择或者堆的方式找出频率 TopK。在海量数据场景下,也是使用先拆分再整合的方式来解决空间问题。 111 | 112 | ## 4.2 Count-Min Sketch 113 | 114 | 维护 d*w 大小的二维统计数组,其中 d 是哈希函数的个数,w 根据情况而定。 115 | 116 | - 在一个数到来时,计算 d 个哈希值,然后分别将哈希值对 w 取模,把对应统计数组上的值加 1; 117 | - 要得到一个数的频率,也是要计算 d 个哈希值并取模,得到 d 个频率,取其中最小值。 118 | 119 | 该算法的思想和布隆过滤器类似,具有一定的误差,特别是当 w 很小时。但是它能够在单机环境下解决海量数据的频率统计问题。 120 | 121 | ![](https://diycode.b0.upaiyun.com/photo/2019/1e6f5d5dc2cf7620e3cf0f1229a49b50.png) 122 | 123 | ```java 124 | public class CountMinSketch { 125 | 126 | private int d; 127 | private int w; 128 | 129 | private long estimators[][]; 130 | 131 | public CountMinSketch(int d, int w) { 132 | this.d = d; 133 | this.w = w; 134 | } 135 | 136 | public void add(int value) { 137 | for (int i = 0; i < d; i++) 138 | estimators[i][hash(value, i)]++; 139 | } 140 | 141 | public long estimateFrequency(int value) { 142 | long minimum = Integer.MAX_VALUE; 143 | for (int i = 0; i < d; i++) { 144 | minimum = Math.min(minimum, estimators[i][hash(value, i)]); 145 | } 146 | return minimum; 147 | } 148 | 149 | private int hash(int value, int i) { 150 | return 0; // use ith hash function 151 | } 152 | } 153 | ``` 154 | 155 | ## 4.3 Trie 156 | 157 | Trie 树可以用于解决词频统计问题,只要在词汇对应节点保存出现的频率。它很好地适应海量数据场景,因为 Trie 树通常不高,需要的空间不会很大。 158 | 159 | ![](https://diycode.b0.upaiyun.com/photo/2019/e50b2538722fa4dde6f91b7ece4db0e7.png) 160 | 161 | # 参考资料 162 | 163 | - [Probabilistic Data Structures for Web Analytics and Data Mining](https://dirtysalt.github.io/html/probabilistic-data-structures-for-web-analytics-and-data-mining.html) 164 | - [Trie](https://zh.wikipedia.org/wiki/Trie) 165 | 166 | # 原文链接 167 | 168 | https://xiaozhuanlan.com/topic/4176082593 -------------------------------------------------------------------------------- /doc/一文帮你理清面试知识点.md: -------------------------------------------------------------------------------- 1 | ⭐️ 2 | 3 | # 1. 前言 4 | 5 | 面试考察的知识点多而杂,要完全掌握需要花费大量的时间和精力。但是面试中经常被问到的知识点却没有多少,你完全可以用 20% 的时间去掌握 80% 常问的知识点。在这里我将这 80% 常问的知识点整理出来,方便大家快速地掌握。这些知识点也标注了重要程度,从而让大家可以根据知识点的重要程度去制定学习计划。 6 | 7 | 如果你想更好的学习这些知识点,可以参考 [知识总结方法](https://xiaozhuanlan.com/topic/4150387926) 一文,这篇文章的最后给出了比本文更详细的思维导图。 8 | 9 | 下图列出了面试考察的九大知识点,也标出了重要程度和复习难度。 10 | 11 | ![](https://diycode.b0.upaiyun.com/photo/2019/75b8075dd70164b907bf52ae26c80cce.png) 12 | 13 | 其实重要程度根据不同的公司会有所不同,针对上图做一下几点说明: 14 | 15 | - 分布式与系统设计部分对于大厂面试来说至关重要,但是小厂面试考察的会少一点。 16 | - 语言基础部分,阿里喜欢问 Java、腾讯喜欢问 C++,其它大厂面试考察会少一些,小厂面试会考察多一些。 17 | - 数据库、中间件和框架部分对于大厂面试来说不是那么重要,但是对于小厂面试会重要一些。 18 | - 项目部分在实习招聘阶段中不是特别重要,但是在校园招聘阶段重要程度会增高。 19 | 20 |
💻 21 | 22 | # 2. 操作系统 23 | 24 | ## 2.1 基础 25 | 26 | - ★★★ 进程与线程的本质区别、以及各自的使用场景。 27 | - ★☆☆ 进程状态。 28 | - ★★★ 进程调度算法的特点以及使用场景。 29 | - ★☆☆ 线程实现的方式。 30 | - ★★☆ 协程的作用。 31 | - ★★☆ 常见进程同步问题。 32 | - ★★★ 进程通信方法的特点以及使用场景。 33 | - ★★★ 死锁必要条件、解决死锁策略,能写出和分析死锁的代码,能说明在数据库管理系统或者 Java 中如何解决死锁。 34 | - ★★★ 虚拟内存的作用,分页系统实现虚拟内存原理。 35 | - ★★★ 页面置换算法的原理,特别是 LRU 的实现原理,最好能手写,再说明它在 Redis 等作为缓存置换算法。 36 | - ★★★ 比较分页与分段的区别。 37 | - ★★★ 分析静态链接的不足,以及动态链接的特点。 38 | 39 | ## 2.2 Linux 40 | 41 | - ★★☆ 文件系统的原理,特别是 inode 和 block。数据恢复原理。 42 | - ★★★ 硬链接与软链接的区别。 43 | - ★★☆ 能够使用常用的命令,比如 cat 文件内容查看、find 搜索文件,以及 cut、sort 等管线命令。了解 grep 和 awk 的作用。 44 | - ★★★ 僵尸进程与孤儿进程的区别,从 SIGCHLD 分析产生僵尸进程的原因。 45 | 46 |
☁️ 47 | 48 | # 3. 网络 49 | 50 | ## 3.1 基础 51 | 52 | - ★★★ 各层协议的作用,以及 TCP/IP 协议的特点。 53 | - ★★☆ 以太网的特点,以及帧结构。 54 | - ★★☆ 集线器、交换机、路由器的作用,以及所属的网络层。 55 | - ★★☆ IP 数据数据报常见字段的作用。 56 | - ★☆☆ ARP 协议的作用,以及维护 ARP 缓存的过程。 57 | - ★★☆ ICMP 报文种类以及作用;和 IP 数据报的关系;Ping 和 Traceroute 的具体原理。 58 | - ★★★ UDP 与 TCP 比较,分析上层协议应该使用 UDP 还是 TCP。 59 | - ★★★ 理解三次握手以及四次挥手具体过程,三次握手的原因、四次挥手原因、TIME_WAIT 的作用。 60 | - ★★★ 可靠传输原理,并设计可靠 UDP 协议。 61 | - ★★☆ TCP 拥塞控制的作用,理解具体原理。 62 | - ★★☆ DNS 的端口号;TCP 还是 UDP;作为缓存、负载均衡。 63 | 64 | ## 3.2 HTTP 65 | 66 | - ★★★ GET 与 POST 比较:作用、参数、安全性、幂等性、可缓存。 67 | - ★★☆ HTTP 状态码。 68 | - ★★★ Cookie 作用、安全性问题、和 Session 的比较。 69 | - ★★☆ 缓存 的Cache-Control 字段,特别是 Expires 和 max-age 的区别。ETag 验证原理。 70 | - ★★★ 长连接与短连接原理以及使用场景,流水线。 71 | - ★★★ HTTP 存在的安全性问题,以及 HTTPs 的加密、认证和完整性保护作用。 72 | - ★★☆ HTTP/1.x 的缺陷,以及 HTTP/2 的特点。 73 | - ★★★ HTTP/1.1 的特性。 74 | - ★★☆ HTTP 与 FTP 的比较。 75 | 76 | ## 3.3 Socket 77 | 78 | - ★★☆ 五种 IO 模型的特点以及比较。 79 | - ★★★ select、poll、epoll 的原理、比较、以及使用场景;epoll 的水平触发与边缘触发。 80 | 81 |
💾 82 | 83 | # 4. 数据库 84 | 85 | ## 4.1 SQL 86 | 87 | - ★★☆ 手写 SQL 语句,特别是连接查询与分组查询。 88 | - ★★☆ 连接查询与子查询的比较。 89 | - ★★☆ drop、delete、truncate 比较。 90 | - ★★☆ 视图的作用,以及何时能更新视图。 91 | - ★☆☆ 理解存储过程、触发器等作用。 92 | 93 | ## 4.2 系统原理 94 | 95 | - ★★★ ACID 的作用以及实现原理。 96 | - ★★★ 四大隔离级别,以及不可重复读和幻影读的出现原因。 97 | - ★★☆ 封锁的类型以及粒度,两段锁协议,隐式和显示锁定。 98 | - ★★★ 乐观锁与悲观锁。 99 | - ★★★ MVCC 原理,当前读以及快照读,Next-Key Locks 解决幻影读。 100 | - ★★☆ 范式理论。 101 | - ★★★ SQL 与 NoSQL 的比较。 102 | 103 | ## 4.3 MySQL 104 | 105 | - ★★★ B+ Tree 原理,与其它查找树的比较。 106 | - ★★★ MySQL 索引以及优化。 107 | - ★★★ 查询优化。 108 | - ★★★ InnoDB 与 MyISAM 比较。 109 | - ★★☆ 水平切分与垂直切分。 110 | - ★★☆ 主从复制原理、作用、实现。 111 | - ★☆☆ redo、undo、binlog 日志的作用。 112 | 113 | ## 4.4 Redis 114 | 115 | - ★★☆ 字典和跳跃表原理分析。 116 | - ★★★ 使用场景。 117 | - ★★★ 与 Memchached 的比较。 118 | - ★☆☆ 数据淘汰机制。 119 | - ★★☆ RDB 和 AOF 持久化机制。 120 | - ★★☆ 事件驱动模型。 121 | - ★☆☆ 主从复制原理。 122 | - ★★★ 集群与分布式。 123 | - ★★☆ 事务原理。 124 | - ★★★ 线程安全问题。 125 | 126 |
🎨 127 | 128 | # 5. 面向对象 129 | 130 | ## 5.1 思想 131 | 132 | - ★★★ 面向对象三大特性 133 | - ★☆☆ 设计原则 134 | 135 | ## 5.2 设计模式 136 | 137 | - ★★☆ 设计模式的作用。 138 | - ★★★ 手写单例模式,特别是双重检验锁以及静态内部类。 139 | - ★★★ 手写工厂模式。 140 | - ★★★ 理解 MVC,结合 SpringMVC 回答。 141 | - ★★★ 理解代理模式,结合 Spring 中的 AOP 回答。 142 | - ★★★ 分析 JDK 中常用的设计模式,例如装饰者模式、适配器模式、迭代器模式等。 143 | 144 | 145 | # 原文链接 146 | 147 | https://xiaozhuanlan.com/topic/3057621498 148 | 149 | -------------------------------------------------------------------------------- /doc/从春招惨败到秋招收获 BAT OFFER.md: -------------------------------------------------------------------------------- 1 | 🚀 2 | 3 | # 1. Offer 情况 4 | 5 | 经过了长达一年左右的复习,秋招也收到了几个比较满意的 Offer,参加面试的都通过了。 6 | 7 | - 百度,企业智能平台; 8 | - 阿里,高德地图; 9 | - 腾讯,IEG 游戏平台,后台研发,SP; 10 | - 字节跳动,头条后台研发,SSP; 11 | - 华为,Cloud Bu; 12 | - 网易游戏,梦幻事业部; 13 | - 顺丰科技。 14 | 15 | 🚀 16 | 17 | # 2. 前期准备 18 | 19 | 也是在去年十一月份左右,看着身边两年制的同学经历了长时间而又艰难的秋招,我开始意识到自己应该提前准备了,否则自己的秋招会很惨。 20 | 21 | 本科的时候,虽然学过计算机网络、操作系统和数据结构等课程,而且 Leetcode 也刷了一两百题,但是离招聘要求还差的很远,学的都很浅只够应付考试,也没有实际的项目经验。 22 | 23 | 我的研究生方向是计算机图形学,研究生期间主要做一些科研项目。在选择招聘方向的时候,我也纠结了是不是找图形学相关方向的,但是考虑到图形学的选择不是很多,所以还是决定投后台研发相关的岗位。 24 | 25 | 于是开始收集各种学习资料,也买了很多纸质书。最开始的学习效率并不是很高,很迷茫,觉得要学的内容很多无从下手。那时候看别人的面经,感觉自己太弱了,很多内容都没接触过,于是更加迷茫。迷茫的时候总想着逃避,要是不复习多好,玩玩游戏每天多简单。但是游戏玩的越多,那种焦虑感越是强烈。解决焦虑的唯一办法就是想办法解决当前问题。当慢慢地从消极的学习态度中调整过来,掌握的知识越多,那种焦虑感也随之消失。当然这个过程并不容易,不仅需要很好的毅力,也要根据自身情况找到问题的有效解决方法。 26 | 27 | 🚀 28 | 29 | # 3. 春招 30 | 31 | ## 3.1 春招开始 32 | 33 | 三月份各个公司就开始春招了,那时候刚把一些基础知识简单地复习了一下,Leetcode 刷到了三四百题。但是没有后台研发相关的项目,于是花了一个星期左右用 PHP 做了一个微博系统。当时做简历特别痛苦,没内容可以写,看着其他人简历各种新技术,自己都没掌握,所以很虚。 34 | 35 | ## 3.2 阿里一轮游 36 | 37 | 最开始投的阿里,实验室大几届有个师兄在天猫精灵团队,所以给我内推了。于是我人生中第一场面试就是阿里,很自然地被虐了一遍。记得当时约好下午两点电话面试,午饭都没吃,怕吃完之后犯困影响状态,然后找了一个很安静又没人的地方呆到了两点,调整自己的状态。可是面试官突然打电话来说有个会议要开,所以推迟了大概一个小时。苦苦等到三点左右,面试正式开始,不出所料面得非常糟糕。首先自己表述的很有问题,很多内容没回答到关键点上,自己会的内容也不怎么继续扩展回答。其次知识掌握得确实不够,连线程安全、ThreadLocal、函数式编程都不会。虽然被虐的很惨,但是也有好处,知道了面试到底是怎样的,自己还有哪方面的不足,该怎么准备。 38 | 39 | ## 3.3 腾讯被鞭尸 40 | 41 | 第二场面试是腾讯,在经历了阿里的面试之后,并且又继续复习了一段时间,我对面试就比较有信心了。一面其实回答的挺理想的,虽然很多问题没有立马回答出来,但是经过面试官的耐心提示之后都能回答一些内容。当时面了一个半小时,面试体验特别好。印象比较深刻的题目有,阅读一个 Redis 源码,分析存在哪些问题。其实就是一个计数器实现的限流算法,会有临界值的问题,但是当时没回答出来,只能听面试官给我解释。还有一个微信扫二维码,这个过程发生了什么,也没回答得很好,不过面试官也很耐心地纠正我回答上的错误。一面顺利通过了,但是总监面挂了。总监面没有问什么技术问题,就是问了问项目和职业规划。自己的项目确实比较 Low,我自己在介绍的时候也说得很不堪。职业规划我说自己希望在一些方面深入学习,因为自己现在在这些方面还很薄弱... 面完之后我就知道挂了,因为整个面试过程我都特别虚,还主动说自己技术能力不行。不出所料,面完的当天晚上,状态变成了不合适。 42 | 43 | 但是过了几天,突然收到腾讯的电话,问我是否愿意去深圳参加面试(笔者学校在广州)。当然我毫不犹豫地答应了,很开心腾讯还能给我机会。经过了上一场面试的启示,这次面试我表现地非常自信,自己知道的知识都很有信心地表达出来,被问到不会的内容也不会那么慌张,和面试官探讨一些细节,然后说说自己的想法,还有自己看过相关的内容。由于这是腾讯云部门,对 Linux 内核和 C++ 有很高的要求,问了几个相关的问题我都没回答出来,比如如何实现守护进程,Linux 信号机制,Linux 线程的不可中断阻塞状态如何进入等等。除了这些问题,其它地回答的都还行。遗憾的是,当天晚上面试官打电话告知我面试没通过。但是他说我其它方面都很不错,所以问我愿不愿意参加腾讯云 Java 部门的招聘,于是第二天我又去了一个新的部门面试。 44 | 45 | 这次面试是在部门的会议室进行的,进到公司之后说实话没有自己想象中那么好,工位很挤环境一般。一开始就先随便聊聊,学校的研究工作,学习之类的。然后看了看项目,看完之后我就知道凉了一半,这个项目确实太水了,面试官看了之后没有接着问,也能感受到面试官有点嫌弃。然后他就问了一些基础知识,问到进程调度算法,面试官让我实现一个任务调度系统。因为是第一次手写代码,而且之前确实没考虑过这个问题,然后就胡乱写了一堆代码,特别乱,而且到处涂改。显然面试官是不满意的,写了也有十几分钟之后,我自己都知道已经凉了,然后面试官没让我接着写,也没给我任何提示,说就到这里,面试结束了,还有没有什么问题想问的。当然看过任务调度系统相关的文章会觉得挺容易的,比如使用时间轮实现等等。我依然记得面试官送我出门时候的热情,送我坐电梯的时候还很热情地和我说,非常感谢参加本次面试,辛苦了。 46 | 47 | ## 3.4 虎牙过于自信 48 | 49 | 经过了阿里和腾讯的面试之后,我觉得自己大概已经知道该怎么面试了,面试时候该注意什么,该怎么表达等等。而且腾讯面试表现也不差,虽然最后没通过。所以在虎牙面试的时候特别放松,觉得应该能通过。前面面的也都还行,虽然有几个问题没回答好,比如分析一下微博的时间线。通过了第一轮面试直接等第二轮,等到了晚上七点多才等到我。虎牙面试还是很注重技术的,虽然问的都不是很深入,只要简单回答到点上就不会接着问下去。二面也有一些问题没回答好,比如 ConcurrentHashMap 的并发机制,问 Spring 直接说不会。也有一些问题回答得比较乱,没有条理。但是我觉得大部分问题都回答的不错,应该能通过。可是面试完之后,面试官问有没有什么问题要问他,由于太过放松,我就问你们都加班到这么晚不吃饭吗,好饿啊,周六周日还加班吗... 问完之后面试官就很严肃了,说平常不加班的,我突然意识到了问题的严重性... 最后还是凉了。 50 | 51 | ## 3.5 百度第一个 Offer 52 | 53 | 被三家连续拒了之后,都开始怀疑自己了,不过还是提醒自己要保持信心。 54 | 55 | 幸运的是,百度的面试非常适合我,三轮都是技术面,而且手写算法题目居多,而我准备最多的是算法,所以很顺利通过了面试。但是面试表现并没有特别好,过了比较长的时间才被捞,而且是工程效率部门,做内部工具的,对个人成长并不好,所以不是特别满意。 56 | 57 | ## 3.6 网易游戏最好的面试体验 58 | 59 | 其实最开始没有打算投网易游戏的,因为被脉脉洗脑,已经放弃了做游戏。但是因为前面面试基本被拒了,担心没有实习 Offer,因此就试试看。 60 | 61 | 因为没有特别想去网易游戏,所以面试过程也比较放松,就当去聊聊天。面试官非常 nice,那天下午挤了很久地铁,比较口渴,然后面试官看我说得沙哑了,到门口帮我买了一瓶可乐,非常感激。面试之前我就提出我对 C++ 不熟悉,最近主要看 Java 的内容。面试官还是说没关系,尽量回答就好。当然最后我都把问题往 Java 那里回答了,比如 Map 的实现,内存管理等等。最后聊了一些玩过的游戏,就让我回去等消息。网易游戏就一轮面试,确实就一轮。周五参加的面试,下周一就给 Offer 了,效率特别高。 62 | 63 | ## 3.7 微众玄学面试 64 | 65 | 通过微众面试我自己都非常吃惊,一面的时候就简单自我介绍了一下,然后面试官开始介绍他自己的工作经历,以及现在部门在做的内容。之后问了我一个场景分析问题,我想了一会儿没想出来,于是面试官拿起草稿纸把各种需求详细说了一遍,然后把系统架构图也画了出来... 最后他问还有什么我优势的地方他没问到的,我问他怎么不问问算法题,他说笔试都通过了没必要再问。面完之后我觉得聊得很开心,但是技术问题没回答好,出乎意料收到了二面通知。二面没问技术,就让介绍了项目,再问问家住哪之类的问题,也顺利通过了。HR 面就不用介绍。收到了微众的 Offer,得知了部门是贷款科技部,非常核心,很吃香,近几年也在扩展一些业务,还是有点小心动的。虽然最后没选择去微众实习,但是一面面试官加了我微信,我很感谢他一面非常耐心给我讲解,并让我通过。他说我是他面试的第一顺位,也就是第一个面试者,所以会放宽很多,也希望我秋招能加入他们。 66 | 67 | ## 3.8 实习选择 68 | 69 | 其实最理想的是去百度实习,秋招也会容易很多。但是考虑到百度是在北京,部门很边缘,而且需要实习很长时间也不一定能转正,所以还是放弃了。最后只能在网易游戏和微众选,虽然自己不想做游戏,但是考虑到网易游戏的平台认可程度比微众好,秋招肯定会更容易一些。而且秋招如果还想进微众的话也会比较容易,因为面试官和 HR 都说秋招的时候会优先考虑我,所以最后还是去了网易游戏实习。 70 | 71 | ## 3.9 实习之前的快速学习期 72 | 73 | 经历了春招之后,认识到了自己身上的不足,比如交流表达能力的欠缺,知识积累得不够,项目深度不够。因此在实习之前的两三个月,开始针对这些问题逐个解决。 74 | 75 | - 交流表达能力欠缺,就提前准备好各种非技术问题,然后对着镜子回答,把自己当成听众,并且也用录音机录下来。 76 | - 知识积累不够,采取的策略是保证广度优先,并且在重要的内容上保证深度。其实之前基础知识已经掌握的比较好了,再学其它技术的时候都有很多相同的地方,所以学起来很快。 77 | - 项目深度不够,就把那个微博系统做了一点改进,学了 Spring 之后改用 Java 实现。 78 | 79 | ## 3.10 不那么安心的实习 80 | 81 | 去实习的时候还是挺惊喜的,因为我被安排的工作是游戏引擎相关的,和自己的研究生方向紧密相关,我觉得做完实习项目之后自己的毕业论文也会比较有灵感。 82 | 83 | 但不幸的是,在去的第一天部门接待聚餐上,服务端主程就说,我们部门工作制是九九六,现在互联网都是九九六。在实习之前我了解的是实习生六点就可以走,而且只用上五天班,听到他这么一说心都凉透了,因为已经想好了晚上和周末时间用来复习。如果知道是九九六,我会选择去百度。 84 | 85 | 其实网易游戏部门氛围还是不错的,对员工很好,而且我的实习导师人也很好,在我生病的那几天很关心我。但是九九六的工作制对秋招复习还是有很大影响的,而且每天上下班花在路上的时间超过了两个小时,下班回寝室之后总想着看会儿视频休息一下,然后又要早早睡觉赶着第二天上班。没办法只能在上下班地铁上复习,还有就是午休时间接着复习。 86 | 87 | 🚀 88 | 89 | # 4. 秋招 90 | 91 | ## 4.1 秋招开始 92 | 93 | 实习之后已经是九月份了,那时候已经错过了所有提前批。而且实习的时候没怎么复习,九月初还是感觉没怎么准备充分,所以就又等了半个月才开始投简历。 94 | 95 | 但是这个时候和春招相比,已经把大部分后台研发相关的知识点过了一遍,很多重要的内容前前后后也看了十几遍,没有春招时候那么迷茫和焦虑。即使被问到没有掌握的知识,我也有把握通过讨论的方式,给出大概的思路,因为很多技术确实是相通的。 96 | 97 | ## 4.2 阿里看不懂的内部流程 98 | 99 | 秋招第一个投递的依然是阿里,最开始系统自动发起了一个新的流程,然后过了几天自动回绝了... 八月末的时候也找人内推了,但是又被阿里直接回绝了... 那时候已经觉得可能是春招面试表现太差,此生无缘阿里了。可是过了一段时间,正式校招的时候,阿里又发起了一个新的流程戏弄我,收到笔试通知的时候,我还犹豫了到底参不参加,因为那时候已经九月中旬,听说阿里已经没有 HC 了。而且按前面回绝我的态度,感觉即使笔试通过面试也通过不了。笔试那天晚上,本来准备看个电影放松一下,后来想了想还是参加了笔试,笔试各种机器学习和数学题,感觉拿错了试卷,笔试完我已经把阿里从我的公司进度列表中删除了,不再纠结阿里。可是过了一段时间收到阿里的面试通知,我以为是走走形式,可能参加笔试的人很少了,所以才选中我参加面试。那时候阿里招聘官网状态一排的已回绝,让我对阿里有一种恐惧感,觉得面试肯定挂。但是真正面试的时候却意外的顺利,收到二面通知的时候特别激动,然后面完二面又让直接等 HR 面,HR 面虽然不是很理想,但是没有很大的问题。又过了很长一段时间,在我去深圳参加腾讯招聘的高铁上,收到了高德地图 HR 的电话,问是否愿意去。虽然得知部门在北京有点小失落,但是还是很开心终于被阿里认可了,摆脱了对阿里的恐惧。 100 | 101 | 实验室上届毕业在阿里云的大佬某天突然和我说,他们部门有新的 HC,让我把简历发给他,他要帮我内推,会帮我安排一场线下面试,如果通过的话,到时候和高德的 HR 沟通一下,直接把我从高德捞过来。很感谢大佬向他老大极力推荐我,给我了这次面试机会。线下面试也很顺利,聊聊实习项目,问问我的开源博客,然后问些 Paxos 等分布式的问题,还有就是手写代码,信号量实现生产者消费者,以及一个位运算的问题。其实位运算的问题面试的时候写的不完善,面试官让我之后完善了再发给他,因为面试一个多小时有点长了。过后我写了详细文档讲解了思路,以及使用 JUnit 进行了详细的单元测试,把文档和代码都发给了他。现在面试已经通过了,但是最近阿里集团 HC 比较紧张,也不知道能不能批到 HC。 102 | 103 | ## 4.3 百度又是不那么满意的部门 104 | 105 | 虽然阿里是最先开始流程,但是第一个参加面试的是百度。因为实习的时候通过了百度的面试,所以这次面试还是比较有信心的。百度面试连续三天,都在同一个地方,最后签约也在同一个地方。还记得每次都坐一个小时左右的地铁去那里,路线已经非常熟悉了,和每天去实习的感觉类似。百度面试比较注重技术,三轮面试基本都是问技术问题,而且问的也比较深入,内容也非常广。但是面的不是那么理想,有两点原因,首先是因为确实有些知识点还没掌握好,比如 AC 自动机,系统故障分析等等;其次是对实习项目的描述上还不够好,没有把实习内容的闪光点描述出来,也没有讲清楚为什么做这个项目,自己通过什么方法去做,以及最后的结果。 106 | 107 | 最后百度给了白菜价,部门是企业智能平台,主要是内部系统,虽然会接触到机器学习和大数据。 108 | 109 | ## 4.4 腾讯虐我千百遍 110 | 111 | 秋招腾讯第一场面试和实习参加腾讯面试的感觉非常像,第一轮技术面感觉很好,手写堆排序算法,二部图分析等等。面完之后通知待会儿二面,听到之后还是很激动的,觉得这次应该没问题了。我在等二面的时候,碰到了室友(他经常不住宿舍,所以不清楚他也去面试),聊着聊着居然发现我两是同一个面试官,而且他是来二面的,也就是等一下我两就要一前一后进去面试。二面的感觉和实习二面非常像,非技术问题回答的支支吾吾,然后面试官开始质疑我说的内容,给我压力,我没有当场反驳,就说了哦,好像是这样的。因为面试官全程都绷着脸,所以我也比较紧张,很多问题没回答好。过了几天,室友和我说收到 HR 面试通知了,我去官网看了一下状态,已经变成了熟悉的不合适。这次面试失败的主要原因是自己在应对这种压力时处理地不是很好,主要体现在失去信心以及紧张。解决方法也简单,做好充分准备来保持信心,受到质疑的时候积极反驳,紧张的时候计时调整心态,可以试试深呼吸或者喝水。 112 | 113 | 因为实习有被捞起来的经历,所以被拒之后我特别希望能继续被捞起来,然后把简历上的面试城市改成了深圳。苦苦等到深圳场面试的前几天,在不经意的一个下午手机突然响了,我记得是短信邮件同时收到面试通知。于是又开始了新一轮被腾讯虐的面试之旅。 114 | 115 | 一面和之前一样也是意外地顺利,虽然问了一些 C++ 的问题,但是我都说到 Java 相关的实现上。在一些问题上确实回答的深度不够,比如网络编程里面的水平触发和边缘触发等问题。然后问了几个算法,本来要求手写,我说我实现过,所以就讲了讲思路。面试和腾讯第一场面试一样持续了一个半小时,面试官也很好,很多问题都会给提示,即使最开始回答的有问题。二面面试官也很好,问了问实习项目,然后再聊一聊一些技术,经过了之前的面试,到这次面试真的就像在聊天一样而不是面试,我们都会说一些对技术上的理解。HR 面其实面得很差,对于非技术问题的吹水能力我还是不太行。最终和我预期的一样,给了我 SP 的 Offer,因为觉得自己面得还可以,但是也不够好到给 SSP,有些 C++ 问题还是没回答的特别好。 116 | 117 | ## 4.5 头条意外的惊喜 118 | 119 | 之前看到学弟收到头条的 Offer,薪资非常诱人,所以也想去试试。也听说头条面试难度非常大,主要考察手写算法,因为自己算法方面准备得比较充分,所以觉得会比较顺利,但是也没有特别高的预期。前两面中规中矩,算法题和其它问题我都回答的比较好,到三面的时候,问了一个错排问题,其实最开始我给了正确的递推公式,但不是面试官想要的答案,所以让我再想想。我想了十几分钟还是觉得没问题,那时候觉得自己已经凉了,因为面试官一直不满意。后面的几个问题也没回答的很好,分析一个 SQL 语句的具体执行过程,比如会怎么利用索引,怎么优化之类的,虽然在他的提示下还是回答了,但是感觉并不好。面完之后我立马查了一下那个错排问题,证实了我的答案是正确的,于是写了一个详细的文档,联系 HR 让她发给面试官。出乎意料的是,HR 让我不用担心,他说面试官对我的评价很好... 不过最后还是让她把文档发给了面试官。之后收到了加面通知,头条加面有两种情况,一是三轮评级都是 4 可以评 SSP,二是面试官评价差别很大,再面一轮决定是否录用。收到加面的时候完全不知道自己属于哪一种,感觉两种情况都有可能。加面回答的也不好,主要是问项目,面了 25 分钟就草草结束,最后面试官说有些内容需要找一些文献参考参考。面完之后我觉得,即使我属于第一种要评 SSP 的情况,加面面的那么差应该也没希望了。苦苦等了好多天之后,最后确定是 SSP 之后,还是很惊喜的,感觉是对自己这么长时间复习的一个认可。 120 | 121 | ## 4.6 顺丰最后的保底 122 | 123 | 投顺丰是因为九月中旬很多公司都结束了招聘,所以那时候比较慌,就投了顺丰当做保底,顺便也练练手。最开始还担心顺丰笔试没通过,因为编程题最后一题没做出来,那题的题目都出错了,而且题目是网上直接 copy 过来的,网上的源码都不能通过,更别说我自己的实现了。顺丰面试主要问了数据库的内容,而且问的特别深,几乎把每种日志的实现和作用都问了一遍。面顺丰的时候也比较早,那时候有些问题的回答上没有组织好,回答得比较凌乱,虽然最后也算给了一个小 SP。 124 | 125 | ## 4.7 华为特别纠结的部门 126 | 127 | 去华为面试确实是没有压力的,因为都知道华为面试不怎么问技术,虽然还是问了我一些技术问题,不过不是问的很深。面试主要介绍项目,我对自己的实习项目还是比较有信心的,因为觉得做的确实不错,而且面了很多场了,知道该怎么介绍项目。面试官问我个人意愿,我说自己对分布式中间件等比较感兴趣,于是面试官把我推荐到了 Cloud Bu。本来没打算签华为的,现场签约也就去看看到底给我开多少。最开始其实给我开了十四级最高的薪资,我本来不是很想去,虽然对这个部门感兴趣,但是薪资确实比不上头条。然后随口问了一句可不可以给十五级,本来 HR 说是可以试着申请一下,不过最后没申请成功。 128 | 129 | 🚀 130 | 131 | # 5. 小结 132 | 133 | 很多人都说,面试和考试一样,要背很多没用的东西。最开始我也认同这种看法,可是参加了几场面试之后,我就不这么认为了。因为面试出的问题,有很多是实际开发中碰到的,所以准备面试相当于提前做入职准备。而且面试中考察的思维能力、交流表达能力、应对压力能力,都是真正工作中所需要的。 134 | 135 | 我觉得自己比别人做的好的地方是,有很强烈的想找到好工作的意愿,才驱使我不断学习,所以态度很重要。 136 | 137 | 信心源自于充分准备,有了信心,面试的时候才能游刃有余。而毫无依据的自我感觉良好,在每次失败之后都看不到自身的不足,而是怪罪于外界因素。 138 | 139 | 做好自己的简历,我在简历上花了很长时间,只要允许,我都会用这个简历给面试官演示:[个人简历](https://cyc2018.github.io)。 140 | 141 | # 原文链接 142 | 143 | https://xiaozhuanlan.com/topic/4235107986 -------------------------------------------------------------------------------- /doc/写好技术简历.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ✍🏻 4 | 5 | # 1. 重要性 6 | 7 | - 各大公司每年都要接收成千上万份简历,但是考虑到招聘成本,简历要先经过系统和 HR 筛选,简历筛选决定了你是否有笔试和面试资格。HR 需要从这么多的简历中筛选出合适的简历,那么一份简历通常不会花特别多的时间进行筛选,所以就需要让 HR 在很短的筛选时间中认可你的简历。 8 | 9 | - 简历通过筛选之后就被放入人才库中,等待发起笔试和面试。如果简历足够优秀,可以在提前批阶段免笔试直接获得面试机会,或者笔试成绩不理想但也有面试机会,这主要看有没有面试官从人才库中把你的简历捞起来。 10 | - 面试过程通常会按照简历上的内容进行提问,所以简历也可以当成是面试大纲。如果你希望某些内容被面试官问到,那么最好把这些内容放在面试官比较能注意到的位置。 11 | 12 | ✍🏻 13 | 14 | # 2. 本质 15 | 16 | 简历相当于向企业推销自己的工具,在简历上你需要尽可能展现自己能给企业带来的价值,这种价值从两方面去评估: 17 | 18 | - 技能的匹配程度。除了判断技能栈是否和岗位所要求的技能栈匹配之外,还要看是否具备公司目前业务相关的项目经历。 19 | 20 | - 能力证明。即使技能匹配了,但是如果能力不足的话依然不能胜任工作任务。可以通过学历、大赛奖项、工作和实习经历、项目中解决的问题等方面去证明自己的能力。应该分清哪个是你的优势和劣势,突出优势并弱化劣势,比如学历不好的话就把教育经历放后面一些。 21 | 22 | ✍🏻 23 | 24 | # 3. 形式 25 | 26 | 除了在招聘网站上填写自己的信息之外,通常还需要附带一份 PDF 简历,并且面试的时候也需要自己携带一份纸质简历(最好多带几份,以免当天有多场面试)。 27 | 28 | 除了 PDF 简历之外,通常还有 Markdown、Word、Latex 简历,但是这些形式的简历最后也都需要转换成 PDF 简历,因为 PDF 排版和格式都不容易在传输过程中发生改变,而且也方便打印,所以适合作为正式文档。 29 | 30 | 推荐使用 Markdown 简历,然后转换为 PDF 简历。因为 Markdown 排版简洁,而且更容易修改。但是 Markdown 简历有一个不足之处,就是不够紧凑,本来一页的简历可能需要两页。但是可以通过修改样式表的方式来解决这个问题,减小行间距、减小边距、缩小字体。以 Typora 编辑器为例,在样式文件夹下建立 base.user.css 文件,并使用 shift+F12 快捷键打开 DevTools 来修改样式,具体步骤请参考 [Add Custom CSS](https://support.typora.io/Add-Custom-CSS/)。 31 | 32 | 上面介绍的简历都以文本为主,如果想要展示图文内容的话,推荐使用 HTML 简历,它可以更详细和直观地演示项目经历等。做好之后部署到 Github Pages 上,并将地址附在 PDF 简历上。 33 | 34 | ✍🏻 35 | 36 | # 4. 模版 37 | 38 | ## 4.1 Markdown-Resume 39 | 40 | 这里提供一下笔者制作的 Markdown 简历模版:[Markdown-Resume](https://github.com/CyC2018/Markdown-Resume),clone 到本地,然后使用 Typora 等编辑器修改,并导出成 PDF。 41 | 42 | ![](https://diycode.b0.upaiyun.com/photo/2019/4e58bf5af094acc04448a6af1ab87738.png) 43 | 44 | ## 4.2 Mobirise 45 | 46 | 笔者的 [HTML 简历](https://cyc2018.github.io/) 主要使用 [Mobirise](https://mobirise.com/) 搭建,它是免费的站点生成器,不需要 HTML 等前端知识就可以使用,基本上拖一拖组件就能搭好一个很好看的 HTML 页面。 47 | 48 | ![](https://diycode.b0.upaiyun.com/photo/2019/ed1d4c4987607960681a87d4de231255.png) 49 | 50 | 如果简历上有很多 GIF 图片,刚进去的时候 GIF 加载不完全的话演示效果特别不好,所以笔者的 HTML 简历增加了一个加载页用来加载图片。加载页没办法使用 Mobirise 来实现,需要有一定的前端基础。 51 | 52 | ✍🏻 53 | 54 | # 5. 内容 55 | 56 | 必须写的内容: 57 | 58 | - 姓名相当于你的 ID,应该把姓名放在最显眼的地方。 59 | - 为了方便 HR 联系你,也要把联系信息也放在和姓名一样显眼的位置。 60 | - 个人博客和 Github 等技术社区账号可以作为加分项。 61 | - 教育经历和工作经历按年份逆序来写。 62 | - 项目经历可以写科研项目、个人项目、实习和工作项目。 63 | - 技能清单最好可以让人一眼就看出你的熟练度,可以使用精通、熟悉、了解等词语,也可以使用 ★★☆ 这种图标。 64 | 65 | 需要谨慎考虑的内容: 66 | 67 | - 有些企业会根据性别、年龄等信息筛选人,最好写上,不写也没什么关系。 68 | - 工作经验和期望薪资对于校招生来说不需要写,但是社招生就一定要写。 69 | - 只写可以给企业带来价值的兴趣爱好,其它的无关兴趣爱好不要写。 70 | - 和技术相关的大赛获奖经历可以写,但是其它一些比赛获奖就不要写。 71 | - 三好学生、奖学金、四六级成绩、绩点可以写,以证明你的学习能力,但是尽量不要占太多行,如果不是特别好的话也可以不写。 72 | - 个人评价如果要写的话不要太空泛,什么热爱学习吃苦耐劳等就不要写了,要写得话可以写在最后当做是简历其它内容的总结。 73 | 74 | ✍🏻 75 | 76 | # 6. 排版 77 | 78 | 虽然内容比排版重要,但是好的排版会让人看着舒服,也就会让面试官和 HR 更愿意去发现你的价值。 79 | 80 | - 中文和数字英文之间加空格,因为中文的字体排版和数字英文的有很大不同,数字英文偏小一些。如果不加空格的话,那么整篇文章看着会非常密集杂乱,中文和数字英文混在一起参差不齐。 81 | - 专有名词一定要保证大小写完全正确,例如使用 Java 而不是 java,jQuery 而不是 Jquery。 82 | - 内容也应该注意对齐,否则会很乱。 83 | - 最重要的是不要太花哨,尽量简洁。 84 | 85 | ✍🏻 86 | 87 | # 7. 项目介绍 88 | 89 | 前面讲到项目经历一方面可以用于匹配技能,另一方面也可以通过你在项目中解决的问题来展示你的能力。可以按照下面的步骤介绍项目: 90 | 91 | - 为什么做这个项目; 92 | - 项目有哪些功能; 93 | - 你在项目中担任的角色; 94 | - 碰到了哪些问题; 95 | - 使用什么方式去解决问题的; 96 | - 解决效果是怎样,和别人相比有什么优势。 97 | 98 | 不要用太过主观的语言,而是用数据等客观事实。比如介绍碰到的问题时,不要用“非常难解决”等词语,而是用“并发用户数达到XXX导致响应时间增加到XXX”等数据。 99 | 100 | 也不要堆叠无意义的技术名词,比如“基于 Spring、SpringMVC、Hibernate 实现了 XXX 系统,包含增删查改等功能”。 101 | 102 | # 原文链接 103 | 104 | https://xiaozhuanlan.com/topic/5039476182 -------------------------------------------------------------------------------- /doc/扫二维码登录过程.md: -------------------------------------------------------------------------------- 1 | # 扫二维码登录 2 | 3 | 近些年来,越来越多的网站支持使用手机 APP 扫二维码进行登录。传统的登录方式需要用户在浏览器中输入账号密码,完成输入之后点击登录按钮将这些数据发送到服务器上,服务器对这些数据进行验证并返回特定的状态码和 Cookie 等信息给浏览器。扫二维码登录这种方式不需要用户输入账号密码,这些信息保存在手机 APP 中,并由 APP 发送到服务器上。但是这种方式有几个问题需要解决: 4 | 5 | 1. 服务器不能主动发送信息给浏览器,那么要怎么将服务器验证结果的状态码让浏览器获得? 6 | 2. 要怎么保证浏览器的登录信息和某个账户关联? 7 | 8 | # 实践 9 | 10 | 打开微信网页版 https://wx.qq.com/ 后出现如下的登录二维码: 11 | 12 | 13 | 14 | 使用二维码识别软件可以得到这个二维码中包含的字符串信息,这是一个 URL 地址,包含了一个参数 `I=AbRKq90dXQ==`。 15 | 16 | 17 | 18 | 再打开 Chrome 的开发者工具观察网络请求,注意到浏览器在不断地发送 `https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=AbRKq90dXQ==&tip=0&r=-1945474617&_=1548133675454` 这个请求,并且其中的 uuid 参数值和二维码中的 I 参数一样。 19 | 20 | ![](https://diycode.b0.upaiyun.com/photo/2019/807e71791f64b17cca22567281558df6.png) 21 | 22 | ``` 23 | loginicon: true 24 | uuid: AbRKq90dXQ== 25 | tip: 0 26 | r: -1945474617 27 | _: 1548133675454 28 | ``` 29 | 30 | # 分析 31 | 32 | 通过上面的实践我们知道,登录二维码包含了服务器的 URL 地址,其中也包含了 uuid 参数。当用户使用手机 APP 扫描二维码登录之后,发送用户名、密码、uuid 等信息给服务器,服务器验证之后就将 uuid 和该账户关联并保存到 Session 中。 33 | 34 | 浏览器需要不断地向服务器发送 AJAX 请求,该请求包含了 uuid 参数,当查询到服务器已经存在 uuid 和账户的关联信息之后,就可以知道是某个账户扫描了该二维码登录,服务器返回 200 成功状态码、Cookie 和账户信息等数据,浏览器接受到这些数据之后就可以完成登录操作。应该注意到,为了使的浏览器能不断发送 AJAX 请求,建立的 HTTP 连接需要是长连接。 35 | 36 | # 演示 37 | 38 | ![](https://diycode.b0.upaiyun.com/photo/2019/c1a616c5241028b911a74d113530e5a7.gif) 39 | 40 | # 扩展 41 | 42 | 二维码除了可以用于 APP 扫码登录操作之外,还可以用来进行支付宝扫码付款等,原理其实差不多。 43 | 44 | # 参考资料 45 | 46 | - [微信扫描二维码登录网页是什么原理,前后两个事件是如何联系的?](https://www.zhihu.com/question/20368066) 47 | - [微信二维码登录的原理](https://www.biaodianfu.com/weixin-qrcode.html) 48 | 49 | # 原文链接 50 | 51 | https://xiaozhuanlan.com/topic/3978105462 -------------------------------------------------------------------------------- /doc/海量数据判重.md: -------------------------------------------------------------------------------- 1 | 2 | # 1. 问题描述 3 | 4 | 对于海量数据,要求判断一个数据是否已经存在。这个数据很有可能是字符串,例如 URL。 5 | 6 | # 2. HashSet 7 | 8 | 最直观的方法是使用 HashSet 存储,那么就能以 O(1) 的时间复杂度判断一个数据是否已经存在。 9 | 10 | 考虑到数据是海量的,那么就需要使用拆分的方式将数据拆分到多台机器上,分别在每台机器上使用 HashSet 存储。我们需要使得相同的数据拆分到相同的机器上,可以使用哈希取模的拆分方式进行实现。 11 | 12 | # 3. BitSet 13 | 14 | 如果海量数据是整数,并且范围不大时,就可以使用 BitSet 存储。通过构建一定大小的比特数组,并且让每个整数都映射到这个比特数组上,就可以很容易地知道某个整数是否已经存在。因为比特数组比整型数组小的多,所以通常情况下单机就能处理海量数据。 15 | 16 | ![](https://diycode.b0.upaiyun.com/photo/2019/5ed4c9af3cdb03261566394c3e52e0b4.png) 17 | 18 | 以下是一个 BitSet 的实现,当然在实际开发中可以直接使用语言内置的实现。 19 | 20 | ```java 21 | class BitSet { 22 | int[] bitset; 23 | 24 | public BitSet(int size) { 25 | bitset = new int[(size >> 5) + 1]; // divide by 32 26 | } 27 | 28 | boolean get(int pos) { 29 | int wordNumber = (pos >> 5); // divide by 32 30 | int bitNumber = (pos & 0x1F); // mod 32 31 | return (bitset[wordNumber] & (1 << bitNumber)) != 0; 32 | } 33 | 34 | void set(int pos) { 35 | int wordNumber = (pos >> 5); // divide by 32 36 | int bitNumber = (pos & 0x1F); // mod 32 37 | bitset[wordNumber] |= 1 << bitNumber; 38 | } 39 | } 40 | ``` 41 | 42 | 使用 BitSet 还可以很容易地解决一个整数出现次数的问题,例如使用两个比特数组就可以存储 0~3 的信息。其实判重问题也可以简单看成一个数据出现的次数是否为 1,因此一个比特数组就够了。 43 | 44 | # 4. 布隆过滤器 45 | 46 | 布隆过滤器能够以极小的空间开销解决海量数据判重问题,但是会有一定的误判概率。它主要用在网页黑名单系统、垃圾邮件过滤系统、爬虫的网址判重系统。 47 | 48 | 布隆过滤器也是使用 BitSet 存储数据,但是它进行了一定的改进,从而解除了 BitSet 要求数据的范围不大的限制。在存储时,它要求数据先经过 k 个哈希函得到 k 个位置,并将 BitSet 中对应位置设置为 1。在查找时,也需要先经过 k 个哈希函数得到 k 个位置,如果所有位置上都为 1,那么表示这个数据存在。 49 | 50 | 由于哈希函数的特点,两个不同的数通过哈希函数得到的值可能相同。如果两个数通过 k 个哈希函数得到的值都相同,那么使用布隆过滤器会将这两个数判为相同。 51 | 52 | 可以知道,令 k 和 m 都大一些会使得误判率降低,但是这会带来更高的时间和空间开销。 53 | 54 | 布隆过滤器会误判,也就是将一个不存在的数判断为已经存在,这会造成一定的问题。例如在垃圾邮件过滤系统中,会将一个邮件误判为垃圾邮件,那么就收不到这个邮件。可以使用白名单的方式进行补救。 55 | 56 | ![](https://diycode.b0.upaiyun.com/photo/2019/2d480a3fd9ab77dcd6297f55eafb7707.png) 57 | 58 | # 5. Trie 59 | 60 | Trie 树又叫又叫字典树、前缀树、单词查找树,它是一颗多叉查找树。与二叉查找树不同,键不是直接保存在节点中,而是由节点在树中的位置决定。 61 | 62 | 如果海量数据是字符串数据,那么就可以用很小的空间开销构建一颗 Trie 树,空间开销和树高有关。 63 | 64 | ![](https://diycode.b0.upaiyun.com/photo/2019/89c09aa7a8717a872c633824e0793514.png) 65 | 66 | [Leetcode : Implement Trie (Prefix Tree)](https://leetcode.com/problems/implement-trie-prefix-tree/description/) 67 | 68 | ```java 69 | class Trie { 70 | private class Node { 71 | Node[] childs = new Node[26]; 72 | boolean isLeaf; 73 | } 74 | 75 | private Node root = new Node(); 76 | 77 | public Trie() { 78 | } 79 | 80 | public void insert(String word) { 81 | insert(word, root); 82 | } 83 | 84 | private void insert(String word, Node node) { 85 | if (node == null) return; 86 | if (word.length() == 0) { 87 | node.isLeaf = true; 88 | return; 89 | } 90 | int index = indexForChar(word.charAt(0)); 91 | if (node.childs[index] == null) { 92 | node.childs[index] = new Node(); 93 | } 94 | insert(word.substring(1), node.childs[index]); 95 | } 96 | 97 | public boolean search(String word) { 98 | return search(word, root); 99 | } 100 | 101 | private boolean search(String word, Node node) { 102 | if (node == null) return false; 103 | if (word.length() == 0) return node.isLeaf; 104 | int index = indexForChar(word.charAt(0)); 105 | return search(word.substring(1), node.childs[index]); 106 | } 107 | 108 | public boolean startsWith(String prefix) { 109 | return startWith(prefix, root); 110 | } 111 | 112 | private boolean startWith(String prefix, Node node) { 113 | if (node == null) return false; 114 | if (prefix.length() == 0) return true; 115 | int index = indexForChar(prefix.charAt(0)); 116 | return startWith(prefix.substring(1), node.childs[index]); 117 | } 118 | 119 | private int indexForChar(char c) { 120 | return c - 'a'; 121 | } 122 | } 123 | ``` 124 | 125 | # 参考资料 126 | 127 | - [Bloom Filters: Is element x in set S?](https://www.abhishek-tiwari.com/bloom-filters-is-element-x-in-set-s/) 128 | 129 | # 原文链接 130 | 131 | https://xiaozhuanlan.com/topic/2847301659 -------------------------------------------------------------------------------- /doc/海量数据处理思路.md: -------------------------------------------------------------------------------- 1 | # 1. 计算容量 2 | 3 | 在解决问题之前,要先计算一下海量数据需要占多大的容量。常见的单位换算如下: 4 | 5 | - 1 byte = 8 bit 6 | - 1 KB = 210 byte = 1024 byte ≈ 103 byte 7 | - 1 MB = 220 byte ≈ 10 6 byte 8 | - 1 GB = 230 byte ≈ 10 9 byte 9 | - 1 亿 = 108 10 | 11 | 1 个整数占 4 byte,1 亿个整数占 4*108 byte ≈ 400 MB。 12 | 13 | # 2. 拆分 14 | 15 | 可以将海量数据拆分到多台机器上和拆分到多个文件上: 16 | 17 | - 如果数据量很大,无法放在一台机器上,就将数据拆分到多台机器上。这种方式可以让多台机器一起合作,从而使得问题的求解更加快速。但是也会导致系统更加复杂,而且需要考虑系统故障等问题; 18 | - 如果在程序运行时无法直接加载一个大文件到内存中,就将大文件拆分成小文件,分别对每个小文件进行求解。 19 | 20 | 有以下策略进行拆分: 21 | 22 | - 按出现的顺序拆分:当有新数据到达时,先放进当前机器,填满之后再将数据放到新增的机器上。这种方法的优点是充分利用系统的资源,因为每台机器都会尽可能被填满。缺点是需要一个查找表来保存数据到机器的映射,查找表可能会非常复杂并且非常大。 23 | 24 | ![](https://diycode.b0.upaiyun.com/photo/2019/db717a04252277657dc989cdcc38062e.png) 25 | 26 | - 按散列值拆分:选取数据的主键 key,然后通过哈希取模 hash(key)%N 得到该数据应该拆分到的机器编号,其中 N 是机器的数量。优点是不需要使用查找表,缺点是可能会导致一台机器存储的数据过多,甚至超出它的最大容量。 27 | 28 | ![](https://diycode.b0.upaiyun.com/photo/2019/868080a2b31ca495d028d9828ae56318.png) 29 | 30 | - 按数据的实际含义拆分:例如一个社交网站系统,来自同一个地区的用户更有可能成为朋友,如果让同一个地区的用户尽可能存储在同一个机器上,那么在查找一个用户的好友信息时,就可以避免到多台机器上查找,从而降低延迟。缺点同样是需要使用查找表。 31 | 32 | ![](https://diycode.b0.upaiyun.com/photo/2019/98a8414e87a4a8eb0df5e590e9b6a26d.png) 33 | 34 | # 3. 整合 35 | 36 | 拆分之后的结果还只是局部结果,需要将局部结果汇总为整体的结果。 37 | 38 | # 参考资料 39 | 40 | - 程序员面试金典 41 | - 程序员代码面试指南 42 | 43 | # 原文链接 44 | 45 | https://xiaozhuanlan.com/topic/4953708126 --------------------------------------------------------------------------------