├── 00_导读 ├── JD示例.md └── 一些有用的资源.md ├── 01_python语言基础 ├── GIL.md ├── python2和3.md ├── 函数.md ├── 单元测试.md ├── 异常机制.md ├── 性能优化与GIL.md ├── 生成器与协程.md ├── 自省.md ├── 语言特性.md ├── 迭代器和生成器.md └── 闭包.md ├── 02_数据结构和算法 ├── 00_算法面试指南.md ├── Python中的数据结构.md ├── 二叉树.md ├── 堆.md ├── 常用的算法思路.md ├── 排序算法.md ├── 栈.md ├── 链表.md └── 队列.md ├── 03_类和面向对象 ├── 装饰器.md ├── 设计模式.md ├── 面向对象.md └── 魔法方法.md ├── 04_操作系统 ├── IO多路复用模型.md ├── 内存管理和垃圾回收.md ├── 协程.md ├── 常用linux命令.md ├── 生产者消费者模型.md └── 线程与进程.md ├── 05_网络 ├── HTTP.md ├── OSI.md ├── TCP与UDP.md └── 浏览器输入url中间经历的过程.md ├── 07_数据库 ├── MySQL锁.md ├── Mysql基础.md ├── Mysql存储优化.md ├── Mysql查询优化.md ├── Mysql索引.md ├── Redis.md ├── SQL大全.md └── mysql事务.md ├── 08_web开发 ├── RESTful.md ├── https.md ├── web安全.md └── wsgi.md ├── 09_运维 ├── celery.md └── docker.md ├── 10_项目经验 ├── 个人项目.md └── 腾讯蓝鲸.md ├── 11_实战模拟 ├── Python十大经典面试题.md ├── 快速自查.md ├── 真实的面试问题.md ├── 真实的面试问题2.md ├── 真实的面试问题3.md ├── 真实的面试问题4.md ├── 真题模拟.md └── 算法高频题目.md ├── 12_Java ├── 01_JVM │ ├── 00_JVM合集.md │ ├── 01_JVM基础.md │ ├── 02_性能指标.md │ ├── 03_字节码技术.md │ ├── 04_Java类加载器.md │ ├── 05_Java内存模型.md │ └── 06_推荐材料.md ├── 02——GC │ └── 学习笔记.md ├── 10_面试知识点 │ └── 知识整理.md └── Java基础.md ├── 13_系统设计 ├── CAP.md ├── 【学习】系统设计.md ├── 性能测试.md └── 高并发.md ├── 14_打小抄 ├── 01_编程语言.md └── 02_零碎知识点.md ├── README.md ├── 专题整理 ├── MySQL │ ├── 常见面试题.md │ ├── 我的整理.md │ ├── 我的整理 │ │ ├── 主从同步.md │ │ ├── 事务.md │ │ ├── 分库分表.md │ │ ├── 性能优化.md │ │ ├── 数据库调优.md │ │ ├── 架构.md │ │ ├── 索引.md │ │ ├── 语法.md │ │ ├── 读写分离.md │ │ ├── 读写方式.md │ │ └── 锁.md │ └── 答案 │ │ ├── SQL.md │ │ ├── 事务.md │ │ ├── 分库分表.md │ │ ├── 存储引擎.md │ │ ├── 索引.md │ │ ├── 调优.md │ │ └── 锁.md ├── MySQL索引.md ├── MySQL调优.md ├── MySQL面试.md ├── Redis │ ├── 常见面试题.md │ ├── 答案 │ │ ├── Redis.md │ │ ├── 数据结构.md │ │ ├── 系统设计.md │ │ ├── 缓存.md │ │ └── 高可用.md │ └── 考点整理 │ │ ├── Redis专栏.md │ │ ├── pipline.md │ │ ├── 主从同步.md │ │ ├── 事务.md │ │ ├── 分布式锁.md │ │ ├── 异步队列.md │ │ ├── 持久化.md │ │ ├── 数据类型.md │ │ ├── 概述.md │ │ ├── 穿透击穿雪崩.md │ │ ├── 缓存使用模式.md │ │ ├── 缓存淘汰策略.md │ │ └── 集群.md ├── 中间件.md ├── 排序算法.md ├── 操作系统 │ ├── 常见面试题.md │ ├── 我的整理.md │ ├── 知识整理.md │ ├── 答案 │ │ ├── 操作系统.md │ │ └── 进程和线程.md │ └── 考点突破 │ │ ├── IO.md │ │ ├── Linux体系结构.md │ │ ├── Linux调优.md │ │ ├── shell.md │ │ ├── 内存管理.md │ │ └── 进程和线程.md ├── 数据库索引.md ├── 核心击破 │ └── 重要面试问题.md ├── 消息队列 │ ├── 常见面试题.md │ └── 考点整理 │ │ └── rocketmq.md ├── 网络 │ ├── 常见面试题.md │ ├── 我的整理.md │ ├── 答案 │ │ ├── HTTP.md │ │ ├── TCP.md │ │ └── 复习建议.md │ └── 考点整理 │ │ ├── HTTP.md │ │ ├── OSI.md │ │ ├── TCP和UDP.md │ │ ├── tcp粘包.md │ │ ├── url过程.md │ │ ├── 优化网络.md │ │ └── 零拷贝.md ├── 进程、线程与协程.md └── 面试复习资料.md ├── 参考资料 └── 博文系列.md ├── 天地大同 ├── MySQL篇.md ├── 算法篇.md ├── 系统设计篇.md ├── 系统设计面试指南.md └── 面经.md └── 模拟面试 ├── Java面试题整理.md ├── ms.md ├── 字节面试题.md ├── 面试题1.md ├── 面试题10.md ├── 面试题2.md ├── 面试题3.md ├── 面试题4.md ├── 面试题5.md ├── 面试题6.md ├── 面试题7.md ├── 面试题8.md └── 面试题9.md /00_导读/JD示例.md: -------------------------------------------------------------------------------- 1 | # JD示例 2 | 3 | 以下JD拉取于拉钩、boss直聘,旨在于帮助求职者根据岗位的介绍,来分析自己的优势和不足,对症下药。 4 | 5 | ## python开发工程师 6 | 7 | **某中型互联网企业** 8 | 9 | 岗位职责: 10 | 11 | 1. 对业务模块进行需求分析并对需求进行详细设计 12 | 2. 根据需求和设计完成项目代码实现及单元测试 13 | 3. 参与评估项目技术难点,技术分析和指导、攻关技术瓶颈,系统重构和优化 14 | 4. 研究应用架构的发展方向,提出主动的架构改进/演化方案,确保系统的健壮性和可扩展性 15 | 16 | 任职要求: 17 | 18 | 1. 有三年以上的开发经验,精通python开发语言,熟练运用django,flask等进行开发 19 | 2. 熟悉MySQL,Postgresql,MongoDb,redis等主流数据库基本原理,具备数据库应用、设计能力 20 | 3. 了解常用的异步框架,负载均衡,线程安全,消息中间件等技术 21 | 4. 熟悉linux基本操作,有算法基础,会使用容器技术 22 | 5. 熟悉HTML,JS,CSS等WEB开发者优先 23 | 6. 具有良好的编码习惯,注重代码的复用,关心系统的可维护性和健壮性。 24 | 7. 善于学习先进的软件开发技术和理念,具有良好的创新能力、良好的人际沟通能力及团队协作能力,能承受工作压力,具有敬业精神和质量意识。性格外向开朗,乐于分享,积极主动,工作认真负责。 25 | 26 | ## 运维开发工程师 27 | 28 | **某消费生活类大型互联网企业** 29 | 30 | 岗位职责: 31 | 32 | 1. 负责公司自动化运维平台的建设,包括变更系统,资源管理系统等; 33 | 2. 负责平台的运维规范制定,持续完善运维流程,参与系统设计、研发、部署等相关工作; 34 | 3. 研究分析业内新兴运维技术探索与应用。优化技术方案,改进产品功能; 35 | 4. 针对运维平台系统现状制定工程能力,提升整体解决方案,推动工作效率和质量提升。 36 | 37 | 岗位要求: 38 | 39 | 1. 计算机相关专业,精通至少一门语言Java/C++/Python/Go等,熟悉 Web 前后端开发和常见框架,如 Flask,Vue 等; 40 | 2. 3 年及以上开发经验,具有扎实的代码功底和实战能力; 41 | 3. 熟悉运维常见系统和场景,具备一定的运维自动化开发经验和产品思维; 42 | 4. 良好的沟通能力和团队协作能力; 43 | 5. 有大规模运维系统或高并发系统的设计和开发经验者优先; 44 | 6. 热爱互联网和新技术,具有极强的快速学习能力,研究过优秀开源软件的源码并有心得者优先。 45 | 46 | **某大型上市新媒体企业** 47 | 48 | 职位描述: 49 | 50 | 1. 自动化运维平台和组件的设计和开发,提升运维质量和效率; 51 | 2. 负责平台基础环境haproxy、nginx、mysql等搭建和维护; 52 | 3. 主动发现生产环境的问题和隐患,各类故障和事务的应急响应; 53 | 4. 配合经理与主管一起研讨技术实现方案,全面推进运维自动化。 54 | 55 | 职位要求: 56 | 57 | 1. 全日制统招本科及以上学历,计算机相关专业; 58 | 2. 2年及以上互联网项目开发经验; 59 | 3. 熟悉Python/Shell语言,熟悉flask或django框架,了解Git和SVN版本控制管理工具 熟悉数据库主从、负载均衡以及服务器集群部署等技术 60 | 4. 具备cmdb开发运维经验者优先; 61 | 5. 熟悉各种运维监控工具与平台,如:Zabbix、grafana等; 62 | 6. 熟悉常用的数据库操作,如Mysql。 63 | 7. 认真负责,诚恳踏实,性格开朗,具有服务他人与团队协作精神,具有较强的自我学习能力。 64 | 65 | **某中型互联网企业** 66 | 67 | 职位职责: 68 | 69 | 1. 负责运维相关平台设计和开发工作,参与运维体系建设; 70 | 2. 参与相关平台系统的日常管理和运维; 71 | 3. 设计&开发容量管理,持续交付,监控系统,CMDB,运维自动化等系统功能; 72 | 4. 新技术研究和应用,并推动技术应用于生产; 73 | 5. 对现有系统提出优化建议并组织实施。 74 | 75 | 职位要求: 76 | 77 | 1. 计算机相关专业本科毕业,能熟练阅读英文文档; 78 | 2. 三年以上DevoOps工作经验,掌握DevoOps理论; 79 | 3. 精通python 具备实际项目经验,熟练掌握Django等web开发框架; 80 | 4. 熟练掌握相关数据库Mysql/MongoDB/Redis/Influxdb等; 81 | 5. 熟练掌握Git、gitLab、Ansible、salt、Jenkins、Maven等相关工具; 82 | 6. 熟悉监控系统zabbix , Prometheus, grafana; 83 | 7. 熟悉k8s 容器虚拟化技术生态核心项目; 84 | 8. 有大型项目Devops工作经验者优先; 85 | 9. 有ELK、kafka、hbase相关经验优先。 86 | 87 | **某高速发展型大型电商** 88 | 89 | 岗位职责: 90 | 91 | 1. 负责运维自动化相关系统平台的架构设计、开发工作。 92 | 2. 主要涉及发布与配置管理、分布式作业平台、容灾演练平台、CDN管理平台、K8S管理平台等系统。 93 | 94 | 岗位要求: 95 | 96 | 1. 本科及以上学历,计算机相关专业,2年以上开发工作经验。 97 | 2. 熟悉golang、php、python、java中的至少一种开发语言。 98 | 3. 了解Vue、React、AngularJS中的至少一种前端开发框架。 99 | 4. 熟悉MySQL数据库,了解MySQL索引优化、查询优化和存储优化。 100 | 5. 熟悉TCP/IP协议,对Socket通信和HTTP协议有深刻理解。 101 | 6. 良好的编码习惯、并具备较强的WEB安全和高可用意识。 102 | -------------------------------------------------------------------------------- /00_导读/一些有用的资源.md: -------------------------------------------------------------------------------- 1 | # 资源整理 2 | 3 | ## 强烈推荐! 4 | 5 | http://www.cyc2018.xyz/ 6 | https://github.com/CyC2018/cs-notes 7 | 8 | 9 | ## 面试题 10 | 11 | 《Python面试必须要看的15个问题》 12 | 13 | https://codingpy.com/article/essential-python-interview-questions/ 14 | 15 | ## github 16 | 17 | 关于python的面试题:https://github.com/taizilongxu/interview_python 18 | 19 | ## 掘金 20 | 21 | 《2019python面试100问》https://juejin.im/post/5cfd1d0d6fb9a07eaf2b844d 22 | 23 | 《剖析 Python 面试知识点(一): 魔法方法、闭包/自省、装饰器/生成器》 24 | 25 | https://juejin.im/post/5c9f9d436fb9a05e4f05780c 26 | 27 | 《全面剖析 Python 面试知识点》 28 | 29 | https://gitbook.cn/gitchat/activity/5c8e364590020a6262806c8d 30 | 31 | ## 实战面经 32 | 33 | 《字节跳动、腾讯后台开发面经分享(2019.5)》 34 | 35 | https://juejin.im/post/5cf7ea91e51d4576bc1a0dc2 36 | 37 | 《作为字节跳动的研发面试官,有些话我不得不说!》 38 | 39 | https://cloud.tencent.com/developer/article/1416289 40 | 41 | 《【面经】记一次字节跳动前端面试经历》 42 | 43 | https://www.jianshu.com/p/04eb1fedf484 44 | 45 | 《你都有哪些面试时被虐的经历?》 46 | 47 | https://www.zhihu.com/question/31225105/answer/582508111 48 | 49 | 《字节跳动面试总结》 50 | 51 | https://blog.51cto.com/xjhznick/2437819 52 | 53 | ## 掘金小册 54 | 55 | ## 个人博客 56 | 57 | 《python之禅》 58 | 59 | https://foofish.net/ 60 | 61 | ## github上有意思的项目 62 | 63 | 《一起写文章,一起看文章》 64 | 65 | https://pyzh.readthedocs.io/en/latest/ 66 | -------------------------------------------------------------------------------- /01_python语言基础/GIL.md: -------------------------------------------------------------------------------- 1 | # GIL 2 | 3 | 4 | 5 | ## 参考资料 6 | 7 | https://www.oschina.net/translate/pythons-hardest-problem 8 | 9 | https://www.cnblogs.com/SuKiWX/p/8804974.html -------------------------------------------------------------------------------- /01_python语言基础/python2和3.md: -------------------------------------------------------------------------------- 1 | # python2和3 2 | 3 | ## python2和python3 4 | 5 | - print成为函数 6 | - 编码问题。python3不再有unicode对象,默认str就是unicode 7 | - 除法变化。python3除号返回浮点数,如果要返回整数,应使用// 8 | - 类型注解。帮助IDE实现类型检查 9 | - 优化的super()方便直接调用父类函数。Python3.x 和 Python2.x 的一个区别是: Python 3 可以使用直接使用 super().xxx 代替 super(Class, self).xxx : 10 | - 高级解包操作。a, b, *rest = range(10) 11 | - keyword only arguments。限定关键字参数 12 | - chained exceptions。python3重新抛出异常不会丢失栈信息 13 | - 一切返回迭代器。range, zip, map, dict.values, etc. are all iterators 14 | - 性能优化等。。。 15 | 16 | ## 习题 17 | 18 | ### python2和python3的对比 19 | 20 | 1. print函数 21 | 22 | print语句没有了,取而代之的是print()函数。 23 | 24 | 2. unicode 25 | 26 | Python 2 有 ASCII str() 类型,unicode() 是单独的,不是 byte 类型。 27 | 现在, 在 Python 3,我们最终有了 Unicode (utf-8) 字符串,以及一个字节类:byte 和 bytearrays。 28 | Python3.X 源码文件默认使用utf-8编码 29 | 30 | 3. 除法 31 | 32 | python3除号返回浮点数,如果要返回整数,应使用// 33 | 34 | 4. 异常 35 | 36 | 捕获异常的语法由 except exc, var 改为 except exc as var。 37 | 38 | 5. 数据类型 39 | 40 | 一切返回迭代器。range, zip, map, dict.values, etc. are all iterators 41 | 42 | 43 | ## 参考资料 44 | 45 | python2和python3中调用父类方法 46 | 47 | https://cloud.tencent.com/developer/article/1365782 48 | https://www.runoob.com/python/python-func-super.html 49 | 50 | -------------------------------------------------------------------------------- /01_python语言基础/函数.md: -------------------------------------------------------------------------------- 1 | # 函数 2 | 3 | ## python如何传递参数 4 | 5 | python既不是值传递也不是引用传递,唯一支持的参数传递是共享传参。 6 | 7 | call by object(call by object reference or call by sharing) 8 | 9 | call by sharing(共享传参),函数形参获得实参中各个引用的副本。 10 | 11 | 变量一切都是对象。list是可变对象,string是不可变对象 12 | 13 | 总结一下:根据对象的引用来传递,根据对象是可变对象还是不可变对象,得到两种不同的结果。如果是可变对象,则直接修改。如果是不可变对象,则生产新对象,让形参指向新对象 14 | 15 | ## python可变/不可变对象 16 | 17 | 不可变对象: bool/int/float/tuple/str/frozenset 18 | 可变对象:list/set/dict 19 | 20 | 练习题: 21 | 22 | ``` python 23 | # 1 24 | def clear_list(l): 25 | l = [] 26 | ll = [1,2,3] 27 | clear_list(ll) 28 | print(ll) 29 | 30 | # 2 31 | def fl(l=[1]): 32 | l.append(1) 33 | print(l) 34 | fl() 35 | fl() 36 | # 记住:默认参数只计算一次 37 | ``` 38 | 39 | ## 9. *args, **kwargs 40 | 41 | 用来处理可变参数,args被打包成tuple,kwargs被打包成dict 42 | 43 | 传递方式有两种 44 | 45 | ``` python 46 | # 第一种 47 | foo(1,2,3) 48 | foo(a=1,b=2) 49 | # 第二种 50 | foo(*[1,2,3]) 51 | foo(**dict(a=1,b=2)}) 52 | ``` 53 | 54 | ## 习题 55 | 56 | ### 函数调用参数的传递方式是值传递还是引用传递? 57 | 58 | Python的参数传递有:位置参数、默认参数、可变参数、关键字参数。 59 | 函数的传值到底是值传递还是引用传递、要分情况: 60 | 不可变参数用值传递:像整数和字符串这样的不可变对象,是通过拷贝进行传递的,因为你无论如何都不可能在原处改变不可变对象。 61 | 可变参数是引用传递:比如像列表,字典这样的对象是通过引用传递、和C语言里面的用指针传递数组很相似,可变对象能在函数内部改变。 62 | -------------------------------------------------------------------------------- /01_python语言基础/单元测试.md: -------------------------------------------------------------------------------- 1 | # 单元测试 2 | 3 | ## python单元测试 4 | 5 | 三无代码不可取(无稳定、无注释、无单测) 6 | 7 | - 保证的代码逻辑的正确性 8 | - 单测影响设计,易测的代码往往是高内聚低耦合的 9 | - 回归测试,防止改一处整个服务不可用 10 | 11 | 单元测试相关的库 12 | 13 | - nose/pytest较为常用 14 | - mock模块用来模拟替换网络请求等 15 | - coverage统计测试覆盖率 16 | -------------------------------------------------------------------------------- /01_python语言基础/异常机制.md: -------------------------------------------------------------------------------- 1 | # 异常机制 2 | 3 | ## python异常机制 4 | 5 | BaseException 6 | 下面有SystemExit/KeyboardInterrupt/GeneratorExit/Exception(其他异常都属于它) 7 | 8 | ``` python 9 | try: 10 | # func # 可能会抛出异常的代码 11 | except (Exception1, Exception2) as e: # 可以捕获多个异常并处理 12 | # 异常处理的代码 13 | else: 14 | # pass # 异常没有发生的时候代码逻辑 15 | finally: 16 | pass # 无论异常有没有发生都会执行的代码,一般处理资源的关闭和释放 17 | ``` 18 | 19 | ## 自定义异常 20 | 21 | 继承Exception实现自定义异常,给异常加上一些附加信息 22 | 23 | 不用baseException是因为这样的话ctrl+c的keybord异常就用不了了 24 | -------------------------------------------------------------------------------- /01_python语言基础/性能优化与GIL.md: -------------------------------------------------------------------------------- 1 | # 性能优化与GIL 2 | 3 | ## python性能分析与优化,GIL 4 | 5 | - cython解释器的内存管理并不是线程安全的 6 | - 保护多线程情况下对python对象的访问 7 | - cython使用简单的锁机制避免多个线程同时执行字节码 8 | 9 | ## GIL的影响 10 | 11 | 限制了程序的多核执行 12 | 13 | - 同一个时间只能有一个线程执行字节码 14 | - CPU密集程序难以利用多核优势 15 | - IO期间会释放GIL,对IO密集程序影响不大 16 | 17 | 如何规避GIL的影响 18 | 19 | - 区分CPU和IO密集程序 20 | - CPU密集可以使用多进程+进程池 21 | - IO密集使用多线程/协程 22 | - cpython扩展 23 | 24 | ## 为什么有了GIL还要关注线程安全 25 | 26 | python中什么操作才是原子的?一步到位执行完 27 | 28 | - 一个操作如果是一个字节码指令可以完成就是原子的 29 | - 非原子操作不是线程安全的 30 | - 原子的是可以保证线程安全的 31 | - 使用dis操作来分析字节码 32 | 33 | ## 服务端性能优化措施 34 | 35 | web应用一般语言不会成为瓶颈 36 | 37 | - 数据结构与算法优化 38 | - 数据库层:索引优化,慢查询消除,批量操作减少IO,NoSQL 39 | - 网络IO:批量操作,pipeline操作,减少IO 40 | - 缓存:使用内存数据库 redis/memcached 41 | - 异步:asyncio,celery 42 | - 并发:gevent/多线程 43 | -------------------------------------------------------------------------------- /01_python语言基础/生成器与协程.md: -------------------------------------------------------------------------------- 1 | # 生成器与协程 2 | 3 | ## Generator 4 | 5 | - 生成器就是可以生成值的函数 6 | - 当一个函数里有了yield关键字就成了生成器 7 | - 生成器可以挂起执行并且保持当前执行的状态 8 | 9 | ## python3原生协程 10 | 11 | async/await支持原生协程 12 | -------------------------------------------------------------------------------- /01_python语言基础/自省.md: -------------------------------------------------------------------------------- 1 | # 自省 2 | 3 | ## 含义 4 | 5 | 自省,也可以说是反射,自省在计算机编程中通常指这种能力:检查某些事物以确定它是什么、它知道什么以及它能做什么。 6 | 7 | ## 主要方法 8 | 9 | - hasattr(object, name) 检查对象是否具体 name 属性。返回 bool 10 | - getattr(object, name, default) 获取对象的 name 属性。 11 | - setattr(object, name, default) 给对象设置 name 属性 12 | - delattr(object, name) 给对象删除 name 属性 13 | - dir([object]) 获取对象大部分的属性 14 | - isinstance(name, object) 检查 name 是不是 object 对象 15 | - type(object) 查看对象的类型 16 | - callable(object) 判断对象是否是可调用对象 -------------------------------------------------------------------------------- /01_python语言基础/语言特性.md: -------------------------------------------------------------------------------- 1 | # 语言特性 2 | 3 | ## python是动态强类型的语言 4 | 5 | 动态还是静态指的是编译器还是运行期确定类型 6 | 7 | 强类型指的是不会发生隐式类型转换。比如js能够执行1+"1",但是python不行,所以python是弱类型的语言。 8 | 9 | ## 鸭子类型 10 | 11 | 当一只鸟走起来像鸭子、游泳起来像鸭子、叫气力啊也像鸭子,那么这只鸟就可以被称为鸭子。 12 | 13 | 鸭子类型关注的是对象的行为,而不是类型。比如file,StringIO,socket对象都支持read/write方法,再比如定义了__iter__魔术方法的对象可以用for迭代。 14 | 15 | ## monkey patch 16 | 17 | 所谓的monkey patch就是运行时替换。 18 | 19 | ## 自省 20 | 21 | 运行时判断一个对象类型的能力。 22 | 23 | python一切皆对象,用type, id, isinstance获取对象类型信息。 24 | 25 | 自省,也可以说是反射,自省在计算机编程中通常指这种能力:检查某些事物以确定它是什么、它知道什么以及它能做什么。 26 | 27 | 与其相关的主要方法: 28 | 29 | - hasattr(object, name)检查对象是否具体 name 属性。返回 bool. 30 | - getattr(object, name, default)获取对象的name属性。 31 | - setattr(object, name, default)给对象设置name属性 32 | - delattr(object, name)给对象删除name属性 33 | - dir([object])获取对象大部分的属性 34 | - isinstance(name, object)检查name是不是object对象 35 | - type(object)查看对象的类型 36 | - callable(object)判断对象是否是可调用对象 37 | 38 | ## 列表和字典推导 39 | 40 | 如 [i for i in range(10) if i % 2 == 0],如果[]改为(),则为生成器 41 | 42 | ## 变量查找顺序 43 | 44 | 函数作用域的LEGB顺序 45 | 46 | L:local 函数内部作用域 47 | E: enclosing 函数内部与内嵌函数之间 48 | G: global 全局作用域 49 | B:build-in 内置作用 50 | 51 | python在函数里面的查找分为4种,称之为LEGB,也正是按照这是顺序来查找的 52 | -------------------------------------------------------------------------------- /01_python语言基础/迭代器和生成器.md: -------------------------------------------------------------------------------- 1 | # 迭代器和生成器 2 | 3 | ## 容器 4 | 5 | container 可以理解为把多个元素组织在一起的数据结构,container 中的元素可以逐个地迭代获取,可以用 in, not in 关键字判断元素是否包含在容器中。在 Python 中,常见的 container 对象有: 6 | 7 | list, deque, .... 8 | set, frozensets, .... 9 | dict, defaultdict, OrderedDict, Counter, .... 10 | tuple, namedtuple, … 11 | str 12 | 13 | ## 可迭代对象(iterables)vs 迭代器(iterator) 14 | 15 | ![迭代器](https://raw.githubusercontent.com/ZhiyuSun/assets/master/pic/%E8%BF%AD%E4%BB%A3%E5%99%A8.jpg) 16 | 17 | 大部分的 container 都是可迭代对象,比如 list or set 都是可迭代对象,可以说只要是可以返回一个迭代器的都可以称作可迭代对象。下面看一个例子: 18 | 19 | ``` python 20 | >>> x = [1, 2, 3] 21 | >>> y = iter(x) 22 | >>> next(y) 23 | 1 24 | >>> next(y) 25 | 2 26 | >>> type(x) 27 | 28 | >>> type(y) 29 | 30 | ``` 31 | 可见, x 是可迭代对象,这里也叫 container。y 则是迭代器,且实现了__iter__ 和 __next__方法。它们之间的关系是: 32 | 33 | 那什么是迭代器了?上面例子中有 2 个方法 iter and next。可见通过 iter 方法后就是迭代器。 34 | 35 | 它是一个带状态的对象,调用 next 方法的时候返回容器中的下一个值,可以说任何实现了iter和 next 方法的对象都是迭代器,iter返回迭代器自身,next 返回容器中的下一个值,如果容器中没有更多元素了,则抛异常。 36 | 37 | 迭代器就像一个懒加载的工厂,等到有人需要的时候才给它生成值返回,没调用的时候就处于休眠状态等待下一次调用。 38 | 39 | ## 生成器 40 | 41 | ![迭代器和生成器](https://github.com/ZhiyuSun/assets/blob/master/pic/%E8%BF%AD%E4%BB%A3%E5%99%A8%E4%B8%8E%E7%94%9F%E6%88%90%E5%99%A8.jpg?raw=true) 42 | 43 | 生成器一定是迭代器,是一种特殊的迭代器,特殊在于它不需要再像上面的 iter() 和 next 方法了,只需要一个 yiled 关键字。下面来看一个例子: 44 | 45 | 用生成器实现斐波拉契 46 | 47 | ``` python 48 | # content of test.py 49 | def fib(n): 50 | prev, curr = 0, 1 51 | while n > 0: 52 | yield curr 53 | prev, curr = curr, curr + prev 54 | n -= 1 55 | ``` 56 | 57 | 到终端执行 fib 函数 58 | 59 | ``` python 60 | >>> from test import fib 61 | >>> y = fib(10) 62 | >>> next(y) 63 | 1 64 | >>> type(y) 65 | 66 | >>> next(y) 67 | 1 68 | >>> next(y) 69 | 2 70 | ``` 71 | 72 | fib 就是一个普通的 Python 函数,它特殊的地方在于函数体中没有 return 关键字,函数的返回值是一个生成器对象(通过 yield 关键字)。当执行 f=fib() 返回的是一个生成器对象,此时函数体中的代码并不会执行,只有显示或隐示地调用 next 的时候才会真正执行里面的代码。 73 | 74 | 假设有千万个对象,需要顺序调取,如果一次性加载到内存,对内存是极大的压力,有生成器之后,可以需要的时候去生成一个,不需要的则也不会占用内存。 75 | 76 | 平常可能还会遇到一些生成器表达式,比如: 77 | 78 | ``` python 79 | >>> a = (x*x for x in range(10)) 80 | >>> a 81 | at 0x102d79a20> 82 | >>> next(a) 83 | 0 84 | >>> next(a) 85 | 1 86 | >>> a.close() 87 | >>> next(a) 88 | Traceback (most recent call last): 89 | File "", line 1, in 90 | StopIteration 91 | ``` 92 | 93 | 这些小技巧也是非常有用的。close 可以关闭生成器。生成器中还有一个 send 方法,其中 send(None) 与 next 是等价的。 94 | 95 | ``` python 96 | >>> def double_inputs(): 97 | ... while True: 98 | ... x = yield 99 | ... yield x * 2 100 | ... 101 | >>> generator = double_inputs() 102 | >>> generator.send(10) 103 | Traceback (most recent call last): 104 | File "", line 1, in 105 | TypeError: can't send non-None value to a just-started generator 106 | >>> generator.send(None) 107 | >>> generator.send(10) 108 | 20 109 | >>> next(generator) 110 | >>> generator.send(20) 111 | 40 112 | ``` 113 | 114 | 从上面的例子中可以看出,生成器可以接收参数,通过 send(value) 方法,且第一次不能直接 send(value),需要 send(None) 或者 next() 执行之后。也就是说调用 send 传入非 None 值前,生成器必须处于挂起状态,否则将抛出异常。 115 | 116 | ## 区别 117 | 118 | - 迭代器是一个更抽象的概念,任何对象,如果它有next方法(next python3,python2 是 __next__方法)和__iter__方法,则可以称作迭代器。 119 | 120 | - 每个生成器都是一个迭代器,但是反过来不行。通常生成器是通过调用一个或多个yield表达式构成的函数生成的。同时满足迭代器的定义。 121 | 122 | - 生成器能做到迭代器能做的所有事,而且因为自动创建了iter()和 next()方法,生成器显得特别简洁,而且生成器也是高效的。 123 | 124 | ## 参考资料 125 | 126 | 《全面剖析 Python 面试知识点》 127 | 128 | https://gitbook.cn/books/5ca40fd11763103ff10b0e43/index.html -------------------------------------------------------------------------------- /01_python语言基础/闭包.md: -------------------------------------------------------------------------------- 1 | # 闭包 2 | 3 | - 绑定了外部作用域的变量的函数 4 | - 即使程序离开外部作用域,如果闭包仍然可见,绑定变量不会销毁 5 | - 每次运行外部函数都会重新创建闭包 6 | 7 | - 闭包:引用了外部自由变量的函数 8 | - 自由变量:不在当前函数定义的变量 9 | - 特性:自由变量会和闭包函数同时存在 10 | 11 | ```python 12 | from functools import wraps 13 | 14 | def cache(func): 15 | store = {} 16 | @wraps(func) 17 | def _(n): 18 | if n in store: 19 | return store[n] 20 | else: 21 | res = func(n) 22 | store[n] = res 23 | return res 24 | return _ 25 | 26 | @cache 27 | def f(n): 28 | if n <= 1: 29 | return 1 30 | return f(n-1) + f(n-2) 31 | ``` 32 | 33 | 为什么用@wraps(func) 34 | 35 | 因为当使用装饰器装饰一个函数时,函数本身就已经是一个新的函数;即函数名称或属性产生了变化。所以在python的functools模块中提供了wraps装饰函数来确保原函数在使用装饰器时不改变自身的函数名及应有属性。 36 | 所以在装饰器的编写中建议加入wraps确保被装饰的函数不会因装饰器带来异常情况。 37 | https://blog.csdn.net/yuyexiaohan/article/details/82860807 38 | 39 | ## 习题 40 | 41 | ### 解释下什么是闭包 42 | 43 | 在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包。 44 | 45 | 简单的说,如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。来看一个简单的例子: 46 | 47 | ``` python 48 | >>>def addx(x): 49 | >>> def adder(y): return x + y 50 | >>> return adder 51 | >>> c = addx(8) 52 | >>> type(c) 53 | 54 | >>> c.__name__ 55 | 'adder' 56 | >>> c(10) 57 | 18 58 | ``` -------------------------------------------------------------------------------- /02_数据结构和算法/00_算法面试指南.md: -------------------------------------------------------------------------------- 1 | 算法面试一直是程序员大厂面试中的必备环节,本人自从系统性的学习了数据结构与算法之后,对这一块内容已经积攒了大量的实践经验,同时也参加了不少面试,对如何准备算法面试也有了自己的理解。接下来,我将从学习思路、学习工具、训练方法、模拟实战这四个角度,去分享我的经验。 2 | 3 | ## 思路篇——博观而约取,厚积而薄发 4 | 5 | ### 时间复杂度,空间复杂度 6 | 7 | 时间复杂度是衡量算法执行效率的重要指标,空间复杂度是衡量算法消耗空间的重要指标。 8 | 9 | 根据算法种类的不同,时间复杂度可以达到常数级,线性级,指数级不等,能否使用最高效的算法,或者说最优的时间复杂度完成编程开发,是衡量软件工程师素质的重要指标。 10 | 11 | ### 数据结构 12 | 13 | 程序=数据结构+算法。作为软件工程师,需要对常见的数据结构了如指掌,它们包括: 14 | 15 | 一维数据结构: 16 | - 基础:数组 array (string), 链表 linked list 17 | - 高级:栈 stack,队列 queue, 双端队列 deque, 集合 set, 映射 map, 18 | 二维数据结构: 19 | - 基础:树 tree, 图 graph 20 | - 高级:二叉搜索树 binary search tree (red-black tree, AVL), 堆 heap, 并查集 disjoint set, 字典树 Trie 21 | 22 | ### 算法思想 23 | 24 | 除了各种数据结构,软件工程师还需要掌握各种常用的算法思想,它们包括: 25 | 26 | - 递归 Recursion (Divide & Conquer, Backtrace) 27 | - 搜索 Search: 深度优先搜索 Depth first search, 广度优先搜索 Breadth first search 28 | - 动态规划 Dynamic Programming 29 | - 二分查找 Binary Search 30 | - 贪心 Greedy 31 | - 数学 Math , 几何 Geometry 32 | 33 | 以上基本列举了算法面试的常考知识点,算法初学者可以先针对每个知识点具体学习,基础很重要,这些是灵活掌握算法的基础。 34 | 35 | ## 工具篇——工欲善其事,必先利其器 36 | 37 | ### 知识vs技能 38 | 39 | 前面一篇主要列举了算法面试中的常考点,但是只局限于知识的层面。想要真正把知识转化成技能,就需要不断的练习。至于如何高效练习,我推荐leetcode网站,http://leetcode-cn.com/ 40 | 41 | ### leetcode 42 | 43 | 该网站有大量的算法题,可以在线提交代码检查运行情况,还能参考各路大牛的题解。 44 | 45 | 通常来讲,在网站上做完300+道题目,就可以基本达到面试大厂的水准。 46 | 47 | 接下来,我将介绍如何利用该网站进行高效的刷题。 48 | 49 | ## 训练篇——积土而为山,积水而为海 50 | 51 | ### 刻意练习 52 | 53 | 刻意练习就是有针对性的反复训练,算法也一样。 54 | 55 | leetcode网站提供了各种类型算法的标签分类,可以选择自己薄弱的地方针对性的练习。 56 | 57 | 另外,针对某一道特定的问题,在做完之后,也要学会去看题解,学习各种各样的解法,在这个过程中体会各种算法优劣的比较,并寻求最优的时间复杂度。 58 | 59 | ### 寻找反馈 60 | 61 | 对于算法初学者而言,刚接触算法是一个非常难熬的过程,你会感到非常的挫败,容易丧失学习的动力。 62 | 63 | 这时候,一靠自律,你必须努力使自己有攻克算法的强烈渴望,比如为了跳槽,为了更高的薪水等等。二是靠反馈,你可以加入一些刷题社群,跟小伙伴们一起学习,互相督促。另外leetcode网站上可以显示你的全站排名,以及刷题数目,也能给予你一定的正反馈。 64 | 65 | ### 温故知新 66 | 67 | 人的学习会有遗忘曲线,有的题目,即便是你当时看题解后会做了,隔了几天后也许又不会做了。 68 | 69 | 这很正常,不需要害怕,你要做到的就是过遍数,重新去温习一下这道题。通常来讲,一道算法题,做完五遍以上,才能算真正的掌握。 70 | 71 | ### 建立体系 72 | 73 | 学习的宗旨是为了掌握它,如何掌握浩如烟海的知识,就需要构建出属于自己的知识体系。把一个个点给连缀成一条线,清楚地认识到每一个知识点所在的位置,才能在做算法题时做到游刃有余。 74 | 75 | ## 实战篇——凡事预则立,不预则废 76 | 77 | 经过了前面几个步骤的练习,你已经掌握了各类数据结构和算法,对常考题的各种解法也了然于心,我想此时你一定对面试跃跃欲试了。 78 | 79 | 可是,面试时做题目跟平时自己联系题目是有区别的,在面试的场景下,很容易由于压力过大或者不习惯白板做题的方式而发挥失常。对于如何针对面试进行训练,我总结了以下几点: 80 | 81 | ### 练习白板编程 82 | 83 | 平时的开发大多是借助IDE(集成开发工具),但是真实的面试中,面试官极有可能就给你一张白纸去写代码。如果是线上面试的话,还可能会借助一些网页工具,实时的共享代码,例如https://codeshare.io/ 84 | 85 | 所以,在平时训练算法题的过程中,要注重自己白板编程能力的培养,这对编程能力,以及代码风格的要求很高,需要多加练习。 86 | 87 | ### 自己想测试用例 88 | 89 | 在平时的练习里,你可以通过在leetcode上提交代码,很快地知道哪个用例没有通过,而在真实的面试中,面试官很少会给你提供测试用例,需要你自己去考虑各种边界情况。 90 | 91 | 所以,平时在leetcode上练习时不要急于提交代码,在心里先把各种异常场景想清楚,比如输入值校验,边界情况,大数据量带来的时间复杂度等等,这个过程也是在训练自己的思考能力。 92 | 93 | ### 模拟面试 94 | 95 | 你可以找你的朋友或同事帮助你进行一下模拟面试,在面试之前先给自己找到面试的感觉。 96 | 97 | 另外,leetcode提供的模拟面试的功能也很有用,我就特别喜欢用它来模拟真实面试场景,限时去完成题目。 98 | 99 | ## 总结 100 | 101 | 学习算法的过程比较枯燥,但它作为程序员的一项内功,是非常值得重视的。 102 | 103 | 篇幅限制,本文只是简单的概括了我在学习算法过程中的一些经验,很多地方没有展开,等到后面有空,再针对具体的案例进行升华。 104 | 105 | 如今,程序员行业的竞争已经越来越激烈,算法能力也越来越重要。如果你打算在行业内长期发展,这一块永远是不可或缺的技能,越早掌握,越是快人一步。 106 | -------------------------------------------------------------------------------- /02_数据结构和算法/Python中的数据结构.md: -------------------------------------------------------------------------------- 1 | # Python中的数据结构 2 | 3 | ## heapq 4 | 5 | topk问题 6 | 7 | 8 | 9 | ## collections 10 | 11 | defaultdict 12 | 13 | count 14 | 15 | orderdict -------------------------------------------------------------------------------- /02_数据结构和算法/二叉树.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhiyuSun/python-interview/3562125bafdc67fd6f2ca6f2ce172d9bf502d9ef/02_数据结构和算法/二叉树.md -------------------------------------------------------------------------------- /02_数据结构和算法/堆.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhiyuSun/python-interview/3562125bafdc67fd6f2ca6f2ce172d9bf502d9ef/02_数据结构和算法/堆.md -------------------------------------------------------------------------------- /02_数据结构和算法/常用的算法思路.md: -------------------------------------------------------------------------------- 1 | # 常用的算法思路 2 | 3 | 1. 双指针 4 | 5 | 适合数组的算法题 6 | 7 | 示例: 8 | 9 | 剑指 Offer 21. 调整数组顺序使奇数位于偶数前面 10 | https://leetcode-cn.com/problems/diao-zheng-shu-zu-shun-xu-shi-qi-shu-wei-yu-ou-shu-qian-mian-lcof/ 11 | 12 | 13 | 2. 空间换时间 14 | 15 | 用字典辅助解题,减小时间复杂度 16 | 17 | 示例: 18 | 19 | 两数之和 20 | https://leetcode-cn.com/problems/two-sum/ 21 | 22 | 3. -------------------------------------------------------------------------------- /02_数据结构和算法/排序算法.md: -------------------------------------------------------------------------------- 1 | # 排序算法 2 | 3 | ## 参考资料 4 | 5 | https://blog.csdn.net/mrlevo520/article/details/77829204 6 | 7 | https://juejin.im/post/5d1323b6e51d45108b2caeaf#heading-22 8 | 9 | https://blog.csdn.net/mrlevo520/article/details/77829204 -------------------------------------------------------------------------------- /02_数据结构和算法/栈.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhiyuSun/python-interview/3562125bafdc67fd6f2ca6f2ce172d9bf502d9ef/02_数据结构和算法/栈.md -------------------------------------------------------------------------------- /02_数据结构和算法/链表.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhiyuSun/python-interview/3562125bafdc67fd6f2ca6f2ce172d9bf502d9ef/02_数据结构和算法/链表.md -------------------------------------------------------------------------------- /02_数据结构和算法/队列.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhiyuSun/python-interview/3562125bafdc67fd6f2ca6f2ce172d9bf502d9ef/02_数据结构和算法/队列.md -------------------------------------------------------------------------------- /03_类和面向对象/装饰器.md: -------------------------------------------------------------------------------- 1 | # 装饰器 2 | 3 | ## 装饰器 4 | 5 | - python中一切皆对象,函数也可以当做参数传递 6 | - 装饰器是接受函数作为参数,添加功能后返回一个新函数的函数(类) 7 | - python中通过@使用装饰器,语法糖 8 | 9 | ``` python 10 | import time 11 | def log_time(func): # 接受一个函数作为参数 12 | def _log(*args, **kwargs): 13 | beg = time.time() 14 | res = func(*args, **kwargs) 15 | print('use time: {}'.format(time.time()-beg)) 16 | return res 17 | return _log 18 | 19 | @log_time # 装饰器语法糖 20 | def mysleep(): 21 | time.sleep(1) 22 | 23 | mysleep() 24 | 25 | # 另一种写法 26 | 27 | def mysleep2(): 28 | time.sleep(1) 29 | 30 | newsleep = log_time(mysleep2) 31 | newsleep() 32 | ``` 33 | 34 | 使用类编写装饰器 35 | 36 | ```python 37 | import time 38 | class LogTime: 39 | def __call__(self, func): # 接受一个函数作为参数 40 | def _log(*args, **kwargs): 41 | beg = time.time() 42 | res = func(*args, **kwargs) 43 | print('use time: {}'.format(time.time()-beg)) 44 | return res 45 | return _log 46 | 47 | @LogTime() 48 | def mysleep(): 49 | time.sleep(1) 50 | 51 | mysleep() 52 | ``` 53 | 54 | 如何给装饰器增加参数?使用类转时期比较方便实现装饰器参数 55 | 56 | ```python 57 | import time 58 | class LogTime: 59 | def __init__(self, use_int=False): 60 | self.use_int = use_int 61 | 62 | def __call__(self, func): # 接受一个函数作为参数 63 | def _log(*args, **kwargs): 64 | beg = time.time() 65 | res = func(*args, **kwargs) 66 | if self.use_int: 67 | print('use time: {}'.format(int(time.time()-beg))) 68 | else: 69 | print('use time: {}'.format(time.time()-beg)) 70 | return res 71 | return _log 72 | 73 | @LogTime(True) 74 | def mysleep(): 75 | time.sleep(1) 76 | 77 | mysleep() 78 | ``` 79 | 80 | ## 再来亿遍 81 | 82 | ### 简单装饰器 83 | 84 | ``` python 85 | def my_logging(func): 86 | 87 | def wrapper(): 88 | print("{} is running.".format(func.__name__)) 89 | return func() 90 | return wrapper 91 | 92 | @my_logging 93 | def foo(): 94 | print("this is foo function.") 95 | 96 | foo() 97 | ``` 98 | 99 | ### 带参数的简单装饰器 100 | 101 | ``` python 102 | def my_logging(func): 103 | 104 | def wrapper(*args, **kwargs): 105 | print("{} is running.".format(func.__name__)) 106 | return func(*args, **kwargs) 107 | return wrapper 108 | 109 | @my_logging 110 | def foo(x, y): 111 | print("this is foo function.") 112 | return x + y 113 | 114 | print(foo(1, 2)) 115 | ``` 116 | 117 | ### 带参数的装饰器 118 | 119 | ``` python 120 | def my_logging(level): 121 | def decorator(func): 122 | def wrapper(*args, **kwargs): 123 | if level == "info": 124 | print("{} is running. level: ".format(func.__name__), level) 125 | elif level == "warn": 126 | print("{} is running. level: ".format(func.__name__), level) 127 | return func(*args, **kwargs) 128 | return wrapper 129 | return decorator 130 | 131 | @my_logging(level="info") 132 | def foo(name="foo"): 133 | print("{} is running".format(name)) 134 | 135 | @my_logging(level="warn") 136 | def bar(name="bar"): 137 | print("{} is running".format(name)) 138 | 139 | foo() 140 | bar() 141 | ``` 142 | 上面的 my_logging 是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。我们可以将它理解为一个含有参数的闭包。当使用 @my_logging(level="info") 调用的时候,Python 能够发现这一层的封装,并把参数传递到装饰器的环境中。 143 | 144 | @my_logging(level="info") 等价于 @decorator 145 | 146 | ### 类装饰器 147 | 148 | 装饰器不仅可以是函数,还可以是类,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器主要依靠类的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。 149 | 150 | ``` python 151 | class MyLogging(object): 152 | 153 | def __init__(self, func): 154 | self._func = func 155 | 156 | def __call__(self, *args, **kwargs): 157 | print("class decorator starting.") 158 | a = self._func(*args, **kwargs) 159 | print("class decorator end.") 160 | return a 161 | 162 | @MyLogging 163 | def foo(x, y): 164 | print("foo is running") 165 | return x + y 166 | 167 | print(foo(1, 2)) 168 | ``` 169 | 170 | 171 | ## 参考资料 172 | 173 | 装饰器的部分讲的不错 174 | 175 | https://gitbook.cn/books/5ca40fd11763103ff10b0e43/index.html 176 | 177 | -------------------------------------------------------------------------------- /03_类和面向对象/设计模式.md: -------------------------------------------------------------------------------- 1 | # 设计模式 2 | 3 | 学习设计模式的一个有效的方式是自己尝试写个示例代码来演示它 4 | 5 | ## 创建型 6 | 7 | ### 工厂模式 8 | 9 | - 解决对象创建问题 10 | - 解耦对象的创建和使用 11 | - 包括工厂方法和抽象工厂 12 | 13 | ``` python 14 | class DogToy: 15 | def speak(self): 16 | print("wang wang") 17 | 18 | class CatToy: 19 | def speak(self): 20 | print("miao miao") 21 | 22 | def toy_factory(toy_type): 23 | if toy_type == 'dog': 24 | return DogToy() 25 | elif toy_type == 'cat': 26 | return CatToy() 27 | ``` 28 | 29 | ### 构造模式 30 | 31 | - 用于控制复杂对象的构造 32 | - 创建和表示分离 33 | 34 | ### 原型模式 35 | 36 | ### 单例模式 37 | 38 | 单例模式应用的场景一般发现在以下条件下: 39 | 资源共享的情况下,避免由于资源操作时导致的性能或损耗等,如日志文件,应用配置。 40 | 控制资源的情况下,方便资源之间的互相通信。如线程池等,1,网站的计数器 2,应用配置 3.多线程池 4数据库配置 数据库连接池 5.应用程序的日志应用... 41 | 42 | - 单例模式的实现有多种方式 43 | - 单例模式:一个类创建出来的对象都是同一个 44 | - python的模块其实就是单例的,只会导入一次 45 | - 使用共享同一个实例的方法来创建单例模式 46 | 47 | ``` python 48 | class Singleton: 49 | def __new__(cls, *args, **kwargs): 50 | if not hasattr(cls, '_instance'): 51 | _instance = super().__new__(cls, *args, **kwargs) 52 | cls._instance = _instance 53 | return cls._instance 54 | 55 | class MyClass(Singleton): 56 | pass 57 | 58 | c1 = MyClass() 59 | c2 = MyClass() 60 | c1 is c2 # true 61 | ``` 62 | 63 | **习题:python如何实现单例模式** 64 | 65 | 第一种方法:使用装饰器 66 | 67 | ``` python 68 | def singleton(cls): 69 | instances = {} 70 | def wrapper(*args, **kwargs): 71 | if cls not in instances: 72 | instances[cls] = cls(*args, **kwargs) 73 | return instances[cls] 74 | return wrapper 75 | 76 | @singleton 77 | class Foo(object): 78 | pass 79 | foo1 = Foo() 80 | foo2 = Foo() 81 | print(foo1 is foo2) # True 82 | ``` 83 | 84 | 第二种方法:使用基类 85 | 86 | New 是真正创建实例对象的方法,所以重写基类的new 方法,以此保证创建对象的时候只生成一个实例 87 | 88 | ``` python 89 | class Singleton(object): 90 | def __new__(cls, *args, **kwargs): 91 | if not hasattr(cls, '_instance'): 92 | cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs) 93 | return cls._instance 94 | 95 | class Foo(Singleton): 96 | pass 97 | 98 | foo1 = Foo() 99 | foo2 = Foo() 100 | 101 | print(foo1 is foo2) # True 102 | ``` 103 | 104 | 第三种方法:元类,元类是用于创建类对象的类,类对象创建实例对象时一定要调用call方法,因此在调用call时候保证始终只创建一个实例即可,type是python的元类 105 | 106 | ``` python 107 | class Singleton(type): 108 | def __call__(cls, *args, **kwargs): 109 | if not hasattr(cls, '_instance'): 110 | cls._instance = super(Singleton, cls).__call__(*args, **kwargs) 111 | return cls._instance 112 | 113 | 114 | # Python2 115 | class Foo(object): 116 | __metaclass__ = Singleton 117 | 118 | # Python3 119 | class Foo(metaclass=Singleton): 120 | pass 121 | 122 | foo1 = Foo() 123 | foo2 = Foo() 124 | print(foo1 is foo2) # True 125 | 126 | ``` 127 | 128 | 129 | ## 结构型 130 | 131 | ### 代理模式 132 | 133 | - 把一个对象的操作代理到另一个对象 134 | - 通常使用has a 组合关系 135 | 136 | ### 适配器模式 137 | 138 | - 把不同对象的接口适配到同一个接口 139 | - 当我们需要给不同的对象统一接口的时候可以使用适配器模式 140 | 141 | ## 行为型 142 | 143 | ### 迭代器模式 144 | 145 | - python内置对迭代器模式的支持 146 | - 比如我们可以用for遍历各种Interable的数据类型 147 | - python里可以实现__next__和__iter__实现迭代器 148 | 149 | ### 观察者模式 150 | 151 | - 发布订阅是一种罪常用的实现方式 152 | - 发布订阅用于解耦逻辑 153 | - 可以通过回调等方式实现,当发生事件时,调用相应的回调函数 154 | 155 | ### 策略模式 156 | 157 | - 根据不同的输入采取不同的策略 158 | - 对外暴露统一的接口,内部采用不同的策略计算 159 | 160 | ## 函数式编程 161 | 162 | ### map/reduce/filter 163 | 164 | ``` python 165 | reduce(lambda x, y: x+y, range(1,6)) 166 | ``` 167 | -------------------------------------------------------------------------------- /03_类和面向对象/面向对象.md: -------------------------------------------------------------------------------- 1 | # 面向对象编程 2 | 3 | ## 封装、继承、多态 4 | 5 | ```python 6 | class Person(object): 7 | def __init__(self, name, age): 8 | self.name = name 9 | self.age = age 10 | def print_name(self): 11 | print self.name 12 | ``` 13 | 14 | ## 组合与继承 15 | 16 | 优先使用组合(has a)而非继承(is a) 17 | 18 | ## 类变量和实例变量的区别 19 | 20 | - 类变量由所有实例共享 21 | - 实例变量由实例单独享有,不同实例之间不影响 22 | - 当我们需要在一个类的不同实例之间共享变量的时候使用类变量 23 | 24 | ## classmethod/staticmethod区别 25 | 26 | - 都可以通过Class.method()的方式使用 27 | - classmethod第一个参数是cls,可以引用类变量 28 | - staticmethod使用起来和普通函数一样,只不过放在类里去组织 29 | 30 | ```python 31 | class Person(object): 32 | Country = 'china' 33 | def __init__(self, name, age): 34 | self.name = name 35 | self.age = age 36 | 37 | def print_name(self): 38 | print self.name 39 | 40 | @classmethod 41 | def print_country(cls): 42 | print(cls.Country) 43 | 44 | @staticmethod 45 | def join_name(first_name, last_name): 46 | return last_name + first_name 47 | ``` 48 | 49 | ## 什么是元类 50 | 51 | 元类是创建类的类 52 | 53 | - 元类允许我们控制类的生成,比如修改类的属性等 54 | - 使用type来定义元类 55 | - 元类最常见的一个使用场景就是ORM框架 56 | - __new__用来生成实例,__init__用来初始化实例 57 | 58 | 例子: 59 | 60 | ``` python 61 | 62 | # 元类继承自type 63 | class LowercaseMeta(type): 64 | """ 修改类的书写名称为小写的元类""" 65 | def __new__(mcs, name, bases, attrs): 66 | lower_attrs = {} 67 | for k, v in attrs.items(): 68 | if not k.startswith('__'): 69 | lower_attrs[k.lower()] = v 70 | else: 71 | lower_attr[k] =v 72 | return type.__new__(mcs, name, bases, lower_attrs) 73 | 74 | class LowercaseClass(metaclass=LowercaseMeta): 75 | BAR = True 76 | 77 | def Hello(self): 78 | print('hello') 79 | 80 | print(dir(LowercaseClass)) 81 | 82 | LowercaseClass().hello() 83 | ``` 84 | 85 | 参考链接: 86 | 87 | https://www.cnblogs.com/huchong/p/8260151.html#_label0 88 | 很详细的解释了call, init, new, 元类等知识 89 | 90 | 91 | ## 习题 92 | 93 | ### python中类方法、类实例方法、静态方法有何区别? 94 | 95 | - 类方法: 是类对象的方法,在定义时需要在上方使用 @classmethod 进行装饰,形参为cls,表示类对象,类对象和实例对象都可调用 96 | - 类实例方法: 是类实例化对象的方法,只有实例对象可以调用,形参为self,指代对象本身; 97 | - 静态方法: 是一个任意函数,在其上方使用 @staticmethod 进行装饰,可以用对象直接调用,静态方法实际上跟该类没有太大关系 98 | 99 | ### python函数重载机制 100 | 101 | 函数重载主要是为了解决两个问题。 102 | 1。可变参数类型。 103 | 2。可变参数个数。 104 | 另外,一个基本的设计原则是,仅仅当两个函数除了参数类型和参数个数不同以外,其功能是完全相同的,此时才使用函数重载,如果两个函数的功能其实不同,那么不应当使用重载,而应当使用一个名字不同的函数。 105 | 好吧,那么对于情况 1 ,函数功能相同,但是参数类型不同,python 如何处理?答案是根本不需要处理,因为 python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数。 106 | 那么对于情况 2 ,函数功能相同,但参数个数不同,python 如何处理?大家知道,答案就是缺省参数。对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的。 107 | 好了,鉴于情况 1 跟 情况 2 都有了解决方案,python 自然就不需要函数重载了。 108 | 109 | ## 参考资料 110 | 111 | 《python中的魔术方法》:https://juejin.im/post/5c9f9d436fb9a05e4f05780c 112 | 113 | -------------------------------------------------------------------------------- /03_类和面向对象/魔法方法.md: -------------------------------------------------------------------------------- 1 | # 魔法方法 2 | 3 | ## 常用魔术方法 4 | 5 | ### __init__ 6 | 7 | - __init__负责初始化工作 8 | 9 | ### __new__ 10 | 11 | - __new__(cls, […]) new是 Python 中对象实例化时所调用的第一个函数,在init之前被调用 12 | - new将 class 作为他的第一个参数, 并返回一个这个 class 的 instance 13 | - init是将 instance 作为参数,并对这个 instance 进行初始化操作 14 | 15 | ### __call__ 16 | 17 | - 平时自定义的函数、内置函数和类都属于可调用对象,但凡是可以把一对括号()应用到某个对象身上都可称之为可调用对象 18 | - 判断对象是否为可调用对象可以用函数 callable 19 | 20 | ### __del__ 21 | 22 | - 析构函数,删除一个对象时,则会执行此方法,对象在内存中销毁时,自动会调用此方法。 23 | 24 | 25 | ## 参考资料 26 | 27 | 《11. (译)Python魔法方法指南》 28 | 29 | https://pyzh.readthedocs.io/en/latest/python-magic-methods-guide.html 30 | 31 | -------------------------------------------------------------------------------- /04_操作系统/IO多路复用模型.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhiyuSun/python-interview/3562125bafdc67fd6f2ca6f2ce172d9bf502d9ef/04_操作系统/IO多路复用模型.md -------------------------------------------------------------------------------- /04_操作系统/内存管理和垃圾回收.md: -------------------------------------------------------------------------------- 1 | # 内存管理和垃圾回收 2 | 3 | ## python的垃圾回收机制原理 4 | 5 | - 引用计数为主(缺点:循环引用无法解决) 6 | - 引入标记清除和分代回收解决引用计数的问题 7 | - 引用计数为主+标记清除和分代回收为辅 8 | 9 | ### 引用计数 10 | 11 | a = None, del a, 离开对象的作用域等等,减少引用计数 12 | 13 | getrefcount(a) 14 | 15 | 什么时候引用计数增加呢? 16 | 17 | - 对象创建 a = 1 18 | - 对象被引用 b = a 19 | - 对象作为参数传递 func(a) 20 | - 对象存储在容器中 l = [a] 21 | 22 | 什么时候引用计数会减少呢? 23 | 24 | - 显示使用del a 25 | - 引用指向了别的对象 b = None 26 | - 离开的对象的作用域(比如函数执行结束) 27 | - 从一个容器移除对象或者销毁容器 28 | 29 | **优缺点** 30 | 31 | 优点 32 | 33 | - 简单 34 | - 实时性:一旦没有引用,内存就直接释放了。不用像其他机制等到特定时机。 35 | 36 | 缺点 37 | 38 | - 需要额外的空间维护引用计数。 39 | - 不能解决对象的循环引用。(主要缺点) 40 | 41 | ### 循环引用 42 | 43 | 标记清除,从根对象找到所有可达的点,不可达的清除掉 44 | 45 | 标记清除主要是解决循环引用问题。 46 | 47 | 标记清除算法是一种基于追踪回收(tracing GC)技术实现的垃圾回收算法。 48 | 49 | 它分为两个阶段:第一阶段是标记阶段,GC 会把所有的 活动对象 打上标记,第二阶段是把那些没有标记的对象 非活动对象 进行回收。那么 GC 又是如何判断哪些是活动对象哪些是非活动对象的呢? 50 | 51 | 对象之间通过引用(指针)连在一起,构成一个有向图,对象构成这个有向图的节点,而引用关系构成这个有向图的边。从根对象(root object)出发,沿着有向边遍历对象,可达的(reachable)对象标记为活动对象,不可达的对象就是要被清除的非活动对象。根对象就是全局变量、调用栈、寄存器。 52 | 53 | 标记清除算法作为 Python 的辅助垃圾收集技术主要处理的是容器对象(container,上面讲迭代器有提到概念),比如 list、dict、tuple 等,因为对于字符串、数值对象是不可能造成循环引用问题。Python 使用一个双向链表将这些容器对象组织起来。 54 | 55 | Python 这种简单粗暴的标记清除算法也有明显的缺点:清除非活动的对象前它必须顺序扫描整个堆内存,哪怕只剩下小部分活动对象也要扫描所有对象。 56 | 57 | ### 分代回收 58 | 59 | 三代,每隔一段时间,分别对每一代进行标记回收 60 | gc.get_threshold() 61 | 62 | 分代回收是一种以空间换时间的操作方式。 63 | 64 | Python 将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代,Python 将内存分为了 3“代”,分别为年轻代(第 0 代)、中年代(第 1 代)、老年代(第 2 代),他们对应的是 3 个链表,**它们的垃圾收集频率与对象的存活时间的增大而减小。**新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python 垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。同时,**分代回收是建立在标记清除技术基础之上。** 65 | 66 | 分代回收同样作为 Python 的辅助垃圾收集技术处理容器对象 67 | 68 | ## 参考资料 69 | 70 | 《全面剖析 Python 面试知识点》 71 | 72 | https://gitbook.cn/books/5ca40fd11763103ff10b0e43/index.html -------------------------------------------------------------------------------- /04_操作系统/协程.md: -------------------------------------------------------------------------------- 1 | # 协程 2 | 3 | 协程,又称微线程。协程是一种用户态的轻量级线程。 4 | 5 | 协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。 6 | 7 | 优点: 8 | 9 | 无需线程上下文切换的开销 10 | 无需原子操作锁定及同步的开销 "原子操作(atomic operation)是不需要 synchronized",所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。视作整体是原子性的核心。 11 | 方便切换控制流,简化编程模型 12 | 高并发+高扩展性+低成本:一个 CPU 支持上万的协程都不是问题。所以很适合用于高并发处理。 13 | 14 | 缺点: 15 | 16 | 无法利用多核资源:协程的本质是个单线程,它不能同时将 单个 CPU 的多个核用上,协程需要和进程配合才能运行在多 CPU 上。 17 | 进行阻塞(Blocking)操作(如 IO 时)会阻塞掉整个程序。 18 | 19 | 特性: 20 | 21 | 必须在只有一个单线程里实现并发 22 | 修改共享数据不需加锁 23 | 用户程序里自己保存多个控制流的上下文栈 24 | 一个协程遇到 IO 操作自动切换到其它协程 25 | 26 | 在 Python3 中 asyncio 模块没有出来之前,协程可以通过 yield、gevent 来实现,asyncio 模块出来之后,基本都是用 asyncio 来开发 27 | 28 | ``` python 29 | import gevent 30 | 31 | def func1(): 32 | print("我在玩游戏...") 33 | gevent.sleep(2) 34 | print("切换回去继续玩游戏...") 35 | 36 | def func2(): 37 | print('我要去上厕所。。。') 38 | gevent.sleep(1) 39 | print('上完厕所了。。。') 40 | 41 | gevent.joinall([ 42 | gevent.spawn(func1), 43 | gevent.spawn(func2), 44 | ]) 45 | 46 | ``` 47 | 48 | ## todo 49 | 50 | 协程真是太难了,到现在还不会 51 | 52 | ## 再来亿遍 53 | 54 | 协程的特点在于是一个线程执行,那和多线程比,协程有何优势? 55 | 56 | 最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。 57 | 58 | 第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。 59 | 60 | 因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。 61 | 62 | ## 参考资料 63 | 64 | 《全面剖析 Python 面试知识点》 65 | 66 | https://gitbook.cn/books/5ca40fd11763103ff10b0e43/index.html 67 | 68 | https://www.cnblogs.com/russellyoung/p/python-zhi-xie-cheng.html 69 | 70 | https://www.cnblogs.com/cheyunhua/p/11017057.html 71 | 72 | https://www.jianshu.com/p/7c851145ee4c 73 | 74 | https://www.liaoxuefeng.com/wiki/897692888725344/923057403198272 -------------------------------------------------------------------------------- /04_操作系统/常用linux命令.md: -------------------------------------------------------------------------------- 1 | # linux命令 2 | 3 | - man,cmd --help 4 | - 多用 5 | -------------------------------------------------------------------------------- /04_操作系统/生产者消费者模型.md: -------------------------------------------------------------------------------- 1 | # 生产者消费者模型 2 | 3 | 在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。 4 | 5 | ## 为什么要使用生产者和消费者模式 6 | 7 | 在线程世界里,生产者就是生产数据的线程,消费者就是消费数据的线程。在多线程开发当中,如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。 8 | 9 | ## 什么是生产者消费者模式 10 | 11 | 生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。 12 | 13 | 有些模块负责生产数据,这些数据由其他模块来负责处理(此处的模块可能是:函数、线程、进程等)。产生数据的模块称为生产者,而处理数据的模块称为消费者。在生产者与消费者之间的缓冲区称之为仓库。 可以说生产者负责往仓库运输商品,而消费者负责从仓库里取出商品,这就构成了生产者消费者模式。 14 | 15 | 此模型经常会在实际生产中遇到,有以下优点: 16 | 17 | - 解耦 18 | - 并发 19 | - 支持忙闲不均 当生产者制造数据快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中,慢慢处理掉。而不至于因为消费者的性能造成数据丢失或影响生产者生产。 20 | 21 | 生产者消费者模型在 Python 中一般有 2 种实现: 22 | 23 | - 多线程和队列 24 | - 生成器 yield 25 | 26 | ## 多线程与队列实现 27 | 28 | 以经典的生产包子为例 29 | 30 | ``` python 31 | # content of queue_test.py 32 | from threading import Thread 33 | from time import sleep 34 | from queue import Queue 35 | 36 | class Producer(Thread): 37 | def __init__(self, worker, queue): 38 | super().__init__() 39 | self._worker = worker 40 | self._queue = queue 41 | 42 | def run(self): 43 | while True: 44 | if 0 <= self._queue.qsize() <= 10: 45 | queue.put('baozi') 46 | print('{} 生产了1个包子, 一共{}个包子'.format(self._worker, 47 | self._queue.qsize())) 48 | sleep(0.5) 49 | elif 10 < self._queue.qsize() <= 20: 50 | queue.put('baozi') 51 | print('{} 生产了1个包子, 一共{}个包子'.format(self._worker, self._queue.qsize())) 52 | sleep(1) 53 | else: 54 | print('仓库较多,生产者休息3秒钟。') 55 | sleep(3) 56 | ``` 57 | 58 | 上面是生产者代码,Queue 为队列,作为仓库角色。接下来看消费者代码 59 | 60 | ``` python 61 | 62 | # content of queue_test.py 63 | class Consumer(Thread): 64 | def __init__(self, client, queue): 65 | super().__init__() 66 | self._client = client 67 | self._queue = queue 68 | 69 | def run(self): 70 | while True: 71 | if self._queue.empty(): 72 | print('仓库没有包子了。。。') 73 | sleep(0.5) 74 | else: 75 | result = self._queue.get() 76 | print('{} 消费了1个包子, 还剩{}个包子'.format(self._client, self._queue.qsize())) 77 | sleep(0.5) 78 | 79 | queue = Queue(maxsize=20) 80 | 81 | for item in ['LiBao', 'YangBao']: 82 | temp = Producer(item, queue) 83 | temp.start() 84 | 85 | for item in ['ChengBaoConsumer', 'TianBaoConsumer']: 86 | temp = Consumer(item, queue) 87 | temp.start() 88 | ``` 89 | 90 | ## yield实现 91 | 92 | ``` python 93 | 94 | import time 95 | 96 | def consumer(name): 97 | print('{}准备吃包子了!'.format(name)) 98 | while True: 99 | baozi = yield #在它就收到内容的时候后就把内容传给baozi 100 | print('包子【{}】来了,被【{}】吃了'.format(baozi,name)) 101 | 102 | def producer(): 103 | c1 = consumer('A') #它只是把c1变成一个生成器 104 | c2 = consumer('B') 105 | c1.__next__() #第一个next只是会走到yield然后停止 106 | c2.__next__() 107 | print('开始做包子了') 108 | for i in range(1,10): 109 | time.sleep(0.5) 110 | print('三秒做了两个包子') 111 | c1.send(i) 112 | c2.send(i+1) 113 | 114 | producer() 115 | 116 | ``` -------------------------------------------------------------------------------- /04_操作系统/线程与进程.md: -------------------------------------------------------------------------------- 1 | # 线程和进程 2 | 3 | ## 对比 4 | 5 | - 进程是对运行时程序的封装,是系统资源调度和分配的基本单位 6 | - 线程是进程的子任务,CPU调度和分配的基本单位,实现进程内并发 7 | - 一个进程可以包含多个线程,线程依赖进程存在,并共享进程内存 8 | 9 | ## 线程安全 10 | 11 | - 一个操作可以在多线程环境中安全使用,获取正确的结果 12 | - 线程安全的操作好比线程是顺序执行而不是并发执行的(i+=1) 13 | - 一般如果涉及到写操作需要考虑如何让多个线程安全访问数据 14 | 15 | ## 进程间通信的方式 16 | 17 | 共享内存,信号量,套接字 18 | 19 | ## Python中如何使用多线程 20 | 21 | threading, start, join 22 | 23 | ## Python中如何使用多进程 24 | 25 | multiprocessing 26 | 27 | ## 几个概念 28 | 29 | ### 进程 vs 线程 30 | 31 | - 进程 进程是表示资源分配的基本单位,又是调度运行的基本单位。程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。 32 | 33 | - 线程 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 34 | 35 | ### 同步 vs 异步 36 | 37 | - 同步 不同程序单元为了完成某个任务,在执行过程中需靠某种通信方式以协调一致,称这些程序单元是同步执行的。同步意味着有序,按顺序执行。 38 | 39 | - 异步 为完成某个任务,不同程序单元之间过程中无需通信协调,即多个任务之间没有先后顺序. 40 | 41 | ### 阻塞 vs 非阻塞 42 | 43 | - 阻塞 阻塞是当请求不能满足的时候就将进程挂起,使调用者不能继续往下执行。 44 | 45 | - 非阻塞 非阻塞则不会阻塞当前进程,可以继续执行,就是说非阻塞的。 46 | 47 | ### 并行 vs 并发 48 | 49 | - 并发 并发描述的是程序的组织结构。指程序要被设计成多个可独立执行的子任务。以利用有限的计算机资源使多个任务可以被实时或近实时执行为目的。 50 | 51 | - 并行 并行描述的是程序的执行状态。指多个任务同时被执行。以利用富余计算资源(多核 CPU)加速完成多个任务为目的。 52 | 53 | ## 全局解释器锁(GIL) 54 | 55 | 全局解释器锁(GIL)表示在同一时刻只有一个线程对共享资源进行存取。 56 | 57 | GIL 并不是 Python 的特性,Python 完全可以不依赖于 GIL。 58 | 59 | 先明确的一点是 GIL 并不是 Python 的特性,它是在实现 Python 解析器(CPython)时所引入的一个概念。就好比 C++ 是一套语言(语法)标准,但是可以用不同的编译器来编译成可执行代码。Python 也一样,同样一段代码可以通过 CPython、PyPy、Psyco 等不同的 Python 执行环境来执行。像其中的 JPython 就没有 GIL。然而因为 CPython 是大部分环境下默认的 Python 执行环境。所以在很多人的概念里 CPython 就是 Python,也就想当然的把 GIL 归结为 Python 语言的缺陷。 60 | 61 | ![GIL](https://raw.githubusercontent.com/ZhiyuSun/assets/master/pic/GIL.jpg) 62 | 63 | 线程何时切换?一个线程无论何时开始睡眠或等待网络 I/O,其他线程总有机会获取 GIL 执行 Python 代码。这是协同式多任务处理。CPython 也还有抢占式多任务处理。如果一个线程不间断地在 Python 2 中运行 1000 字节码指令,或者不间断地在 Python 3 运行15 毫秒,那么它便会放弃 GIL,而其他线程可以运行。 64 | 65 | Python 的多线程在多核 CPU 上,只对于 IO 密集型计算产生正面效果;而当有至少有一个 CPU 密集型线程存在,那么多线程效率会由于 GIL 而大幅下降。 66 | 67 | 如何避免 GIL 对性能的影响: 68 | 69 | - 多进程 70 | - 使用别的解析器,比如 Jpython 71 | 72 | 73 | ## 多线程 74 | 75 | 多线程在 Python3 中 2 种实现方式: 76 | 77 | - Threading 模块 78 | - concurrent.futures 79 | 80 | ``` python 81 | # 直接调用 82 | import threading 83 | import time 84 | 85 | def sayhi(num): #定义每个线程要运行的函数 86 | print("running on number:%s" %num) 87 | time.sleep(3) 88 | 89 | if __name__ == '__main__': 90 | t1 = threading.Thread(target=sayhi,args=(1,)) #生成一个线程实例 91 | t2 = threading.Thread(target=sayhi,args=(2,)) #生成另一个线程实例 92 | t1.start() #启动线程 93 | t2.start() #启动另一个线程 94 | 95 | print(t1.getName()) #获取线程名 96 | print(t2.getName()) 97 | # 继承调用 98 | import threading 99 | import time 100 | 101 | class MyThread(threading.Thread): 102 | def __init__(self,num): 103 | threading.Thread.__init__(self) 104 | self.num = num 105 | 106 | def run(self):#定义每个线程要运行的函数 107 | print("running on number:%s" %self.num) 108 | time.sleep(3) 109 | 110 | if __name__ == '__main__': 111 | t1 = MyThread(1) 112 | t2 = MyThread(2) 113 | t1.start() 114 | t2.start() 115 | # 线程池 116 | from concurrent.futures import ThreadPoolExecutor 117 | import time 118 | 119 | def return_future_result(message): 120 | time.sleep(0.5) 121 | return message 122 | 123 | pool = ThreadPoolExecutor(max_workers=2) # 创建一个最大可容纳2个task的线程池 124 | future1 = pool.submit(return_future_result, ("hello")) # 往线程池里面加入一个task 125 | future2 = pool.submit(return_future_result, ("world")) # 往线程池里面加入一个task 126 | print(future1.done()) # 判断task1是否结束 127 | time.sleep(1) 128 | print(future2.done()) # 判断task2是否结束 129 | print(future1.result()) # 查看task1返回的结果 130 | print(future2.result()) # 查看task2返回的结果k 131 | 132 | ``` 133 | 134 | ## 多进程 135 | 136 | 多进程 在 Python3 中 2 种实现方式: 137 | 138 | - multiprocessing 139 | - concurrent.futures ProcessPoolExecutor 140 | 141 | ``` python 142 | from multiprocessing import Process 143 | import time 144 | def f(name): 145 | time.sleep(2) 146 | print('hello', name) 147 | 148 | if __name__ == '__main__': 149 | p = Process(target=f, args=('bob',)) 150 | p.start() 151 | p.join() 152 | 153 | from multiprocessing import Process 154 | import os 155 | 156 | def info(title): 157 | print(title) 158 | print('module name:', __name__) 159 | print('parent process:', os.getppid()) 160 | print('process id:', os.getpid()) 161 | print("\n\n") 162 | 163 | def f(name): 164 | info('\033[31;1mfunction f\033[0m') 165 | print('hello', name) 166 | 167 | if __name__ == '__main__': 168 | info('\033[32;1mmain process line\033[0m') 169 | p = Process(target=f, args=('bob',)) 170 | p.start() 171 | p.join() 172 | 173 | from concurrent.futures import ProcessPoolExecutor 174 | import os,time,random 175 | def task(n): 176 | print('%s is running' %os.getpid()) 177 | time.sleep(0.2) 178 | return n**2 179 | 180 | if __name__ == '__main__': 181 | p=ProcessPoolExecutor() #不填则默认为cpu的个数 182 | l=[] 183 | start=time.time() 184 | start = time.time() 185 | with ProcessPoolExecutor() as p: 186 | future_tasks = [p.submit(task, i) for i in range(10)] 187 | print('=' * 30) 188 | print([obj.result() for obj in future_tasks]) 189 | print(time.time() - start) 190 | 191 | ``` 192 | 193 | ## 参考资料 194 | 195 | 《全面剖析 Python 面试知识点》 196 | 197 | https://gitbook.cn/books/5ca40fd11763103ff10b0e43/index.html -------------------------------------------------------------------------------- /05_网络/HTTP.md: -------------------------------------------------------------------------------- 1 | # HTTP协议 2 | 3 | 状态行+响应头+响应正文 4 | 5 | ## 状态码 6 | 7 | 状态码共分为五类,以1-5数字开头进行标识,如下: 8 | 9 | - 1xxs - 信息性:服务器正在处理请求。 10 | - 2xxs - 成功信息:请求已经完成,服务器向浏览器提供了预期的响应。 11 | - 3xxs - 重定向:你的请求被重定向到了其他地方。服务器收到了请求,但是有某种重定向。 12 | - 4xxs – 客户端错误:客户端发生错误,导致服务器无法处理请求。 13 | - 5xxs – 服务端错误:客户端发出了有效的请求,但是服务器未能正确处理请求。 14 | 15 | 几个比较常用的: 16 | 17 | - 100 Continue:表明目前为止,所有的请求内容都是可行的,客户端应该继续请求,如果完成,则忽略它。 18 | - 200 OK:请求成功。 19 | - 201 Created:请求已经成功,并因此创建了一个新的资源。这通常是在PUT或POST请求之后发送的响应。 20 | - 301 Moved Permanently:被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用响应返回的若干个URI之一。 21 | - 302 Found(Previously "Moved temporarily"):请求的资源现在临时从不同的URI响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在Cache-Control或Expires中进行了指定的情况下,这个响应才是可缓存的。 22 | - 401 Unauthorized:这意味着你的登录凭证无效。服务器不知道你是谁,这时,你需要尝试重新登录。 23 | - 403 Forbidden:服务器已经理解请求,但是拒绝执行它。与401不同,403知道是你登录了,但是还是拒绝了你。 24 | - 404 Not Found:请求失败,你请求所希望得到的资源未在服务器上发现。 25 | - 500 Internal Server Error:服务器内部错误,服务器遇到了不知道如何处理的情况。比如后端同学写错了model啥的~ 26 | - 502 Bad Gateway:此错误响应表明服务器作为网关需要得到一个处理这个请求的响应,但是得到一个错误的响应。 27 | - 503 Service Unavailable:服务器没有准备好处理请求。常见的原因是服务器因维护或重载而停机。 28 | - 504 Gateway Timeout:网关超时,服务器未能快速的做出反应。请求接口返回pedding时间过长基本就是这个问题了,囧。 29 | 30 | ## GET VS POST 31 | 32 | ## HTTP长连接 33 | 34 | - 短连接:建立连接——数据传输——关闭连接 35 | - 长连接:Connection: keep-alive。保持TCP连接不断开 36 | 37 | ## cookie和session的区别 38 | -------------------------------------------------------------------------------- /05_网络/OSI.md: -------------------------------------------------------------------------------- 1 | # OSI 2 | 3 | 七层协议: 4 | 5 | 应用层 6 | 表示层 7 | 会话层 8 | 传输层 9 | 网络层 10 | 数据链路层 11 | 物理层 12 | 13 | TCP/IP四层协议 14 | 应用层 15 | 运输层 16 | 网际层 17 | 网络接口层 18 | 19 | -------------------------------------------------------------------------------- /05_网络/TCP与UDP.md: -------------------------------------------------------------------------------- 1 | # TCP三次握手 2 | 3 | ## TCP/UDP的区别 4 | 5 | - 面向连接、可靠的、基于字节流 6 | - 无连接、不可靠、面向报文 7 | 8 | ## 三次握手 9 | 10 | ![三次握手](https://raw.githubusercontent.com/ZhiyuSun/assets/master/pic/%E4%B8%89%E6%AC%A1%E6%8F%A1%E6%89%8B.jpg) 11 | 12 | 第一次握手:客户端将标志位 SYN 置为 1,随机产生一个值 seq=J,并将该数据包发送给服务器端,客户端进入 SYN_SENT 状态,等待服务器端确认。 13 | 14 | 第二次握手:服务器端收到数据包后由标志位 SYN=1 知道客户端请求建立连接,服务器端将标志位 SYN 和 ACK 都置为 1,ack=J+1,随机产生一个值 seq=K,并将该数据包发送给客户端以确认连接请求,服务器端进入 SYN_RCVD 状态。 15 | 16 | 第三次握手:客户端收到确认后,检查 ack 是否为 J+1,ACK 是否为 1,如果正确则将标志位 ACK 置为 1,ack=K+1,并将该数据包发送给服务器端,服务器端检查 ack 是否为 K+1,ACK 是否为 1,如果正确则连接建立成功,客户端和服务器端进入 ESTABLISHED 状态,完成三次握手,随后客户端与服务器端之间可以开始传输数据了。 17 | 18 | ## 四次挥手 19 | 20 | ![四次挥手](https://raw.githubusercontent.com/ZhiyuSun/assets/master/pic/%E5%9B%9B%E6%AC%A1%E6%8C%A5%E6%89%8B.jpg) 21 | 22 | 第一次挥手:客户端发送一个 FIN=M,用来关闭客户端到服务器端的数据传送,客户端进入 FIN_WAIT_1 状态。意思是说"我客户端没有数据要发给你了",但是如果你服务器端还有数据没有发送完成,则不必急着关闭连接,可以继续发送数据。 23 | 24 | 第二次挥手:服务器端收到 FIN 后,先发送 ack=M+1,告诉客户端,你的请求我收到了,但是我还没准备好,请继续你等我的消息。这个时候客户端就进入 FIN_WAIT_2 状态,继续等待服务器端的FIN报文。 25 | 26 | 第三次挥手:当服务器端确定数据已发送完成,则向客户端发送 FIN=N 报文,告诉客户端,好了,我这边数据发完了,准备好关闭连接了。服务器端进入 LAST_ACK 状态。 27 | 28 | 第四次挥手:客户端收到 FIN=N 报文后,就知道可以关闭连接了,但是他还是不相信网络,怕服务器端不知道要关闭,所以发送 ack=N+1 后进入 TIME_WAIT 状态,如果 Server 端没有收到 ACK 则可以重传。服务器端收到 ACK 后,就知道可以断开连接了。客户端等待了 2MSL 后依然没有收到回复,则证明服务器端已正常关闭,那好,我客户端也可以关闭连接了。最终完成了四次握手。 29 | 30 | ## 为什么握手三次,挥手四次 31 | 32 | 首先 TCP 的定位是全双工的、支持半关闭的、可靠的传输协议。三次握手是可以最低限度地确定双方的信息是双向可用的(全双工)。 33 | 34 | 假设是 A 向 B 发起请求。 第二次握手成功表明 A => B 没问题。 第三次握手成功表明 B => A 没问题。 35 | 36 | 同时服务端在 LISTEN 状态下,收到建立连接请求的 SYN 报文后,把 ACK 和 SYN 放在一个报文里发送给客户端。 37 | 38 | 而四次挥手,TCP 要支持半关闭连接。建立的连接是全双工的,A <=> B 双方都可以读写。支持半关闭意味着,TCP 支持 A 和 B 双方独立关闭通道。因此会有两次独立的关闭写通道的请求。一次关闭请求(FIN),对应一个 ACK。 39 | 40 | -------------------------------------------------------------------------------- /05_网络/浏览器输入url中间经历的过程.md: -------------------------------------------------------------------------------- 1 | # 浏览器输入一个url中间经历的过程 2 | 3 | DNS查询——TCP握手——HTTP请求——反向代理Nginx——uwsgi——web app响应 4 | -------------------------------------------------------------------------------- /07_数据库/MySQL锁.md: -------------------------------------------------------------------------------- 1 | # MySQL锁 2 | 3 | ## 悲观锁和乐观锁 4 | 5 | 悲观锁,就是对数据的冲突采取一种悲观的态度,也就是说假设数据肯定会冲突,所以在数据开始读取的时候就把数据锁定住。(数据锁定:数据将暂时不会得到修改) 6 | 7 | 乐观锁,认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则让用户返回错误的信息。让用户决定如何去做。 8 | 9 | 使用: 10 | 悲观锁通常依靠数据库提供的锁机制实现,比如mysql 的排他锁,select … for update 来实现悲观锁。 11 | 乐观锁不依靠数据库提供的锁机制,需要我们自已实现,实现方式一般是记录数据版本,一种是通过版本号,一种是通过时间戳。 -------------------------------------------------------------------------------- /07_数据库/Mysql基础.md: -------------------------------------------------------------------------------- 1 | # mysql 2 | 3 | ## 事务 4 | 5 | - 事务是数据库并发控制的基本单位 6 | - 事务可以看做是一系列SQL语句的集合 7 | - 事务必须要么全部执行成功,要么全部执行失败 8 | 9 | ## ACID 10 | 11 | - 原子性(Atomicity): 一个事务中所有操作全部完成或失败 12 | - 一致性(Consistency): 事务开始和结束之后数据完整性没有被破坏 13 | - 隔离性(Isolation): 允许多个事务同时对数据库修改和读写 14 | - 持久性(Durability): 事务结束之后,修改是永久的不会丢失 15 | 16 | ## 乐观锁,悲观锁 17 | 18 | - 悲观锁是先获取锁再进行操做。一锁二查三更新。select for update 19 | - 乐观锁先修改,更新的时候发现数据已经变了就回滚。check and set 20 | - 使需要根据响应速度、冲突频率、重试代价来判断使用哪一种 21 | 22 | ## musql常用类型 23 | 24 | char/varchar/tinytext/text 25 | tinyint/smallint/mediumint/int/sigint/float/double 26 | date/datetime/timestamp 27 | 28 | ## InnoDB vs MyISAM 29 | 30 | - MyISAM不支持事务,InnoDB支持事务 31 | - MyISAM不支持外键,InnoDB支持外键 32 | - MyISAM只支持表锁,InnoDB支持行锁和表锁 33 | 34 | 35 | 2021.01.05更新 36 | - MyISAM是非事务安全的,而InnoDB是事务安全的 37 | - MyISAM锁的粒度是表级的,而InnoDB支持行级锁 38 | - MyISAM支持全文类型索引,而InnoDB不支持全文索引 39 | - MyISAM相对简单,效率上要优于InnoDB,小型应用可以考虑使用MyISAM 40 | - MyISAM表保存成文件形式,跨平台使用更加方便 41 | 42 | 参考资料:https://juejin.cn/post/6903101301429796871 43 | 44 | ## 参考资料 45 | 46 | 《『MySQL』深入理解事务的来龙去脉》 47 | 48 | https://juejin.im/post/5cbc049de51d456e7b372089 -------------------------------------------------------------------------------- /07_数据库/Mysql存储优化.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhiyuSun/python-interview/3562125bafdc67fd6f2ca6f2ce172d9bf502d9ef/07_数据库/Mysql存储优化.md -------------------------------------------------------------------------------- /07_数据库/Mysql查询优化.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhiyuSun/python-interview/3562125bafdc67fd6f2ca6f2ce172d9bf502d9ef/07_数据库/Mysql查询优化.md -------------------------------------------------------------------------------- /07_数据库/Mysql索引.md: -------------------------------------------------------------------------------- 1 | # mysql索引 2 | 3 | ## 索引是什么 4 | 5 | 官方文档写了索引的作用和没有索引会带来全表扫描,非常费时间。Indexes are used to find rows with specific column values quickly. Without an index, MySQL must begin with the first row and then read through the entire table to find the relevant rows. 6 | 简单的说索引是提高查询速度。这个很好理解,就像是以前的英文词典,找单词如果没有前面目录的话,效率很低,得全文找一遍。 7 | 8 | ## 为什么需要索引 9 | 10 | - 索引是数据表中一个或者多个列进行排序的数据结构 11 | - 索引能够大幅提升检索速度 12 | - 创建、更新索引本身也会耗费空间和时间 13 | 14 | ## 创建索引的方式 15 | 16 | 直接创建索引,例如使用CREATE INDEX 语句或者使用创建索引向导。 17 | 间接创建索引,例如在表中定义主键约束或者唯一性键约束时,同时也创建了索引。 18 | 19 | ## 索引的优缺点 20 | 21 | ### 优点 22 | - 协助快速查询、更新数据库表中数据。 23 | - 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。 24 | - 可以大大加快数据的检索速度 25 | - 可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义 26 | - 在使用分组和排序子句进行数据检索时,同样可以显著减少查询中分组和排序的时间。 27 | - 通过使用索引,可以在查询的过程中,使用优化隐藏器,提高系统的性能 28 | 29 | ### 缺点 30 | - 增加了数据库的存储空间 31 | - 在插入和修改数据时要花费较多的时间(因为索引也要随之变动) 32 | - 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。 33 | - 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。 34 | - 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。 35 | 36 | ## 创建索引的原则 37 | 38 | - 最左前缀匹配原则(一直向右匹配直到遇到范围查询就停止匹配); 39 | - =和in 可以乱序(建立索引是可以任意顺序的,mysql 的查询优化器会帮你优 40 | 化成索引可以识别的形式); 41 | - 尽量选择区分度高的列作为索引(区分度的公式是count(distinctcol)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一 42 | 键的区分度是1,); 43 | - 索引列不能参与计算(保持列“干净”,); 44 | - 尽量的扩展索引,不要新建索引(如a->(a,b)只需要修改原来的索引); 45 | - 选择唯一性索引(唯一性索引的值是唯一的,可以更快速的通过该索引来确定某 46 | 条记录。); 47 | - 为经常需要排序、分组和联合操作的字段建立索引; 48 | - 为常作为查询条件的字段建立索引; 49 | - 限制索引的数目; 50 | - 尽量使用数据量少的索引; 51 | - 尽量使用前缀来索引; 52 | - 删除不再使用或者很少使用的索引。 53 | 54 | ## 索引的实现原理 55 | 56 | 要搞清楚索引的实现原理,先看看索引的底层实现,MySQL索引大部分采用B-Tree实现,B-Tree又有B-树和B+树。还有一些使用Hash索引。 57 | 58 | 59 | ### 二叉搜索树 60 | 61 | 理解二叉搜索树,对于后面理解B-和B+树很有帮助,因为这2种有些特性跟二叉搜索树很像。二叉搜索树的特点是左孩子的值小于父亲节点的值,父亲节点的值小于右孩子的值,即按二叉树的中序遍历,刚好是一个按小到大排序的。二叉搜索树的查找就可以使用二分查找,如果要查找10,因为10比27小,所以往左孩子找,10<14,还在左孩子找。最坏的情况下,查找的次数等于树的高度。 62 | 63 | 64 | ### B-树,B+树 65 | 66 | ## 连接 67 | 68 | - 内连接(inner join):两个表都存在匹配时,才会返回匹配行 69 | - 外连接(left join right join):返回一个表的行,即使另一个没有匹配 70 | - 全连接(full join): 只要某一个表存在匹配就返回 71 | 72 | **内连接** 73 | 74 | select * from A inner join B on a.id = b.id 75 | 76 | **外连接** 77 | 78 | select * from A left join B on a.id = b.id 79 | 80 | ## 81 | 82 | 83 | 84 | ## 参考资料 85 | 86 | https://juejin.im/post/5c754718e51d4525f05461b8 87 | https://juejin.im/post/5c8bd3ebf265da2db2797d92 88 | https://juejin.im/post/5c7a8f2e6fb9a049cd54e8ff 89 | -------------------------------------------------------------------------------- /07_数据库/Redis.md: -------------------------------------------------------------------------------- 1 | 2 | # redis 3 | 4 | ## 缓存 5 | 6 | - 缓解关系数据库(Mysql)并发访问的压力:热点数据 7 | - 减少响应时间:内存IO速度比磁盘快 8 | - 提升吞吐量:Redis等内存数据库单机就可以支撑很大并发 9 | 10 | ## 数据类型 11 | 12 | - string:用来实现简单的kv键值对存储,比如计数器 13 | - list:实现双向链表,比如用户的关注,粉丝列表 14 | - hash:用来存储彼此相关信息的键值对 15 | - set:存储不重复元素,比如用户的关注着 16 | - sorted set:实时信息排行榜 17 | 18 | ## 持久化方式 19 | 20 | - 快照方式:把数据快照放在磁盘二进制文件中,dump,.rdb 21 | - AOF:每一个写命令追加到appendonly.aof中 22 | 23 | ## redis事务 24 | 25 | - 将多个请求打包,一次性、按序执行多个命令的机制 26 | - redis通过MULTI,EXEC,WATCH等命令实现事务功能 27 | - python redis-py pipeline=conn.pipeline(transaction=True) 28 | 29 | ## 分布式锁 30 | 31 | - 使用setnx实现加锁,可以同时通过expire添加超时时间 32 | - 锁的value值可以使用一个随机的uuid或者特定的命名 33 | - 释放锁的时候,通过uuid判断是否是该锁,是则执行释放锁 34 | 35 | ## 缓存使用模式 36 | 37 | - cache aside:同时更新缓存和数据库 38 | - read/write through:先更新缓存,缓存负责同步更新数据库 39 | - write behind caching:先更新缓存,缓存定期异步更新数据库 40 | 41 | ## 缓存穿透,缓存击穿,缓存雪崩 42 | -------------------------------------------------------------------------------- /07_数据库/SQL大全.md: -------------------------------------------------------------------------------- 1 | # sql大全 2 | 3 | ## sql优化 4 | 5 | 1. 尽量避免使用select * ,返回无用的字段会降低效率。优化方式:只能使用具 6 | 体的字段代替select 具体字段,只返回使用到的字段。 7 | 8 | 2. 尽量避免使用in 和not in,会导致数据库引擎放弃索引进行全表扫描。优化方 9 | 式:如果是连续数值,可以用betwween 代替,如果是子查询,可以用exists 10 | 代替。(有疑点todo) 11 | 12 | 3. 尽量避免在字段开头模糊查询,会导致数据库引擎放弃索引进行全表扫描。优化 13 | 方式:尽量在字段后面使用模糊查询。 14 | 15 | 4. 尽量避免进行null 值的判断,会导致数据库引擎放弃索引进行全表扫描。优化 16 | 方式:可以给字段添加默认值0,对0 值进行判断。 17 | 18 | ## SQL语句执行顺序 19 | 20 | - FROM 子句, 组装来自不同数据源的数据; 21 | - WHERE 子句, 基于指定的条件对记录进行筛选 22 | - GROUP BY 子句, 将数据划分为多个分组 23 | - 使用聚合函数进行计算 24 | - 使用HAVING 子句筛选分组 25 | - 计算所有的表达式 26 | - 使用ORDER BY 对结果集进行排序 27 | 28 | 即:from—>where—>group by—>having—>计算所有的表达式—>orderby—>select 输出 29 | 30 | ## 注意点 31 | 32 | ### exists和in 33 | 34 | 35 | -------------------------------------------------------------------------------- /07_数据库/mysql事务.md: -------------------------------------------------------------------------------- 1 | # mysql事务 2 | 3 | ## 事务隔离级别 4 | 脏读 不可重复读 幻读 5 | 读未提交(read uncommitted) T T T 6 | 7 | 读已提交(read committed) F T T 8 | 9 | 可重复读(repeated read) F F T 10 | 11 | 可串行化(serializable) F F F 12 | 13 | MySQL数据库支持上面四种隔离级别,默认隔离级别为可重复读(repeated read) 14 | 15 | 16 | ## 脏读 不可重复读 幻读 17 | 18 | 脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack 了操作,则后一个事务所读取的数据就会是不正确的。 19 | 幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。 20 | 不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。 21 | 22 | ## 事务的四大特性 23 | 24 | ACID 25 | 26 | 原子性:事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用。 27 | 一致性:执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的; 28 | 隔离性:并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的; 29 | 持久性:一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。 30 | 31 | 32 | ## 参考资料 33 | 34 | http://www.hollischuang.com/archives/898 -------------------------------------------------------------------------------- /08_web开发/RESTful.md: -------------------------------------------------------------------------------- 1 | # 前后端分离 2 | 3 | ## 优点 4 | 5 | - 前后端解耦,接口复用(前端和客户端公用接口),减少开发量。 6 | - 各司其职,前后端同步开发,提升工作效率。定义好接口规范。 7 | - 更有利于调试(mock),测试和运维部署 8 | 9 | ## restful 10 | 11 | Representational State Transfer 12 | 13 | - 表现层状态转移 14 | - 资源,表现层,状态转化 15 | - 是一种以资源为中心的web软件架构风格,可以用ajax和restful web服务构建应用。 16 | 17 | - resource:使用URI指定的一个实体 18 | - representation:资源的表现方式,比如图片、HTML文本等 19 | - State Transfer: GET,POST,PUT,DELETE,HTTP动词来操作资源,实现资源状态的改变。 20 | 21 | - 所有事物抽象为资源,资源对应唯一的标识(identifier) 22 | - 资源通过接口进行操作实现状态转移,操作本身是无状态的 23 | - 对资源的操作不会改变资源的标识 24 | -------------------------------------------------------------------------------- /08_web开发/https.md: -------------------------------------------------------------------------------- 1 | # https 2 | -------------------------------------------------------------------------------- /08_web开发/web安全.md: -------------------------------------------------------------------------------- 1 | # web安全 2 | 3 | ## SQL注入 4 | 5 | web安全一大原则:永远不要相信用户的任何输入 6 | 7 | - 对输入参数做好检查(类型和范围):过滤和转移特殊字符 8 | - 不要直接拼接sql,使用ORM可以大大降低sql诸如风险 9 | - 数据库层:做好权限管理配置,不要铭文存储敏感信息 10 | 11 | ## XSS(cross site scripting),跨站脚本攻击 12 | 13 | - 恶意用户将代码植入到提供给其他用户使用的页面中,未经转义的恶意代码输出到其他用户的浏览器被执行 14 | - 用户浏览页面的时候嵌入页面中的脚本(js)会被执行,攻击用户 15 | - 主要分为两类:反射性(非持久型),存储型(持久型) 16 | 17 | ## CSRF -------------------------------------------------------------------------------- /08_web开发/wsgi.md: -------------------------------------------------------------------------------- 1 | # WSGI 2 | 3 | ## 什么是WSGI 4 | 5 | - python web server gateway inteerface 6 | - 解决python web server 乱象mod_python,CGI,FastCGI等 7 | - 描述了web server(uwsgi)如何与web框架(django)交互,web框架如何处理请求 8 | 9 | ## python web框架 10 | 11 | ## MVC 12 | 13 | ## ORM 14 | 15 | - 用于实现业务对象与数据表中的字段映射 16 | - 优势:代码更加面对对象,代码量更少,灵活性高,提升开发效率 17 | -------------------------------------------------------------------------------- /09_运维/celery.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhiyuSun/python-interview/3562125bafdc67fd6f2ca6f2ce172d9bf502d9ef/09_运维/celery.md -------------------------------------------------------------------------------- /09_运维/docker.md: -------------------------------------------------------------------------------- 1 | # docker 2 | 3 | ## 参考资料 4 | 5 | 《这可能是最为详细的Docker入门吐血总结》 6 | https://blog.csdn.net/deng624796905/article/details/86493330 -------------------------------------------------------------------------------- /10_项目经验/个人项目.md: -------------------------------------------------------------------------------- 1 | # soup 2 | 3 | 作者 4 | - id 5 | - 名字 6 | 7 | 金句表 8 | - id 9 | - 文本 10 | - 作者id 11 | - 创建时间 12 | - 更新时间 13 | - 浏览数 14 | 15 | -------------------------------------------------------------------------------- /10_项目经验/腾讯蓝鲸.md: -------------------------------------------------------------------------------- 1 | # 腾讯蓝鲸 2 | 3 | ## 总体介绍 4 | 5 | 腾讯蓝鲸智云,简称蓝鲸,是一套基于 PaaS 的技术解决方案,提供了完善的前后台开发框架、调度引擎、公共组件等模块,帮助业务的产品和技术人员快速构建低成本、免运维的支撑工具和运营系统。 6 | 7 | 目前,腾讯蓝鲸智云团队秉承着开放共赢的态度,正逐渐开放其自主研发的一套具有多项探索式创新的体系—蓝鲸智云软件体系。该套体系不仅提供了基础运维(发布变更、监控处理、数值调整、数据提取等)的无人值守服务,而且还给运维人员提供了解决方案(工具),并随时调整,避免重复性的操作服务。而运维人员则可以做些“用户体验优化”和“运营决策辅助”等运维增值工作,并且可以通过蓝鲸智云提供的集成平台,低成本的学习 DevOps技能,进一步提升自己的能力,加速转型。此外,这套体系还可以运行轻应用、管理类、及职能类应用,是个全方位的运维、管理平台。 8 | 9 | 10 | 11 | 腾讯蓝鲸智云体系由平台级产品和通用 SaaS 服务组成,平台包括管控平台、配置平台、作业平台、数据平台、容器管理平台、挖掘平台、PaaS 平台、移动平台等,通用 SaaS 包括节点管理、标准运维、日志检索、蓝鲸监控、故障自愈等,为各种云(公有云、私有云、混合云)的用户提供不同场景、不同需求的一站式技术运营解决方案。 12 | 13 | 腾讯蓝鲸智云体系依托企业级 SOA、 集成等理念,运用 Docker 等最先进的云技术构建起了全新的运维模式, 致力于以“原子服务集成”和“低成本工具构建”的方式落地 DevOps,帮助运维快速实现“基础服务无人值守”及“增值服务”,并进一步通过 DevOps 的落地实现企业更全面和可持续的效率提升。 14 | 15 | ## 管控平台 16 | 17 | 蓝鲸管控平台,是整个蓝鲸平台的底层管控系统,是蓝鲸所有其他服务的基础,是蓝鲸服务体系与用户机器的连接器。 18 | 19 | 蓝鲸管控平台是典型的两层分布式 C/S 结构,主要包含智能 Agent,提供各种服务的 Server,以及 zookeeper、redis、MySQL 等周边保障模块。其中Agent 是部署在业务机器上的程序,每台业务机器理论上只可以部署一个;其他模块部署无具体要求,用户可以单独部署,也可以混合部署。 20 | 21 | 在整个蓝鲸体系中,唯独蓝鲸管控平台没有直面用户,但蓝鲸管控平台在蓝鲸体系中却是不可或缺的,它为蓝鲸其他平台提供了人机交互的通道与能力。蓝鲸管控平台主要提供了三种类型的服务能力:文件分发传输能力、命令实时执行与反馈的能力、大数据采集与传输的能力。 22 | 23 | ## CMDB 24 | 25 | 蓝鲸配置平台(CC)是一款面向应用的 CMDB,在 ITIL 体系里,配置管理数据库(CMDB)是构建其它流程的基础,配置平台作为面向业务层面的 CMDB,为蓝鲸体系的其它平台提供了各种运维场景的配置数据服务,存储与管理企业 IT 架构中设备的各种配置信息,它与所有服务支持和服务交付流程都紧密相联,支持这些流程的运转、发挥配置信息的价值,同时依赖于相关流程保证数据的准确性。配置平台提供的主要功能有主机管理、业务拓扑、业务管理、资源池管理、自定义属性管理、操作审计等。 26 | 27 | ## JOB平台 28 | 29 | 蓝鲸作业平台(Job)是一套底层基于管控之上的基础运维操作平台,并且具备海量的并发处理能力,除了支持脚本执行、文件拉取/分发、定时执行等一系列可实现的基础运维场景以外,还运用流程化的理念很好的将零碎的单个任务组装成一个作业流程。同时,可通过平台提供的 API 实现对任意作业的调用、查看等操作,与其它平台或系统联动,实现调度自动化。作业平台的主要功能有:快速传输文件、web化脚本管理、支持批量高效执行、流程式管理,一切皆“作业”等。 30 | 31 | ## 蓝鲸监控 32 | 33 | 蓝鲸监控是一款针对主机/容器和互联网应用进行监控的产品,监控服务可用于收集主机/容器资源(系统性能、组件服务、数据库、日志等)的监控指标,探测互联网应用服务的可用性,并对指标进行告警和自动执行处理。 34 | -------------------------------------------------------------------------------- /11_实战模拟/Python十大经典面试题.md: -------------------------------------------------------------------------------- 1 | # Python十大经典面试题 2 | 3 | 1. 什么是闭包 4 | 5 | 6 | 7 | 2. 什么是迭代器和生成器 8 | 3. 什么是GIL 9 | 10 | GIL,是最流行的 Python 解释器 CPython 中的一个技术术语。它的意思是全局解释器锁,本质上是类似操作系统的 Mutex。每一个 Python 线程,在 CPython 解释器中执行时,都会先锁住自己的线程,阻止别的线程执行。 11 | 12 | 当然,CPython 会做一些小把戏,轮流执行 Python 线程。这样一来,用户看到的就是“伪并行”——Python 线程在交错执行,来模拟真正并行的线程。 13 | 14 | 4. 进程和线程的区别 15 | 5. 什么是协程 16 | 6. Python中的魔术方法 17 | 7. Python中的装饰器 18 | 8. Python的垃圾回收 19 | 9. Python的单例模式 20 | 10. Python的元类 -------------------------------------------------------------------------------- /11_实战模拟/快速自查.md: -------------------------------------------------------------------------------- 1 | # 快速自查 2 | 3 | 1. 什么是闭包? 4 | 5 | 在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为闭包。 6 | 7 | 简单的说,如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure)。来看一个简单的例子: 8 | 9 | ``` python 10 | def addx(x): 11 | def adder(y): 12 | return x + y 13 | return adder 14 | c = addx(8) 15 | type(c) # 16 | c.__name__ # adder 17 | c(10) # 18 18 | ``` 19 | 20 | 2. 装饰器 21 | 22 | 23 | 3. 迭代器和生成器 24 | 25 | 可迭代对象:the iterable 26 | 生成器:the iterator 27 | 迭代器:generator 28 | 29 | 可迭代对象,如container,通过iter方法后就是迭代器 30 | 生成器是一种特殊的迭代器,不占用内存。 31 | 生成器通过函数yield,或者生成器表达式 32 | 33 | 4. 进程和线程 34 | 35 | 进程 进程是表示资源分配的基本单位,又是调度运行的基本单位。程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行,而这种执行的程序就称之为进程。程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。 36 | 37 | 线程 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。 -------------------------------------------------------------------------------- /11_实战模拟/真实的面试问题.md: -------------------------------------------------------------------------------- 1 | # 面试问题 2 | 3 | 以下问题整理自我的面试 4 | 5 | ## 技术类 6 | 7 | 1. 如何设计一个秒杀系统 8 | 9 | 2. 冒泡排序,快速排序 10 | 11 | 美团 12 | 13 | 1. 堆和栈的区别 14 | 15 | 2. 为什么了有了GIL还要关注线程安全 16 | 17 | 3. python如何加锁 18 | 19 | 4. 什么是线程安全 20 | 21 | 5. 什么是io多路复用 22 | 23 | 6. 有gil锁为什么要用lock 24 | 25 | 7. 什么是io密集 cpu密集,他们出现的场景以及相应的问题排查手段 26 | 27 | 8. 算法:整数列表,最大k个数,时间复杂度 28 | 29 | 9. django 统计所有请求的失败数 30 | 31 | 10. 500 502 504的含义 32 | 33 | 字节跳动 34 | 35 | 1. 什么是GIL 36 | 37 | 2. IO密集和CPU密集,方案选择,协程gevent 38 | 39 | 3. django中间件的开发 40 | 41 | 4. drf里面的scheme是什么 42 | 43 | 5. celery的实现原理,如何配置queue 44 | 45 | 6. 七层和四层协议,http是哪一层,tcp哪一层 46 | 47 | 7. http的请求码,3xx,4xx,5xx的含义 48 | 49 | 8. django如何实现用户认证的,cas 50 | 51 | 9. python的类装载机制 52 | 53 | 10. python如何引用远程的代码 54 | 55 | 11. int的父类是什么 56 | 57 | 12. mysql的分库分表 58 | 59 | 13. 如何自定义migrations 60 | 61 | 14. nginx的一致性哈希是什么 62 | 63 | 15. 如何调试远程的代码,pdb 64 | 65 | 16. 输入一维数组array和n,找出和值为n的任意两个元素。例如: 66 | array = [2, 3, 1, 10, 4, 30] n = 31 67 | 则结果应该输出1, 30 顺序不重要 68 | 如果有多个满足条件的,返回任意一对即可,不能使用哈希表; 69 | 70 | 其他: 71 | 72 | 1. django的中间件 73 | 74 | 2. 事务是什么,隔离等级 75 | 76 | 3. 数据库的索引,b+树 77 | 78 | 4. GIL锁是什么 79 | 80 | 5. HTTPS和HTTP的区别 81 | 82 | 6. nginx 83 | 84 | 7. 线程和进程 85 | 86 | 8. 手写闭包,手写生成器,手写装饰器 87 | 88 | 9. django的登录验证机制 89 | 90 | 10. python中的设计模式 91 | 92 | 11. 如何理解动态解释型的语言 93 | 94 | 12. 协程 95 | 96 | 13. count(*), count(1), count(column)的区别 97 | 98 | 14. redis的研究和使用 99 | 100 | 15. mysql的索引 101 | 102 | 16. 手写循环矩阵 103 | 104 | 17. 迭代器,可迭代对象是什么 105 | 106 | 18. python中的元类 107 | 108 | 19. python的__slots__ 109 | 110 | 20. python中如何绑定方法 111 | 112 | 21. mysql调优做过吗 113 | 114 | ## 项目类 115 | 116 | 1. 谈一下你的项目 117 | 118 | 2. 你最有成就感的是什么 119 | 120 | 3. 项目中你遇到的最大的困难是什么 121 | 122 | 4. 谈一下部门内的集成测试和持续发布是如何做的 123 | 124 | 5. 用白板画一画你们公司持续发布的流程设计 125 | 126 | 6. 用白板画一画你设计的AWS资源申请系统(着重产品设计,而不是功能开发) 127 | 128 | 7. 项目中最难的地方,解决的最困难的问题 129 | 130 | ### 其他类 131 | 132 | 1. 为什么从上一家公司离职 133 | 134 | 2. 为什么选我们公司 135 | 136 | 3. 你有什么特长或爱好 137 | 138 | 4. 为什么不在上海 139 | 140 | 5. 为什么又回到上海 141 | 142 | ### 算法题 143 | 144 | 输入一维数组array和n,找出和值为n的任意两个元素。例如: 145 | array = [2, 3, 1, 10, 4, 30] n = 31 146 | 则结果应该输出1, 30 顺序不重要 147 | 如果有多个满足条件的,返回任意一对即可,不能使用哈希表; 148 | -------------------------------------------------------------------------------- /11_实战模拟/真实的面试问题2.md: -------------------------------------------------------------------------------- 1 | # 真实的面试问题2 2 | 3 | ## 美团 4 | 5 | 1. 说一下Young GC和Full GC,什么时候会触发 6 | 2. 如何人为的制造GC 7 | 3. hashmap是线程安全的吗,和concurrenthashmap的区别 8 | 4. synchronized关键字的内部实现原理 9 | 5. Java中的线程池 10 | 6. Java中的设计模式,工厂模式的使用场景 11 | 7. Java的类加载机制 12 | 8. Spring是怎么加载bean的 13 | 9. MySQL中的联合索引 14 | 10. 悲观锁和乐观锁,分别适用于什么样的场景 15 | 11. 一张订单表order,如下,查询出总额最高的表,并按order id分组,按总额排序 16 | 17 | order_id, quantity, unit_price 18 | 1 10 5 19 | 2 5 5 20 | 1 3 5 21 | 22 | 12. 如何删除一张表到只剩一条记录 23 | 13. MySQL的主从同步,同步延时是受什么影响 24 | 25 | 26 | 面试mt之后,被面试官评价说,水平还不如应届生 27 | 如果想要转Java,就要付出比别人更多的努力 28 | 29 | 业务线和技术线,业务线可以参与技术线 -------------------------------------------------------------------------------- /11_实战模拟/真实的面试问题3.md: -------------------------------------------------------------------------------- 1 | # 真实的面试问题3 2 | 3 | ## 字节跳动 4 | 5 | 1. GET和POST区别 6 | 7 | 8 | 9 | 2. 链表和数组的区别 10 | 11 | 3. python的dict实现 12 | 13 | 4. python和java的垃圾回收机制 14 | 15 | 5. mysql的联合索引的结构 16 | 17 | 6. rabbitmq高性能的原因 18 | 19 | 7. rabbitmq的架构 20 | 21 | 8. celery的架构 22 | 23 | 9. 非递归写中序遍历 24 | 25 | https://leetcode-cn.com/problems/binary-tree-inorder-traversal/ 26 | 27 | ``` python 28 | class Solution: 29 | def inorderTraversal(self, root: TreeNode) -> List[int]: 30 | res = [] 31 | stack = [] 32 | while stack or root: 33 | if root: 34 | stack.append(root) 35 | root = root.left 36 | else: 37 | tmp = stack.pop() 38 | res.append(tmp.val) 39 | root = tmp.right 40 | return res 41 | 42 | ``` 43 | 44 | 10. 打家劫舍 45 | 46 | https://leetcode-cn.com/problems/house-robber/ 47 | 48 | ``` python 49 | class Solution: 50 | def rob(self, nums: List[int]) -> int: 51 | if not nums: 52 | return 0 53 | 54 | size = len(nums) 55 | if size == 1: 56 | return nums[0] 57 | 58 | dp = [0] * size 59 | dp[0] = nums[0] 60 | dp[1] = max(nums[0], nums[1]) 61 | for i in range(2, size): 62 | dp[i] = max(dp[i - 2] + nums[i], dp[i - 1]) 63 | 64 | return dp[size - 1] 65 | ``` -------------------------------------------------------------------------------- /11_实战模拟/真实的面试问题4.md: -------------------------------------------------------------------------------- 1 | # ms 2 | 3 | ## hr 4 | 5 | 英文自我介绍,问了跳槽原因,想往哪个方向发展,期望薪资 6 | 7 | ## 一面 8 | 9 | 自我介绍 10 | 项目中的难点,如何克服的 11 | 算法题 12 | 1. 两个数组,改变其中一个数,使两个数组的和相等,最小变动的数目 13 | 2. 一个数,去掉k个数,使得剩下的数最小 14 | 15 | ## 二面 16 | 17 | 按出现频数和相对位置重新排序字符串 18 | 类似题: 19 | 1636. 按照频率将数组升序排序 20 | 21 | ## 三面 22 | 23 | 一个数字字符串中,是否存在连续子数组,使得和为目标值 24 | 325. 和等于K的最长子数组长度 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 | 设计短网址系统,写代码 52 | 53 | 微服务里面的一致性 54 | 55 | 为什么选择微软 56 | 57 | ## 六面 58 | 59 | 求平方根 60 | 61 | 你与其他程序员不同的地方,举例子 62 | 63 | 堆栈区别,垃圾回收 64 | 65 | 线程,进程,协程 66 | 67 | 68 | https://osjobs.net/topk/%E5%BE%AE%E8%BD%AF/ -------------------------------------------------------------------------------- /11_实战模拟/真题模拟.md: -------------------------------------------------------------------------------- 1 | # 真题模拟 2 | 3 | ## 基础题 4 | 5 | 1. 如何让print()函数不换行 6 | 7 | print('hello world', end='') 8 | 9 | 2. __name__=='__main__', __name__的值是什么 10 | 11 | 当执行文件本身时候__name__变量等于main,当调用该模块的时候__name__等于模块名 12 | 13 | 3. crontab的含义 14 | 15 | 分时日月周 16 | 17 | 例子:每年的4月份每周的周一到周三的11点执行脚本。 18 | 19 | ``` shell 20 | 00 11 * 4 1-3 /bin/sh /home/omc/h.sh 21 | ``` 22 | 23 | 4. celery启动命令 24 | 25 | celery -A tasks worker --loglevel=info 26 | 27 | 5. 简单正则的含义 28 | 29 | https://www.cnblogs.com/guyuyun/p/5839881.html 30 | 31 | \d:匹配单个字符 32 | \D:匹配非数字字符 33 | \s:匹配空白字符:空格,\t,\r,\n,\f,\v 34 | \S:匹配非空白字符 35 | \w:匹配单词字符:[a-zA-Z0-9_] 36 | \W:匹配非单次字符 37 | 38 | 6. 字典排序 39 | 40 | list1= sorted(dict1.items(),key=lambda x:x[1]) 41 | 42 | 7. docker的常用命令 43 | 44 | 8. 什么是csrf 45 | 46 | CSRF跨站点请求伪造(Cross—Site Request Forgery) 47 | 48 | 1 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A; 49 | 2 在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A; 50 | 3 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B; 51 | 4 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A; 52 | 53 | 9. 堆和栈的区别 54 | 55 | 10. 为什么了有了GIL还要关注线程安全 56 | 57 | 首先明确:GIL并不是Python的特性,Python完全可以不依赖于GIL 58 | 59 | GIL:保证了同一进程中的多线程在同一时刻只能有一个线程使用CPU执行指令。 60 | 61 | Lock互斥锁:保证了某一段代码,在没有执行完毕之前无法被另一个线程执行。 62 | 63 | 参考资料: 64 | https://www.cnblogs.com/ryxiong-blog/p/10730085.html 65 | https://blog.csdn.net/qq_37189082/article/details/95391482 66 | 67 | 11. IO密集,CPU密集 68 | 69 | - 所谓IO密集型任务,是指磁盘IO、网络IO占主要的任务,计算量很小。比如请求网页、读写文件等。当然我们在Python中可以利用sleep达到IO密集型任务的目的。 70 | 71 | - 所谓计算密集型任务,是指CPU计算占主要的任务,CPU一直处于满负荷状态。比如在一个很大的列表中查找元素(当然这不合理),复杂的加减乘除等。 72 | 73 | https://blog.csdn.net/youanyyou/article/details/78990156 74 | https://blog.csdn.net/cn_wk/article/details/80216310 75 | 76 | ## 编程题 77 | 78 | 1. 手写单例模式 79 | 80 | 第一种方法:使用装饰器 81 | 82 | ``` python 83 | def singleton(cls): 84 | instances = {} 85 | def wrapper(*args, **kwargs): 86 | if cls not in instances: 87 | instances[cls] = cls(*args, **kwargs) 88 | return instances[cls] 89 | return wrapper 90 | 91 | @singleton 92 | class Foo(object): 93 | pass 94 | foo1 = Foo() 95 | foo2 = Foo() 96 | print(foo1 is foo2) # True 97 | ``` 98 | 99 | 第二种方法:使用基类 100 | 101 | New 是真正创建实例对象的方法,所以重写基类的new 方法,以此保证创建对象的时候只生成一个实例 102 | 103 | ``` python 104 | class Singleton(object): 105 | def __new__(cls, *args, **kwargs): 106 | if not hasattr(cls, '_instance'): 107 | cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs) 108 | return cls._instance 109 | 110 | class Foo(Singleton): 111 | pass 112 | 113 | foo1 = Foo() 114 | foo2 = Foo() 115 | 116 | print(foo1 is foo2) # True 117 | ``` 118 | 119 | 第三种方法:元类,元类是用于创建类对象的类,类对象创建实例对象时一定要调用call方法,因此在调用call时候保证始终只创建一个实例即可,type是python的元类 120 | 121 | ``` python 122 | class Singleton(type): 123 | def __call__(cls, *args, **kwargs): 124 | if not hasattr(cls, '_instance'): 125 | cls._instance = super(Singleton, cls).__call__(*args, **kwargs) 126 | return cls._instance 127 | 128 | 129 | # Python2 130 | class Foo(object): 131 | __metaclass__ = Singleton 132 | 133 | # Python3 134 | class Foo(metaclass=Singleton): 135 | pass 136 | 137 | foo1 = Foo() 138 | foo2 = Foo() 139 | print(foo1 is foo2) # True 140 | 141 | ``` 142 | 143 | 2. 读大文件 144 | 145 | ``` python 146 | def readInChunks(fileObj, chunkSize=4096): 147 | while 1: 148 | data = fileObj.read(chunkSize): 149 | if not data: 150 | break 151 | yield data 152 | 153 | with open('bigFile') as f: 154 | for chunk in readInChunks(f): 155 | pass 156 | 157 | ``` 158 | 159 | 3. ajax 160 | 161 | 4. 手写排序 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /11_实战模拟/算法高频题目.md: -------------------------------------------------------------------------------- 1 | 1. 两数之和 2 | https://leetcode-cn.com/problems/two-sum/ 3 | 4 | 2. 无重复字符的最长子串 5 | https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/ -------------------------------------------------------------------------------- /12_Java/01_JVM/00_JVM合集.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ZhiyuSun/python-interview/3562125bafdc67fd6f2ca6f2ce172d9bf502d9ef/12_Java/01_JVM/00_JVM合集.md -------------------------------------------------------------------------------- /12_Java/01_JVM/01_JVM基础.md: -------------------------------------------------------------------------------- 1 | # JVM基础 2 | 3 | ## 常见的编程语言类型 4 | 5 | ### 编程语言的分类 6 | 7 | 首先,我们可以把形形色色的编程从底向上划分为最基本的三大类:机器语言、汇编语言、高级语言。 8 | 9 | 如果按照有没有虚拟机来划分,高级编程语言可分为两类: 10 | - 有虚拟机:Java,Lua,Ruby,部分JavaScript的实现等等 11 | - 无虚拟机:C,C++,C#,Golang,以及大部分常见的编程语言 12 | 13 | 如果按照变量是不是有确定的类型,还是类型可以随意变化来划分,高级编程语言可以分为: 14 | - 静态类型:Java,C,C++等等 15 | - 动态类型:所有脚本类型的语言 16 | 17 | 如果按照是编译执行,还是解释执行,可以分为: 18 | - 编译执行:C,C++,Golang,Rust,C#,Java,Scala,Clojure,Kotlin,Swift...等等 19 | - 解释执行:JavaScript的部分实现和NodeJS,Python,Perl,Ruby...等等 20 | 21 | ## 关于跨平台 22 | 23 | - 脚本语言直接使用不同平台的解释器执行,称之为脚本跨平台,平台间的差异由不同平台上的解释器去解决。这样的话代码很通用,但是需要解释和翻译,效率较低。 24 | - 编译型语言的代码跨平台,同一份代码,需要被不同平台的编译器编译成相应的二进制文件,然后再去分发和执行,不同平台间的差异由编译器去解决。编译产生的文件是直接针对平台的可执行指令,运行效率很高。但是在不同平台上编译复杂软件,依赖配置可能会产生很多环境方面问题,导致开发和维护的成本较高。 25 | - 编译型语言的二进制跨平台,同一份代码,先编译成一份通用的二进制文件,然后分发到不同平台,由虚拟机运行时来加载和执行,这样就会综合另外两种跨平台语言的优势,方便快捷地运行于各种平台,虽然运行效率可能比起本地编译类型语言要稍低一点。 而这些优缺点也是Java虚拟机的优缺点。 26 | 27 | ## 关于跨平台、运行时(Runtime)与虚拟机(VM) 28 | 29 | 我们前面提到了很多次 Java运行时和JVM虚拟机,简单的说JRE就是Java的运行时,包括虚拟机和相关的库等资源。 30 | 可以说运行时提供了程序运行的基本环境,JVM在启动时需要加载所有运行时的核心库等资源,然后再加载我们的应用程序字节码,才能让应用程序字节码运行在JVM这个容器里。 31 | 32 | 33 | ## 关于内存管理和垃圾回收(GC) 34 | 35 | Java/Golang完全不相信程序员,但也惯着程序员。所有的内存生命周期都由JVM/运行时统一管理。 在绝大部分场景下,你可以非常自由的写代码,而且不用关心内存到底是什么情况。 内存使用有问题的时候,我们可以通过JVM来信息相关的分析诊断和调整。 这也是本课程的目标。 36 | 37 | -------------------------------------------------------------------------------- /12_Java/01_JVM/02_性能指标.md: -------------------------------------------------------------------------------- 1 | # JVM 2 | 3 | ## JDK、JRE、JVM 4 | 5 | JDK(Java Development Kit) 是用于开发 Java 应用程序的软件开发工具集合,包括了 Java 运行时的环境(JRE)、解释器(Java)、编译器(javac)、Java 归档(jar)、文档生成器(Javadoc)等工具。简单的说我们要开发Java程序,就需要安装某个版本的JDK工具包。 6 | 7 | JRE(Java Runtime Enviroment )提供 Java 应用程序执行时所需的环境,由 Java虚拟机(JVM)、核心类、支持文件等组成。简单的说,我们要是想在某个机器上运行Java程序,可以安装JDK,也可以只安装JRE,后者体积比较小。 8 | 9 | Java Virtual Machine(Java 虚拟机)有三层含义,分别是: 10 | - JVM规范要求 11 | - 满足 JVM 规范要求的一种具体实现(一种计算机程序) 12 | - 一个 JVM 运行实例,在命令提示符下编写 Java 命令以运行 Java 类时,都会创建一个 JVM 实例,我们下面如果只记到JVM则指的是这个含义;如果我们带上了某种JVM的名称,比如说是Zing JVM,则表示上面第二种含义 13 | 14 | ### 三者关系 15 | 16 | 就范围来说,JDK > JRE > JVM: 17 | JDK = JRE + 开发工具 18 | JRE = JVM + 类库 19 | 20 | 三者在开发运行Java程序时的交互关系: 21 | 简单的说,就是通过JDK开发的程序,编译以后,可以打包分发给其他装有JRE的机器上去运行 22 | 而运行的程序,则是通过java命令启动的一个JVM实例,代码逻辑的执行都运行在这个JVM实例上 23 | 24 | Java程序的开发运行过程为: 25 | 我们利用 JDK (调用 Java API)开发Java程序,编译成字节码或者打包程序 26 | 然后可以用 JRE 则启动一个JVM实例,加载、验证、执行 Java 字节码以及依赖库,运行Java程序 27 | 而JVM 将程序和依赖库的Java字节码解析并变成本地代码执行,产生结果 28 | 29 | ## 常用性能指标 30 | 31 | 没有量化就没有改进 32 | 33 | 1. 分析系统性能问题: 比如是不是达到了我们预期性能指标,判断资源层面有没有问题,JVM层面有没有问题,系统的关键处理流程有没有问题,业务流程是否需要优化 34 | 2. 通过工具收集系统的状态,日志,包括打点做内部的指标收集,监控并得出关键性能指标数据,也包括进行压测,得到一些相关的压测数据和性能内部分析数据 35 | 3. 根据分析结果和性能指标,进行资源配置调整,并持续进行监控和分析,以优化性能,直到满足系统要求,达到系统的最佳性能状态 36 | 37 | 计算机系统中,性能相关的资源主要分为这几类: 38 | - CPU:CPU是系统最关键的计算资源,在单位时间内有限,也是比较容易由于业务逻辑处理不合理而出现瓶颈的地方,浪费了CPU资源和过渡消耗CPU资源都不是理想状态,我们需要监控相关指标; 39 | - 内存:内存则对应程序运行时直接可使用的数据快速暂存空间,也是有限的,使用过程随着时间的不断的申请内存又释放内存,好在JVM的GC帮我们处理了这些事情,但是如果GC配置的不合理,一样会在一定的时间后,产生包括OOM宕机之类的各种问题,所以内存指标也需要关注; 40 | - IO(存储+网络):CPU在内存中把业务逻辑计算以后,为了长期保存,就必须通过磁盘存储介质持久化,如果多机环境、分布式部署、对外提供网络服务能力,那么很多功能还需要直接使用网络,这两块的IO都会比CPU和内存速度更慢,所以也是我们关注的重点。 41 | 42 | 一般衡量系统性能的维度有3个: 43 | - 延迟(Latency): 一般衡量的是响应时间(Response Time),比如平均响应时间。但是有时候响应时间抖动的特别厉害,也就是说有部分用户的响应时间特别高,这时我们一般假设我们要保障95%的用户在可接受的范围内响应,从而提供绝大多数用户具有良好的用户体验,这就是延迟的95线(P95,平均100个用户请求中95个已经响应的时间),同理还有99线,最大响应时间等(95线和99线比较常用;用户访问量大的时候,对网络有任何抖动都可能会导致最大响应时间变得非常大,最大响应时间这个指标不可控,一般不用)。 44 | - 吞吐量(Throughput): 一般对于交易类的系统我们使用每秒处理的事务数(TPS)来衡量吞吐能力,对于查询搜索类的系统我们也可以使用每秒处理的请求数(QPS)。 45 | - 系统容量(Capacity): 也叫做设计容量,可以理解为硬件配置,成本约束。 46 | 47 | 性能指标还可分为两类: 48 | - 业务需求指标:如吞吐量(QPS、TPS)、响应时间(RT)、并发数、业务成功率等。 49 | - 资源约束指标:如CPU、内存、I/O等资源的消耗情况。 50 | 51 | 我们可采用的手段和方式包括: 52 | - 使用JDWP或开发工具做本地/远程调试 53 | - 系统和JVM的状态监控,收集分析指标 54 | - 性能分析: CPU使用情况/内存分配分析 55 | - 内存分析: Dump分析/GC日志分析 56 | - 调整JVM启动参数,GC策略等等 57 | 58 | 性能调优的第一步是制定指标,收集数据,第二步是找瓶颈,然后分析解决瓶颈问题。 59 | 60 | 脱离场景谈性能都是耍流氓 61 | 过早的优化是万恶之源 62 | 63 | -------------------------------------------------------------------------------- /12_Java/01_JVM/03_字节码技术.md: -------------------------------------------------------------------------------- 1 | # 字节码技术 2 | 3 | -------------------------------------------------------------------------------- /12_Java/01_JVM/04_Java类加载器.md: -------------------------------------------------------------------------------- 1 | # Java类加载器 2 | 3 | ## 类的生命周期和加载过程 4 | 5 | 一个类在JVM里的生命周期有7个阶段,分别是加载(Loading)、 6 | 验证(Verification)、准备(Preparation)、解析(Resolution)、 7 | 初始化(Initialization)、使用(Using)、卸载(Unloading)。 8 | 9 | ## 类加载时机 10 | 11 | ## 类加载器机制 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /12_Java/01_JVM/05_Java内存模型.md: -------------------------------------------------------------------------------- 1 | # Java内存模型 2 | 3 | Java Memory Model,简称JMM 4 | 5 | ## JVM内存结构 6 | 7 | JVM内部使用的Java内存模型, 在逻辑上将内存划分为 线程栈(thread stacks)和堆内存 (heap)两个部分。 8 | 9 | JVM中,每个正在运行的线程,都有自己的线程栈。 线程栈包含了当前正在执行的方法链/调用链上的所有方法的状态信息。 10 | 所以线程栈又被称为“ 方法栈”或“ 调用栈”(call stack)。 线程在执行代码时,调用栈中的信息会一直在变化。 11 | 12 | 线程栈里面保存了调用链上正在执行的所有方法中的局部变量。 13 | - 每个线程都只能访问自己的线程栈。 14 | - 每个线程都不能访问(看不见)其他线程的局部变量。 15 | 16 | 即使两个线程正在执行完全相同的代码,但每个线程都会在自己的线程栈内创建对应代码中声明的局部变量。 所以每个线程都有一份自己的局部变量副本。 17 | 18 | - 所有原生类型的局部变量都存储在线程栈中,因此对其他线程是不可见的。 19 | - 线程可以将一个原生变量值的副本传给另一个线程,但不能共享原生局部变量本身。 20 | - 堆内存中包含了Java代码中创建的所有对象,不管是哪个线程创建的。 其中也涵盖了包装类型(例如Byte , Integer , Long 等)。 21 | - 不管是创建一个对象并将其赋值给局部变量, 还是赋值给另一个对象的成员变量, 创建的对象都会被保存到堆内存中。 22 | 23 | 如果是原生数据类型的局部变量,那么它的内容就全部保留在线程栈上。 24 | 如果是对象引用,则栈中的局部变量槽位中保存着对象的引用地址,而实际的对象内容保存在堆中。 25 | 对象的成员变量与对象本身一起存储在堆上, 不管成员变量的类型是原生数值,还是对象引用。 26 | 类的静态变量则和类定义一样都保存在堆中。 27 | 28 | 总结一下:方法中使用的原生数据类型和对象引用地址在栈上存储;对象、对象成员与类定义、静态变量在堆上。 29 | 30 | ## 栈内存的结构 31 | 32 | ## 堆内存的结构 33 | 34 | ## CPU指令与乱序执行 35 | 36 | ## JMM简介 37 | 38 | ## 小结 39 | 40 | 1. JVM的内存区域分为: 堆内存 和 栈内存; 41 | 2. 堆内存的实现可分为两部分: 堆(Heap) 和 非堆(Non‐Heap) ; 42 | 3. 堆主要由GC负责管理,按分代的方式一般分为: 老年代+年轻代;年轻代=新生代+存活区; 43 | 4. CPU有一个性能提升的利器: 指令重排序; 44 | 5. JMM规范对应的是 JSR133, 现在由Java语言规范和JVM规范来维护; 45 | 6. 内存屏障的分类与作用。 46 | 47 | ## 经验总结 48 | 49 | -Xmx 最大堆内存 50 | -Xms 最小堆内存 51 | -Xmn 新生代的大小 52 | -XX:MaxPermSize:持久代的最大值, -XX:PermSize:持久代的初始大小 53 | -Xss 线程栈的大小 54 | 55 | -------------------------------------------------------------------------------- /12_Java/01_JVM/06_推荐材料.md: -------------------------------------------------------------------------------- 1 | 《Java程序性能优化 让你的Java程序更快、更稳定》 2 | 3 | 《垃圾回收的算法与实现》 4 | 5 | -------------------------------------------------------------------------------- /12_Java/02——GC/学习笔记.md: -------------------------------------------------------------------------------- 1 | # GC 2 | 3 | ## 引用计数 4 | 5 | Java语言中,单纯的使用引用计数器算法实现垃圾回收是不可行的 6 | 7 | ## 回收算法 8 | 9 | 内存分代:新生代,老年代 10 | 新生代使用复制算法,老年代使用标记-压缩算法 11 | 12 | -------------------------------------------------------------------------------- /12_Java/10_面试知识点/知识整理.md: -------------------------------------------------------------------------------- 1 | # 知识整理 2 | 3 | static 4 | 5 | static关键字主要有两个作用:第一,为某特定数据类型或对象分配单一的存储空间,而与创建对象的个数无关。第二,实现某个方法或属性与类而不是对象关联在一起,也就是说,在不创建对象的情况下就可以通过类来直接使用类的方法或者属性 6 | 7 | final 8 | 9 | final用于声明属性、方法和类,分别表示属性不可变、方法不可覆盖、类不可被继承(不能再派生出新的子类)。 10 | 11 | **反射** 12 | 13 | -------------------------------------------------------------------------------- /12_Java/Java基础.md: -------------------------------------------------------------------------------- 1 | # Java基础 2 | 3 | ## 基本数据 4 | 5 | - byte/8 6 | - char/16 7 | - short/16 8 | - int/32 9 | - float/32 10 | - long/64 11 | - double/64 12 | - boolean/~ 13 | 14 | ### 自动装箱自动拆箱 15 | ### 缓存池 16 | 17 | ## String 18 | 19 | String 被声明为 final,因此它不可被继承。 20 | 21 | ### String, StringBuffer and StringBuilder 22 | 23 | 1. 可变性 24 | 25 | String 不可变 26 | StringBuffer 和 StringBuilder 可变 27 | 28 | 2. 线程安全 29 | 30 | String 不可变,因此是线程安全的 31 | StringBuilder 不是线程安全的 32 | StringBuffer 是线程安全的,内部使用 synchronized 进行同步 33 | 34 | ## 运算 35 | 36 | ### 参数传递 37 | 38 | Java 的参数是以值传递的形式传入方法中,而不是引用传递。 39 | 40 | ### float与double 41 | 42 | ### 隐式类型转换 43 | 44 | ### switch 45 | 46 | ## 关键字 47 | 48 | ### final 49 | -------------------------------------------------------------------------------- /13_系统设计/CAP.md: -------------------------------------------------------------------------------- 1 | # CAP原理 2 | 3 | **一致性Consistency** 4 | 5 | 一致性是说,每次读取的数据都应该是最近写入的数据或者返回一个错误(Every readreceives the most recent write or an error),而不是过期数据,也就是说,数据是一致的。 6 | 7 | **可用性Availability** 8 | 9 | 可用性是说,每次请求都应该得到一个响应,而不是返回一个错误或者失去响应,不过这个响应不需要保证数据是最近写入的(Every request receives a (non-error)response, without the guarantee that it contains the most recent write),也就是说系统需要一直都是可以正常使用的,不会引起调用者的异常,但是并不保证响应的数据是最新的。 10 | 11 | **分区耐受性Partition tolerance** 12 | 13 | 分区耐受性说,即使因为网络原因,部分服务器节点之间消息丢失或者延迟了,系统依然应该是可以操作的(The system continues to operate despite an arbitrary number of messages being dropped (or delayed) by the network between nodes)。 14 | 15 | ## CAP 原理 16 | 17 | 当网络分区失效发生的时候,我们要么取消操作,这样数据就是一致的,但是系统却不可用;要么我们继续写入数据,但是数据的一致性就得不到保证。 18 | 19 | 对于一个分布式系统而言,网络失效一定会发生,也就是说,分区耐受性是必须要保证的,那么在可用性和一致性上就必须二选一。 20 | 21 | 当网络分区失效,也就是网络不可用的时候,如果选择了一致性,系统就可能返回一个错误码或者干脆超时,即系统不可用。如果选择了可用性,那么系统总是可以返回一个数据,但是并不能保证这个数据是最新的。 22 | 23 | 所以,关于CAP 原理,更准确的说法是,在分布式系统必须要满足分区耐受性的前提下,可用性和一致性无法同时满足。 -------------------------------------------------------------------------------- /13_系统设计/【学习】系统设计.md: -------------------------------------------------------------------------------- 1 | # 系统设计 2 | 3 | ## 经验整理 4 | 5 | https://www.youtube.com/watch?v=ZgdS0EUmn70 6 | 看了youtube上的视频,给我的感觉是,系统设计类的问题没有标准答案,关键是调度起你的所有知识,努力尝试去解决这个问题,将问题一步一步的分解。 7 | 如果之前的工作中没有负责过大型系统,也要主动去阅读一些材料,参加一些技术会议等等,培养这方面的知识储备。 8 | 9 | https://www.palantir.com/2011/10/how-to-rock-a-systems-design-interview/ 10 | 关注思考过程 11 | 真实世界没有正确答案,所有的都是一个权衡 12 | 13 | 几个你必须要熟悉的议题: 14 | 并发,网络,抽象,真实表现,估算值,可用性和可靠性 15 | 不是去寻找精通所有主题的人,而是看熟悉程度 16 | 17 | 文章还讲了如何准备,不一一整理 18 | 19 | https://www.hiredintech.com/system-design 20 | 21 | 这一个教程非常精彩 22 | 约束和用例 23 | The very first thing you should do with any system design question is to clarify the system's constraints and to identify what use cases the system needs to satisfy. 24 | 25 | Usually, part of what the interviewer wants to see is if you can gather the requirements about the problem at hand, and design a solution that covers them well. Never assume things that were not explicitly stated. 26 | 27 | 28 | 29 | ## 经验数值 30 | 31 | 依据一些云厂商的 Benchmark 的结果,在 4 核 8G 的机器上运行 MySQL 5.7 时,大概可以支撑 500 的 TPS 和 10000 的 QPS 32 | 33 | 34 | 35 | 36 | 参考资料 37 | 38 | https://github.com/donnemartin/system-design-primer/blob/master/README-zh-Hans.md 39 | 40 | https://www.palantir.com/2011/10/how-to-rock-a-systems-design-interview/ 41 | 42 | https://www.hiredintech.com/system-design 43 | 44 | https://www.youtube.com/watch?v=ZgdS0EUmn70 -------------------------------------------------------------------------------- /13_系统设计/性能测试.md: -------------------------------------------------------------------------------- 1 | # 性能测试 2 | 3 | https://blog.csdn.net/makang110/article/details/52248597 4 | 5 | 这里就大致根据理论最大QPS,给网站做几个分类 6 | 7 | 50QPS以下——小网站 8 | 9 | 没什么好说的,简单的小网站而已,就如同本站这样,你可以用最简单的方法快速搭建,短期没有太多的技术瓶颈,只要服务器不要太烂就好。 10 | 11 | 50~100QPS——DB极限型 12 | 13 | 大部分的关系型数据库的每次请求大多都能控制在0.01秒左右,即便你的网站每页面只有一次DB请求,那么页面请求无法保证在1秒钟内完成100个请求,这个阶段要考虑做Cache或者多DB负载。无论那种方案,网站重构是不可避免的。 14 | 15 | 300~800QPS——带宽极限型 16 | 17 | 目前服务器大多用了IDC提供的“百兆带宽”,这意味着网站出口的实际带宽是8M Byte左右。假定每个页面只有10K Byte,在这个并发条件下,百兆带宽已经吃完。首要考虑是CDN加速/异地缓存,多机负载等技术。 18 | 19 | 500~1000QPS——内网带宽极限+Memcache极限型 20 | 21 | 由于Key/value的特性,每个页面对memcache的请求远大于直接对DB的请求,Memcache的悲观并发数在2w左右,看似很高,但事实上大多数情况下,首先是有可能在次之前内网的带宽就已经吃光,接着是在8K QPS左右的情况下,Memcache已经表现出了不稳定,如果代码上没有足够的优化,可能直接将压力转嫁到了DB层上,这就最终导致整个系统在达到某个阀值之上,性能迅速下滑。 22 | 23 | 1000~2000QPS——FORK/SELECT,锁模式极限型 24 | 25 | 好吧,一句话:线程模型决定吞吐量。不管你系统中最常见的锁是什么锁,这个级别下,文件系统访问锁都成为了灾难。这就要求系统中不能存在中央节点,所有的数据都必须分布存储,数据需要分布处理。总之,关键词:分布 26 | 27 | 2000QPS以上——C10K极限 28 | 29 | 尽管现在很多应用已经实现了C25K,但短板理论告诉我们,决定网站整体并发的永远是最低效的那个环节。我承认我生涯中从未遇到过2000QPS以上,甚至1.5K以上的网站,希望有此经验的哥们可以一起交流下 30 | 31 | -------------------------------------------------------------------------------- /13_系统设计/高并发.md: -------------------------------------------------------------------------------- 1 | # 高并发 2 | 3 | 使用Synchronized 解决,给生成ID 的代码加上同步代码块,成功解决问题; 4 | 作用是:同一时刻,只有一个线程可以执行该代码块 5 | 6 | 使用Lock 锁解决问题:给生成ID 的代码加上Lock 锁,成功解决问题; 7 | 8 | 悲观锁---在修改数据的时候,采用锁定状态,排斥外部请求的修改,遇到加锁的状态,就必须等待 9 | 10 | 弊端: 在高并发下,每个请求都需要等待’锁’,某些线程可能永远都没有机会抢到这个锁,请求就会死在那里,这种请求会很多,瞬间增系统的平均响应时间,结果时可用链接数被耗尽,系统陷入异常 11 | 12 | FIFO 队列----采用FIFO(First Input First Output,先进先出),这样就不会导致某些请求永远获取不到锁 13 | 14 | 弊端:请求很多,很有可能一瞬间将队列内存”撑爆”,系统陷入到异常状态,或者设计一个极大的内存队列,但是系统处理完一个队列,内请求的速度根本无法和疯狂涌入队列中的数目相比,也就是说,队列内的请求会越积累越多,最终WEB 系统平均响应时候还是会大幅下降,系统还是陷入异常。 15 | 16 | 乐观锁---相对于”悲观锁”采用更为宽松的加锁机制,大都是采用带版本号更新,实现就是,这个数据所有请求都有资格去修改,但会获得一个该数据的版本号,只有版本号符合才能更新成功,其他的返回抢购失败,这样就不用了考虑队列的问题,会增大CPU 的计算开销, 17 | 18 | -------------------------------------------------------------------------------- /14_打小抄/01_编程语言.md: -------------------------------------------------------------------------------- 1 | ## java 2 | 3 | Map hashtable = new HashMap(); 4 | hashtable.put(nums[i], i); 5 | hashtable.containsKey(target - nums[i]) 6 | hashtable.get(target - nums[i]) 7 | 8 | Set hashset = new HashSet(); 9 | hashset.add(x.getValue()); 10 | 11 | public class TreeNode { 12 | int val; 13 | TreeNode left; 14 | TreeNode right; 15 | TreeNode() {} 16 | TreeNode(int val) { this.val = val; } 17 | TreeNode(int val, TreeNode left, TreeNode right) { 18 | this.val = val; 19 | this.left = left; 20 | this.right = right; 21 | } 22 | } 23 | 24 | ## python 25 | 26 | class TreeNode: 27 | def __init__(self, val=0, left=None, right=None): 28 | self.val = val 29 | self.left = left 30 | self.right = right 31 | -------------------------------------------------------------------------------- /14_打小抄/02_零碎知识点.md: -------------------------------------------------------------------------------- 1 | # 零碎知识点 2 | 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # python-interview 2 | 3 | python面试知识点整理,涵盖 4 | 5 | - 语言基础 6 | - 数据结构和算法 7 | - 类和面向对象 8 | - 操作系统 9 | - 网络 10 | - 数据库 11 | - web开发 12 | 13 | 既是个人学习总结,也希望能对广大程序员朋友有帮助 14 | 15 | 持续更新中~~~ 16 | 17 | 欢迎star 18 | -------------------------------------------------------------------------------- /专题整理/MySQL/常见面试题.md: -------------------------------------------------------------------------------- 1 | # MySQL 2 | 3 | ## MySQL存储引擎 4 | 5 | mysql MyISAM与innodb的区别 6 | 7 | ## MySQL锁 8 | 9 | mysql 中的乐观锁和悲观锁实现 10 | 乐观锁和悲观锁 11 | MySQL 锁机制是怎样的 12 | 如果避免、减少锁等待、团队中如何监控MySQL 的锁等待的情况 13 | MySQL 有哪些锁 14 | 15 | ## 数据库事务 16 | 17 | mysql的事务是怎么实现的 18 | 19 | 事务隔离级别 20 | 21 | 数据库的事物隔离及其各自产生的赃读或幻读现象 22 | 23 | 事务的基本特征,事务的隔离级别 24 | 25 | MySQL 如何避免幻读 26 | 27 | ## 数据库索引 28 | 29 | MySQL索引结构优缺点 30 | mysql联合索引,实现,优点 31 | 数据库的索引建立实例,where 子句和order by 子句同时存在如何建立索引以及为什么 32 | mysql 联合索引在什么情况能用到 33 | sql什么时候不适合用索引,索引太多会有啥问题 34 | 复合索引的结构 35 | mysql 索引的数据结构 36 | mysql 索引结构,B+树,IN 是否参与索引,最左查询 37 | MySQL 为什么采用B+树而不用B 树 38 | 索引什么时候不生效 39 | 40 | ## SQL 41 | 42 | 批量插入数据库有啥优化点,文件导入或单句执行(原因锁表开销少) 43 | mysql优化 44 | mysql explain 的作用 45 | 一个表user_id,order_date,要查用户订单,某一天订单,某个用户某天订单,如何建索引 46 | 怎么分析优化慢查询 47 | 数据库参数调优 48 | 49 | ## 数据库分库分表 50 | 51 | ## mysql 的主从复制 52 | 53 | 54 | 55 | 56 | ### 6. MySQL两种存储引擎的差异 57 | 58 | 简单介绍区别: 59 | 60 | - MyISAM是非事务安全的,而InnoDB是事务安全的 61 | - MyISAM锁的粒度是表级的,而InnoDB支持行级锁 62 | - MyISAM支持全文类型索引,而InnoDB不支持全文索引 63 | - MyISAM相对简单,效率上要优于InnoDB,小型应用可以考虑使用MyISAM 64 | - MyISAM表保存成文件形式,跨平台使用更加方便 65 | 66 | 1、MyISAM管理非事务表,提供高速存储和检索以及全文搜索能力,如果再应用中执行大量select操作,应该选择MyISAM 67 | 2、InnoDB用于事务处理,具有ACID事务支持等特性,如果在应用中执行大量insert和update操作,应该选择InnoDB 68 | 69 | 参考资料:https://juejin.cn/post/6903101301429796871 70 | 71 | ### 7. where a>1 and b>1;where a = 1;where b = 2;如何为这种条件语句建立索引 72 | 73 | table(a,b) 74 | table(b) 75 | 76 | ### 5. 遇到过哪些慢查询,如何优化 77 | ### 5. Oracle相对于MySQL有什么优势 78 | 79 | 略 80 | 81 | ### 7. MySQL存储引擎区别 82 | 83 | ### 8. MySQL索引结构优缺点 84 | 85 | ### 4. mysql联合索引,实现,优点 86 | 87 | ### 9. mysql的事务是怎么实现的 88 | 89 | ### 10. mysql 联合索引在什么情况能用到 90 | ### 1. 数据库的索引建立实例,where 子句和order by 子句同时存在如何建立索引以及为什么 91 | 92 | ### 2. 数据库的事物隔离及其各自产生的赃读或幻读现象 93 | 94 | ### 3. 批量插入数据库有啥优化点,文件导入或单句执行(原因锁表开销少) 95 | ### 7. mysql相关: 引擎innodb特性,锁,事务 96 | 97 | ### 1. sql什么时候不适合用索引,索引太多会有啥问题, 98 | 99 | # 字节面试题 100 | 101 | ## 数据库篇 102 | 103 | ### 1. mysql 中的乐观锁和悲观锁实现 104 | 105 | ### 2. mysql explain 的作用 106 | 107 | ### 3. mysql 的主从复制 108 | 109 | ### 4. mysql 的引擎区别 110 | 111 | ### 5. 一个表user_id,order_date,要查用户订单,某一天订单,某个用户某天订单,如何建索引 112 | 113 | ### 6. 复合索引的结构 114 | 115 | ### 7. mysql 索引的数据结构 116 | 117 | ### 8. 事务的基本特征,事务的隔离级别 118 | 119 | ### 9. 怎么分析优化慢查询 120 | 121 | ### 10. mysql 索引结构,B+树,IN 是否参与索引,最左查询 122 | 123 | ### 11. 乐观锁和悲观锁 124 | 125 | ### 12. 数据库参数调优 126 | 127 | ### 13. MySQL 如何避免幻读 128 | 129 | ### 14. MySQL 锁机制是怎样的 130 | 131 | ### 15. 如果避免、减少锁等待、团队中如何监控MySQL 的锁等待的情况 132 | 133 | ### 16. MySQL 为什么采用B+树而不用B 树 134 | 135 | ### 17. MySQL 有哪些锁 136 | 137 | ### 18. 索引什么时候不生效 138 | -------------------------------------------------------------------------------- /专题整理/MySQL/我的整理.md: -------------------------------------------------------------------------------- 1 | 彻底理解事务 2 | http://www.hollischuang.com/archives/898 3 | 4 | 索引 5 | http://blog.codinglabs.org/articles/theory-of-mysql-index.html 6 | https://tech.meituan.com/2014/06/30/mysql-index.html 7 | 8 | 4 乐观锁和悲观锁 9 | 悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作 10 | 11 | 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。 12 | 13 | 乐观锁与悲观锁的具体区别: http://www.cnblogs.com/Bob-FD/p/3352216.html 14 | 15 | 16 | 5 MVCC 17 | ​ 全称是Multi-Version Concurrent Control,即多版本并发控制,在MVCC协议下,每个读操作会看到一个一致性的snapshot,并且可以实现非阻塞的读。MVCC允许数据具有多个版本,这个版本可以是时间戳或者是全局递增的事务ID,在同一个时间点,不同的事务看到的数据是不同的。 18 | 19 | MySQL的innodb引擎是如何实现MVCC的 20 | innodb会为每一行添加两个字段,分别表示该行创建的版本和删除的版本,填入的是事务的版本号,这个版本号随着事务的创建不断递增。在repeated read的隔离级别(事务的隔离级别请看这篇文章)下,具体各种数据库操作的实现: 21 | 22 | select:满足以下两个条件innodb会返回该行数据: 23 | 该行的创建版本号小于等于当前版本号,用于保证在select操作之前所有的操作已经执行落地。 24 | 该行的删除版本号大于当前版本或者为空。删除版本号大于当前版本意味着有一个并发事务将该行删除了。 25 | insert:将新插入的行的创建版本号设置为当前系统的版本号。 26 | delete:将要删除的行的删除版本号设置为当前系统的版本号。 27 | update:不执行原地update,而是转换成insert + delete。将旧行的删除版本号设置为当前版本号,并将新行insert同时设置创建版本号为当前版本号。 28 | 其中,写操作(insert、delete和update)执行时,需要将系统版本号递增。 29 | 30 | ​ 由于旧数据并不真正的删除,所以必须对这些数据进行清理,innodb会开启一个后台线程执行清理工作,具体的规则是将删除版本号小于当前系统版本的行删除,这个过程叫做purge。 31 | 32 | 通过MVCC很好的实现了事务的隔离性,可以达到repeated read级别,要实现serializable还必须加锁。 33 | 34 | 参考:MVCC浅析http://blog.csdn.net/chosen0ne/article/details/18093187 35 | 36 | 6 MyISAM和InnoDB 37 | MyISAM 适合于一些需要大量查询的应用,但其对于有大量写操作并不是很好。甚至你只是需要update一个字段,整个表都会被锁起来,而别的进程,就算是读进程都无法操作直到读操作完成。另外,MyISAM 对于 SELECT COUNT(*) 这类的计算是超快无比的。 38 | 39 | InnoDB 的趋势会是一个非常复杂的存储引擎,对于一些小的应用,它会比 MyISAM 还慢。他是它支持“行锁” ,于是在写操作比较多的时候,会更优秀。并且,他还支持更多的高级应用,比如:事务。 40 | 41 | mysql 数据库引擎: http://www.cnblogs.com/0201zcr/p/5296843.html MySQL存储引擎--MyISAM与InnoDB区别: https://segmentfault.com/a/1190000008227211 42 | 43 | -------------------------------------------------------------------------------- /专题整理/MySQL/我的整理/主从同步.md: -------------------------------------------------------------------------------- 1 | # 主从同步 2 | 3 | https://time.geekbang.org/column/article/135697 -------------------------------------------------------------------------------- /专题整理/MySQL/我的整理/事务.md: -------------------------------------------------------------------------------- 1 | # 事务 2 | 3 | ## 数据库事务的四大特性 4 | 5 | ACID 6 | - 原子性(Atomic) 7 | - 一致性(Consistencey) 8 | - 隔离性(Isolation):事务之间不互相影响 9 | - 持久性(Durability):事务一旦提交,对数据库的修改是永久的 10 | 11 | ## 事务隔离级别以及各级别下的并发访问问题 12 | 13 | 事务并发访问引起的问题以及如何避免 14 | - 更新丢失:MySQL所有事务隔离级别在数据库层面上均可避免 15 | - 脏读——READ-COMMITED事务隔离级别以上可避免。事务允许读到未提交的事务,前一个事务如果回滚,会出错 16 | - 不可重复度——REPEATABLE-READ事务隔离级别以上可避免。一个事务读的时候,另一个事务在修改,导致前面的事务读的两个数据不一致 17 | - 幻读——SERIALIZABLE事务隔离级别可避免。一个事务在更新的时候,如果另一个事务插入或删除行,第一个事务会发现更新的数目多了 18 | 19 | 总结: 20 | 21 | 事务隔离级别(从低到高) 更新丢失 脏读 不可重复读 幻读 22 | 未提交读 避免 发生 发生 发生 23 | 已提交读 避免 避免 发生 发生 (Oracle默认) 24 | 可重复读 避免 避免 避免 发生 (MySQL默认) 25 | 串行化 避免 避免 避免 避免 26 | 27 | 出于性能考虑,事务隔离级别越高,越容易降低并发度 28 | 事务隔离级越高,串行化越高,并发度越低 29 | 30 | ## InnoDB可重复读隔离级别下如何避免幻读 31 | 32 | 表象:快照读(非阻塞读)——伪MvCC 33 | 内在:next-key锁(行锁+gap锁) 34 | 35 | 当前读和快照读 36 | 当前读:select ……lock in share mode(共享锁), select …… for update(排它锁) 37 | 当前读:update,delete,insert (排它锁) 38 | 快照读:不加锁的非阻塞读,select 39 | 40 | RC隔离级别下,当前读和快照读的结果是一样的 41 | RR隔离级别下,当前读返回最新版本,快照读返回历史版本。创建快照的时机决定了读的版本 42 | 43 | gap锁,好难,根本记不住 44 | 45 | ## RC、RR级别下的Innodb非阻塞读如何实现 46 | 47 | 快照读——非阻塞读 48 | 49 | ## redo log undo log bin log 50 | 51 | ## 事务隔离做不好会产生 52 | - 脏读:读到还未提交的内容 53 | - 不可重复度:两次读读到更新后的数据 54 | - 幻读:两次读读到新增的数据 55 | 56 | ## 事务隔离级别 57 | - 读未提交:脏读,不可重复读,幻读 58 | - 读已提交:不可重复读,幻读 59 | - 可重复读:幻读(MYSQL默认隔离级别,MySQL可解决(MVCC+当前读,搞定了这个问题) 60 | - 串行化 61 | 62 | 知鱼君注: 63 | select for update 是当前读,会阻塞 64 | select 是快照读(mvcc,记录最早读到的版本) 65 | 66 | 幻读和MVCC 67 | 68 | ## Mysql 在可重复读的隔离级别下会不会有幻读的情况,为什么? 69 | 70 | InnoDB 是如何解决幻读的。 71 | 72 | 在可重复读的情况下,InnoDB 可以通过 Next-Key 锁 +MVCC 来解决幻读问题。 73 | 74 | 在读已提交的情况下,即使采用了 MVCC 方式也会出现幻读。如果我们同时开启事务 A 和事务 B,先在事务 A 中进行某个条件范围的查询,读取的时候采用排它锁,在事务 B 中增加一条符合该条件范围的数据,并进行提交,然后我们在事务 A 中再次查询该条件范围的数据,就会发现结果集中多出一个符合条件的数据,这样就出现了幻读。 75 | 76 | 出现幻读的原因是在读已提交的情况下,InnoDB 只采用记录锁(Record Locking)。这里要介绍下 InnoDB 三种行锁的方式: 77 | - 记录锁:针对单个行记录添加锁。 78 | - 间隙锁(Gap Locking):可以帮我们锁住一个范围(索引之间的空隙),但不包括记录本身。采用间隙锁的方式可以防止幻读情况的产生。 79 | - Next-Key 锁:帮我们锁住一个范围,同时锁定记录本身,相当于间隙锁 + 记录锁,可以解决幻读的问题。 80 | 81 | 在隔离级别为可重复读时,InnoDB 会采用 Next-Key 锁的机制,帮我们解决幻读问题。 82 | 83 | 还是这个例子,我们能看到当我们想要插入球员艾利克斯·伦(身高 2.16 米)的时候,事务 B 会超时,无法插入该数据。这是因为采用了 Next-Key 锁,会将 height>2.08 的范围都进行锁定,就无法插入符合这个范围的数据了。然后事务 A 重新进行条件范围的查询,就不会出现幻读的情况。 84 | 85 | 极客时间 SQL必知必会 86 | https://time.geekbang.org/column/article/120351 87 | 88 | ## MySQL事务是如何实现的? 89 | 90 | - 原子性:通过undo log实现的。每条数据变更都伴随一条undo log日志的生成,当系统发生错误或执行回滚根据undo log做逆向操作 91 | - 持久性:通过redo log实现的。redo log记录了数据的修改日志。数据持久化到磁盘,先是储存到缓冲池里,然后缓冲池中的数据定期同步到磁盘中,如果系统宕机,可能会丢失数据,系统重启后会读取redo log恢复数据 92 | - 隔离性:mysql数据库通过MVCC + next-key机制实现了隔离性 93 | - 一致性:以上3大特性,保障了事务的一致性 94 | 95 | ## Binlog 和 Redo log 的区别是什么,分别是什么用? 96 | 97 | - binlog是二进制文件,记录了对数据库执行更改的所有操作,不包括 select、show,因为这两个操作没有对数据本身做修改。但是若操作了数据,但是数据没有发生变化,也会记录到binlog。常用来数据恢复,数据备份。 98 | - redo log又叫做重做日志文件,记录了事务的修改,不管事务是否提交都记录下来。在实例和介质失败时,InnoDB存储引擎会使用redo log恢复到之前的状态,保证数据的完整性 99 | 100 | ## 有了MySQL事务隔离级别,为什么有并发请求的时候还要加锁(乐观锁,悲观锁)呢? 101 | 102 | https://bbs.csdn.net/topics/392134885 103 | 104 | https://www.cnblogs.com/hbuwdx/p/6593145.html 105 | 106 | https://blog.csdn.net/qq_21294095/article/details/84888802 107 | 108 | ## 参考资料 109 | 110 | https://blog.csdn.net/w139074301/article/details/112004430 -------------------------------------------------------------------------------- /专题整理/MySQL/我的整理/分库分表.md: -------------------------------------------------------------------------------- 1 | # 分库分表 2 | 3 | - 垂直拆分 4 | - 水平拆分 5 | - 多主多从 6 | 7 | join的表不能做跨库 8 | 9 | 垂直拆分 10 | - user,user_collect,item 11 | 12 | 水平拆分 13 | - 路由位 14 | - 时间戳 15 | - -------------------------------------------------------------------------------- /专题整理/MySQL/我的整理/性能优化.md: -------------------------------------------------------------------------------- 1 | # 性能优化 2 | 3 | 注意: 4 | 优化任何一个内容之前,都不需要考虑分布式。如果连单机问题都没法解决好,把大力气放到分布式上得不偿失。先把单机弄好,再去做分库分表,读写分离 5 | 6 | ## 数据库的读写性能 7 | 8 | 单机数据库 9 | - 查询优化 10 | - 批量写 11 | - 索引优化 12 | - innodb相关优化 13 | 14 | 查询优化 15 | - 主键查询:千万条记录 1-10ms 16 | - 唯一索引:千万条记录 10-100ms 17 | - 非唯一索引:千万条记录 100-1000ms 18 | - 无索引:百万条记录 1000ms+ 19 | 20 | 批量写 21 | - for each(insert into table values(1))——效率低 22 | - execute once insert into table values 1,2,3,4 23 | - sql编译N次和1次的时间和空间复杂度 24 | - 网络消耗的时间复杂度 25 | - 磁盘寻址的复杂度 26 | 27 | 单机配置优化 28 | - max_connection=1000 增加最大连接数,默认为100 29 | - innodb_file_per_table=1 可以存储每个innodb表和他的索引在自己的文件中 30 | - innodb_buffer_pool_size=1G 缓存池大小,设置为当前数据库服务内存的60%-80% 31 | - innodb_log_file_size=256m 一般取256m可以兼顾性能和recovery的速度,写满后只能切换日志靠buffer存储 32 | - innodb_log_buffer_size=16m 33 | - innodb_flush_log_at_trx_commit=2(这个经常考) 34 | - 1:日志缓冲写到日志文件,对日志文件做到磁盘操作的刷新。Truly ACID。速度慢。 35 | - 2:日志缓冲写到系统缓冲,但不对日志文件做到磁盘操作的刷新。然后根据innodb_flush_log_at_timeout(默认为1s)时间flush disk只有操作系统崩溃或者停电才会删除最后一秒的事务,不然不会丢失事务 36 | - 0时,效率更高,但安全性差。每秒才write日志,任何mysqld进程的崩溃会删除崩溃前最后一秒的事务 37 | - innodb_data_file_path=ibdata1:1G;ibdata2:1G;ibdata3:1G:autoextend 指定表数据和索引存储的空间,可以使一个或者多个文件 38 | 39 | ## 分布式应用的性能优化 40 | 41 | 主从扩展 42 | - 开启bin_log 43 | - 设置主从同步账号,配置主从同步 44 | 45 | 任何MySQL主从切换不靠谱,必定会比主库慢,就算阿里也不敢切 46 | 47 | 主从作用: 48 | - 备份 49 | - 读写分离 50 | 51 | MySQL提供了半同步的机制,至少一台返回确认,才成功 52 | 53 | ## MySQL多主多从 54 | 55 | - 数据分片 56 | - 分片维度 57 | - 分片冗余一致性保障 58 | - 无迁移扩展 59 | 60 | 数据分片 61 | - hash+mode分片 62 | 63 | MySQL数据分片维度 64 | - 固定路由位(比如用户id,路由到唯一的数据库) 65 | - 时间自增分片(2019年所有数据放在一个数据库) 66 | 67 | MySQL数据分片冗余 68 | - 用户订单 69 | - 商户订单 70 | 因为是按用户路由的,如果商户想查所有订单就要遍历所有的库,因此要冗余 71 | - 因为通过rocketmq去制造冗余 72 | - 监听binlog传过去 73 | 74 | MySQL数据无迁移扩展 75 | - mod位数据迁移 76 | - 弹性自增(order id>xx走新规则) 77 | 78 | ## 一致性原理 79 | 80 | - 强一致性 81 | - 弱一致性 82 | - 最终一致性 83 | 84 | CAP 85 | - C:一致性 86 | - A:可用性 87 | - P:分片性 88 | 89 | Base 90 | - Basic available:基本可用 91 | - S:软状态 92 | - E:最终一致性 93 | 94 | mysql主从同步时有半同步的机制 95 | 分布式条件下,要保证强一致性,只有二阶段提交 96 | 事务处理接收者维护自己的状态,如果是半同步状态,外部client要查数据时会把block掉 97 | 98 | mysql同步复制,就抱着了强一致性 -------------------------------------------------------------------------------- /专题整理/MySQL/我的整理/数据库调优.md: -------------------------------------------------------------------------------- 1 | # 数据库调优 2 | 3 | 这部分出自Java金职业 4 | 5 | 业务需求 6 | - 不合理的需求,可能造成很多问题 7 | - 拨乱反正 8 | 9 | 系统架构 10 | - 做架构设计的时候,应充分考虑业务的实际情况,考虑好数据库的各种选择 11 | - 读写分离?高可用?实例个数?分库分表?用什么数据库? 12 | 13 | SQL及索引 14 | - 根据需求编写良好的SQL, 并去创建足够高效的索引 15 | 16 | 表结构 17 | - 设计良好的表结构 18 | 19 | 数据库参数设置 20 | - 设置合理的数据库性能参数 21 | 22 | 系统配置 23 | - 操作系统提供了各种资源使用策略,设置合理的配置,以便于数据库充分利用资源 24 | 25 | 硬件 26 | - 选用什么配置的机器? 27 | 28 | ## 测试数据准备与数据库操作工具 29 | 30 | explain 31 | 32 | ## 索引 33 | 34 | 最左前缀原则 35 | 36 | 需要创建索引的场景 37 | - select语句,频繁作为where条件的字段 38 | - update/delete语句的where条件 39 | - 需要分组、排序的字段 40 | - distinct所使用的字段 41 | - 字段的值有唯一性约束 42 | - 对于多表查询,连接字段应创建索引 43 | 44 | 不建议创建索引的场景 45 | - where子句里用不到的字段 46 | - 表的记录非常少 47 | - 有大量重复数据,选择性低 48 | - 频繁更新的字段,如果创建索引要考虑其索引维护开销 49 | 50 | ### 索引失效和解决方案 51 | 52 | - 索引字段不独立 53 | - 索引字段进行了表达式计算 54 | - 索引字段是函数的参数 55 | - 使用了左模糊 56 | - 使用or查询的部分字段没有索引(分别为两个字段创建索引) 57 | - 字符串条件未使用''引起来(规范地编写SQL) 58 | - 不符合最左前缀原则的查询 59 | - 索引字段建议添加NOT NUll约束 60 | - 单列索引无法存储null值,复合索引无法存储全为null的值 61 | - 查询时,采用is null条件时,不能利用到索引,只能全表扫描 62 | - 隐式转换导致索引失效 63 | 64 | ### 索引调优技巧 65 | 66 | #### 长字段的调优技巧 67 | 68 | - 引入hash字段,作为索引 69 | - 使用前缀索引 70 | 71 | #### 单列索引 VS 组合索引 72 | 73 | - SQL存在多个条件,多个单列索引,会使用索引合并 74 | - 如果出现索引合并,往往说明索引不够合理 75 | - 如果SQL暂时没有性能问题,暂时可以不管 76 | - 组合索引要注意索引列顺序【最左前缀原则】 77 | 78 | #### 覆盖索引 79 | 80 | 尽量只返回想要的字段 81 | - 使用覆盖索引 82 | - 减少网络传输的开销 83 | 84 | #### 重复索引、冗余索引、未使用的索引 85 | 86 | 重复索引 87 | - 在相同的列上按照相同的顺序创建的索引 88 | - 尽量避免重复索引,如果发现重复索引应该删除 89 | 90 | 冗余索引 91 | - 如果已经存在索引index(A,B),又创建了index(A),那么index(A)就是index(A,B)的冗余索引 92 | 93 | 未使用的索引 94 | - 某个索引根本未曾使用 95 | 96 | #### JOIN优化 97 | 98 | 驱动表vs被驱动表 99 | - 外层循环的表是驱动表,内层循环的表是被驱动表 100 | 101 | Join调优原则 102 | - 用小表驱动大表 103 | - 一般无需人工考虑,关联查询优化器会自动选择最优的执行顺序 104 | - 如果优化器抽风,可使用STRAIGHT_JOIN 105 | - 如果有where条件,应当要能够使用索引,并尽可能地减少外层循环的数据量 106 | - join的字段尽量创建索引 107 | - join字段的类型要保持一致 108 | - 尽量减少扫描的行数(explain-rows) 109 | - 尽量控制在百万以内(经验之谈,仅供参考) 110 | - 参与join的表不要太多 111 | - 阿里编程规约建议不超过3张 112 | - 不要以编写复杂SQL为荣,在业务代码里处理 113 | - 如果被驱动表的join字段用不了索引,且内存较为充足,可以考虑把join buffer设置得大一些 114 | 115 | #### limit优化 116 | 117 | limit 300000, 10 118 | rows 299999 119 | 120 | 方案1:覆盖索引 121 | 方案2:覆盖索引+join 122 | 方案3:覆盖索引+子查询 123 | 方案4:范围查询+limit语句 124 | 方案5:如果能获得起始主键值&结束主键值 125 | 方案6:禁止传入过大的页码 126 | 127 | #### count优化 128 | 129 | 当没有非主键索引时,会使用主键索引 130 | 如果存在非主键索引的话,会使用非主键索引 131 | 如果存在多个非主键索引,会使用一个最小的非主键索引 132 | 133 | count(*)和count(1)没有区别 134 | count(*)不会排除为null的行,而count(字段)会排除 135 | 136 | 如果没有特殊需求,尽量用count(*) 137 | 138 | - 创建一个更小的非主键索引 139 | - 把数据库引擎换成MyISAM——实际项目用的很少,一般不会修改数据库引擎 140 | - 汇总表 table[table_name, count] 141 | - 好处:结果比较准确 142 | - 缺点:增加了维护成本 143 | - 缓存 144 | - 优点:性能比较高,结果比较准确,有误差但是比较小 145 | - 缺点:引入了额外的组件,增加了架构的复杂度 146 | 147 | #### group by语句调优 148 | 149 | ### 表结构设计优化 150 | 151 | 第一范式:原子性 152 | 第二范式:互不依赖 153 | 第三范式:不存在传递依赖 154 | 155 | 表设计原则 156 | - 字段少而精,建议20个以内,超过可以拆分 157 | - 大字段独立出去 158 | - 尽量用小型字段 159 | - 避免使用允许为NULL的字段 160 | - 合理平衡范式与冗余 161 | - 如果数据量非常大,考虑分库分表 162 | 163 | -------------------------------------------------------------------------------- /专题整理/MySQL/我的整理/架构.md: -------------------------------------------------------------------------------- 1 | # 架构 2 | 3 | ## 如何设计一个关系型数据库 4 | 5 | 存储 6 | 7 | 程序实例 8 | - 存储管理 9 | - 缓存机制 10 | - SQL解析 11 | - 日志管理 12 | - 权限划分 13 | - 容灾机制 14 | - 索引管理 15 | - 锁管理 -------------------------------------------------------------------------------- /专题整理/MySQL/我的整理/索引.md: -------------------------------------------------------------------------------- 1 | # 索引 2 | 3 | ## 为什么要使用索引 4 | 5 | 避免全表扫描查找数据,提升查询效率 6 | 7 | ## 什么样的信息能成为索引 8 | 9 | 有一定区分度的字段 10 | 11 | ## 索引的数据结构 12 | 13 | 从二叉查找数上阵 14 | 15 | 时间复杂度O(logn),容易退化成O(n) 16 | 深度每增加1,就会增加一次IO,红黑树也这样 17 | 18 | B-tree 19 | - 跟节点至少包括两个孩子 20 | - 树中每个节点最多含有m个孩子(m>=2) 21 | - 除跟节点和叶节点外,其他每个节点至少有ceil(m/2)个孩子 22 | - 所有叶子都位于同一层 23 | 24 | 让每个索引快尽可能存储更多信息 25 | 26 | B+-Tree 27 | - 非叶子节点的子树指针与关键字个数相同 28 | - 非叶子节点的子树指针P[i],指向关键字值(K[i],K[i+1])的子树 29 | - 非叶子节点仅用来索引,数据都保存在叶子节点中 30 | - 所有叶子节点均有一个指针指向下一个节点(方便做范围统计) 31 | 32 | B+Tree更适合用来做存储索引 33 | - B+树的磁盘读写代价更低(非叶子节点不存数据,能读的量就越多) 34 | - B+树的查询效率更加稳定(任何关键字查找一定要走到叶子节点的路) 35 | - B+树更有利于对数据库的扫描(范围查询有更高的性能) 36 | 37 | Hash索引 38 | 39 | 效率高, 40 | 缺点: 41 | - 仅仅满足=、 in,不能使用范围查询 42 | - 无法被用来避免数据的排序操作 43 | - 不能利用部分索引键查询(组合索引) 44 | - 不能避免表扫描 45 | - 遇到大量Hash值相等的情况后性能并不一定就会比B-Tree索引高 46 | 47 | ## 密集索引和稀疏索引的区别 48 | 49 | 我感觉就是聚簇索引和非聚簇索引。 50 | 51 | Innodb 52 | - 若一个主键被定义,该主键则作为密集索引 53 | - 如没有主键被定义,该表的第一个唯一非空索引则作为密集索引 54 | - 若不满足以上条件,innodb内部会生成一个隐藏主键(密集索引) 55 | - 非主键索引存储相关键位和其对应的主键值,包含两次查找 56 | 57 | myisam,只要一次,跟innodb不同 58 | 59 | inndob的索引和数据是存一块的,myIsam是分开的 60 | 61 | ## 如何定位并优化慢sql 62 | 63 | - 根据慢查询日志定位慢查询sql 64 | - 使用explain等工具分析sql 65 | - 修改sql或者尽量让sql走索引 66 | 67 | 68 | long_query_time 69 | show_query_log 70 | show_query_log_file 71 | 72 | explain关键字段 73 | 74 | type: 75 | system>const>eq_ref>ref>fulltext>ref_or_null>index_merge>unique_subquery>index_subquery>range>index>all 76 | all表明走的是全表扫描,需要优化 77 | 78 | extra: 79 | 出现下面两个意味着MySQL根本不能使用索引,效率会受到重大影响。应尽可能对此进行优化。 80 | using filesort:表示MySQL会对结果使用一个外部索引排序,而不是从表里按索引次序读到相关内容。可能在内存或者磁盘上进行排序。MySQL中无法利用索引完成的排序操作称为“文件排序” 81 | using temporary:表示MySQL在对查询结果排序时使用临时表。常见于排序order by和分组查询group by 82 | 83 | 问题:count(*),count(1),count(字段)的区别 84 | 85 | force index去测试各种索引 86 | 87 | ## 联合索引的最左匹配原则 88 | 89 | - 最左前缀匹配原则:mysql会一直向右匹配直到遇到范围查询(>,<,between,like)就停止匹配,比如a=3 and b=4 and c>5 and d=6,如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整 90 | - =和in可以乱序,比如a=1 and b=2 and c=3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化所以可以识别的形式 91 | 92 | ## 联合索引的最左匹配原则的成因 93 | 94 | 只有后面的,没法走b+树索引 95 | 96 | a,b,c的联合索引,b+树是按照a建立的 97 | 98 | ## 索引是建立的越多越好吗 99 | 100 | - 数据量小的表不需要建立索引,建立会增加额外的索引开销 101 | - 数据变更需要维护索引,因此更多的索引意味着更多的维护成本 102 | - 更多的索引意味着也需要更多的空间 103 | 104 | ## 不建议用UUID为主键 105 | 106 | 数字比字符串要快很多 107 | 108 | 字符串的比较太慢了 109 | 110 | ## 什么样的查询语句用不到索引 111 | 112 | 函数计算 113 | 数据类型转换 114 | like %开头 115 | or运算 116 | 复合索引,没有左边字段 117 | 118 | ## 索引类型分类 119 | 120 | B+树 121 | - 所有内容都在叶子节点 122 | - 叶子节点通过指针连接 123 | - 方便范围查询 124 | 125 | Hash 126 | - 范围查询时不建议用Hash 127 | 128 | ## 索引形态分类 129 | 130 | - 聚簇索引:直接通过索引路由找到数据。聚簇索引是有一个 131 | - 非聚簇索引:通过磁盘寻址去找到数据。非聚簇索引有很多 132 | 133 | 不会无限制增加非聚簇索引,增加insert,delete开销 134 | 135 | ## 索引优化 136 | 137 | - 经常被查询的区分度高的列做索引(比如status,区分度低,降级为全表扫描)(20-40%左右,70%没意义) 138 | - 做左原则(a=3 and b>4 and c=5 是用到a和b,c不能用在范围之后。a=3 and b like "kk%" and c=5使用到 a,b,c) 139 | - 回盘排序(排序列联合索引) 140 | - 覆盖索引(select a,b from , 减少一跳) 141 | - 小表驱动大表 142 | 143 | ## 索引调优 144 | 145 | - system:仅一行 146 | - const:主键or唯一键的常量等值查询 147 | - eq_ref:主键or唯一键的扫描或关联查询 148 | - ref:非唯一索引的常量等值查询 149 | - range:索引的范围查询 150 | - index:索引全查询 151 | - all:遍历表查询 152 | 153 | 优化到至少range范围 154 | 155 | ## 极客时间专栏SQL必知必会关于索引的讨论 156 | 157 | 一、数据库索引,为什么不适用用二叉树: 158 | 1. 平衡二叉树必须满足(所有节点的左右子树高度差不超过1)。执行插入还是删除操作,只要不满足上述条件,就要通过旋转来保持平衡,而旋转是非常耗时的,所以AVL树适合用于查找多的情况。 159 | 2. 二叉树的数据结构,会导致“深度”,比较深,这种“瘦高”的特性,加大了平均查询的磁盘IO次数,随着数据量的增多,查询效率也会受到影响; 160 | 161 | 二、B+ 树和 B 树在构造和查询性能上有什么差异呢? 162 | B+ 树的中间节点并不直接存储数据。 163 | 1. B+树的查询效率更加稳定:由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。 164 | 2. B+树的磁盘读写代价更低:B+树的内部节点并没有指向关键字具体信息的指针,因此其内部节点相对B树更小,如果把所有同一内部节点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多,一次性读入内存的需要查找的关键字也就越多,相对IO读写次数就降低了。 165 | 3、由于B+树的数据都存储在叶子结点中,分支结点均为索引,方便扫库,只需要扫一遍叶子结点即可,但是B树因为其分支结点同样存储着数据,我们要找到具体的数据,需要进行一次中序遍历按序来扫,所以B+树更加适合在区间查询的情况,所以通常B+树用于数据库索引。 166 | 167 | 168 | ## 参考资料 169 | 170 | leetcode上的索引讨论: 171 | https://leetcode-cn.com/circle/discuss/F7bKlM/ 172 | https://leetcode-cn.com/circle/discuss/N5PqWI/ 173 | 174 | 175 | 因为在数据库中,涉及磁盘的随机IO的访问是数据库中最耗时的操作之一。 176 | 177 | 2020字节跳动数据库面试题及答案: 178 | https://blog.csdn.net/w139074301/article/details/112004430 -------------------------------------------------------------------------------- /专题整理/MySQL/我的整理/语法.md: -------------------------------------------------------------------------------- 1 | # 语法 2 | 3 | - group by 4 | - having 5 | - 统计相关:count,sum,max,min,avg 6 | 7 | group by 8 | - 满足select子句中的列名必须为分组列或列函数 9 | - 列函数对于group by子句定义的每个组各返回一个结果 10 | - 如果用group by,那么你的select语句中的列要么是你group by里用到的列,要么就是带有之前我们说的如sum,min等列函数的列 11 | - select student_id, count(course_id), sum(score) from score group by student_id 12 | - 要注意:select里面不能有group by里面没出现的列 13 | - select s.student_id, stu.name, count(s.count_id), sum(s.score) from score s, student stu where s.student_id = stu.student.id group by s.student_id 14 | - group by里出现某个表的子弹,select里面的列要么是该group by里出现的列,要么是别的表的列或者带有函数的列。 15 | 16 | Having 17 | - 通常与group by子句一起使用 18 | - where过滤行,having过滤组 19 | - 出现在同一SQL的顺序:where > group by > having 20 | - 查询平均成绩大于60分的同学的学号和平均成绩 21 | - select student_id,avg(score) from score group by student_id having avg(socre) > 60 22 | - 查询没有学全所有可的同学的学号、姓名 23 | - select stu.student_id,stu.name from student stu, score s where stu.student_id=s.student_id group by s.student_id having count(*) < (select count(*) from course) 24 | 25 | ## 内连接,外连接 26 | 27 | ## 外键的危害 28 | 29 | 形成闭环结构,数据难以维护 30 | 31 | ## 应不应该使用子查询 32 | 33 | 子查询 34 | 35 | from 子句 只执行一次 36 | 37 | select子句就要反复执行多次子查询 38 | where子句也要反复执行子查询 -------------------------------------------------------------------------------- /专题整理/MySQL/我的整理/读写分离.md: -------------------------------------------------------------------------------- 1 | # 读写分离 2 | 3 | - 一主多从 4 | - 读库延迟问题处理 5 | - 主从切换处理 6 | 7 | 主从复制默认是异步的方案 8 | master和slave只能是最终一致性,不能是强一致性 9 | 10 | 应用层面做让步 11 | - loading页面 12 | - 强制路由到master上 13 | 14 | 目前主从同步基本控制在1ms以内 15 | 例外原因: 16 | - 网络 17 | - 负载压力 18 | 19 | 主从切换 20 | - 有风险 21 | - 半同步方案,至少其中一个slave同步完后,才提交 22 | - 只需dba寻找binlog最新的slave,提升为master 23 | 24 | 半同步也容易导致slave比master多一条记录的情况 25 | - slave返回master时ack丢失 26 | 27 | -------------------------------------------------------------------------------- /专题整理/MySQL/我的整理/读写方式.md: -------------------------------------------------------------------------------- 1 | # 读写方式 2 | 3 | ## 不同的读写方式 4 | 5 | 读快照 6 | 7 | select * from user where ** group by ** 8 | 读的是事务开启时的数据 9 | 10 | 整个数据库都是依赖mvcc的版本机制 11 | 12 | 13 | 当前读 14 | 15 | select * from user where ** group by ** for update 16 | 阻塞 17 | 18 | update/delete 19 | 20 | ### 当前读的锁机制 21 | 22 | 行锁 23 | - 主键锁行 24 | - 普通索引,锁普通索引的行,可能有很多 25 | - 无索引,表锁 26 | 27 | 表锁(无索引) 28 | 29 | 间隙锁(对insert操作有影响,查普通索引时,即使是查c=10,也会产生间隙锁(5-10),防止insert插入10。查c=6-8自然会产生间隙锁) 30 | 31 | 唯一索引不会产生间隙锁(推荐)(所有的select for update都是行锁) 32 | 33 | -------------------------------------------------------------------------------- /专题整理/MySQL/我的整理/锁.md: -------------------------------------------------------------------------------- 1 | # 锁 2 | 3 | ## MyISAM与Innodb关于锁方面的区别是什么 4 | 5 | - MyISAM默认用的是表级锁,不支持行级锁 6 | - Innodb默认用的是行级锁,也支持表级锁 7 | 8 | myISAM里查询的时候,会对表加上表级的读锁 9 | 10 | 共享锁,排它锁。这一块好难,根本看不懂 11 | 12 | ## MyISAM的适合场景 13 | 14 | - 频繁执行全表的count语句 15 | - 对数据进行增删改查的频率不高,查询非常频繁 16 | - 没有事务 17 | 18 | ## Innodb适合场景 19 | - 数据增删改查都相当频繁。增删改都是某些行被锁,避免了阻塞。而不是MyISAM,每次增删改查都是锁整张表 20 | - 可靠性要求比较高,要求支持事务 21 | 22 | ## 数据库锁的分类 23 | - 按锁的粒度划分,可分为表级锁,行级锁,页级锁 24 | - 按锁级别划分,可分为共享锁,排他锁 25 | - 按加锁方式划分,可分为自动锁,显示锁 26 | - 按操作划分,可分为DML锁(增删改查),DDL锁 27 | - 按使用方式划分,可分为乐观锁,悲观锁(程序中也常见) 28 | 29 | 悲观锁:保守机制,先取锁再访问 30 | 乐观锁:提交时才对数据的冲突进行检测(版本号,时间戳) 31 | - 先读取数据,得到的version值为versionValue 32 | select version from test where id =2 33 | - 每次更新表里的字段时,为了防止发生冲突,先去检查version再做更新,更新成功的话version+1 34 | update test set money = 123,version = 0+1 where version=0 and id=2 35 | 36 | ## 乐观锁和悲观锁 37 | 38 | - 悲观锁是先获取锁再进行操做。一锁二查三更新。select for update 39 | - 乐观锁先修改,更新的时候发现数据已经变了就回滚。check and set 40 | - 使需要根据响应速度、冲突频率、重试代价来判断使用哪一种 41 | 42 | -------------------------------------------------------------------------------- /专题整理/MySQL/答案/SQL.md: -------------------------------------------------------------------------------- 1 | # SQL 2 | 3 | ## 5⼀条SQL语句在MySQL中如何执⾏的 4 | 5 | ⼀条SQL语句在MySQL中如何执⾏的 6 | https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485097&idx=1&sn=84c89da477b1338bdf3e9fcd65514ac1&chksm=cea24962f9d5c074d8d3ff1ab04ee8f0d6486e3d015cfd783503685986485c11738ccb542ba7&token=79317275&lang=zh_CN%23rd 7 | 8 | ## MySQL⾼性能优化规范建议 9 | 10 | MySQL⾼性能优化规范建议 11 | https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485117&idx=1&sn=92361755b7c3de488b415ec4c5f46d73&chksm=cea24976f9d5c060babe50c3747616cce63df5d50947903a262704988143c2eeb4069ae45420&token=79317275&lang=zh_CN%23rd 12 | 13 | ## ⼀条SQL语句执⾏得很慢的原因有哪些? 14 | 15 | 腾讯⾯试:⼀条SQL语句执⾏得很慢的原因有哪些?---不看后悔系列 16 | https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485185&idx=1&sn=66ef08b4ab6af5757792223a83fc0d45&chksm=cea248caf9d5c1dc72ec8a281ec16aa3ec3e8066dbb252e27362438a26c33fbe842b0e0adf47&token=79317275&lang=zh_CN%23rd 17 | 18 | ## 后端程序员必备:书写⾼质量SQL的30条建议 19 | 20 | 后端程序员必备:书写⾼质量SQL的30条建议 21 | https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247486461&idx=1&sn=60a22279196d084cc398936fe3b37772&chksm=cea24436f9d5cd20a4fa0e907590f3e700d7378b3f608d7b33bb52cfb96f503b7ccb65a1deed&token=1987003517&lang=zh_CN%23rd -------------------------------------------------------------------------------- /专题整理/MySQL/答案/事务.md: -------------------------------------------------------------------------------- 1 | # 事务 2 | 3 | ## 什么是事务 4 | 5 | 事务是逻辑上的⼀组操作,要么都执⾏,要么都不执⾏。 6 | 7 | ## 事物的四⼤特性(ACID) 8 | 9 | 1. 原⼦性(Atomicity): 事务是最⼩的执⾏单位,不允许分割。事务的原⼦性确保动作要么全部完成,要么完全不起作⽤; 10 | 2. ⼀致性(Consistency): 执⾏事务前后,数据保持⼀致,多个事务对同⼀个数据读取的结果是相同的; 11 | 3. 隔离性(Isolation): 并发访问数据库时,⼀个⽤户的事务不被其他事务所⼲扰,各并发事务之间数据库是独⽴的; 12 | 4. 持久性(Durability): ⼀个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发⽣故障也不应该对其有任何影响。 13 | 14 | ## 并发事务带来哪些问题? 15 | 16 | 在典型的应⽤程序中,多个事务并发运⾏,经常会操作相同的数据来完成各⾃的任务(多个⽤户对同⼀数据进⾏操作)。并发虽然是必须的,但可能会导致以下的问题。 17 | 18 | 脏读(Dirty read): 当⼀个事务正在访问数据并且对数据进⾏了修改,⽽这种修改还没有提交到数据库中,这时另外⼀个事务也访问了这个数据,然后使⽤了这个数据。因为这个数据是还没有提交的数据,那么另外⼀个事务读到的这个数据是“脏数据”,依据“脏数据”所做的操作可能是不正确的。 19 | 20 | 丢失修改(Lost to modify): 指在⼀个事务读取⼀个数据时,另外⼀个事务也访问了该数据,那么在第⼀个事务中修改了这个数据后,第⼆个事务也修改了这个数据。这样第⼀个事务内的修改结果就被丢失,因此称为丢失修改。 例如:事务1读取某表中的数据A=20,事务2也读取A=20,事务1修改A=A-1,事务2也修改A=A-1,最终结果A=19,事务1的修改被丢失。 21 | 22 | 不可重复读(Unrepeatableread): 指在⼀个事务内多次读同⼀数据。在这个事务还没有结束时,另⼀个事务也访问该数据。那么,在第⼀个事务中的两次读数据之间,由于第⼆个事务的修改导致第⼀个事务两次读取的数据可能不太⼀样。这就发⽣了在⼀个事务内两次读到的数据是不⼀样的情况,因此称为不可重复读。 23 | 24 | 幻读(Phantom read): 幻读与不可重复读类似。它发⽣在⼀个事务(T1)读取了⼏⾏数据,接着另⼀个并发事务(T2)插⼊了⼀些数据时。在随后的查询中,第⼀个事务(T1) 25 | 就会发现多了⼀些原本不存在的记录,就好像发⽣了幻觉⼀样,所以称为幻读。 26 | 27 | 不可重复读和幻读区别: 28 | 29 | 不可重复读的重点是修改⽐如多次读取⼀条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除⽐如多次读取⼀条记录发现记录增多或减少了。 30 | 31 | ## 事务隔离级别有哪些?MySQL的默认隔离级别是? 32 | 33 | SQL 标准定义了四个隔离级别: 34 | 35 | - READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。 36 | - READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻⽌脏读,但是幻读或不可重复读仍有可能发⽣。 37 | - REPEATABLE-READ(可重复读): 对同⼀字段的多次读取结果都是⼀致的,除⾮数据是被本身事务⾃⼰所修改,可以阻⽌脏读和不可重复读,但幻读仍有可能发⽣。 38 | - SERIALIZABLE(可串⾏化): 最⾼的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执⾏,这样事务之间就完全不可能产⽣⼲扰,也就是说,该级别可以防⽌脏读、不可重复读以及幻读。 39 | 40 | 隔离级别 脏读 不可重复读 幻影读 41 | READ-UNCOMMITTED √ √ √ 42 | READ-COMMITTED × √ √ 43 | REPEATABLE-READ × × √ 44 | SERIALIZABLE × × × 45 | 46 | MySQL InnoDB 存储引擎的默认⽀持的隔离级别是 REPEATABLE-READ(可重读)。 47 | 48 | 这⾥需要注意的是:与 SQL 标准不同的地⽅在于 InnoDB 存储引擎在 REPEATABLEREAD(可重读)事务隔离级别下使⽤的是Next-Key Lock 锁算法,因此可以避免幻读的产⽣,这与其他数据库系统(如 SQL Server)是不同的。所以说InnoDB 存储引擎的默认⽀持的隔离级别是 REPEATABLE-READ(可重读)已经可以完全保证事务的隔离性要求,即达到了 49 | SQL标准的 SERIALIZABLE(可串⾏化) 隔离级别。因为隔离级别越低,事务请求的锁越少,所以⼤部分数据库系统的隔离级别都是 READ-COMMITTED(读取提交内容) ,但是你要知道的是InnoDB 存储引擎默认使⽤ REPEAaTABLE-READ(可重读) 并不会有任何性能损失。 -------------------------------------------------------------------------------- /专题整理/MySQL/答案/分库分表.md: -------------------------------------------------------------------------------- 1 | # 分库分表 2 | 3 | ## 分库分表之后,id 主键如何处理? 4 | 5 | 因为要是分成多个表之后,每个表都是从 1 开始累加,这样是不对的,我们需要⼀个全局唯⼀的id 来⽀持。 6 | ⽣成全局 id 有下⾯这⼏种⽅式: 7 | - UUID:不适合作为主键,因为太⻓了,并且⽆序不可读,查询效率低。⽐᫾适合⽤于⽣成唯⼀的名字的标示⽐如⽂件的名字。 8 | - 数据库⾃增 id : 两台数据库分别设置不同步⻓,⽣成不重复ID的策略来实现⾼可⽤。这种⽅式⽣成的 id 有序,但是需要独⽴部署数据库实例,成本⾼,还会有性能瓶颈。 9 | - 利⽤ redis ⽣成 id : 性能⽐᫾好,灵活⽅便,不依赖于数据库。但是,引⼊了新的组件造成系统更加复杂,可⽤性降低,编码更加复杂,增加了系统成本。 10 | - Twitter的snowflake算法 :Github 地址:https://github.com/twitter-archive/snowflake。 11 | - 美团的Leaf分布式ID⽣成系统 :Leaf 是美团开源的分布式ID⽣成器,能保证全局唯⼀性、趋势递增、单调递增、信息安全,⾥⾯也提到了⼏种分布式⽅案的对⽐,但也需要依赖关系数据库、Zookeeper等中间件。感觉还不错。美团技术团队的⼀篇⽂章:https://tech.meituan.com/2017/04/21/mt-leaf.html 。 12 | ...... 13 | 14 | -------------------------------------------------------------------------------- /专题整理/MySQL/答案/存储引擎.md: -------------------------------------------------------------------------------- 1 | # 存储引擎 2 | 3 | ## MyISAM和InnoDB区别 4 | 5 | MyISAM是MySQL的默认数据库引擎(5.5版之前)。虽然性能极佳,⽽且提供了⼤量的特性,包括全⽂索引、压缩、空间函数等,但MyISAM不⽀持事务和⾏级锁,⽽且最⼤的缺陷就是崩溃后⽆法安全恢复。不过,5.5版本之后,MySQL引⼊了InnoDB(事务性数据库引擎),MySQL5.5版本后默认的存储引擎为InnoDB。 6 | 7 | ⼤多数时候我们使⽤的都是 InnoDB 存储引擎,但是在某些情况下使⽤ MyISAM 也是合适的⽐如读密集的情况下。(如果你不介意 MyISAM 崩溃恢复问题的话)。 8 | 9 | 两者的对⽐: 10 | 11 | 1. 是否⽀持⾏级锁 : MyISAM 只有表级锁(table-level locking),⽽InnoDB ⽀持⾏级锁(rowlevel locking)和表级锁,默认为⾏级锁。 12 | 2. 是否⽀持事务和崩溃后的安全恢复: MyISAM 强调的是性能,每次查询具有原⼦性,其执⾏速度⽐InnoDB类型更快,但是不提供事务⽀持。但是InnoDB 提供事务⽀持事务,外部键等⾼级数据库功能。 具有事务(commit)、回滚(rollback)和崩溃修复能⼒(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表。 13 | 3. 是否⽀持外键: MyISAM不⽀持,⽽InnoDB⽀持。 14 | 4. 是否⽀持MVCC :仅 InnoDB ⽀持。应对⾼并发事务, MVCC⽐单纯的加锁更⾼效;MVCC只在 READ COMMITTED 和 REPEATABLE READ 两个隔离级别下⼯作;MVCC可以使⽤ 乐 15 | 观(optimistic)锁 和 悲观(pessimistic)锁来实现;各数据库中MVCC实现并不统⼀。推荐阅读:MySQL-InnoDB-MVCC多版本并发控制 16 | 5. ...... 17 | 18 | 《MySQL⾼性能》上⾯有⼀句话这样写到: 19 | 20 | 不要轻易相信“MyISAM⽐InnoDB快”之类的经验之谈,这个结论往往不是绝对的。在很多我们已知场景中,InnoDB的速度都可以让MyISAM望尘莫及,尤其是⽤到了聚簇索引,或者需要访问的数据都可以放⼊内存的应⽤。 21 | 22 | ⼀般情况下我们选择 InnoDB 都是没有问题的,但是某些情况下你并不在乎可扩展能⼒和并发能⼒,也不需要事务⽀持,也不在乎崩溃后的安全恢复问题的话,选择MyISAM也是⼀个不错的选择。但是⼀般情况下,我们都是需要考虑到这些问题的。 23 | 24 | 多版本并发控制: 25 | https://segmentfault.com/a/1190000012650596 26 | 27 | ## 字符集及校对规则 28 | 29 | 字符集指的是⼀种从⼆进制编码到某类字符符号的映射。校对规则则是指某种字符集下的排序规则。MySQL中每⼀种字符集都会对应⼀系列的校对规则。 30 | 31 | MySQL采⽤的是类似继承的⽅式指定字符集的默认值,每个数据库以及每张数据表都有⾃⼰的默认值,他们逐层继承。⽐如:某个库中所有表的默认字符集将是该数据库所指定的字符集(这些表在没有指定字符集的情况下,才会采⽤默认字符集) PS:整理⾃《Java⼯程师修炼之道》 32 | 33 | https://www.cnblogs.com/geaozhang/p/6724393.html 34 | 35 | -------------------------------------------------------------------------------- /专题整理/MySQL/答案/索引.md: -------------------------------------------------------------------------------- 1 | # 索引 2 | 3 | MySQL索引使⽤的数据结构主要有BTree索引 和 哈希索引 。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝⼤多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余⼤部分场景,建议选择BTree索引。 4 | 5 | MySQL索引使⽤的数据结构主要有BTree索引 和 哈希索引 。对于哈希索引来说,底层的数据结构就是哈希表,因此在绝⼤多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余⼤部分场景,建议选择BTree索引。 6 | 7 | MySQL的BTree索引使⽤的是B树中的B+Tree,但对于主要的两种存储引擎的实现⽅式是不同的。 8 | 9 | MyISAM: B+Tree叶节点的data域存放的是数据记录的地址。在索引检索的时候,⾸先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其 data 域的值,然后以 data 域的值为地址读取相应的数据记录。这被称为“⾮聚簇索引”。 10 | InnoDB: 其数据⽂件本身就是索引⽂件。相⽐MyISAM,索引⽂件和数据⽂件是分离的,其表数据⽂件本身就是按B+Tree组织的⼀个索引结构,树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据⽂件本身就是主索引。这被称为“聚簇索引(或聚集索引)”。⽽其余的索引都作为ᬀ助索引,ᬀ助索引的data域存储相应记录主键的值⽽不是地址,这也是和MyISAM不同的地⽅。在根据主索引搜索时,直接找到key所在的节点即可取出数据;在根据辅助索引查找时,则需要先取出主键的值,再⾛⼀遍主索引。 因此,在设计表的时候,不建议使⽤过⻓的字段作为主键,也不建议使⽤⾮单调的字段作为主键,这样会造成主索引频繁分裂。 PS:整理⾃《Java⼯程师修炼之道》 11 | 12 | -------------------------------------------------------------------------------- /专题整理/MySQL/答案/调优.md: -------------------------------------------------------------------------------- 1 | # 调优 2 | 3 | ## ⼤表优化 4 | 5 | 当MySQL单表记录数过⼤时,数据库的CRUD性能会明显下降,⼀些常⻅的优化措施如下: 6 | 7 | 限定数据的范围 8 | - 务必禁⽌不带任何限制数据范围条件的查询语句。⽐如:我们当⽤户在查询订单历史的时候,我们可以控制在⼀个⽉的范围内; 9 | 10 | 读/写分离 11 | - 经典的数据库拆分⽅案,主库负责写,从库负责读; 12 | 13 | 垂直分区 14 | - 根据数据库⾥⾯数据表的相关性进⾏拆分。 例如,⽤户表中既有⽤户的登录信息⼜有⽤户的基本信息,可以将⽤户表拆分成两个单独的表,甚⾄放到单独的库做分库。简单来说垂直拆分是指数据表列的拆分,把⼀张列⽐较多的表拆分为多张表。 如下图所示,这样来说⼤家应该就更容易理解了。 15 | 垂直拆分的优点: 可以使得列数据变⼩,在查询时减少读取的Block数,减少I/O次数。此外,垂直分区可以简化表的结构,易于维护。 16 | 垂直拆分的缺点: 主键会出现冗余,需要管理冗余列,并会引起Join操作,可以通过在应⽤层进⾏Join来解决。此外,垂直分区会让事务变得更加复杂; 17 | 18 | - ⽔平分区 19 | - 保持数据表结构不变,通过某种策略存储数据分⽚。这样每⼀⽚数据分散到不同的表或者库中,达到了分布式的⽬的。 ⽔平拆分可以⽀撑⾮常⼤的数据量。 20 | ⽔平拆分是指数据表⾏的拆分,表的⾏数超过200万⾏时,就会变慢,这时可以把⼀张的表的数据拆成多张表来存放。举个例⼦:我们可以将⽤户信息表拆分成多个⽤户信息表,这样就可以避免单⼀表数据量过⼤对性能造成影响。 21 | ⽔平拆分可以⽀持⾮常⼤的数据量。需要注意的⼀点是:分表仅仅是解决了单⼀表数据过⼤的问题,但由于表的数据还是在同⼀台机器上,其实对于提升MySQL并发能⼒没有什么意义,所以⽔平拆分最好分库 。 22 | ⽔平拆分能够 ⽀持⾮常⼤的数据量存储,应⽤端改造也少,但 分⽚事务难以解决 ,跨节点Join性能较差,逻辑复杂。《Java⼯程师修炼之道》的作者推荐 尽量不要对数据进⾏分⽚,因为拆分会带来逻辑、部署、运维的各种复杂度 ,⼀般的数据表在优化得当的情况下⽀撑千万以下的数据量是没有太⼤问题的。如果实在要分⽚,尽量选择客户端分⽚架构,这样可以减少⼀次和中间件的⽹络I/O。 23 | 24 | 下⾯补充⼀下数据库分⽚的两种常⻅⽅案: 25 | - 客户端代理: 分⽚逻辑在应⽤端,封装在jar包中,通过修改或者封装JDBC层来实现。 当当⽹的 Sharding-JDBC 、阿⾥的TDDL是两种⽐᫾常⽤的实现。 26 | - 中间件代理: 在应⽤和数据中间加了⼀个代理层。分⽚逻辑统⼀维护在中间件服务中。 我们现在谈的 Mycat 、360的Atlas、⽹易的DDB等等都是这种架构的实现。 27 | 28 | MySQL⼤表优化⽅案: 29 | https://segmentfault.com/a/1190000006158186 30 | 31 | ## 解释⼀下什么是池化设计思想。什么是数据库连接池?为什么需要数据库连接池? 32 | 33 | https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485679&idx=1&sn=57dbca8c9ad49e1f3968ecff04a4f735&chksm=cea24724f9d5ce3212292fac291234a760c99c0960b5430d714269efe33554730b5f71208582&token=1141994790&lang=zh_CN%23rd 34 | 35 | 池化设计应该不是⼀个新名词。我们常⻅的如java线程池、jdbc连接池、redis连接池等就是这类设计的代表实现。这种设计会初始预设资源,解决的问题就是抵消每次获取资源的消耗,如创建线程的开销,获取远程连接的开销等。就好⽐你去⻝堂打饭,打饭的⼤妈会先把饭盛好⼏份放那⾥,你来了就直接拿着饭盒加菜即可,不⽤再临时⼜盛饭⼜打菜,效率就⾼了。除了初始化资源,池化设计还包括如下这些特征:池⼦的初始值、池⼦的活跃值、池⼦的最⼤值等,这些特征可以直接映射到java线程池和数据库连接池的成员属性中。这篇⽂章对池化设计思想介绍的还不错,直接复制过来,避免重复造轮⼦了。 36 | 37 | 数据库连接本质就是⼀个 socket 的连接。数据库服务端还要维护⼀些缓存和⽤户权限信息之类的 所以占⽤了⼀些内存。我们可以把数据库连接池是看做是维护的数据库连接的缓存,以便将来需要对数据库的请求时可以重⽤这些连接。为每个⽤户打开和维护数据库连接,尤其是对动态数据库驱动的⽹站应⽤程序的请求,既昂贵⼜浪费资源。在连接池中,创建连接后,将其放置在池中,并再次使⽤它,因此不必建⽴新的连接。如果使⽤了所有连接,则会建⽴⼀个新连接并将其添加到池中。 连接池还减少了⽤户必须等待建⽴与数据库的连接的时间。 38 | 39 | -------------------------------------------------------------------------------- /专题整理/MySQL/答案/锁.md: -------------------------------------------------------------------------------- 1 | # 锁 2 | 3 | ## 锁机制 4 | 5 | MyISAM和InnoDB存储引擎使⽤的锁: 6 | MyISAM采⽤表级锁(table-level locking)。 7 | InnoDB⽀持⾏级锁(row-level locking)和表级锁,默认为⾏级锁 8 | 9 | 表级锁和⾏级锁对⽐: 10 | 表级锁: MySQL中锁定 粒度最⼤ 的⼀种锁,对当前操作的整张表加锁,实现简单,资源消耗也⽐᫾少,加锁快,不会出现死锁。其锁定粒度最⼤,触发锁冲突的概率最⾼,并发度最低,MyISAM和 InnoDB引擎都⽀持表级锁。 11 | ⾏级锁: MySQL中锁定 粒度最⼩ 的⼀种锁,只针对当前操作的⾏进⾏加锁。 ⾏级锁能⼤⼤减少数据库操作的冲突。其加锁粒度最⼩,并发度⾼,但加锁的开销也最⼤,加锁慢,会出现死锁。 12 | 13 | https://blog.csdn.net/qq_34337272/article/details/80611486 -------------------------------------------------------------------------------- /专题整理/MySQL索引.md: -------------------------------------------------------------------------------- 1 | # MySQL索引 2 | 3 | ## 索引是万能的吗? 4 | 5 | 索引就是帮助数据库管理系统高效获取数据的数据结构。 6 | 7 | 索引不是万能的,在有些情况下使用索引反而会让效率变低。 8 | 9 | 索引的价值是帮我们从海量数据中找到想要的数据,如果数据量少,那么是否使用索引对结果的影响并不大。 10 | 11 | 在数据表中的数据行数比较少的情况下,比如不到 1000 行,是不需要创建索引的。另外,当数据重复度大,比如高于 10% 的时候,也不需要对这个字段使用索引。 12 | 13 | 我之前讲到过,如果是性别这个字段,就不需要对它创建索引。这是为什么呢?如果你想要在 100 万行数据中查找其中的 50 万行(比如性别为男的数据),一旦创建了索引,你需要先访问 50 万次索引,然后再访问 50 万次数据表,这样加起来的开销比不使用索引可能还要大。 14 | 15 | ## 索引的种类有哪些? 16 | 17 | **从功能逻辑上说,索引主要有 4 种,分别是普通索引、唯一索引、主键索引和全文索引。** 18 | 19 | 普通索引是基础的索引,没有任何约束,主要用于提高查询效率。 20 | 21 | 唯一索引就是在普通索引的基础上增加了数据唯一性的约束,在一张数据表里可以有多个唯一索引。 22 | 23 | 主键索引在唯一索引的基础上增加了不为空的约束,也就是 NOT NULL+UNIQUE,一张表里最多只有一个主键索引。 24 | 25 | 全文索引用的不多,MySQL 自带的全文索引只支持英文。我们通常可以采用专门的全文搜索引擎,比如 ES(ElasticSearch) 和 Solr。 26 | 27 | 其实前三种索引(普通索引、唯一索引和主键索引)都是一类索引,只不过对数据的约束性逐渐提升。在一张数据表中只能有一个主键索引,这是由主键索引的物理实现方式决定的,因为数据存储在文件中只能按照一种顺序进行存储。但可以有多个普通索引或者多个唯一索引。 28 | 29 | **按照物理实现方式,索引可以分为 2 种:聚集索引和非聚集索引。我们也把非聚集索引称为二级索引或者辅助索引。** 30 | 31 | 聚集索引可以按照主键来排序存储数据 32 | 33 | 聚集索引指表中数据行按索引的排序方式进行存储,对查找行很有效。只有当表包含聚集索引时,表内的数据行才会按找索引列的值在磁盘上进行物理排序和存储。每一个表只能有一个聚集索引,因为数据行本身只能按一个顺序存储。 34 | 35 | 聚集索引与非聚集索引的原理不同,在使用上也有一些区别: 36 | 37 | - 聚集索引的叶子节点存储的就是我们的数据记录,非聚集索引的叶子节点存储的是数据位置。非聚集索引不会影响数据表的物理存储顺序。 38 | - 一个表只能有一个聚集索引,因为只能有一种排序存储的方式,但可以有多个非聚集索引,也就是多个索引目录提供数据检索。 39 | - 使用聚集索引的时候,数据的查询效率高,但如果对数据进行插入,删除,更新等操作,效率会比非聚集索引低。 40 | 41 | 对 WHERE 子句的字段建立索引,可以大幅提升查询效率。 42 | 采用聚集索引进行数据查询,比使用非聚集索引的查询效率略高。如果查询次数比较多,还是尽量使用主键索引进行数据查询。 43 | 44 | **除了业务逻辑和物理实现方式,索引还可以按照字段个数进行划分,分成单一索引和联合索引。** 45 | 46 | 这里需要说明的是联合索引存在最左匹配原则,也就是按照最左优先的方式进行索引的匹配。 47 | 48 | 使用索引可以帮助我们从海量的数据中快速定位想要查找的数据,不过索引也存在一些不足,比如占用存储空间、降低数据库写操作的性能等,如果有多个索引还会增加索引选择的时间。当我们使用索引时,需要平衡索引的利(提升查询效率)和弊(维护索引所需的代价)。 49 | 50 | 在实际工作中,我们还需要基于需求和数据本身的分布情况来确定是否使用索引,尽管索引不是万能的,但数据量大的时候不使用索引是不可想象的,毕竟索引的本质,是帮助我们提升数据检索的效率。 51 | 52 | ## 如何评价索引的数据结构设计好坏 53 | 54 | 数据库服务器有两种存储介质,分别为硬盘和内存。内存属于临时存储,容量有限,而且当发生意外时(比如断电或者发生故障重启)会造成数据丢失;硬盘相当于永久存储介质,这也是为什么我们需要把数据保存到硬盘上。 55 | 56 | 虽然内存的读取速度很快,但我们还是需要将索引存放到硬盘上,这样的话,当我们在硬盘上进行查询时,也就产生了硬盘的 I/O 操作。相比于内存的存取来说,硬盘的 I/O 存取消耗的时间要高很多。我们通过索引来查找某行数据的时候,需要计算产生的磁盘 I/O 次数,当磁盘 I/O 次数越多,所消耗的时间也就越大。如果我们能让索引的数据结构尽量减少硬盘的 I/O 操作,所消耗的时间也就越小。 57 | 58 | ## 什么是B树 59 | 60 | 如果用二叉树作为索引的实现结构,会让树变得很高,增加硬盘的 I/O 次数,影响数据查询的时间。因此一个节点就不能只有 2 个子节点,而应该允许有 M 个子节点 (M>2)。 61 | 62 | B 树的出现就是为了解决这个问题,B 树的英文是 Balance Tree,也就是平衡的多路搜索树,它的高度远小于平衡二叉树的高度。在文件系统和数据库系统中的索引结构经常采用 B 树来实现。 63 | 64 | 你能看出来在 B 树的搜索过程中,我们比较的次数并不少,但如果把数据读取出来然后在内存中进行比较,这个时间就是可以忽略不计的。而读取磁盘块本身需要进行 I/O 操作,消耗的时间比在内存中进行比较所需要的时间要多,是数据查找用时的重要因素,B 树相比于平衡二叉树来说磁盘 I/O 操作要少,在数据查询中比平衡二叉树效率要高。 65 | 66 | ## 什么是B+树 67 | 68 | 整个过程一共进行了 3 次 I/O 操作,看起来 B+ 树和 B 树的查询过程差不多,但是 B+ 树和 B 树有个根本的差异在于,B+ 树的中间节点并不直接存储数据。这样的好处都有什么呢? 69 | 70 | 首先,B+ 树查询效率更稳定。因为 B+ 树每次只有访问到叶子节点才能找到对应的数据,而在 B 树中,非叶子节点也会存储数据,这样就会造成查询效率不稳定的情况,有时候访问到了非叶子节点就可以找到关键字,而有时需要访问到叶子节点才能找到关键字。 71 | 72 | 其次,B+ 树的查询效率更高,这是因为通常 B+ 树比 B 树更矮胖(阶数更大,深度更低),查询所需要的磁盘 I/O 也会更少。同样的磁盘页大小,B+ 树可以存储更多的节点关键字。 73 | 74 | 不仅是对单个关键字的查询上,在查询范围上,B+ 树的效率也比 B 树高。这是因为所有关键字都出现在 B+ 树的叶子节点中,并通过有序链表进行了链接。而在 B 树中则需要通过中序遍历才能完成查询范围的查找,效率要低很多。 75 | 76 | 磁盘的 I/O 操作次数对索引的使用效率至关重要。虽然传统的二叉树数据结构查找数据的效率高,但很容易增加磁盘 I/O 操作的次数,影响索引使用的效率。因此在构造索引的时候,我们更倾向于采用“矮胖”的数据结构。 77 | 78 | B 树和 B+ 树都可以作为索引的数据结构,在 MySQL 中采用的是 B+ 树,B+ 树在查询性能上更稳定,在磁盘页大小相同的情况下,树的构造更加矮胖,所需要进行的磁盘 I/O 次数更少,更适合进行关键字的范围查询。 79 | 80 | ## Hash 索引与 B+ 树索引的区别 81 | 82 | - Hash 索引不能进行范围查询,而 B+ 树可以。这是因为 Hash 索引指向的数据是无序的,而 B+ 树的叶子节点是个有序的链表。 83 | - Hash 索引不支持联合索引的最左侧原则(即联合索引的部分索引无法使用),而 B+ 树可以。对于联合索引来说,Hash 索引在计算 Hash 值的时候是将索引键合并后再一起计算 Hash 值,所以不会针对每个索引单独计算 Hash 值。因此如果用到联合索引的一个或者几个索引时,联合索引无法被利用。 84 | - Hash 索引不支持 ORDER BY 排序,因为 Hash 索引指向的数据是无序的,因此无法起到排序优化的作用,而 B+ 树索引数据是有序的,可以起到对该字段 ORDER BY 排序优化的作用。同理,我们也无法用 Hash 索引进行模糊查询,而 B+ 树使用 LIKE 进行模糊查询的时候,LIKE 后面前模糊查询(比如 % 开头)的话就可以起到优化作用。 85 | 86 | ## 创建索引有哪些规律? 87 | 88 | 1. 字段的数值有唯一性的限制,比如用户名 89 | 2. 频繁作为 WHERE 查询条件的字段,尤其在数据表大的情况下 90 | 3. 需要经常 GROUP BY 和 ORDER BY 的列 91 | 92 | 实际上多个单列索引在多条件查询时只会生效一个索引(MySQL 会选择其中一个限制最严格的作为索引),所以在多条件联合查询的时候最好创建联合索引。 93 | 94 | 4. UPDATE、DELETE 的 WHERE 条件列,一般也需要创建索引 95 | 96 | 5. DISTINCT 字段需要创建索引 97 | 98 | 6. 做多表 JOIN 连接操作时,创建索引需要注意以下的原则 99 | 100 | ## 什么时候不需要创建索引 101 | 102 | WHERE 条件(包括 GROUP BY、ORDER BY)里用不到的字段不需要创建索引,索引的价值是快速定位,如果起不到定位的字段通常是不需要创建索引的。 103 | 104 | 第二种情况是,如果表记录太少,比如少于 1000 个,那么是不需要创建索引的。 105 | 106 | 第三种情况是,字段中如果有大量重复数据,也不用创建索引,比如性别字段。 107 | 108 | 最后一种情况是,频繁更新的字段不一定要创建索引。 109 | 110 | ## 什么情况下索引失效 111 | 112 | 1. 如果索引进行了表达式计算,则会失效 113 | 114 | 2. 如果对索引使用函数,也会造成失效 115 | 116 | 3. 在 WHERE 子句中,如果在 OR 前的条件列进行了索引,而在 OR 后的条件列没有进行索引,那么索引会失效。 117 | 118 | 4. 当我们使用 LIKE 进行模糊查询的时候,前面不能是 % 119 | 120 | 5. 索引列尽量设置为 NOT NULL 约束。 121 | 122 | 6. 我们在使用联合索引的时候要注意最左原则 123 | 124 | 实际工作中,查询的需求多种多样,创建的索引也会越来越多。这时还需要注意,我们要尽可能扩展索引,而不是新建索引,因为索引数量过多需要维护的成本也会变大,导致写效率变低。同时,我们还需要定期查询使用率低的索引,对于从未使用过的索引可以进行删除,这样才能让索引在 SQL 查询中发挥最大价值。 125 | 126 | -------------------------------------------------------------------------------- /专题整理/MySQL调优.md: -------------------------------------------------------------------------------- 1 | # MySQL调优 2 | 3 | ## 数据库调优的目标是什么? 4 | 5 | 如何确定调优的目标 6 | 1. 用户的反馈 7 | 2. 日志的分析 8 | 3. 服务器资源使用监控 9 | 4. 数据库内部状况监控 10 | 11 | ## 如果要进行调优,都有哪些维度可以选择? 12 | 13 | 调优对象是整个数据库管理系统,而不仅包括SQL查询 14 | 15 | 1. 使用适合的DBMS 16 | 17 | RDBMS中,如果对事务性处理以及安全性要求高的话,可以选择商业的数据库产品。 18 | 19 | 采用开源的 MySQL 进行存储,有很多存储引擎可以选择,如果进行事务处理的话可以选择 InnoDB,非事务处理可以选择 MyISAM。 20 | 21 | InnoDB支持事务和行级锁,是MySQL默认的存储引擎 22 | MyISAM只支持表级锁,非事务安全性,更适合读取数据库的情况, 23 | 小型的应用,如果需要大量的SELECT查询,可以考虑MyISAM。如果是事务处理应用需要选择InnoDB。 24 | 两种引擎各有特点,另外在MySQL中不同的数据表也可以选择不同的存储引擎。 25 | 26 | 2. 优化表设计 27 | 28 | - 表结构要尽量遵循第三范式的原则(关于第三范式,我在后面章节会讲)。这样可以让数据结构更加清晰规范,减少冗余字段,同时也减少了在更新,插入和删除数据时等异常情况的发生。 29 | - 如果分析查询应用比较多,尤其是需要进行多表联查的时候,可以采用反范式进行优化。反范式采用空间换时间的方式,通过增加冗余字段提高查询的效率。 30 | - 表字段的数据类型选择,关系到了查询效率的高低以及存储空间的大小。一般来说,如果字段可以采用数值类型就不要采用字符类型;字符长度要尽可能设计得短一些。针对字符类型来说,当确定字符长度固定时,就可以采用 CHAR 类型;当长度不固定时,通常采用 VARCHAR 类型。 31 | 32 | 数据表的结构设计很基础,也很关键。好的表结构可以在业务发展和用户量增加的情况下依然发挥作用,不好的表结构设计会让数据表变得非常臃肿,查询效率也会降低。 33 | 34 | 35 | 3. 优化逻辑查询 36 | 37 | SQL 查询优化,可以分为逻辑查询优化和物理查询优化。逻辑查询优化就是通过改变 SQL 语句的内容让 SQL 执行效率更高效,采用的方式是对 SQL 语句进行等价变换,对查询进行重写。重写查询的数学基础就是关系代数。 38 | 39 | SQL 的查询重写包括了子查询优化、等价谓词重写、视图重写、条件简化、连接消除和嵌套连接消除等。 40 | 41 | 比如我们在讲解 EXISTS 子查询和 IN 子查询的时候,会根据小表驱动大表的原则选择适合的子查询。在 WHERE 子句中会尽量避免对字段进行函数运算,它们会让字段的索引失效。 42 | 43 | 4. 优化物理查询 44 | 45 | 物理查询优化是将逻辑查询的内容变成可以被执行的物理操作符,从而为后续执行器的执行提供准备。它的核心是高效地建立索引,并通过这些索引来做各种优化。 46 | 47 | 但你要知道索引不是万能的,我们需要根据实际情况来创建索引。那么都有哪些情况需要考虑呢? 48 | 49 | - 如果数据重复度高,就不需要创建索引。通常在重复度超过 10% 的情况下,可以不创建这个字段的索引。比如性别这个字段(取值为男和女)。 50 | - 要注意索引列的位置对索引使用的影响。比如我们在 WHERE 子句中对索引字段进行了表达式的计算,会造成这个字段的索引失效。 51 | - 要注意联合索引对索引使用的影响。我们在创建联合索引的时候会对多个字段创建索引,这时索引的顺序就很重要了。比如我们对字段 x, y, z 创建了索引,那么顺序是 (x,y,z) 还是 (z,y,x),在执行的时候就会存在差别。 52 | - 要注意多个索引对索引使用的影响。索引不是越多越好,因为每个索引都需要存储空间,索引多也就意味着需要更多的存储空间。此外,过多的索引也会导致优化器在进行评估的时候增加了筛选出索引的计算时间,影响评估的效率。 53 | 54 | 查询优化器在对 SQL 语句进行等价变换之后,还需要根据数据表的索引情况和数据情况确定访问路径,这就决定了执行 SQL 时所需要消耗的资源。SQL 查询时需要对不同的数据表进行查询,因此在物理查询优化阶段也需要确定这些查询所采用的路径,具体的情况包括: 55 | 56 | - 单表扫描:对于单表扫描来说,我们可以全表扫描所有的数据,也可以局部扫描。 57 | - 两张表的连接:常用的连接方式包括了嵌套循环连接、HASH 连接和合并连接。 58 | - 张表的连接:多张数据表进行连接的时候,顺序很重要,因为不同的连接路径查询的效率不同,搜索空间也会不同。我们在进行多表连接的时候,搜索空间可能会达到很高的数据量级,巨大的搜索空间显然会占用更多的资源,因此我们需要通过调整连接顺序,将搜索空间调整在一个可接收的范围内。 59 | 60 | 物理查询优化是在确定了逻辑查询优化之后,采用物理优化技术(比如索引等),通过计算代价模型对各种可能的访问路径进行估算,从而找到执行方式中代价最小的作为执行计划。在这个部分中,我们需要掌握的重点是对索引的创建和使用。 61 | 62 | 5. 使用 Redis 或 Memcached 作为缓存 63 | 64 | 因为数据都是存放到数据库中,我们需要从数据库层中取出数据放到内存中进行业务逻辑的操作,当用户量增大的时候,如果频繁地进行数据查询,会消耗数据库的很多资源。如果我们将常用的数据直接放到内存中,就会大幅提升查询的效率。 65 | 66 | 6. 库级优化 67 | 68 | 如果读和写的业务量都很大,并且它们都在同一个数据库服务器中进行操作,那么数据库的性能就会出现瓶颈,这时为了提升系统的性能,优化用户体验,我们可以采用读写分离的方式降低主数据库的负载,比如用主数据库(master)完成写操作,用从数据库(slave)完成读操作。 69 | 70 | 除此以外,我们还可以对数据库分库分表。当数据量级达到亿级以上时,有时候我们需要把一个数据库切成多份,放到不同的数据库服务器上,减少对单一数据库服务器的访问压力。如果你使用的是 MySQL,就可以使用 MySQL 自带的分区表功能,当然你也可以考虑自己做垂直切分和水平切分。 71 | 72 | 采用垂直分表的形式,就是将一张数据表分拆成多张表,采用水平拆分的方式,就是将单张数据量大的表按照某个属性维度分成不同的小表。 73 | 74 | 但需要注意的是,分拆在提升数据库性能的同时,也会增加维护和使用成本。 75 | 76 | ## 如何思考和分析数据库调优这件事? 77 | 78 | 做任何事情之前,我们都需要确认目标。在数据库调优中,我们的目标就是响应时间更快,吞吐量更大。利用宏观的监控工具和微观的日志分析可以帮我们快速找到调优的思路和方式。 79 | 80 | 虽然每个人的情况都不一样,但我们同样需要对数据库调优这件事有一个整体的认知。在思考数据库调优的时候,可以从三个维度进行考虑。 81 | 82 | 1. 选择比努力更重要 83 | 84 | 在进行 SQL 调优之前,可以先选择 DBMS 和数据表的设计方式。 85 | 86 | 2. 你可以把 SQL 查询优化分成两个部分,逻辑查询优化和物理查询优化 87 | 88 | 虽然 SQL 查询优化的技术有很多,但是大方向上完全可以分成逻辑查询优化和物理查询优化两大块。逻辑查询优化就是通过 SQL 等价变换提升查询效率,直白一点就是说,换一种查询写法执行效率可能更高。物理查询优化则是通过索引和表连接方式等技术来进行优化,这里重点需要掌握索引的使用。 89 | 90 | 3. 我们可以通过外援来增强数据库的性能 91 | 92 | -------------------------------------------------------------------------------- /专题整理/MySQL面试.md: -------------------------------------------------------------------------------- 1 | # MySQL面试 2 | 3 | ## 比较好的参考资料 4 | 5 | 李智慧《后端技术面试》 6 | https://time.geekbang.org/column/article/172000 7 | 8 | 主键索引就是聚簇索引,非主键索引就是非聚簇索引 9 | 10 | 我们只需要掌握数据库的架构原理与执行过程,数据库文件的存储原理与索引的实现方式,以及数据库事务与数据库复制的基本原理就可以了。 -------------------------------------------------------------------------------- /专题整理/Redis/常见面试题.md: -------------------------------------------------------------------------------- 1 | # Redis 2 | 3 | ## 数据结构 4 | 5 | redis Zset数据结构、新增数据的时间复杂度 6 | redis 数据类型 7 | zset 的底层数据结构,跳跃表如何实现 8 | redis对长字符串和短字符串处理有什么不同 9 | 10 | ## 高可用 11 | 12 | redis 主从怎么实现的 13 | redis 的aof 太大如何优化 14 | redis 如何持久化RDB 15 | redis cluster 如何搭建,master-slave 模式,如何选主 16 | 介绍下Redis 中的哨兵机制 17 | Reids 如何持久化方式 18 | 19 | ## 系统设计 20 | 21 | 结合redis 和本地缓存如何实现黑名单id过滤功能(考虑海量请求、同时会有读写情况) 22 | memcache 与redis 区别 23 | redis 过期策略 24 | Reids 如何实现分布式锁 25 | 如何避免Redis 缓存穿透 26 | 分布式锁如何实现,Redis 如何实现分布式锁 27 | 28 | -------------------------------------------------------------------------------- /专题整理/Redis/答案/Redis.md: -------------------------------------------------------------------------------- 1 | # Redis 2 | 3 | ## 简单介绍⼀下 Redis 呗! 4 | 5 | 简单来说 Redis 就是⼀个使⽤ C 语⾔开发的数据库,不过与传统数据库不同的是 Redis 的数据是存在内存中的 ,也就是它是内存数据库,所以读写速度⾮常快,因此 Redis 被⼴泛应⽤于缓存⽅向。 6 | 7 | 另外,Redis 除了做缓存之外,Redis 也经常⽤来做分布式锁,甚⾄是消息队列。Redis 提供了多种数据类型来⽀持不同的业务场景。Redis 还⽀持事务 、持久化、Lua 脚本、多种集群⽅案。 8 | 9 | ## 说⼀下 Redis 和 Memcached 的区别和共同点 10 | 11 | 现在公司⼀般都是⽤ Redis 来实现缓存,⽽且 Redis ⾃身也越来越强⼤了!不过,了解 Redis 和Memcached 的区别和共同点,有助于我们在做相应的技术选型的时候,能够做到有理有据! 12 | 13 | 共同点 : 14 | 1. 都是基于内存的数据库,⼀般都⽤来当做缓存使⽤。 15 | 2. 都有过期策略。 16 | 3. 两者的性能都⾮常⾼。 17 | 18 | 区别 : 19 | 1. Redis ⽀持更丰富的数据类型(⽀持更复杂的应⽤场景)。Redis 不仅仅⽀持简单的 k/v 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储。Memcached 只⽀持最简单的 k/v 数据类型。 20 | 2. Redis ⽀持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进⾏使⽤,⽽ Memecache 把数据全部存在内存之中。 21 | 3. Redis 有灾难恢复机制。 因为可以把缓存中的数据持久化到磁盘上。 22 | 4. Redis 在服务器内存使⽤完之后,可以将不⽤的数据放到磁盘上。但是,Memcached 在服务器内存使⽤完之后,就会直接报异常。 23 | 5. Memcached 没有原⽣的集群模式,需要依靠客户端来实现往集群中分⽚写⼊数据;但是Redis ⽬前是原⽣⽀持 cluster 模式的. 24 | 6. Memcached 是多线程,⾮阻塞 IO 复⽤的⽹络模型;Redis 使⽤单线程的多路 IO 复⽤模型。 (Redis 6.0 引⼊了多线程 IO ) 25 | 7. Redis ⽀持发布订阅模型、Lua 脚本、事务等功能,⽽ Memcached 不⽀持。并且,Redis⽀持更多的编程语⾔。 26 | 8. Memcached过期数据的删除策略只⽤了惰性删除,⽽ Redis 同时使⽤了惰性删除与定期删除。 27 | 28 | ## 为什么要用缓存/Redis 29 | 30 | ⾼性能: 31 | 假如⽤户第⼀次访问数据库中的某些数据的话,这个过程是⽐᫾慢,毕竟是从硬盘中读取的。但是,如果说,⽤户访问的数据属于⾼频数据并且不会经常改变的话,那么我们就可以很放⼼地将该⽤户访问的数据存在缓存中。 32 | 这样有什么好处呢? 那就是保证⽤户下⼀次再访问这些数据的时候就可以直接从缓存中获取了。操作缓存就是直接操作内存,所以速度相当快。 33 | 不过,要保持数据库和缓存中的数据的⼀致性。 如果数据库中的对应数据改变的之后,同步改变缓存中相应的数据即可! 34 | 35 | ⾼并发: 36 | ⼀般像 MySQL 这类的数据库的 QPS ⼤概都在 1w 左右(4 核 8g) ,但是使⽤ Redis 缓存之后很容易达到 10w+,甚⾄最⾼能达到 30w+(就单机 redis 的情况,redis 集群的话会更⾼)。 37 | QPS(Query Per Second):服务器每秒可以执⾏的查询次数; 38 | 所以,直接操作缓存能够承受的数据库请求数量是远远⼤于直接访问数据库的,所以我们可以考虑把数据库中的部分数据转移到缓存中去,这样⽤户的⼀部分请求会直接到缓存这⾥⽽不⽤经过数据库。进⽽,我们也就提⾼的系统整体的并发。 39 | 40 | ## Redis 单线程模型详解 41 | 42 | Redis 基于 Reactor 模式来设计开发了⾃⼰的⼀套⾼效的事件处理模型 (Netty 的线程模型也基于 Reactor 模式,Reactor 模式不愧是⾼性能 IO 的基⽯),这套事件处理模型对应的是 Redis中的⽂件事件处理器(file event handler)。由于⽂件事件处理器(file event handler)是单线程⽅式运⾏的,所以我们⼀般都说 Redis 是单线程模型。 43 | 44 | 既然是单线程,那怎么监听⼤量的客户端连接呢? 45 | 46 | Redis 通过IO 多路复⽤程序 来监听来⾃客户端的⼤量连接(或者说是监听多个 socket),它会将感兴趣的事件及类型(读、写)注册到内核中并监听每个事件是否发⽣。 47 | 48 | 这样的好处⾮常明显: I/O 多路复⽤技术的使⽤让 Redis 不需要额外创建多余的线程来监听客户端的⼤量连接,降低了资源的消耗(和 NIO 中的 Selector 组件很像)。 49 | 50 | ## Redis 没有使⽤多线程?为什么不使⽤多线程? 51 | 52 | 虽然说 Redis 是单线程模型,但是, 实际上,Redis 在 4.0 之后的版本中就已经加⼊了对多线程的⽀持。 53 | 54 | 不过,Redis 4.0 增加的多线程主要是针对⼀些⼤键值对的删除操作的命令,使⽤这些命令就会使⽤主处理之外的其他线程来“异步处理”。 55 | 56 | ⼤体上来说,Redis 6.0 之前主要还是单线程处理。 57 | 58 | 那,Redis6.0 之前 为什么不使⽤多线程? 59 | 60 | 我觉得主要原因有下⾯ 3 个: 61 | 1. 单线程编程容易并且更容易维护; 62 | 2. Redis 的性能瓶颈不再 CPU ,主要在内存和⽹络; 63 | 3. 多线程就会存在死锁、线程上下⽂切换等问题,甚⾄会影响性能。 64 | 65 | ## Redis6.0 之后为何引⼊了多线程? 66 | 67 | Redis6.0 引⼊多线程主要是为了提⾼⽹络 IO 读写性能,因为这个算是 Redis 中的⼀个性能瓶颈 68 | (Redis 的瓶颈主要受限于内存和⽹络)。 69 | 70 | 虽然,Redis6.0 引⼊了多线程,但是 Redis 的多线程只是在⽹络数据的读写这类耗时操作上使⽤了, 执⾏命令仍然是单线程顺序执⾏。因此,你也不需要担⼼线程安全问题。 71 | 72 | Redis6.0 的多线程默认是禁⽤的,只使⽤主线程。 73 | 74 | 1. Redis 6.0 新特性-多线程连环 13 问! https://mp.weixin.qq.com/s/FZu3acwK6zrCBZQ_3HoUgw 75 | 2. 为什么 Redis 选择单线程模型 https://draveness.me/whys-the-design-redis-single-thread/ 76 | 77 | -------------------------------------------------------------------------------- /专题整理/Redis/答案/数据结构.md: -------------------------------------------------------------------------------- 1 | # 数据结构 2 | 3 | ## Redis 常⻅数据结构以及使⽤场景分析 4 | 5 | https://try.redis.io/ 6 | 7 | ### string 8 | 9 | 1. 介绍 :string 数据结构是简单的 key-value 类型。虽然 Redis 是⽤ C 语⾔写的,但是 Redis并没有使⽤ C 的字符串表示,⽽是⾃⼰构建了⼀种 简单动态字符串(simple dynamic string,SDS)。相⽐于 C 的原⽣字符串,Redis 的 SDS 不光可以保存⽂本数据还可以保存⼆进制数据,并且获取字符串⻓度复杂度为 O(1)(C 字符串为 O(N)),除此之外,Redis 的SDS API 是安全的,不会造成缓冲区溢出。 10 | 2. 常⽤命令: set,get,strlen,exists,dect,incr,setex 等等。 11 | 3. 应⽤场景 :⼀般常⽤在需要计数的场景,⽐如⽤户的访问次数、热点⽂章的点赞转发数量等等。 12 | 13 | ### list 14 | 15 | 1. 介绍 :list 即是 链表。链表是⼀种⾮常常⻅的数据结构,特点是易于数据元素的插⼊和删除并且且可以灵活调整链表⻓度,但是链表的随机访问困难。许多⾼级编程语⾔都内置了链表的实现⽐如 Java 中的 LinkedList,但是 C 语⾔并没有实现链表,所以 Redis 实现了⾃⼰的链表数据结构。Redis 的 list 的实现为⼀个 双向链表,即可以⽀持反向查找和遍历,更⽅便操作,不过带来了部分额外的内存开销。 16 | 2. 常⽤命令: rpush,lpop,lpush,rpop,lrange,llen 等。 17 | 3. 应⽤场景: 发布与订阅或者说消息队列、慢查询。 18 | 19 | ### hash 20 | 21 | 1. 介绍 :hash 类似于 JDK1.8 前的 HashMap,内部实现也差不多(数组 + 链表)。不过,Redis 的 hash 做了更多优化。另外,hash 是⼀个 string 类型的 field 和 value 的映射表,特别适合⽤于存储对象,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值。 ⽐如我们可以 hash 数据结构来存储⽤户信息,商品信息等等。 22 | 2. 常⽤命令: hset,hmset,hexists,hget,hgetall,hkeys,hvals 等。 23 | 3. 应⽤场景: 系统中对象数据的存储。 24 | 25 | ### set 26 | 27 | 1. 介绍 : set 类似于 Java 中的 HashSet 。Redis 中的 set 类型是⼀种⽆序集合,集合中的元素没有先后顺序。当你需要存储⼀个列表数据,⼜不希望出现重复数据时,set 是⼀个很好的选择,并且 set 提供了判断某个成员是否在⼀个 set 集合内的重要接⼝,这个也是 list 所不能提供的。可以基于 set 轻易实现交集、并集、差集的操作。⽐如:你可以将⼀个⽤户所有的关注⼈存在⼀个集合中,将其所有粉丝存在⼀个集合。Redis 可以⾮常⽅便的实现如共同关注、共同粉丝、共同喜好等功能。这个过程也就是求交集的过程。 28 | 2. 常⽤命令: sadd,spop,smembers,sismember,scard,sinterstore,sunion 等。 29 | 3. 应⽤场景: 需要存放的数据不能重复以及需要获取多个数据源交集和并集等场景 30 | 31 | ### sorted set 32 | 33 | 1. 介绍: 和 set 相⽐,sorted set 增加了⼀个权重参数 score,使得集合中的元素能够按 score 进⾏有序排列,还可以通过 score 的范围来获取元素的列表。有点像是 Java 中 HashMap 和 TreeSet 的结合体。 34 | 2. 常⽤命令: zadd,zcard,zscore,zrange,zrevrange,zrem 等。 35 | 3. 应⽤场景: 需要对数据根据某个权重进⾏排序的场景。⽐如在直播系统中,实时排⾏信息包含直播间在线⽤户列表,各种礼物排⾏榜,弹幕消息(可以理解为按消息维度的消息排⾏榜)等信息。 36 | 37 | 参考: 38 | https://juejin.cn/post/6844903644798664712 -------------------------------------------------------------------------------- /专题整理/Redis/答案/系统设计.md: -------------------------------------------------------------------------------- 1 | # 系统设计 2 | 3 | ## Redis事务 4 | 5 | Redis 可以通过 MULTI,EXEC,DISCARD 和 WATCH 等命令来实现事务(transaction)功能。 6 | 7 | 使⽤ MULTI命令后可以输⼊多个命令。Redis不会⽴即执⾏这些命令,⽽是将它们放到队列,当调⽤了EXEC命令将执⾏所有命令。 8 | 9 | Redis 是不⽀持 roll back 的,因⽽不满⾜原⼦性的(⽽且不满⾜持久性)。 10 | 11 | 你可以将Redis中的事务就理解为 :Redis事务提供了⼀种将多个命令请求打包的功能。然后,再按顺序执⾏打包的所有命令,并且不会被中途打断。 12 | 13 | https://zhuanlan.zhihu.com/p/43897838 -------------------------------------------------------------------------------- /专题整理/Redis/答案/缓存.md: -------------------------------------------------------------------------------- 1 | # 缓存 2 | 3 | ## 过期的数据的删除策略了解么 4 | 5 | 如果假设你设置了⼀批 key 只能存活 1 分钟,那么 1 分钟后,Redis 是怎么对这批 key 进⾏删除的呢? 6 | 7 | 常⽤的过期数据的删除策略就两个(重要!⾃⼰造缓存轮⼦的时候需要格外考虑的东⻄): 8 | 1. 惰性删除 :只会在取出key的时候才对数据进⾏过期检查。这样对CPU最友好,但是可能会 9 | 造成太多过期 key 没有被删除。 10 | 2. 定期删除 : 每隔⼀段时间抽取⼀批 key 执⾏删除过期key操作。并且,Redis 底层会通过限 11 | 制删除操作执⾏的时⻓和频率来减少删除操作对CPU时间的影响。 12 | 13 | 定期删除对内存更加友好,惰性删除对CPU更加友好。两者各有千秋,所以Redis 采⽤的是 定期删除+惰性/懒汉式删除 。 14 | 15 | 但是,仅仅通过给 key 设置过期时间还是有问题的。因为还是可能存在定期删除和惰性删除漏掉了很多过期 key 的情况。这样就导致⼤量过期 key 堆积在内存⾥,然后就Out of memory了。 16 | 17 | 怎么解决这个问题呢?答案就是: Redis 内存淘汰机制。 18 | 19 | ## Redis 内存淘汰机制了解么? 20 | 21 | Redis 提供 6 种数据淘汰策略: 22 | 1. volatile-lru(least recently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使⽤的数据淘汰 23 | 2. volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰 24 | 3. volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰 25 | 4. allkeys-lru(least recently used):当内存不⾜以容纳新写⼊数据时,在键空间中,移除最近最少使⽤的 key(这个是最常⽤的) 26 | 5. allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰 27 | 6. no-eviction:禁⽌驱逐数据,也就是说当内存不⾜以容纳新写⼊数据时,新写⼊操作会报错。这个应该没⼈使⽤吧! 28 | 29 | 4.0 版本后增加以下两种: 30 | 7. volatile-lfu(least frequently used):从已设置过期时间的数据集(server.db[i].expires)中挑选最不经常使⽤的数据淘汰 31 | 8. allkeys-lfu(least frequently used):当内存不⾜以容纳新写⼊数据时,在键空间中,移除最不经常使⽤的 key 32 | 33 | ## 缓存穿透 34 | 35 | 缓存穿透说简单点就是⼤量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这⼀层。举个例⼦:某个⿊客故意制造我们缓存中不存在的 key 发起⼤量请求,导致⼤量请求落到数据库。 36 | 37 | ### 有哪些解决办法 38 | 39 | 最基本的就是⾸先做好参数校验,⼀些不合法的参数请求直接抛出异常信息返回给客户端。⽐如查询的数据库 id 不能⼩于 0、传⼊的邮箱格式不对的时候直接返回错误消息给客户端等等。 40 | 41 | #### 缓存⽆效 key 42 | 43 | 如果缓存和数据库都查不到某个 key 的数据就写⼀个到 Redis 中去并设置过期时间,具体命令如下: SET key value EX 10086 。这种⽅式可以解决请求的 key 变化不频繁的情况,如果⿊客恶意攻击,每次构建不同的请求 key,会导致 Redis 中缓存⼤量⽆效的 key 。很明显,这种⽅案并不能从根本上解决此问题。如果⾮要⽤这种⽅式来解决穿透问题的话,尽量将⽆效的 key 的过期时间设置短⼀点⽐如 1 分钟。 44 | 45 | #### 布隆过滤器 46 | 47 | 布隆过滤器是⼀个⾮常神奇的数据结构,通过它我们可以⾮常⽅便地判断⼀个给定数据是否存在于海量数据中。我们需要的就是判断 key 是否合法,有没有感觉布隆过滤器就是我们想要找的那个“⼈”。 48 | 49 | 具体是这样做的:把所有可能存在的请求的值都存放在布隆过滤器中,当⽤户请求过来,先判断⽤户发来的请求的值是否存在于布隆过滤器中。不存在的话,直接返回请求参数错误信息给客户端,存在的话才会⾛下⾯的流程。 50 | 51 | 但是,需要注意的是布隆过滤器可能会存在误判的情况。总结来说就是: 布隆过滤器说某个元素存在,⼩概率会误判。布隆过滤器说某个元素不在,那么这个元素⼀定不在。 52 | 53 | 为什么会出现误判的情况呢? 我们还要从布隆过滤器的原理来说! 54 | 55 | 我们先来看⼀下,当⼀个元素加⼊布隆过滤器中的时候,会进⾏哪些操作: 56 | 1. 使⽤布隆过滤器中的哈希函数对元素值进⾏计算,得到哈希值(有⼏个哈希函数得到⼏个哈希值)。 57 | 2. 根据得到的哈希值,在位数组中把对应下标的值置为 1。 58 | 59 | 我们再来看⼀下,当我们需要判断⼀个元素是否存在于布隆过滤器的时候,会进⾏哪些操作: 60 | 1. 对给定元素再次进⾏相同的哈希计算; 61 | 2. 得到值之后判断位数组中的每个元素是否都为 1,如果值都为 1,那么说明这个值在布隆过滤器中,如果存在⼀个值不为 1,说明该元素不在布隆过滤器中。 62 | 63 | 然后,⼀定会出现这样⼀种情况:不同的字符串可能哈希出来的位置相同。 (可以适当增加位数组⼤⼩或者调整我们的哈希函数来降低概率) 64 | 65 | https://github.com/Snailclimb/JavaGuide/blob/master/docs/dataStructures-algorithms/data-structure/bloom-filter.md 66 | 67 | ## 缓存雪崩 68 | 69 | 我发现缓存雪崩这名字起的有点意思,哈哈。 70 | 71 | 实际上,缓存雪崩描述的就是这样⼀个简单的场景:缓存在同⼀时间⼤⾯积的失效,后⾯的请求都直接落到了数据库上,造成数据库短时间内承受⼤量请求。 这就好⽐雪崩⼀样,摧枯拉朽之势,数据库的压⼒可想⽽知,可能直接就被这么多请求弄宕机了。 72 | 73 | 举个例⼦:系统的缓存模块出了问题⽐如宕机导致不可⽤。造成系统的所有访问,都要⾛数据库。 74 | 75 | 还有⼀种缓存雪崩的场景是:有⼀些被⼤量访问数据(热点缓存)在某⼀时刻⼤⾯积失效,导致对应的请求直接落到了数据库上。 这样的情况,有下⾯⼏种解决办法: 76 | 77 | 举个例⼦ :秒杀开始 12 个⼩时之前,我们统⼀存放了⼀批商品到 Redis 中,设置的缓存过期时间也是 12 个⼩时,那么秒杀开始的时候,这些秒杀的商品的访问直接就失效了。导致的情况就是,相应的请求直接就落到了数据库上,就像雪崩⼀样可怕。 78 | 79 | ### 有哪些解决办法? 80 | 81 | #### 针对 Redis 服务不可⽤的情况: 82 | 83 | 1. 采⽤ Redis 集群,避免单机出现问题整个缓存服务都没办法使⽤。 84 | 2. 限流,避免同时处理⼤量的请求。 85 | 86 | #### 针对热点缓存失效的情况: 87 | 88 | 1. 设置不同的失效时间⽐如随机设置缓存的失效时间。 89 | 2. 缓存永不失效。 90 | 91 | ## 如何保证缓存和数据库数据的⼀致性? 92 | 93 | 这块作者写的不好 94 | 95 | ## Java高级面试里的应对缓存问题 96 | 97 | 穿透、击穿、雪崩 98 | - 穿透:数据不存在(永远无法查到数据,每次落到数据库上,解决方案:1尝试设置无效标志位(默认值)代表数据不存在到redis,new Item(-1)) 99 | - 击穿:同一数据击穿到数据库(缓存不存在,大量QPS都落到数据库上,1.增加消息组件,阻塞排队 2. 在java内部给item-id上锁(synchronize或retenLock),先上锁的做mysql查询,可以把分布式的方案压缩成单体方案) 100 | - 雪崩:不同的数据击穿到数据库(大量的key同一时间失效,1.redis.set(itemid,data,10_random())) 101 | 102 | ## Java高级面试里的应对缓存脏数据问题 103 | 104 | - 脏读产生的原因 105 | - 脏读如何避免 106 | 107 | 后台应用,消息给java引用,删key或更新 108 | 109 | 老师:脏读无法避免,只有二阶段提交能避免 110 | 只要使用了缓存,就无法避免脏读 111 | 112 | 脏读本质上无法避免 113 | 114 | 缓存必须要设置超时时间 115 | 116 | ## Java高级面试里的多级缓存 117 | 118 | - 前台 119 | - H5页面,提前静态化好,CDN 120 | - 中台 121 | - nginx(不建议) 122 | - proxy cache 123 | - lua 124 | - java(堆内堆外缓存) 125 | - 后台 126 | - redis 127 | 128 | 越往前效率越高,但是更新越难 -------------------------------------------------------------------------------- /专题整理/Redis/答案/高可用.md: -------------------------------------------------------------------------------- 1 | # 高可用 2 | 3 | ## Redis 持久化机制 4 | 5 | 很多时候我们需要持久化数据也就是将内存中的数据写⼊到硬盘⾥⾯,⼤部分原因是为了之后重⽤数据(⽐如重启机器、机器故障之后恢复数据),或者是为了防⽌系统故障⽽将数据备份到⼀个远程位置。 6 | 7 | Redis 不同于 Memcached 的很重要⼀点就是,Redis ⽀持持久化,⽽且⽀持两种不同的持久化操作。Redis 的⼀种持久化⽅式叫快照(snapshotting,RDB),另⼀种⽅式是只追加⽂件(append-only file, AOF)。这两种⽅法各有千秋,下⾯我会详细这两种持久化⽅法是什么,怎么⽤,如何选择适合⾃⼰的持久化⽅法。 8 | 9 | ### 快照(snapshotting)持久化(RDB) 10 | 11 | Redis 可以通过创建快照来获得存储在内存⾥⾯的数据在某个时间点上的副本。Redis 创建快照之后,可以对快照进⾏备份,可以将快照复制到其他服务器从⽽创建具有相同数据的服务器副本(Redis 主从结构,主要⽤来提⾼ Redis 性能),还可以将快照留在原地以便重启服务器的时候使⽤。 12 | 13 | 快照持久化是 Redis 默认采⽤的持久化⽅式,在 Redis.conf 配置⽂件中默认有此下配置: 14 | 15 | save 900 1 #在900秒(15分钟)之后,如果⾄少有1个key发⽣变化,Redis就会⾃动触发BGSAVE命令创建快照。 16 | save 300 10 #在300秒(5分钟)之后,如果⾄少有10个key发⽣变化,Redis就会⾃动触发BGSAVE命令创建快照。 17 | save 60 10000 #在60秒(1分钟)之后,如果⾄少有10000个key发⽣变化,Redis就会⾃动触发BGSAVE命令创建快照。 18 | 19 | ### AOF(append-only file)持久化 20 | 21 | 与快照持久化相⽐,AOF 持久化 的实时性更好,因此已成为主流的持久化⽅案。默认情况下Redis 没有开启 AOF(append only file)⽅式的持久化,可以通过 appendonly 参数开启: 22 | 23 | appendonly yes 24 | 25 | 开启 AOF 持久化后每执⾏⼀条会更改 Redis 中的数据的命令,Redis 就会将该命令写⼊硬盘中的 AOF ⽂件。AOF ⽂件的保存位置和 RDB ⽂件的位置相同,都是通过 dir 参数设置的,默认的⽂件名是 appendonly.aof。 26 | 27 | 在 Redis 的配置⽂件中存在三种不同的 AOF 持久化⽅式,它们分别是: 28 | 29 | appendfsync always #每次有数据修改发⽣时都会写⼊AOF⽂件,这样会严重降低Redis的速度 30 | appendfsync everysec #每秒钟同步⼀次,显示地将多个写命令同步到硬盘 31 | appendfsync no #让操作系统决定何时进⾏同步 32 | 33 | 为了兼顾数据和写⼊性能,⽤户可以考虑 appendfsync everysec 选项 ,让 Redis 每秒同步⼀次AOF ⽂件,Redis 性能⼏乎没受到任何影响。⽽且这样即使出现系统崩溃,⽤户最多只会丢失⼀秒之内产⽣的数据。当硬盘忙于执⾏写⼊操作的时候,Redis 还会优雅的放慢⾃⼰的速度以便适应硬盘的最⼤写⼊速度。 34 | 35 | ### 拓展:Redis 4.0 对于持久化机制的优化 36 | 37 | Redis 4.0 开始⽀持 RDB 和 AOF 的混合持久化(默认关闭,可以通过配置项 aof-use-rdbpreamble 开启)。 38 | 39 | 如果把混合持久化打开,AOF 重写的时候就直接把 RDB 的内容写到 AOF ⽂件开头。这样做的好处是可以结合 RDB 和 AOF 的优点, 快速加载同时避免丢失过多的数据。当然缺点也是有的,AOF ⾥⾯的 RDB 部分是压缩格式不再是 AOF 格式,可读性较差。 40 | 41 | -------------------------------------------------------------------------------- /专题整理/Redis/考点整理/Redis专栏.md: -------------------------------------------------------------------------------- 1 | # 专栏 2 | 3 | https://static001.geekbang.org/resource/image/79/e7/79da7093ed998a99d9abe91e610b74e7.jpg?wh=2001*1126 4 | 5 | - 高性能主线,包括线程模型、数据结构、持久化、网络框架;、 6 | - 高可靠主线,包括主从复制、哨兵机制; 7 | - 高可扩展主线,包括数据分片、负载均衡。 8 | 9 | https://static001.geekbang.org/resource/image/70/b4/70a5bc1ddc9e3579a2fcb8a5d44118b4.jpeg?wh=2048*1536 10 | 11 | -------------------------------------------------------------------------------- /专题整理/Redis/考点整理/pipline.md: -------------------------------------------------------------------------------- 1 | # pipeline 2 | 3 | ## 使用pipeline的好处 4 | 5 | - pipeline和Linux管道类似 6 | - Redis基于请求/响应模型,单个请求处理需要一一应答 7 | - pipeline批量执行指令,节省多次IO往返的时间 8 | - 有顺序依赖的指令建议分批发送 9 | -------------------------------------------------------------------------------- /专题整理/Redis/考点整理/主从同步.md: -------------------------------------------------------------------------------- 1 | # 主从同步 2 | 3 | 4 | ## Redis的同步机制 5 | 6 | 主从同步原理 7 | 8 | 全同步过程 9 | - Slave发送sync命令到Master 10 | - Master启动一个后台进程,将Redis中的数据快照保存到文件中 11 | - Master将保存数据快照期间接收到的写命令缓存起来 12 | - Master完成写文件操作后,将该文件发送给Slave 13 | - 使用新的AOF文件替换掉旧的AOF文件 14 | - Master将这期间收集的增量写命令发送给Slave端 15 | 16 | 增量同步流程 17 | - Master接收到用户的操作指令,判断是否需要传播到Slave 18 | - 将操作记录追加到AOF文件 19 | - 将操作传播到其他Slave: 20 | - 对齐主从库 21 | - 往响应缓存写入指令 22 | - 将缓存中的数据发送给Slave 23 | 24 | 主从模式的弊端就是不具备高可用性 25 | master挂掉之后,将不能提供写入操作,因此sentinel应运而生 26 | 27 | ## Redis Sentinel 28 | 29 | 解决主从同步Master宕机后的主从切换问题: 30 | - 监控:检查主从服务器是否运行正常 31 | - 提醒:通过API向管理员或者其他应用程序发送故障通知 32 | - 自动故障迁移:主从切换 33 | 34 | ## 流言协议gossip 35 | 36 | 在杂乱无章中寻求一致 37 | - 每个节点都随机地与对方通信,最终所有节点的状态达成一致 38 | - 种子节点定期随机向其他节点发送节点列表以及需要传播的消息 39 | - 不保证信息一定会传递给所有节点,但是最终会趋于一致 40 | 41 | -------------------------------------------------------------------------------- /专题整理/Redis/考点整理/事务.md: -------------------------------------------------------------------------------- 1 | # 事务 2 | 3 | - 将多个请求打包,一次性、按序执行多个命令的机制 4 | - redis通过MULTI,EXEC,WATCH等命令实现事务功能 5 | - python redis-py pipeline=conn.pipeline(transaction=True) 6 | 7 | 原子性,但不支持回滚 8 | 不能完全实现持久化 9 | 有隔离性 10 | 一致性 11 | 12 | -------------------------------------------------------------------------------- /专题整理/Redis/考点整理/分布式锁.md: -------------------------------------------------------------------------------- 1 | # 分布式锁 2 | 3 | ## 如何通过Redis实现分布式锁 4 | 5 | 分布式锁需要解决的问题: 6 | - 互斥性 7 | - 安全性 8 | - 死锁 9 | - 容错 10 | 11 | setnx key value:如果key不存在,则创建并赋值 12 | - 时间复杂度O(1) 13 | - 返回值:设置成功,返回1;设置失败,返回0 14 | 15 | ## 如何解决SETNX长期有效的问题 16 | 17 | expire key seconds 18 | - 设置key的生存时间,当key过期时(生存时间为0),会被自动删除 19 | - 缺点:原子性得不到满足 20 | 21 | 但是上面的操作没有原子性 22 | 23 | set key value [EX seconds] [PX millisecond] [NX|XX] 24 | - EX second:设置键的过期时间为second秒 25 | - PX millisecond: 设置键的过期时间为millisecond毫秒 26 | - NX:只在键不存在时,才对键进行设置操作 27 | - XX: 只在键已经存在时,才对键进行设置操作 28 | - SET操作成功完成时,返回OK,否则返回nil 29 | 30 | set locktarget 12345 ex 10 nx 31 | 32 | ## 大量的key同时过期的注意事项 33 | 34 | 集中过期,由于清除大量的key很耗时,会出现短暂的卡顿现象 35 | - 解决方案:在设置key的过期时间的时候,给每个key加上随机值 36 | 37 | ## python面试慕课 38 | 39 | - 使用setnx实现加锁,可以同时通过expire添加超时时间 40 | - 锁的value值可以使用一个随机的uuid或者特定的命名 41 | - 释放锁的时候,通过uuid判断是否是该锁,是则执行释放锁 -------------------------------------------------------------------------------- /专题整理/Redis/考点整理/异步队列.md: -------------------------------------------------------------------------------- 1 | # 异步队列 2 | 3 | ## 如何使用Redis做异步队列 4 | 5 | 使用List作为队列,RPUSH生产消息,LPOP消费消息 6 | - 缺点:没有等待队列里有值就直接消费 7 | - 弥补:可以通过在应用层引入sleep机制去调用LPOP重试 8 | 9 | BLPOP key [key...] timeout : 阻塞直到队列有消息或者超时 10 | - 缺点:只能供一个消费者消费 11 | 12 | bloop能替代sleep做精准的阻塞控制 13 | 14 | pub/sub:主题订阅者模式 15 | - 发送者(pub)发送消息,订阅者(sub)接收消息 16 | - 订阅者可以订阅任意数量的频道 17 | - 缺点:消息的发布是无状态的,无法保证可达 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /专题整理/Redis/考点整理/持久化.md: -------------------------------------------------------------------------------- 1 | # 持久化 2 | 3 | RDB(快照)持久化:保存某个时间点的全量数据快照。 4 | 5 | 平衡性能和安全 6 | 7 | - SAVE:阻塞Redis的服务器进程,直到RDB文件被创建完毕 8 | - BGSAVE:Fork出一个子进程来创建RDB文件,不阻塞服务器进程 9 | 10 | 11 | ## 自动化出发RDB持久化的方式 12 | 13 | - 根据redis.conf配置里的SAVE m n定时出发(用的是BGSAVE) 14 | - 主从复制时,主节点自动触发 15 | - 执行Debug Reload 16 | - 执行Shutdown且没有开启AOF持久化 17 | 18 | ## BGSAVE原理 19 | 20 | - 系统调用fork():创建进程,实现了copy-on-write 21 | - copy-on-write:如果有多个调用者同时要求相同资源(如内存或磁盘上的数据存储),他们会共同获取相同的指针指向相同的资源,直到某个调用者试图修改资源的内容时,系统才会真正复制一份专用副本给该调用者,而其他调用者所见到的最初的资源仍然保持不变。 22 | 23 | ## RDB持久化缺点 24 | 25 | - 内存数据的全量同步,数据量大会由于IO而严重影响性能 26 | - 可能会因为Redis挂掉而丢失从当前至最近一次快照期间的数据 27 | 28 | ## AOF(Append-Only-File)持久化:保存写状态 29 | 30 | - 记录下除了查询以外的所有变更数据库状态的指令 31 | - 以append的形式追加保存在AOF文件中(增量) 32 | 33 | ## 日志重写解决AOF文件大小不断增大的问题,原理如下: 34 | 35 | - 调用fork(),创建一个子进程 36 | - 子进程把新的AOF写到一个临时文件里,不依赖原来的AOF文件 37 | - 主进程持续将新的变动同时写到内存和原来的AOF里 38 | - 主进程获取子进程重写AOF的完成信号,往新AOF同步增量变动 39 | - 使用新的AOF文件替换掉旧的AOF文件 40 | 41 | ## Redis数据的恢复 42 | 43 | RDB和AOF文件共存情况下的恢复流程 44 | 45 | ## RDB和AOF的缺点 46 | - RDB 47 | - 优点:全量数据快照,文件小,恢复快 48 | - 缺点:无法保存最近一次快照之后的数据 49 | - AOF 50 | - 优点:可读性高,适合保存增量数据,数据不易丢失 51 | - 缺点:文件体积大,恢复时间长 52 | 53 | ## Redis如何做持久化 54 | 55 | RDB-AOF混合持久化方式 56 | - BGSAVE做镜像全量持久化,AOF做增量持久化 57 | 58 | ### 数据持久化 59 | 60 | - rdb(rdb备份) 61 | - aof(到期写:900s写一次。到次数写:900写一次) -------------------------------------------------------------------------------- /专题整理/Redis/考点整理/数据类型.md: -------------------------------------------------------------------------------- 1 | # 数据类型 2 | 3 | - string:用来实现简单的kv键值对存储,比如计数器 4 | - list:实现双向链表,比如用户的关注,粉丝列表 5 | - hash:用来存储彼此相关信息的键值对 6 | - set:存储不重复元素,比如用户的关注着 7 | - sorted set:实时信息排行榜 8 | 9 | 数据类型丰富,支持持久化,主从,分片 10 | 11 | ## 为什么Redis能这么快 12 | 13 | 10W QPS 14 | 15 | - 完全基于内存,绝大部分请求时纯粹的内存操作,执行效率高 16 | - 数据结构简单,对数据操作也简单 17 | - 采用单线程,单线程也能处理高并发请求,想多核也可启动多实例 18 | (不会有并发问题,避免了上下文切换和锁竞争) 19 | - 使用多路IO复用模型,非阻塞IO 20 | 21 | ## 多路IO复用模型 22 | 23 | fd:file descriptor,文件描述符 24 | 25 | 一个打开的文件通过唯一的描述符进行引用,该描述符是打开文件的元数据到文件本身的映射。 26 | 27 | select系统调用 28 | 29 | Redis采用的IO多路复用函数:epoll/kqueue/evport/select? 30 | - 因地制宜 31 | - 优先选择时间复杂度为O(1)的IO多路复用函数作为底层实现 32 | - 以时间复杂度为O(n)的select作为保底 33 | - 基于react设计模式监听IO事件 34 | 35 | ## 说说你用过的Redis的数据类型 36 | 37 | - String:最基本的数据类型,二进制安全 38 | - Hash:String元素组成的字典,适合用于存储对象 39 | - List:列表,按照String元素插入顺序排序 40 | - Set:String元素组成的无序集合,通过哈希表实现,不允许重复 41 | - Sorted Set:通过分数来为集合中的成员进行从小到大的排序 42 | (增加了权重) 43 | - 用来计数的HyperLogLog,用于支持存储地理位置信息的Geo 44 | 45 | zrangebyscore myzset 46 | zadd myzset 3 abc 47 | 48 | 底层数据类型基础 49 | - 简单动态字符串 50 | - 链表 51 | - 字典 52 | - 跳跃表 53 | - 整数集合 54 | - 压缩列表 55 | - 对象 56 | 57 | - String:整数或者sds(simple dynamic string) 58 | - list:ziplist或者double linked list(ziplist:通过一个连续的内存块实现list结构,其中的每个entry节点头部保存前后节点长度信息,实现双向链表功能) 59 | - hash:ziplist或者hashtable 60 | - set:intset或者hashtable 61 | - sortedset:skiplist 跳跃表 62 | 63 | 《Redis设计与实现》 64 | 65 | 66 | ## 从海量数据里查询某一固定前缀的key 67 | 68 | keys pattern:查找所有符合给定模式pattern的key 69 | - keys指令一次性返回所有匹配的key 70 | - 键的数量过大会使服务卡顿 71 | 72 | scan cursor [MATCH pattern] [COUNT count] 73 | - 基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程 74 | - 以0作为游标开始一次新的迭代,直到命令返回游标0完成一次遍历 75 | - 不保证每次执行都返回某个给定数量的元素,支持模糊查询 76 | - 一次返回的数量不可控,只能是大概率符合count参数 77 | 78 | scan 0 match k1* count 10 79 | 80 | ## 数据结构 81 | 82 | - string 83 | - hash 84 | - list 85 | - set 86 | - zset(跳表+压缩表) 87 | 88 | 节点数量比较少时,用压缩表 89 | 节点数量比较大,128,变成跳表 90 | 91 | ## 单线程及原子性 92 | 93 | - setnx 94 | - 为什么能保证原子性 95 | - setnx——IO thread——work thread 单线程模型——key exist & set -------------------------------------------------------------------------------- /专题整理/Redis/考点整理/概述.md: -------------------------------------------------------------------------------- 1 | # 概述 2 | 3 | - 缓解关系数据库(Mysql)并发访问的压力:热点数据 4 | - 减少响应时间:内存IO速度比磁盘快 5 | - 提升吞吐量:Redis等内存数据库单机就可以支撑很大并发 6 | 7 | ## 参考资料 8 | 9 | leetcode上的一个题目汇总,不过没多大用 10 | 11 | https://blog.csdn.net/w139074301/article/details/112550908 -------------------------------------------------------------------------------- /专题整理/Redis/考点整理/穿透击穿雪崩.md: -------------------------------------------------------------------------------- 1 | # 穿透击穿雪崩 2 | 3 | ## 穿透 4 | 5 | 大量查询不到的数据的请求落到后端数据库,数据库压力增大 6 | - 由于大量缓存查不到就去数据库取,数据库也没有要查的数据 7 | - 解决:对于没查到返回为None的数据也缓存 8 | - 插入数据的时候删除相应缓存,或者设置较短的超时时间 9 | 10 | ## 击穿 11 | 12 | 某些非常热点的数据key过期,大量请求打倒后端数据库 13 | - 热点数据key失效导致大量请求打到数据库增加数据库压力 14 | - 分布式锁:获取锁的线程从数据库拉数据更新缓存,其他线程等待 15 | - 异步后台更新:后台任务针对过期的key自动刷新 16 | 17 | ## 雪崩 18 | 19 | 缓存不可用或者大量缓存key同时失效,大量请求直接打到数据库 20 | - 多级缓存:不同级别的key设置不同的超时时间 21 | - 随机超时:key的超时时间随机设置,防止同时超时 22 | - 架构层:提升系统可用性。监控、报警完善 23 | 24 | -------------------------------------------------------------------------------- /专题整理/Redis/考点整理/缓存使用模式.md: -------------------------------------------------------------------------------- 1 | # 缓存使用模式 2 | 3 | - cache aside:同时更新缓存和数据库 4 | - read/write through:先更新缓存,缓存负责同步更新数据库 5 | - write behind caching:先更新缓存,缓存定期异步更新数据库 6 | 7 | 一般先更新数据库然后删除缓存 -------------------------------------------------------------------------------- /专题整理/Redis/考点整理/缓存淘汰策略.md: -------------------------------------------------------------------------------- 1 | # 缓存淘汰策略 2 | 3 | - lru:最近最少使用的淘汰。 4 | allkeys:所有 5 | volatile:设置过期时间的 6 | - ttl:从已设置过期时间中挑选将要过期的淘汰 7 | - random:数据中随机淘汰 8 | allkeys:所有 9 | volatile:设置过期时间的 10 | - no-enviction:禁止驱逐,直接报错 11 | -------------------------------------------------------------------------------- /专题整理/Redis/考点整理/集群.md: -------------------------------------------------------------------------------- 1 | # 集群 2 | 3 | ## Redis的集群原理 4 | 5 | 如何从海量数据里快速找到所需? 6 | - 分片:按照某种规则去划分数据,分散存储在多个节点上 7 | - 常规的按照哈希划分无法实现节点的动态增减 8 | 9 | 一致性哈希算法:对2^32取模,将哈希值空间组织成虚拟的圆环 10 | 11 | 将数据key使用相同的函数hash计算出哈希值 12 | 13 | hash环的数据倾斜问题 14 | 15 | 引入虚拟节点解决数据倾斜的问题 16 | 17 | 结合集群,我们还可以用主从+哨兵,形成高可用,这就是主流的做法 -------------------------------------------------------------------------------- /专题整理/中间件.md: -------------------------------------------------------------------------------- 1 | # 中间件 2 | 3 | ## Redis 4 | 5 | 数据类型 6 | 7 | 内存,持久化 8 | 9 | ## rabbitmq 10 | 11 | 模型 12 | 13 | ## kafka 14 | 15 | -------------------------------------------------------------------------------- /专题整理/排序算法.md: -------------------------------------------------------------------------------- 1 | 详见leetcode面试题912 2 | 3 | https://leetcode-cn.com/problems/sort-an-array/ 4 | -------------------------------------------------------------------------------- /专题整理/操作系统/常见面试题.md: -------------------------------------------------------------------------------- 1 | # 操作系统 2 | 3 | ## Linux指令 4 | 5 | 查看系统负载。使用什么命令查看系统负载,第一行是什么 6 | 7 | ## IO 8 | 9 | 套接字底层选项功能及阻塞和非阻塞场景应用注意事项 10 | 同步io和异步io区别, 11 | epoll 和select 12 | socket 建立过程 13 | 如何高效处理socket 14 | NIO 的原理和,连接切换方式 15 | 16 | ## 进程线程 17 | 18 | 线程的状态有几种 19 | 进程间通信方法有几种 20 | 管道如何使用 21 | 22 | 1.管道:速度慢,容量有限,只有父子进程能通讯 23 | 24 | 2.FIFO:任何进程间都能通讯,但速度慢 25 | 26 | 3.消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题 27 | 28 | 4.信号量:不能传递复杂消息,只能用来同步 29 | 30 | 5.共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存 31 | 32 | 参考资料:https://www.cnblogs.com/zgq0/p/8780893.html 33 | 34 | 进程和线程 35 | 36 | 进程是资源分配的最小单位,线程是CPU调度的最小单位 37 | 38 | - 所有与进程相关的资源,都被记录在PCB中 39 | - 进程是抢占处理机的调度单位;线程属于某个进程,共享其资源 40 | - 线程只由堆栈寄存器、程序计数器和TCB组成 41 | 42 | 总结: 43 | - 线程不能看做独立应用,而进程可看做独立应用 44 | - 进程有独立的地址空间,相互不影响,线程只是进程的不同执行路径 45 | - 线程没有独立的地址空间,多进程的程序比多线程程序健壮 46 | - 进程的切换比线程的切换开销大 47 | -------------------------------------------------------------------------------- /专题整理/操作系统/我的整理.md: -------------------------------------------------------------------------------- 1 | 1 select,poll和epoll 2 | 其实所有的I/O都是轮询的方法,只不过实现的层面不同罢了. 3 | 4 | 这个问题可能有点深入了,但相信能回答出这个问题是对I/O多路复用有很好的了解了.其中tornado使用的就是epoll的. 5 | 6 | selec,poll和epoll区别总结 7 | 8 | 基本上select有3个缺点: 9 | 10 | 连接数受限 11 | 查找配对速度慢 12 | 数据由内核拷贝到用户态 13 | poll改善了第一个缺点 14 | 15 | epoll改了三个缺点. 16 | 17 | 关于epoll的: http://www.cnblogs.com/my_life/articles/3968782.html 18 | 19 | 2 调度算法 20 | 先来先服务(FCFS, First Come First Serve) 21 | 短作业优先(SJF, Shortest Job First) 22 | 最高优先权调度(Priority Scheduling) 23 | 时间片轮转(RR, Round Robin) 24 | 多级反馈队列调度(multilevel feedback queue scheduling) 25 | 常见的调度算法总结:http://www.jianshu.com/p/6edf8174c1eb 26 | 27 | 实时调度算法: 28 | 29 | 最早截至时间优先 EDF 30 | 最低松弛度优先 LLF 31 | 3 死锁 32 | 原因: 33 | 34 | 竞争资源 35 | 程序推进顺序不当 36 | 必要条件: 37 | 38 | 互斥条件 39 | 请求和保持条件 40 | 不剥夺条件 41 | 环路等待条件 42 | 处理死锁基本方法: 43 | 44 | 预防死锁(摒弃除1以外的条件) 45 | 避免死锁(银行家算法) 46 | 检测死锁(资源分配图) 47 | 解除死锁 48 | 剥夺资源 49 | 撤销进程 50 | 死锁概念处理策略详细介绍:https://wizardforcel.gitbooks.io/wangdaokaoyan-os/content/10.html 51 | 52 | 4 程序编译与链接 53 | 推荐: http://www.ruanyifeng.com/blog/2014/11/compiler.html 54 | 55 | Bulid过程可以分解为4个步骤:预处理(Prepressing), 编译(Compilation)、汇编(Assembly)、链接(Linking) 56 | 57 | 以c语言为例: 58 | 59 | 1 预处理 60 | 预编译过程主要处理那些源文件中的以“#”开始的预编译指令,主要处理规则有: 61 | 62 | 将所有的“#define”删除,并展开所用的宏定义 63 | 处理所有条件预编译指令,比如“#if”、“#ifdef”、 “#elif”、“#endif” 64 | 处理“#include”预编译指令,将被包含的文件插入到该编译指令的位置,注:此过程是递归进行的 65 | 删除所有注释 66 | 添加行号和文件名标识,以便于编译时编译器产生调试用的行号信息以及用于编译时产生编译错误或警告时可显示行号 67 | 保留所有的#pragma编译器指令。 68 | 2 编译 69 | 编译过程就是把预处理完的文件进行一系列的词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件。这个过程是整个程序构建的核心部分。 70 | 71 | 3 汇编 72 | 汇编器是将汇编代码转化成机器可以执行的指令,每一条汇编语句几乎都是一条机器指令。经过编译、链接、汇编输出的文件成为目标文件(Object File) 73 | 74 | 4 链接 75 | 链接的主要内容就是把各个模块之间相互引用的部分处理好,使各个模块可以正确的拼接。 链接的主要过程包块 地址和空间的分配(Address and Storage Allocation)、符号决议(Symbol Resolution)和重定位(Relocation)等步骤。 76 | 77 | 5 静态链接和动态链接 78 | 静态链接方法:静态链接的时候,载入代码就会把程序会用到的动态代码或动态代码的地址确定下来 静态库的链接可以使用静态链接,动态链接库也可以使用这种方法链接导入库 79 | 80 | 动态链接方法:使用这种方式的程序并不在一开始就完成动态链接,而是直到真正调用动态库代码时,载入程序才计算(被调用的那部分)动态代码的逻辑地址,然后等到某个时候,程序又需要调用另外某块动态代码时,载入程序又去计算这部分代码的逻辑地址,所以,这种方式使程序初始化时间较短,但运行期间的性能比不上静态链接的程序 81 | 82 | 6 虚拟内存技术 83 | 虚拟存储器是指具有请求调入功能和置换功能,能从逻辑上对内存容量加以扩充的一种存储系统. 84 | 85 | 7 分页和分段 86 | 分页: 用户程序的地址空间被划分成若干固定大小的区域,称为“页”,相应地,内存空间分成若干个物理块,页和块的大小相等。可将用户程序的任一页放在内存的任一块中,实现了离散分配。 87 | 88 | 分段: 将用户程序地址空间分成若干个大小不等的段,每段可以定义一组相对完整的逻辑信息。存储分配时,以段为单位,段与段在内存中可以不相邻接,也实现了离散分配。 89 | 90 | 分页与分段的主要区别 91 | 页是信息的物理单位,分页是为了实现非连续分配,以便解决内存碎片问题,或者说分页是由于系统管理的需要.段是信息的逻辑单位,它含有一组意义相对完整的信息,分段的目的是为了更好地实现共享,满足用户的需要. 92 | 页的大小固定,由系统确定,将逻辑地址划分为页号和页内地址是由机器硬件实现的.而段的长度却不固定,决定于用户所编写的程序,通常由编译程序在对源程序进行编译时根据信息的性质来划分. 93 | 分页的作业地址空间是一维的.分段的地址空间是二维的. 94 | 8 页面置换算法 95 | 最佳置换算法OPT:不可能实现 96 | 先进先出FIFO 97 | 最近最久未使用算法LRU:最近一段时间里最久没有使用过的页面予以置换. 98 | clock算法 99 | 9 边沿触发和水平触发 100 | 边缘触发是指每当状态变化时发生一个 io 事件,条件触发是只要满足条件就发生一个 io 事件 101 | 102 | 103 | 揭开socket编程的面纱 104 | 105 | http://www.360doc.com/content/11/0609/15/5482098_122692444.shtml 106 | 107 | -------------------------------------------------------------------------------- /专题整理/操作系统/知识整理.md: -------------------------------------------------------------------------------- 1 | 上下文切换。进程上下文切换成本比线程高 2 | 3 | -------------------------------------------------------------------------------- /专题整理/操作系统/答案/操作系统.md: -------------------------------------------------------------------------------- 1 | # 操作系统 2 | 3 | ## 什么是操作系统 4 | 5 | 1. 操作系统(Operating System,简称 OS)是管理计算机硬件与软件资源的程序,是计算机的基⽯。 6 | 2. 操作系统本质上是⼀个运⾏在计算机上的软件程序 ,⽤于管理计算机硬件和软件资源。 举例:运⾏在你电脑上的所有应⽤程序都通过操作系统来调⽤系统内存以及磁盘等等硬件。 7 | 3. 操作系统存在屏蔽了硬件层的复杂性。 操作系统就像是硬件使⽤的负责⼈,统筹着各种相关事项。 8 | 4. 操作系统的内核(Kernel)是操作系统的核⼼部分,它负责系统的内存管理,硬件设备的管理,⽂件系统的管理以及应⽤程序的管理。 内核是连接应⽤程序和硬件的桥梁,决定着系统的性能和稳定性。 9 | 10 | ## 什么是系统调⽤ 11 | 12 | 介绍系统调⽤之前,我们先来了解⼀下⽤户态和系统态。 13 | 14 | 根据进程访问资源的特点,我们可以把进程在系统上的运⾏分为两个级别: 15 | 1. ⽤户态(user mode) : ⽤户态运⾏的进程或可以直接读取⽤户程序的数据。 16 | 2. 系统态(kernel mode):可以简单的理解系统态运⾏的进程或程序⼏乎可以访问计算机的任何资源,不受限制。 17 | 18 | 说了⽤户态和系统态之后,那么什么是系统调⽤呢? 19 | 我们运⾏的程序基本都是运⾏在⽤户态,如果我们调⽤操作系统提供的系统态级别的⼦功能咋办呢?那就需要系统调⽤了! 20 | 也就是说在我们运⾏的⽤户程序中,凡是与系统态级别的资源有关的操作(如⽂件管理、进程控制、内存管理等),都必须通过系统调⽤⽅式向操作系统提出服务请求,并由操作系统代为完成。 21 | 这些系统调⽤按功能⼤致可分为如下⼏类: 22 | - 设备管理。完成设备的请求或释放,以及设备启动等功能。 23 | - ⽂件管理。完成⽂件的读、写、创建及删除等功能。 24 | - 进程控制。完成进程的创建、撤销、阻塞及唤醒等功能。 25 | - 进程通信。完成进程之间的消息传递或信号传递等功能。 26 | - 内存管理。完成内存的分配、回收以及获取作业占⽤内存区⼤⼩及地址等功能。 27 | 28 | -------------------------------------------------------------------------------- /专题整理/操作系统/答案/进程和线程.md: -------------------------------------------------------------------------------- 1 | # 进程和线程 2 | 3 | ## 进程和线程的区别 4 | 5 | 从上图可以看出:⼀个进程中可以有多个线程,多个线程共享进程的堆和⽅法区 (JDK1.8 之后的元空间)资源,但是每个线程有⾃⼰的程序计数器、虚拟机栈 和 本地⽅法栈。 6 | 7 | 总结: 线程是进程划分成的更⼩的运⾏单位,⼀个进程在其执⾏的过程中可以产⽣多个线程。线程和进程最⼤的不同在于基本上各进程是独⽴的,⽽各线程则不⼀定,因为同⼀进程中的线程极有可能会相互影响。线程执⾏开销⼩,但不利于资源的管理和保护;⽽进程正相反。 8 | 9 | https://snailclimb.gitee.io/javaguide/#/docs/java/jvm/Java%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F 10 | 11 | 12 | ## 进程有哪⼏种状态? 13 | 14 | 我们⼀般把进程⼤致分为 5 种状态,这⼀点和线程很像! 15 | 16 | 创建状态(new) :进程正在被创建,尚未到就绪状态。 17 | 就绪状态(ready) :进程已处于准备运⾏状态,即进程获得了除了处理器之外的⼀切所需资源,⼀旦得到处理器资源(处理器分配的时间⽚)即可运⾏。 18 | 运⾏状态(running) :进程正在处理器上上运⾏(单核 CPU 下任意时刻只有⼀个进程处于运⾏状态)。 19 | 阻塞状态(waiting) :⼜称为等待状态,进程正在等待某⼀事件⽽暂停运⾏如等待某资源为可⽤或等待 IO 操作完成。即使处理器空闲,该进程也不能运⾏。 20 | 结束状态(terminated) :进程正在从系统中消失。可能是进程正常结束或其他原因中断退出运⾏。 21 | 22 | 线程的状态: 23 | 24 | https://snailclimb.gitee.io/javaguide/#/docs/java/multi-thread/2020%E6%9C%80%E6%96%B0Java%E5%B9%B6%E5%8F%91%E5%9F%BA%E7%A1%80%E5%B8%B8%E8%A7%81%E9%9D%A2%E8%AF%95%E9%A2%98%E6%80%BB%E7%BB%93?id=_6-%e8%af%b4%e8%af%b4%e7%ba%bf%e7%a8%8b%e7%9a%84%e7%94%9f%e5%91%bd%e5%91%a8%e6%9c%9f%e5%92%8c%e7%8a%b6%e6%80%81 25 | 26 | ## 进程间通信的方式 27 | 28 | ⼤概有 7 种常⻅的进程间的通信⽅式: 29 | 30 | 1. 管道/匿名管道(Pipes) :⽤于具有亲缘关系的⽗⼦进程间或者兄弟进程之间的通信。 31 | 2. 有名管道(Names Pipes) : 匿名管道由于没有名字,只能⽤于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道。有名管道严格遵循先进先出(first in first out)。有名管道以磁盘⽂件的⽅式存在,可以实现本机任意两个进程通信。 32 | 3. 信号(Signal) :信号是⼀种⽐᫾复杂的通信⽅式,⽤于通知接收进程某个事件已经发⽣; 33 | 4. 消息队列(Message Queuing) :消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识。管道和消息队列的通信数据都是先进先出的原则。与管道(⽆名管道:只存在于内存中的⽂件;命名管道:存在于实际的磁盘介质或者⽂件系统)不同的是消息队列存放在内核中,只有在内核重启(即,操作系统重启)或者显示地删除⼀个消息队列时,该消息队列才会被真正的删除。消息队列可以实现消息的随机查询,消息不⼀定要以先进先出的次序读取,也可以按消息的类型读取.⽐ FIFO 更有优势。消息队列克服了信号承载信息量少,管道只能承载⽆格式字 节流以及缓冲区⼤⼩受限等缺。 34 | 5. 信号量(Semaphores) :信号量是⼀个计数器,⽤于多进程对共享数据的访问,信号量的意图在于进程间同步。这种通信⽅式主要⽤于解决与同步相关的问题并避免竞争条件。 35 | 6. 共享内存(Shared memory) :使得多个进程可以访问同⼀块内存空间,不同进程可以及时看到对⽅进程中对共享内存中数据的更新。这种⽅式需要依靠某种同步操作,如互斥锁和信号量等。可以说这是最有⽤的进程间通信⽅式。 36 | 7. 套接字(Sockets) : 此⽅法主要⽤于在客户端和服务器之间通过⽹络进⾏通信。套接字是⽀持 TCP/IP 的⽹络通信的基本操作单元,可以看做是不同主机之间的进程进⾏双向通信的端点,简单的说就是通信的两⽅的⼀种约定,⽤套接字中的相关函数来完成通信过程。 37 | 38 | https://www.jianshu.com/p/c1015f5ffa74 39 | 40 | ## 线程间的同步的⽅式 41 | 42 | 线程同步是两个或多个共享关键资源的线程的并发执行。应该同步线程以避免关键的资源使⽤冲突。操作系统⼀般有下⾯三种线程同步的⽅式: 43 | 1. 互斥量(Mutex):采⽤互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有⼀个,所以可以保证公共资源不会被多个线程同时访问。⽐如 Java 中的synchronized 关键词和各种 Lock 都是这种机制。 44 | 2. 信号量(Semphares) :它允许同⼀时刻多个线程访问同⼀资源,但是需要控制同⼀时刻访问此资源的最⼤线程数量 45 | 3. 事件(Event) :Wait/Notify:通过通知操作的⽅式来保持多线程同步,还可以⽅便的实现多线程优先级的比较 46 | 47 | ## 进程的调度算法 48 | 49 | 为了确定首先执行哪个进程以及最后执行哪个进程以实现最大 CPU 利⽤率,计算机科学家已经 50 | 定义了⼀些算法,它们是: 51 | 52 | - 先到先服务(FCFS)调度算法 : 从就绪队列中选择⼀个最先进入该队列的进程为之分配资源,使它立即执行并一直执行到完成或发生某事件而被阻塞放弃占用CPU 时再重新调度。 53 | - 短作业优先(SJF)的调度算法 : 从就绪队列中选出⼀个估计运行时间最短的进程为之分配资源,使它立即执行并⼀直执行到完成或发生某事件而被阻塞放弃占用CPU 时再重新调度。 54 | - 时间片轮转调度算法 : 时间片轮转调度是⼀种最古老,最简单,最公平且使用最广的算法,又称 RR(Round robin)调度。每个进程被分配⼀个时间段,称作它的时间片,即该进程允许运行的时间。 55 | - 多级反馈队列调度算法 :前面介绍的几种进程调度的算法都有⼀定的局限性。如短进程优先的调度算法,仅照顾了短进程而忽略了长进程 。多级反馈队列调度算法既能使高优先级的作业得到响应⼜能使短作业(进程)迅速完成。因而它是目前被公认的⼀种较好的进程调度算法,UNIX 操作系统采取的便是这种调度算法。 56 | - 优先级调度 : 为每个流程分配优先级,首先执行具有最高优先级的进程,依此类推。具有相同优先级的进程以 FCFS 方式执行。可以根据内存要求,时间要求或任何其他资源要求来确定优先级。 57 | 58 | -------------------------------------------------------------------------------- /专题整理/操作系统/考点突破/IO.md: -------------------------------------------------------------------------------- 1 | # IO 2 | 3 | ## 如何提升并发能力 4 | 5 | 一些常见的提升并发能力的方式 6 | - 多线程模型,创建新的线程处理请求 7 | - 多进程模型,创建新的进程处理请求 8 | - IO多路复用,实现单进程同时处理多个socket请求 9 | 10 | - 线程/进程创建开销比较大,可以用线程池方式解决 11 | - 线程和进程比较占用资源,难以同时创建太多 12 | 13 | ## 什么是IO多路复用 14 | 15 | 操作系统提供的同时监听多个socket的机制 16 | - 为了实现高并发需要一种机制并发处理多个socket 17 | - Linux常见的是select/poll/epoll 18 | - 可以使用单线程单进程处理多个socket 19 | 20 | 两个过程: 21 | - 内核等待数据 22 | - 数据从内核拷贝到用户进程 23 | 24 | ## IO相关 25 | 26 | - BIO:同步阻塞式调用 27 | - NIO:同步非阻塞。(Java中广泛应用) 28 | select 29 | - AIO:异步IO。 30 | 31 | BIO(blockio):InputStream和OutputStream,Reader和writer 32 | 33 | NIO(nonblock-io):构建多路复用的,同步非阻塞的IO操作 34 | - nio-channels 35 | - nio-buffer 36 | - nio-selector 37 | nio的底层用了IO多路复用 38 | 39 | AIO:基于事件和回调机制 40 | 41 | BIO、NIO、AIO对比 42 | 属性\模型 阻塞BIO 非阻塞NIO 异步AIO 43 | blocking 阻塞并同步 非阻塞但同步 非阻塞并异步 44 | 线程数 1:1 1:N 0:N 45 | 复杂度 简单 较复杂 复杂 46 | 吞吐量 低 高 高 47 | 48 | ## 五种IO 49 | 50 | 前四种都是同步 51 | 52 | 同步阻塞IO: 去饭馆吃饭 去了之后都在门口等着不能离开 53 | 同步非阻塞IO: 去了饭馆吃饭 就可以去干别的了 时不时回来看看 54 | 同步阻塞IO复用:去了饭馆门口,饭馆有个服务员,付服务员 有位了就让你进去 55 | 同步非阻塞信号驱动: 去了饭馆吃饭 去了之后领个号 就可以去干别的了 到你了 直接微信给你弹消息让你来 56 | 异步IO模型: 你去签个到 就去干别的了 做好了 直接找你去送过来 57 | 58 | ## select和epoll的区别 59 | 60 | - 数量上限 61 | - 轮询 or 回调 62 | 63 | select模型 64 | 65 | 应用程序——select——解除阻塞——循环遍历查找收到读信号的连接——socket recv 66 | select缺陷:1024数组个数的限制,遍历轮询触发找到真正有信号的socket连接 67 | 68 | epoll模型 69 | 70 | epoll改进:没有1024数组个数的限制.异步回调的方式去执行handler操作 71 | 应用程序——epoll,socket连接handler——触发handler回调机制——handler 72 | 73 | epoll性能比select强很多,nginx就是epoll模型 74 | 75 | Java面试里面有三种的比较,讲的还挺好的 76 | 有很多图,还有对比 77 | 78 | select、epoll、epoll的区别 79 | - 支持一个进程所能打开的最大连接数。 80 | - select:单个进程所能打开的最大连接数由FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小是32*32,64位机器上FD_SETSIZE为32*64),我们可以对其进行修改,然后重新编译内核,但是性能无法保证,需要做进一步测试 81 | - poll:本质上与select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的 82 | - epoll:虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接 83 | - FD剧增后带来的IO效率问题 84 | - select:因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度的线性下降的性能问题 85 | - poll:同上 86 | - epoll:由于epoll是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll不会有线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。 87 | - 消息传递方式 88 | - select:内核需要将消息传递到用户空间,需要内核的拷贝动作 89 | - poll:同上 90 | - epoll:通过内核和用户空间共享一块内存来实现,性能较高 91 | 92 | 93 | ## 我的整理 94 | 95 | 两个步骤: 96 | - 等待返回数据 97 | - 数据从内核复制到用户空间 98 | 99 | 100 | 101 | ## 参考资料 102 | 103 | 慕课网Java面试 104 | 小林coding 105 | http://www.cyc2018.xyz/%E8%AE%A1%E7%AE%97%E6%9C%BA%E5%9F%BA%E7%A1%80/Socket/Socket.html#%E5%BC%82%E6%AD%A5-i-o 106 | https://segmentfault.com/a/1190000003063859 107 | 108 | 109 | 一篇讲的很好的关于Java NIO和IO模型的文字 110 | https://www.cnblogs.com/ljl150/p/12642726.html 111 | 112 | 美团技术团队,NIO浅析 113 | https://tech.meituan.com/2016/11/04/nio.html -------------------------------------------------------------------------------- /专题整理/操作系统/考点突破/Linux体系结构.md: -------------------------------------------------------------------------------- 1 | # Linux体系结构 2 | 3 | - 体系结构主要分为用户态(用户上层活动)和内核态 4 | - 内核:本质是一段管理计算机硬件设备的程序(cpu,内存,硬盘,网络) 5 | - 系统调用:内核的访问接口,是一种能再简化的操作 6 | - 公用函数库:系统调用的组合拳 7 | - shell:命令解释器,可编程 8 | -------------------------------------------------------------------------------- /专题整理/操作系统/考点突破/Linux调优.md: -------------------------------------------------------------------------------- 1 | # Linux调优 2 | 3 | top 4 | ps -ef 5 | jobs 6 | pgrep 7 | 8 | meminfo 9 | free 10 | vmstat 11 | df 12 | du 13 | 14 | 网络 15 | netstat 16 | route 17 | lsof -i: 8000 占用8000端口的进程 18 | 19 | 学习技巧 20 | - --help 21 | - man:查看帮助手册 22 | 23 | -------------------------------------------------------------------------------- /专题整理/操作系统/考点突破/shell.md: -------------------------------------------------------------------------------- 1 | # shell 2 | 3 | ## 如何查找特定的文件 4 | 5 | find 6 | 7 | find path [options] params 8 | 9 | 作用:在指定目录下查找文件 10 | 11 | find -name "target3.java" 12 | 当前目录递归寻找该目录 13 | 14 | find / -name "target3.java" 15 | 从根目录递归寻找该文件 16 | 17 | find ~ -name "target*" 模糊查询 18 | find ~ -iname "target*" 忽略大小写 19 | 20 | man find 21 | 22 | ## 检索文件内容 23 | 24 | grep 25 | 26 | grep [options] pattern file 27 | 28 | 查找文件里符合条件的字符串 29 | 30 | grep "mooc" target* 31 | 32 | ## 管道操作符 | 33 | 34 | 可将指令连接起来,前一个指令的输出作为后一个指令的输入 35 | 36 | find ~ | grep "target" 37 | 38 | 使用管道注意点: 39 | - 只处理前一个命令正确输出,不处理错误输出 40 | - 右边命令必须能够接收标准输入流,否则传递过程中数据会被抛弃 41 | - sed,awk,grep,cur,head,top,less,more,wc,join,sort,split等 42 | 43 | grep 'xxx\[true\]' xx.log | grep -o 'engine\[[0-9a-z]*\]' 44 | 45 | ps -ef | grep tomcat 46 | 47 | ps -ef | grep tomcat | grep -v "grep" 过滤掉包含grep的内容 48 | 49 | ## 对文件内容做统计 50 | awk 51 | 数据抽取和统计 52 | 53 | ## 批量替换文件内容 54 | sed 55 | 56 | 适用于对文本的行内容进行处理 57 | 58 | -------------------------------------------------------------------------------- /专题整理/操作系统/考点突破/内存管理.md: -------------------------------------------------------------------------------- 1 | # 内存管理 2 | 3 | ## 什么是分页机制 4 | 5 | ## 什么是分段机制 6 | 7 | 分段是为了满足代码的一些逻辑需求 8 | 9 | ## 分页和分段的区别 10 | 11 | ## 什么是虚拟内存 12 | 13 | 通过把一部分暂时不用的内存信息放到硬盘上 14 | - 局部性原理,程序运行时候只有部分必要的信息装入内存 15 | - 内存中暂时不需要的内容放到硬盘上 16 | - 系统似乎提供了比实际内存大得多的容量,称之为虚拟内存 17 | 18 | ## 什么是内存抖动(颠簸) 19 | 20 | 本质是频繁的页调度行为 21 | - 频繁的页调度,进程不断产生缺页中断 22 | - 置换一个页,又不断再次需要这个页 23 | - 运行程序太多;页面替换策略不好。终止进程或者增加物理内存 24 | 25 | ## Python的垃圾回收机制原理? 26 | 27 | - 引用技术为主(缺点:循环引用无法解决) 28 | - 引入标记清除和分代回收解决引用计数的问题 29 | - 引用计数为主+标记清除和分代回收为辅 30 | 31 | -------------------------------------------------------------------------------- /专题整理/操作系统/考点突破/进程和线程.md: -------------------------------------------------------------------------------- 1 | # 进程和线程 2 | 3 | ## 进程和线程 4 | 5 | - 进程是对运行时程序的封装,是系统资源调度和分配的基本单位 6 | - 线程是进程的子任务,CPU调度和分配的基本单位,实现进程内并发 7 | - 一个进程可以包含多个线程,线程依赖进程存在,并共享进程内存 8 | 9 | ## 线程安全 10 | 11 | - 一个操作可以在多线程环境中安全使用,获取正确的结果 12 | - 线程安全的操作好比线程是顺序执行而不是并发执行的(i+=1) 13 | - 一般如果涉及到写操作需要考虑如何让多个线程安全访问数据 14 | 15 | ## 线程同步的方式 16 | 17 | 了解线程同步的方式,如何保证线程安全 18 | - 互斥量(锁):通过互斥机制防止多个线程同时访问公共资源 19 | - 信号量(Semphare):控制同一时刻多个线程访问同一个资源的线程数 20 | - 事件(信号):通过通知的方式保持多个线程同步 21 | 22 | (这一块不知道讲的啥) 23 | 24 | ## 进程间通信的方式 25 | 26 | Inter-Process Communication进程间传递信号或者数据 27 | 28 | - 管道/匿名管道/有名管道(pipe) 29 | - 信号(Signal):比如用户使用Ctrl+c产生SIGINT程序终止信号 30 | - 消息队列(Message) 31 | - 共享内存(share memory) 32 | - 信号量(Semaphore) 33 | - 套接字(socket):最常用的方式,我们的web应用都是这种方式 34 | 35 | ## python中如何使用多线程 36 | 37 | - threading.Thread类用来创建线程 38 | - start()方法启动线程 39 | - 可以用join()等待线程结束 40 | 41 | 42 | ## python中如何使用多进程 43 | 44 | Python有GIL,可以用多进程实现CPU密集程序 45 | - multiprocessing多进程模块 46 | - multiprocessing.Process类实现多进程 47 | - 一般用在cpu密集程序里,避免GIL的影响 48 | 49 | -------------------------------------------------------------------------------- /专题整理/数据库索引.md: -------------------------------------------------------------------------------- 1 | # 数据库索引 2 | 3 | http://blog.codinglabs.org/articles/theory-of-mysql-index.html -------------------------------------------------------------------------------- /专题整理/核心击破/重要面试问题.md: -------------------------------------------------------------------------------- 1 | # 重要面试问题 2 | 3 | 分享一些我面试中的高频问题 4 | 5 | ## 类加载机制 6 | 7 | ## GET和POST区别 8 | 9 | 1.GET请求的数据会附在URL之后,以?分割URL和传输数据,参数之间以&相连, 10 | POST把提交的数据则放置在是HTTP包的包体中。 11 | 12 | 2.GET的长度受限于url的长度,而url的长度限制是特定的浏览器和服务器设置的,理论上GET的长度可以无限长。 13 | 14 | 3.POST是没有大小限制的,HTTP协议规范也没有进行大小限制,起限制作用的是服务器的处理程序的处理能力 15 | 16 | 17 | https://www.zhihu.com/question/28586791 18 | 19 | ## 链表和数组的区别 20 | 21 | ## python的dict实现 22 | 23 | https://blog.csdn.net/weixin_43064185/article/details/107565845 24 | https://www.cnblogs.com/muyiblog/p/7662262.html 25 | 26 | ## python和java的垃圾回收机制 27 | 28 | https://blog.csdn.net/xiongchengluo1129/article/details/80462651 29 | https://www.cnblogs.com/ArsenalfanInECNU/p/9077967.html 30 | 31 | 推荐:https://zhuanlan.zhihu.com/p/83251959 32 | 33 | 5. mysql的联合索引的结构 34 | 35 | MySQL使用经验 36 | 37 | 6. rabbitmq高性能的原因 38 | 39 | erlang,爱立信开发,一门专门为交换机软件开发诞生的编程语言 40 | 41 | erlang特点 42 | - 通用的面向并发的编程语言,适用于分布式系统 43 | - 基于虚拟机解释运行,跨平台部署 44 | - 进程间上下文切换效率远高于C语言 45 | - 有着和原生Socket一样的延迟 46 | 47 | (ATTENTION;这一块要重点看看) 48 | 49 | 总结 50 | - RabbitMQ底层使用Erlang实现,天生具有高性能基因 51 | - RabbitMQ在互联网和金融领域都有广泛的应用 52 | 53 | 总结2 54 | - erlang进程间上下文切换效率远高于C语言和Java,进一步提高了RabbitMQ的并发性能 55 | - erlang的网络性能有着和原生Socket一样的延迟,使得RabbitMQ的网络IO性能极高 56 | 57 | 7. rabbitmq的架构 58 | 59 | 60 | Exchange总结 61 | - AMQP协议直接决定了RabbitMQ的内部结构和外部行为 62 | - 对于发送者来说,将消息发给特定的Exchange 63 | - 消息经过Exchange路由后,到达具体队列 64 | - 消费者将消息从监听的队列中取走 65 | - Exchange主要有3种类型:Direct / Topic / Fanout 66 | - Direct(直接路由):Routing Key = Binding Key,容易配置和使用 67 | - Fanout(广播路由):群发绑定的所有队列,适用于消息广播 68 | - Topic(话题路由):功能较为复杂,但能降级为Direct,建议优先使用,为以后拓展留余地 69 | 70 | 71 | 8. celery的架构 72 | 73 | 9. 非递归写中序遍历 74 | 75 | 堆栈区别,垃圾回收 76 | 77 | 线程,进程,协程 -------------------------------------------------------------------------------- /专题整理/消息队列/常见面试题.md: -------------------------------------------------------------------------------- 1 | 135. rabbitmq 的使用场景有哪些? 2 | 3 | 136. rabbitmq 有哪些重要的角色? 4 | 5 | 137. rabbitmq 有哪些重要的组件? 6 | 7 | 138. rabbitmq 中 vhost 的作用是什么? 8 | 9 | 139. rabbitmq 的消息是怎么发送的? 10 | 11 | 140. rabbitmq 怎么保证消息的稳定性? 12 | 13 | 141. rabbitmq 怎么避免消息丢失? 14 | 15 | 142. 要保证消息持久化成功的条件有哪些? 16 | 17 | 143. rabbitmq 持久化有什么缺点? 18 | 19 | 144. rabbitmq 有几种广播类型? 20 | 21 | 145. rabbitmq 怎么实现延迟消息队列? 22 | 23 | 146. rabbitmq 集群有什么用? 24 | 25 | 147. rabbitmq 节点的类型有哪些? 26 | 27 | 148. rabbitmq 集群搭建需要注意哪些问题? 28 | 29 | 149. rabbitmq 每个节点是其他节点的完整拷贝吗?为什么? 30 | 31 | 150. rabbitmq 集群中唯一一个磁盘节点崩溃了会发生什么情况? 32 | 33 | 151. rabbitmq 对集群节点停止顺序有要求吗? 34 | 35 | 36 | 37 | ### 9. kafka 的原理,以及使用中遇到的问题 38 | ### 2. 消息可达性和唯一消费 39 | ### 8. mq:kafka,rocketmq,同步机制和事务机制 40 | 41 | -------------------------------------------------------------------------------- /专题整理/消息队列/考点整理/rocketmq.md: -------------------------------------------------------------------------------- 1 | # rocketmq 2 | 3 | ‌rocketmq为例看结构 4 | - name server 命名查询服务器 5 | - producer 消息生产者 6 | - message broker 消息队列 7 | - consumer group 消费组 8 | - consumer 消息消费者 9 | - topic 消息主题 10 | - partition 消息分片/队列 11 | - replication 消息副本(rocketmq无) 12 | 13 | 14 | ‌消息消费确认 15 | - ack 16 | - offset 17 | 18 | 19 | 消息队列集群的结构,常见的名词 -------------------------------------------------------------------------------- /专题整理/网络/常见面试题.md: -------------------------------------------------------------------------------- 1 | # 网络 2 | 3 | ## tcp 4 | 5 | tcp3次握手和四次挥手,tcp time wait,tcp还有哪些状态 6 | tcp 拥塞控制 7 | TCP 如何保证可靠性,解释一下滑动窗口 8 | 9 | ## http 10 | 11 | 500表示什么,502 504 12 | 301、302、500、502、504是什么含义 13 | cookie与session区别; 没有cookie的话session是否有效 14 | HTTP与HTTPS区别,HTTPS如何做到安全 15 | 登录的过程,cookie如何写入的 16 | session 怎么用 cookie 实现的,session 和 cookie 的区别。 17 | http和https的区别,https的实现 18 | https 的建立链接过程 19 | https 的传输数据是否是对称加密 20 | 长连接短连接区别,应用场景 21 | 22 | ## nginx 23 | 24 | nginx 如何处理连接 25 | nginx 如何做性能优化 26 | apache 和nginx 区别 27 | 28 | ## web 29 | 30 | 网页加载时如何重定向 31 | -------------------------------------------------------------------------------- /专题整理/网络/我的整理.md: -------------------------------------------------------------------------------- 1 | get和post 2 | https://www.zhihu.com/question/31640769?rf=37401322 3 | 4 | https://www.cnblogs.com/nankezhishi/archive/2012/06/09/getandpost.html -------------------------------------------------------------------------------- /专题整理/网络/答案/HTTP.md: -------------------------------------------------------------------------------- 1 | # HTTP 2 | 3 | ## 在浏览器中输⼊url地址 ->> 显示主⻚的过程 4 | 5 | 1. DNS解析 6 | 2. TCP连接 7 | 3. 发送HTTP请求 8 | 4. 服务器处理请求并返回HTTP报⽂ 9 | 5. 浏览器解析渲染⻚⾯ 10 | 6. 连接结束 11 | 12 | https://segmentfault.com/a/1190000006879700 13 | 14 | ## 状态码 15 | 16 | - 1xxs - 信息性:服务器正在处理请求。 17 | - 2xxs - 成功信息:请求已经完成,服务器向浏览器提供了预期的响应。 18 | - 3xxs - 重定向:你的请求被重定向到了其他地方。服务器收到了请求,但是有某种重定向。 19 | - 4xxs – 客户端错误:客户端发生错误,导致服务器无法处理请求。 20 | - 5xxs – 服务端错误:客户端发出了有效的请求,但是服务器未能正确处理请求。 21 | 22 | 500表示服务端错误,常见的有: 23 | 24 | - 500 Internal Server Error:服务器内部错误,服务器遇到了不知道如何处理的情况。比如后端同学写错了model啥的~ 25 | - 502 Bad Gateway:此错误响应表明服务器作为网关需要得到一个处理这个请求的响应,但是得到一个错误的响应。 26 | - 503 Service Unavailable:服务器没有准备好处理请求。常见的原因是服务器因维护或重载而停机。 27 | - 504 Gateway Timeout:网关超时,服务器未能快速的做出反应。请求接口返回pedding时间过长基本就是这个问题了,囧。 28 | 29 | ## 长连接短连接 30 | 31 | 在HTTP/1.0中默认使⽤短连接。也就是说,客户端和服务器每进⾏⼀次HTTP操作,就建⽴⼀次连接,任务结束就中断连接。当客户端浏览器访问的某个HTML或其他类型的Web⻚中包含有其他的Web资源(如JavaScript⽂件、图像⽂件、CSS⽂件等),每遇到这样⼀个Web资源,浏览器就会重新建⽴⼀个HTTP会话。 32 | 33 | ⽽从HTTP/1.1起,默认使⽤⻓连接,⽤以保持连接特性。使⽤⻓连接的HTTP协议,会在响应头加⼊这⾏代码: 34 | 35 | `Connection:keep-alive` 36 | 37 | 在使⽤⻓连接的情况下,当⼀个⽹⻚打开完成后,客户端和服务器之间⽤于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使⽤这⼀条已经建⽴的连接。KeepAlive不会永久保持连接,它有⼀个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现⻓连接需要客户端和服务端都⽀持⻓连接。 38 | 39 | https://www.cnblogs.com/gotodsp/p/6366163.html 40 | 41 | ## HTTP是不保存状态的协议,如何保存⽤户状态? 42 | 43 | HTTP 是⼀种不保存状态,即⽆状态(stateless)协议。也就是说 HTTP 协议⾃身不对请求和响应之间的通信状态进⾏保存。那么我们保存⽤户状态呢?Session 机制的存在就是为了解决这个问题,Session 的主要作⽤就是通过服务端记录⽤户的状态。典型的场景是购物⻋,当你要添加商品到购物⻋的时候,系统不知道是哪个⽤户操作的,因为 HTTP 协议是⽆状态的。服务端给特定的⽤户创建特定的 Session 之后就可以标识这个⽤户并且跟踪这个⽤户了(⼀般情况下,服务器会在⼀定时间内保存这个 Session,过了时间限制,就会销毁这个Session)。 44 | 45 | 在服务端保存 Session 的⽅法很多,最常⽤的就是内存和数据库(⽐如是使⽤内存数据库redis保存)。既然 Session 存放在服务器端,那么我们如何实现 Session 跟踪呢?⼤部分情况下,我们都是通过在 Cookie 中附加⼀个 Session ID 来⽅式来跟踪。 46 | 47 | ## Cookie的作⽤是什么?和Session有什么区别? 48 | 49 | Cookie 和 Session都是⽤来跟踪浏览器⽤户身份的会话⽅式,但是两者的应⽤场景不太⼀样。 50 | 51 | Cookie ⼀般⽤来保存⽤户信息 ⽐如: 52 | 53 | ①我们在 Cookie 中保存已经登录过得⽤户信息,下次访问⽹站的时候⻚⾯可以⾃动帮你登录的⼀些基本信息给填了; 54 | ②⼀般的⽹站都会有保持登录也就是说下次你再访问⽹站的时候就不需要重新登录了,这是因为⽤户登录的时候我们可以存放了⼀个Token 在 Cookie 中,下次登录的时候只需要根据 Token 值来查找⽤户即可(为了安全考虑,重新登录⼀般要将 Token 重写); 55 | ③登录⼀次⽹站后访问⽹站其他⻚⾯不需要重新登录。 56 | 57 | Session 的主要作⽤就是通过服务端记录⽤户的状态。 58 | 59 | 典型的场景是购物⻋,当你要添加商品到购物⻋的时候,系统不知道是哪个⽤户操作的,因为 HTTP 协议是⽆状态的。服务端给特定的⽤户创建特定的 Session 之后就可以标识这个⽤户并且跟踪这个⽤户了。 60 | 61 | Cookie 数据保存在客户端(浏览器端),Session 数据保存在服务器端。 62 | Cookie 存储在客户端中,⽽Session存储在服务器上,相对来说 Session 安全性更⾼。如果要在Cookie 中存储⼀些敏感信息,不要直接写⼊ Cookie 中,最好能将 Cookie 信息加密然后使⽤到的时候再去服务器端解密。 63 | 64 | ## HTTP 1.0和HTTP 1.1的主要区别是什么? 65 | 66 | HTTP1.0最早在⽹⻚中使⽤是在1996年,那个时候只是使⽤⼀些᫾为简单的⽹⻚上和⽹络请求上,⽽HTTP1.1则在1999年才开始⼴泛应⽤于现在的各⼤浏览器⽹络请求中,同时HTTP1.1也是当前使⽤最为⼴泛的HTTP协议。 主要区别主要体现在: 67 | 68 | 1. ⻓连接 : 在HTTP/1.0中,默认使⽤的是短连接,也就是说每次请求都要重新建⽴⼀次连接。HTTP 是基于TCP/IP协议的,每⼀次建⽴或者断开连接都需要三次握⼿四次挥⼿的开销,如果每次请求都要这样的话,开销会⽐᫾⼤。因此最好能维持⼀个⻓连接,可以⽤个⻓连接来发多个请求。HTTP 1.1起,默认使⽤⻓连接 ,默认开启Connection: keep-alive。 HTTP/1.1的持续连接有⾮流⽔线⽅式和流⽔线⽅式 。流⽔线⽅式是客户在收到HTTP的响应报⽂之前就能接着发送新的请求报⽂。与之相对应的⾮流⽔线⽅式是客户在收到前⼀个响应后才能发送下⼀个请求。 69 | 2. 错误状态响应码 :在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发⽣冲突;410(Gone)表示服务器上的某个资源被永久性的删除。 70 | 3. 缓存处理 :在HTTP1.0中主要使⽤header⾥的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引⼊了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match,If-None-Match等更多可供选择的缓存头来控制缓存策略。 71 | 4. 带宽优化及⽹络连接的使⽤ :HTTP1.0中,存在⼀些浪费带宽的现象,例如客户端只是需要某个对象的⼀部分,⽽服务器却将整个对象送过来了,并且不⽀持断点续传功能,HTTP1.1则在请求头引⼊了range头域,它允许只请求资源的某个部分,即返回码是206(PartialContent),这样就⽅便了开发者⾃由的选择以便于充分利⽤带宽和连接。 72 | 73 | https://mp.weixin.qq.com/s/GICbiyJpINrHZ41u_4zT-A? 74 | 75 | ## HTTP 和 HTTPS 的区别? 76 | 77 | 1. 端⼝ :HTTP的URL由“http://”起始且默认使⽤端⼝80,⽽HTTPS的URL由“https://”起始且默认使⽤端⼝443。 78 | 79 | 2. 安全性和资源消耗: HTTP协议运⾏在TCP之上,所有传输的内容都是明⽂,客户端和服务器端都⽆法验证对⽅的身份。HTTPS是运⾏在SSL/TLS之上的HTTP协议,SSL/TLS 运⾏在TCP之上。所有传输的内容都经过加密,加密采⽤对称加密,但对称加密的密钥⽤服务器⽅的证书进⾏了⾮对称加密。所以说,HTTP 安全性没有 HTTPS⾼,但是 HTTPS ⽐HTTP耗费更多服务器资源。 80 | 对称加密:密钥只有⼀个,加密解密为同⼀个密码,且加解密速度快,典型的对称加密算法有DES、AES等; 81 | ⾮对称加密:密钥成对出现(且根据公钥⽆法推知私钥,根据私钥也⽆法推知公钥),加密解密使⽤不同密钥(公钥加密需要私钥解密,私钥加密需要公钥解密),相对对称 82 | 加密速度较慢,典型的⾮对称加密算法有RSA、DSA等。 83 | 84 | ## HTTP2.0相比于HTTP1.x新特性 85 | 86 | 新的二进制格式(Binary Format),HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。 87 | 88 | 多路复用(MultiPlexing),即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。 89 | 90 | header压缩,如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。 91 | 92 | 服务端推送(server push),同SPDY一样,HTTP2.0也具有server push功能。 93 | 94 | ### HTTP2.0的多路复用和HTTP1.X中的长连接复用有什么区别? 95 | 96 | HTTP/1.* 一次请求-响应,建立一个连接,用完关闭;每一个请求都要建立一个连接; 97 | 98 | HTTP/1.1 Pipeling解决方式为,若干个请求排队串行化单线程处理,后面的请求等待前面请求的返回才能获得执行机会,一旦有某请求超时等,后续请求只能被阻塞,毫无办法,也就是人们常说的线头阻塞; 99 | 100 | HTTP/2多个请求可同时在一个连接上并行执行。某个请求任务耗时严重,不会影响到其它连接的正常执行 101 | 102 | ### 服务端推送 103 | 104 | 服务端推送能把客户端所需要的资源伴随着index.html一起发送到客户端,省去了客户端重复请求的步骤。 105 | 106 | 正因为没有发起请求,建立连接等操作,所以静态资源通过服务端推送的方式可以极大地提升速度 107 | 108 | ### 为什么需要头部压缩? 109 | 110 | 假定一个页面有100个资源需要加载(这个数量对于今天的Web而言还是挺保守的), 而每一次请求都有1kb的消息头(这同样也并不少见,因为Cookie和引用等东西的存在), 则至少需要多消耗100kb来获取这些消息头。HTTP2.0可以维护一个字典,差量更新HTTP头部,大大降低因头部传输产生的流量。具体参考:HTTP/2 头部压缩技术介绍 111 | 112 | ### HTTP2.0多路复用有多好? 113 | 114 | HTTP 性能优化的关键并不在于高带宽,而是低延迟。TCP 连接会随着时间进行自我「调谐」,起初会限制连接的最大速度,如果数据成功传输,会随着时间的推移提高传输的速度。这种调谐则被称为 TCP 慢启动。由于这种原因,让原本就具有突发性和短时性的 HTTP 连接变的十分低效。 115 | 116 | HTTP/2 通过让所有数据流共用同一个连接,可以更有效地使用 TCP 连接,让高带宽也能真正的服务于 HTTP 的性能提升。 117 | 118 | ## https的实现 119 | ## https 的建立链接过程 120 | ## https 的传输数据是否是对称加密 121 | 122 | ## 登录相关的参考 123 | 124 | JWT 125 | 126 | https://snailclimb.gitee.io/javaguide/#/docs/system-design/authority-certification/JWT%E4%BC%98%E7%BC%BA%E7%82%B9%E5%88%86%E6%9E%90%E4%BB%A5%E5%8F%8A%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88 127 | 128 | sso 129 | 130 | https://snailclimb.gitee.io/javaguide/#/docs/system-design/authority-certification/SSO%E5%8D%95%E7%82%B9%E7%99%BB%E5%BD%95%E7%9C%8B%E8%BF%99%E4%B8%80%E7%AF%87%E5%B0%B1%E5%A4%9F%E4%BA%86 131 | 132 | -------------------------------------------------------------------------------- /专题整理/网络/答案/TCP.md: -------------------------------------------------------------------------------- 1 | # TCP 2 | 3 | ## 为什么要三次握手 4 | 5 | 三次握⼿的⽬的是建⽴可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,⽽三次握⼿最主要的⽬的就是双⽅确认⾃⼰与对⽅的发送与接收是正常的。 6 | 7 | 第⼀次握⼿:Client 什么都不能确认;Server 确认了对⽅发送正常,⾃⼰接收正常 8 | 9 | 第⼆次握⼿:Client 确认了:⾃⼰发送、接收正常,对⽅发送、接收正常;Server 确认了:对⽅发送正常,⾃⼰接收正常 10 | 11 | 第三次握⼿:Client 确认了:⾃⼰发送、接收正常,对⽅发送、接收正常;Server 确认了:⾃⼰发送、接收正常,对⽅发送、接收正常 12 | 13 | 所以三次握⼿就能确认双发收发功能都正常,缺⼀不可。 14 | 15 | ## 为什么要传回 SYN 16 | 17 | 接收端传回发送端所发送的 SYN 是为了告诉发送端,我接收到的信息确实就是你所发送的信号了。 18 | 19 | SYN 是 TCP/IP 建⽴连接时使⽤的握⼿信号。在客户机和服务器之间建⽴正常的 TCP ⽹络连接时,客户机⾸先发出⼀个 SYN 消息,服务器使⽤ SYN-ACK 应答表示接收到了这个消息,最后客户机再以 ACK(Acknowledgement[汉译:确认字符 ,在数据通信传输中,接收站发给发送站的⼀种传输控制字符。它表示确认发来的数据已经接受⽆误。 ])消息响应。这样在客户机和服务器之间才能建⽴起可靠的TCP连接,数据才可以在客户机和服务器之间传递。 20 | 21 | ## 传了 SYN,为啥还要传 ACK 22 | 23 | 双⽅通信⽆误必须是两者互相发送信息都⽆误。传了 SYN,证明发送⽅到接收⽅的通道没有问题,但是接收⽅到发送⽅的通道还需要 ACK 信号来进⾏验证。 24 | 25 | ## 什么是四次挥手 26 | 27 | 断开⼀个 TCP 连接则需要“四次挥⼿”: 28 | 客户端-发送⼀个 FIN,⽤来关闭客户端到服务器的数据传送 29 | 服务器-收到这个 FIN,它发回⼀ 个 ACK,确认序号为收到的序号加1 。和 SYN ⼀样,⼀个FIN 将占⽤⼀个序号 30 | 服务器-关闭与客户端的连接,发送⼀个FIN给客户端 31 | 客户端-发回 ACK 报⽂确认,并将确认序号设置为收到序号加1 32 | 33 | ## 为什么要四次挥⼿ 34 | 35 | 任何⼀⽅都可以在数据传送结束后发出连接释放的通知,待对⽅确认后进⼊半关闭状态。当另⼀⽅也没有数据再发送的时候,则发出连接释放通知,对⽅确认后就完全关闭了TCP连接。 36 | 37 | 举个例⼦:A 和 B 打电话,通话即将结束后,A 说“我没啥要说的了”,B回答“我知道了”,但是 B可能还会有要说的话,A 不能要求 B 跟着⾃⼰的节奏结束通话,于是 B 可能⼜巴拉巴拉说了⼀通,最后 B 说“我说完了”,A 回答“知道了”,这样通话才算结束。 38 | 39 | https://blog.csdn.net/qzcsu/article/details/72861891 40 | 41 | ## TCP和UDP区别 42 | 43 | UDP 在传送数据之前不需要先建⽴连接,远地主机在收到 UDP 报⽂后,不需要给出任何确认。虽然 UDP 不提供可靠交付,但在某些情况下 UDP 确是⼀种最有效的⼯作⽅式(⼀般⽤于即时通信),⽐如: QQ 语⾳、 QQ 视频 、直播等等 44 | 45 | TCP 提供⾯向连接的服务。在传送数据之前必须先建⽴连接,数据传送结束后要释放连接。TCP 不提供⼴播或多播服务。由于 TCP 要提供可靠的,⾯向连接的传输服务(TCP的可靠体现在TCP在传递数据之前,会有三次握⼿来建⽴连接,⽽且在数据传递时,有确认、窗⼝、重传、拥塞控制机制,在数据传完后,还会断开连接⽤来节约系统资源),这⼀难以避免增加了许多开销,如确认,流量控制,计时器以及连接管理等。这不仅使协议数据单元的⾸部增⼤很多,还要占⽤许多处理机资源。TCP ⼀般⽤于⽂件传输、发送和接收邮、远程登录等场景。 46 | 47 | ## TCP 协议如何保证可靠传输 48 | 49 | 1. 应⽤数据被分割成 TCP 认为最适合发送的数据块。 50 | 2. TCP 给发送的每⼀个包进⾏编号,接收⽅对数据包进⾏排序,把有序数据传送给应⽤层。 51 | 3. 校验和: TCP 将保持它⾸部和数据的检验和。这是⼀个端到端的检验和,⽬的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP 将丢弃这个报⽂段和不确认收到此报⽂段。 52 | 4. TCP 的接收端会丢弃重复的数据。 53 | 5. 流量控制: TCP 连接的每⼀⽅都有固定⼤⼩的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收⽅来不及处理发送⽅的数据,能提示发送⽅降低发送的速率,防⽌包丢失。TCP 使⽤的流量控制协议是可变⼤⼩的滑动窗⼝协议。 (TCP 利⽤滑动窗⼝实现流量控制) 54 | 6. 拥塞控制: 当⽹络拥塞时,减少数据的发送。 55 | 7. ARQ协议: 也是为了实现可靠传输的,它的基本原理就是每发完⼀个分组就停⽌发送,等待对⽅确认。在收到确认后再发下⼀个分组。 56 | 8. 超时重传: 当 TCP 发出⼀个段后,它启动⼀个定时器,等待⽬的端确认收到这个报⽂段。如果不能及时收到⼀个确认,将重发这个报⽂段。 57 | 58 | ## 滑动窗⼝和流量控制 59 | 60 | TCP 利⽤滑动窗⼝实现流量控制。流量控制是为了控制发送⽅发送速率,保证接收⽅来得及接收。 接收⽅发送的确认报⽂中的窗⼝字段可以⽤来控制发送⽅窗⼝⼤⼩,从⽽影响发送⽅的发送速率。将窗⼝字段设置为 0,则发送⽅不能发送数据。 61 | 62 | ## 拥塞控制 63 | 64 | -------------------------------------------------------------------------------- /专题整理/网络/答案/复习建议.md: -------------------------------------------------------------------------------- 1 | # 复习建议 2 | 3 | ⾮常推荐⼤家看⼀下 《图解HTTP》 这本书,这本书⻚数不多,但是内容很是充实,不管是⽤来系统的掌握⽹络⽅⾯的⼀些知识还是说纯粹为了应付⾯试都有很⼤帮助。下⾯的⼀些⽂章只是参考。⼤⼆学习这⻔课程的时候,我们使⽤的教材是 《计算机⽹络第七版》(谢希仁编著),不推荐⼤家看这本教材,书⾮常厚⽽且知识偏理论,不确定⼤家能不能⼼平⽓和的读完。 4 | 5 | https://blog.csdn.net/qq_16209077/article/details/52718250 6 | https://blog.csdn.net/zixiaomuwu/article/details/60965466 7 | https://blog.csdn.net/turn__back/article/details/73743641 8 | https://mp.weixin.qq.com/s/GICbiyJpINrHZ41u_4zT-A? -------------------------------------------------------------------------------- /专题整理/网络/考点整理/HTTP.md: -------------------------------------------------------------------------------- 1 | # HTTP 2 | 3 | 状态行+响应头+响应正文 4 | 5 | ## HTTP简介 6 | 7 | 特点: 8 | - 支持客户/服务器模式 9 | - 简单快速 10 | - 灵活 11 | - 无连接 12 | - 无状态 13 | 14 | 面试以1.1为准 15 | 16 | http1.1 keep-alive 17 | 18 | 请求/响应步骤: 19 | - 客户端连接到web服务器 20 | - 发送HTTP请求 21 | - 服务器接受请求并返回HTTP响应 22 | - 释放TCP连接 23 | - 客户端浏览器解析HTML内容 24 | 25 | 在浏览器地址栏键入URL,按下回车之后经历的流程 26 | - DNS解析 27 | - TCP连接。和服务建立 28 | - 发送HTTP连接 29 | - 服务器处理请求并返回HTTP报文 30 | - 浏览器解析渲染页面 31 | - 连接结束 32 | 33 | HTTP状态码 34 | - 1xx:指示信息——表示请求已接收,继续处理 35 | - 2xx:成功——表会请求已被成功接收、理解、接受 36 | - 3xx:重定向——要完成请求必须进行更进一步的操作 37 | - 4xx:客户端错误——请求有语法错误或请求无法实现 38 | - 5xx:服务端错误——服务器未能实现合法的请求 39 | 40 | 常见状态码: 41 | - 200 ok:正常返回信息 42 | - 400 Bad Request:客户端请求有语法错误,不能被服务器所理解 43 | - 401 unauthorized:请求未经授权 44 | - 403 forbidden:服务器收到请求,但是拒绝提供服务 45 | - 404 Not Found:请求资源不存在,eg,输入了错误的url 46 | - 500 Internal Server Error:服务器发生不可预期的错误 47 | - 503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常。 48 | 49 | GET请求和POST请求的区别 50 | - HTTP报文层面:GET将请求信息放在URL(有长度限制),POST放在报文体中 51 | - 数据库层面:GET符合幂等性和安全性,POST不符合 52 | - 其他层面:GET可以被缓存、被存储,而POST不行 53 | 54 | Cookie和Session的区别 55 | - Cookie 56 | - 是由服务器发给客户端的特殊信息,以文本的形式存放在客户端 57 | - 客户端再次请求的时候,会把cookie回发 58 | - 服务器接收到后,会解析Cookie生成与客户端相对应的内容 59 | - Session的简介 60 | - 服务器端的机制,在服务器上保存的信息 61 | - 解析客户端请求并操作session id,按需保存状态信息 62 | - Session的实现方式 63 | - 使用Cookie来实现(JSESSIONID) 64 | - 使用URL回写来实现 65 | - Cookie和Session的区别 66 | - Cookie数据存放在客户的浏览器上,Session数据放在服务器上 67 | - Session相对于Cookie更安全 68 | - 若考虑减轻服务器负担,应当使用Cookie 69 | 70 | ## HTTP和HTTPS的区别 71 | 72 | SSL(Security Sockets Layer,安全套接层) 73 | - 为网络通信提供安全及数据完整性的一种安全协议 74 | - 是操作系统对外的API,SSL3.0后更名为TLS 75 | - 采用身份验证和数据加密保证网络通信的安全和数据的完整性 76 | 77 | 加密的方式 78 | - 对称加密:加密和解密都使用同一个密钥 79 | - 非对称加密:加密使用的密钥和解密使用的密钥是不相同的 80 | - 哈希算法:将任意长度的信息转换为固定长度的值,算法不可逆 81 | - 数字签名:证明某个消息或者文件是某人发出/认同的 82 | 83 | HTTPS数据传输流程 84 | - 浏览器将支持的加密算法信息发送给服务器 85 | - 服务器选择一套浏览器支持的加密算法,以证书的形式回发浏览器 86 | - 浏览器验证证书合法性,并结合证书公钥加密信息发送给服务器 87 | - 服务器使用私钥解密信息,验证哈希,加密响应消息回发浏览器 88 | - 浏览器解密响应消息,并对消息进行验真,之后进行加密交互数据 89 | 90 | HTTP和HTTPS的区别 91 | - HTTPS需要CA申请证书,HTTP不需要 92 | - HTTPS密文传输,HTTP明文传输 93 | - 连接方式不同,HTTPS默认使用443端口,HTTP使用80端口 94 | - HTTPS=HTTP+加密+认证+完整性保护,较HTTP安全 95 | 96 | Socket简介 97 | (pid是本地唯一继承 98 | IP地址+协议+端口号) 99 | Socket是对TCP/IP协议的抽象,是操作系统对外开放的接口 100 | 101 | Socket通信流程 102 | 103 | Socket相关的面试题,javabasic.socket 104 | 105 | ### https加密 106 | 107 | - 非对称运算 108 | - 对称运算 109 | 110 | HTTPS 在 HTTP 与 TCP 层之间加⼊了 TLS 协议,来解决上述的⻛险。 111 | 112 | HTTPS 是应⽤层协议,需要先完成 TCP 连接建⽴,然后⾛ TLS 握⼿过程后,才能建⽴通信安全的连接。 113 | 114 | 事实上,不同的密钥交换算法,TLS 的握⼿过程可能会有⼀些区别。 115 | 116 | 这⾥先简单介绍下密钥交换算法,因为考虑到性能的问题,所以双⽅在加密应⽤信息时使⽤的是对称加密密钥,⽽对称加密密钥是不能被泄漏的,为了保证对称加密密钥的安全性,所以使⽤⾮对称加密的⽅式来保护对称加密密钥的协商,这个⼯作就是密钥交换算法负责的。 117 | 118 | TLS第一次握手 119 | - 客户端⾸先会发⼀个「Client Hello」消息。 120 | - 消息⾥⾯有客户端使⽤的 TLS 版本号、⽀持的密码套件列表,以及⽣成的随机数(Client Random),这个随机数会被服务端保留,它是⽣成对称加密密钥的材料之⼀。 121 | 122 | TLS第二次握手 123 | - 当服务端收到客户端的「Client Hello」消息后,会确认 TLS 版本号是否⽀持,和从密码套件列表中选择⼀个密码套件,以及⽣成随机数(Server Random)。 124 | - 接着,返回「Server Hello」消息,消息⾥⾯有服务器确认的 TLS 版本号,也给出了随机数(Server Random),然后从客户端的密码套件列表选择了⼀个合适的密码套件。其实这两个随机数是后续作为⽣成「会话密钥」的条件,所谓的会话密钥就是数据传输时,所使⽤的对称加密密钥。 125 | - 然后,服务端为了证明⾃⼰的身份,会发送「Server Certificate」给客户端,这个消息⾥含有数字证书。 126 | - 随后,服务端发了「Server Hello Done」消息,⽬的是告诉客户端,我已经把该给你的东⻄都给你了,本次打招呼完毕。 127 | 128 | TLS 第三次握⼿ 129 | - 客户端验证完证书后,认为可信则继续往下⾛。接着,客户端就会⽣成⼀个新的随机数 (pre-master),⽤服务器的 RSA 公钥加密该随机数,通过「Change Cipher Key Exchange」消息传给服务端。 130 | - 服务端收到后,⽤ RSA 私钥解密,得到客户端发来的随机数 (pre-master)。 131 | - ⾄此,客户端和服务端双⽅都共享了三个随机数,分别是 Client Random、Server Random、pre-master。 132 | - 于是,双⽅根据已经得到的三个随机数,⽣成会话密钥(Master Secret),它是对称密钥,⽤于对后续的 HTTP请求/响应的数据加解密。 133 | - ⽣成完会话密钥后,然后客户端发⼀个「Change Cipher Spec」,告诉服务端开始使⽤加密⽅式发送消息。 134 | - 然后,客户端再发⼀个「Encrypted Handshake Message(Finishd)」消息,把之前所有发送的数据做个摘要,再⽤会话密钥(master secret)加密⼀下,让服务器做个验证,验证加密通信是否可⽤和之前握⼿信息是否有被中途篡改过。 135 | - 可以发现,「Change Cipher Spec」之前传输的 TLS 握⼿数据都是明⽂,之后都是对称密钥加密的密⽂ 136 | 137 | TLS 第四次握⼿ 138 | - 服务器也是同样的操作,发「Change Cipher Spec」和「Encrypted Handshake Message」消息,如果双⽅都验证加密和解密没问题,那么握⼿正式完成。 139 | - 最后,就⽤「会话密钥」加解密 HTTP 请求和响应了。 140 | 141 | ### http2.0 142 | 143 | - 二进制传输(增加传输效率,减少带宽占用) 144 | - 多路复用内容数据,(包1-4 HTML 包5-8 CSS html数据 css数据) 145 | - 服务端推送(访问html,服务端push css js) 146 | 147 | ### 长连接和短连接 148 | 149 | - 短连接:建立连接——数据传输——关闭连接 150 | - 长连接:Connection: keep-alive。保持TCP连接不断开 151 | - 如何区分不同的HTTP请求呢?Content-Length | Transfer-Encoding:chunked 152 | 153 | ### cookie和aession 154 | 155 | 156 | 157 | ## 我的新得 158 | 159 | 我们可以知道,非对称加密在性能上不如对称加密,那是否能将两者结合起来呢?例如,公钥私钥主要用于传输对称加密的秘钥,而真正的双方大数据量的通信都是通过对称加密进行的。 160 | 161 | 162 | ## 参考资料 163 | 164 | 小林coding,图解网络 165 | 极客时间:https://time.geekbang.org/column/article/9492 -------------------------------------------------------------------------------- /专题整理/网络/考点整理/OSI.md: -------------------------------------------------------------------------------- 1 | # OSI 2 | 3 | 物理层:比特流。网卡 4 | 数据链路层:如何格式化数据,确保数据传输可靠性,比特流组成帧。交换机 5 | 网络层:网络地址翻译成物理地址,数据包,IP协议。路由器 6 | 传输层:流量控制,TCP/UDP,分段 7 | 会话层:建立和管理应用程序员之间的通信 8 | 表示层:数据 9 | 应用层:HTTP 10 | 11 | OSI并不是一个标准,而是一个概念 -------------------------------------------------------------------------------- /专题整理/网络/考点整理/TCP和UDP.md: -------------------------------------------------------------------------------- 1 | # TCP和UDP 2 | 3 | ## TCP和UDP的区别 4 | 5 | 面向连接、可靠的、基于字节流 6 | 无连接、不可靠、面向报文 7 | 8 | TCP三次握手 9 | 确认机制 10 | TCP有缓冲区 11 | 12 | ## TCP/IP 13 | 14 | OSI的实现:TCP/IP 15 | 16 | 应用层:应表会 HTTP数据 17 | 传输层:传输层 TCP首部 18 | 网络层:网络层 IP首部 19 | 链路层:物数 以太网首部 20 | 21 | ## 说说TCP的三次握手 22 | 23 | 传输控制协议TCP简介: 24 | - 面向连接的,可靠的,基于字节流的传输层通信协议 25 | - 将应用层的数据流分隔成报文段并发送给目标节点的TCP层 26 | - 数据包都有序号,对方收到则发送ACK确认,未收到则重传 27 | - 使用校验和来检验数据在传输过程中是否有误 28 | 29 | TCP Flags 30 | - URG:紧急指针标志 31 | - ACK:确认序号标志 32 | - PSH:push标志 33 | - RST:重置连接标志 34 | - SYN:同步序号,用于建立连接过程 35 | - FIN:finish标志,用于释放连接 36 | 37 | 握手是为了建立连接,TCP三次握手的流程图如下: 38 | (pic) 39 | 在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。 40 | 第一次握手:建立连接时,客户端发送SYN包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认; 41 | 第二次握手:服务器收到SYN包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态; 42 | 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。 43 | 44 | 不要用大白话来回答,要用行话来回答,不然显得不专业 45 | 46 | ## 为什么需要三次握手才能建立起连接? 47 | 48 | 为了初始化Sequence Number的初始值 49 | 50 | todo 51 | 52 | ## 首次握手的隐患---SYN超时 53 | 54 | ## 建立连接后,Client出现故障怎么办 55 | 56 | 保活机制 57 | - 向对方发送保活探测报文,如果未收到响应则继续发送 58 | - 尝试次数达到保活探测数仍未收到响应则中断连接 59 | 60 | ## TCP的四次挥手 61 | 62 | 挥手是为了终止连接,TCP四次挥手的流程图如下 63 | 64 | - 第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态 65 | - 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态 66 | - 第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态; 67 | - 第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手 68 | 69 | 这几个状态图要弄清楚 70 | 71 | 为什么会有TIME_WAIT状态? 72 | - 确保有足够的时间让对方收到ACK包 73 | - 避免新旧连接混淆 74 | 75 | 为什么需要四次挥手才能断开连接? 76 | - 因为全双工,发送方和接收方都需要FIN报文和ACK报文 77 | 78 | 服务器出现大量CLOSE_WAIT状态的原因 79 | - 对方关闭socket连接,我方忙于读或写,没有及时关闭连接 80 | - 检查代码,特别是释放资源的代码 81 | - 检查配置,特别是处理请求的线程配置(可用netstat排查) 82 | 83 | netstat -n | awk '/^tcp/{++S[$NF]}END{for (a in S)} print a,S[a]' 84 | 85 | ## UDP 86 | 87 | 源端口,目标端口,长度,校验值 88 | 89 | 特点 90 | - 面向非连接 91 | - 不维护连接状态,支持同时向多个客户端传输相同的消息 92 | - 数据包报头只有8个字节,额外开销较小 93 | - 吞吐量只受限于数据生成速率、传输速率以及机器性能 94 | - 尽最大努力交付,不保证可靠交付,不需要维持复杂的链接状态表 95 | - 面向报文,不对应用程序提交的报文信息进行拆分或者合并 96 | 97 | ## TCP和UDP的区别 98 | 99 | 结论: 100 | - 面向连接VS无连接 101 | - 可靠性 102 | - 有序性 103 | - 速度 104 | - 量级 105 | 106 | ## TCP的滑动窗口 107 | 108 | RTT和RTO: 109 | - RTT:发送一个数据包到收到对应的ACK,所花费的时间 110 | - RTO:重传时间间隔 111 | 112 | TCP使用滑动窗口做流量控制与乱序重排 113 | - 保证TCP的可靠性 114 | - 保证TCP的流控特性 115 | 116 | 这个有点难 117 | 118 | 119 | ## 什么是TCP 120 | 121 | TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。 122 | 123 | - 面向连接:一定是一对一才能连接,不能像UDP协议可以一个主机同时向多个主机发送消息,也就是一对多无法做到。 124 | - 可靠的:无论网络链路中出现了怎样的链路变化,TCP都可以保证一个报文一定能到达接收端。 125 | - 字节流:消息是没有边界的,所以无论我们消息有多大都可以进行传输。并且消息是有序的,当前一个消息没有收到的时候,即使它先收到了后面的字节,那么也不能扔给应用层去处理,同时对重复的报文会自动丢弃。 126 | 127 | ### 什么是面向连接 128 | 129 | 所谓的建立连接,是为了在客户端和服务端维护连接,而建立一定的数据结构来维护双方交互的状态,用这样的数据结构来保证所谓的面向连接的特性。 130 | 131 | TCP 提供可靠交付。通过 TCP 连接传输的数据,无差错、不丢失、不重复、并且按序到达。 132 | 133 | IP 包是没有任何可靠性保证的。UDP 继承了 IP 包的特性,不保证不丢失,不保证按顺序到达。 134 | 135 | ### 什么是面向字节流 136 | 137 | TCP 是面向字节流的。发送的时候发的是一个流,没头没尾。IP 包可不是一个流,而是一个个的 IP 包。之所以变成了流,这也是 TCP 自己的状态维护做的事情。而 UDP 继承了 IP 的特性,基于数据报的,一个一个地发,一个一个地收。 138 | 139 | 还有 TCP 是可以有拥塞控制的。它意识到包丢弃了或者网络的环境不好了,就会根据情况调整自己的行为,看看是不是发快了,要不要发慢点。UDP 就不会,应用让我发,我就发,管它洪水滔天。 140 | 141 | ## TCP和UDP的区别 142 | 143 | 连接 144 | - TCP是面向连接的传输层协议,传输数据前先要建立连接 145 | - UDP是不需要连接,即刻传输数据 146 | 147 | 服务对象 148 | - TCP是一对一的两点服务,即一条连接只有两个端点 149 | - UDP支持一对一、一对多、多对多的交互通信 150 | 151 | 可靠性 152 | - TCP是可靠交付数据的,数据可以无差错、不丢失、不重复、按需到达。 153 | - UDP是尽最大努力交付,不保证可靠交付数据。 154 | 155 | 拥塞控制、流量控制 156 | - TCP有拥塞控制和流量控制机制,保证数据传输的安全性 157 | - UDP则没有,即使网络非常拥堵了,也不会影响UDP的发送速率 158 | 159 | 首部开销 160 | - TCP首部长度较长,会有一定的开销,首部在没有使用选项字段时是20个字节,如果使用了选项字段则会变长的。 161 | - UDP首部只有8个字节,并且是固定不变的,开销较小。 162 | 163 | 传输方式 164 | - TCP是流式传输,没有边界,但保证顺序和可靠 165 | - UDP是一个包一个包的发送,是有边界的,但可能会丢包和乱序 166 | 167 | 由于TCP是面向连接,能保证数据的可靠性交付,因此经常用于: 168 | - FTP 文件传输 169 | - HTTP / HTTPS 170 | 171 | 由于 UDP 面向无连接,它可以随时发送数据,再加上UDP本身的处理既简单又高效,因此经常用于: 172 | - 包总量较少的通信,如 DNS 、SNMP 等 173 | - 视频、音频等多媒体通信 174 | - 广播通信 175 | 176 | 177 | | 类型 |是否面向连接|传输可靠性|传输形式|传输效率|所需资源|应用场景|首部字节 | 178 | | --- | --- | --- | --- | --- | --- | --- | --- | 179 | |TCP|面向连接|可靠|字节流|慢|多|要求通信数据可靠|20-60| 180 | |UDP|无连接|不可靠|数据报文段|快|少|要求通信速度高|8个字节| 181 | 182 | TCP 建立连接时,通过三次握手能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号。序列号能够保证数据包不重复、不丢弃和按序传输。 183 | 184 | 不使用「两次握手」和「四次握手」的原因: 185 | 186 | 「两次握手」:无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号; 187 | 188 | 「四次握手」:三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数。 189 | 190 | ## TCP 的重传机制、滑动窗口、流量控制、拥塞控制 191 | 192 | ### 重传机制 193 | 194 | TCP 实现可靠传输的方式之一,是通过序列号与确认应答。 195 | 196 | 在 TCP 中,当发送端的数据到达接收主机时,接收端主机会返回一个确认应答消息,表示已收到消息。 197 | 198 | 但在错综复杂的网络,并不一定能如上图那么顺利能正常的数据传输,万一数据在传输过程中丢失了呢? 199 | 200 | 所以 TCP 针对数据包丢失的情况,会用重传机制解决。 201 | 202 | ### 滑动窗口 203 | 204 | 我们都知道 TCP 是每发送一个数据,都要进行一次确认应答。当上一个数据包收到了应答了, 再发送下一个。 205 | 206 | 这个模式就有点像我和你面对面聊天,你一句我一句。但这种方式的缺点是效率比较低的。 207 | 208 | 如果你说完一句话,我在处理其他事情,没有及时回复你,那你不是要干等着我做完其他事情后,我回复你,你才能说下一句话,很显然这不现实。 209 | 210 | 所以,这样的传输方式有一个缺点:数据包的往返时间越长,通信的效率就越低。 211 | 212 | 为解决这个问题,TCP 引入了窗口这个概念。即使在往返时间较长的情况下,它也不会降低网络通信的效率。 213 | 214 | 那么有了窗口,就可以指定窗口大小,窗口大小就是指无需等待确认应答,而可以继续发送数据的最大值。 215 | 216 | ### 流量控制 217 | 218 | 发送方不能无脑的发数据给接收方,要考虑接收方处理能力。 219 | 220 | 如果一直无脑的发数据给对方,但对方处理不过来,那么就会导致触发重发机制,从而导致网络流量的无端的浪费。 221 | 222 | 为了解决这种现象发生,TCP 提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量,这就是所谓的流量控制。 223 | 224 | ### 拥塞控制 225 | 226 | 前面的流量控制是避免「发送方」的数据填满「接收方」的缓存,但是并不知道网络的中发生了什么。 227 | 228 | 一般来说,计算机网络都处在一个共享的环境。因此也有可能会因为其他主机之间的通信使得网络拥堵。 229 | 230 | 在网络出现拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是一重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这个情况就会进入恶性循环被不断地放大…. 231 | 232 | 所以,TCP 不能忽略网络上发生的事,它被设计成一个无私的协议,当网络发送拥塞时,TCP 会自我牺牲,降低发送的数据量。 233 | 234 | 于是,就有了拥塞控制,控制的目的就是避免「发送方」的数据填满整个网络。 235 | 236 | 为了在「发送方」调节所要发送数据的量,定义了一个叫做「拥塞窗口」的概念。 237 | 238 | ## TCP和UDP可以同时监听相同的端口吗 239 | 240 | https://blog.51cto.com/u_12083623/2362539 241 | https://blog.csdn.net/ma2595162349/article/details/108269351 242 | 243 | 244 | TCP和UDP可以同时监听相同的端口吗(可以) 245 | 246 | IP数据包首部有个叫做协议的字段,指出了上层协议是TCP还是UDP还是其他。操作系统有能力根据接受的报文的IP字段里面的协议部分判断这个报文是什么报文,就是说,系统读数据的时候还没有读到上层报文(TCP/UDP)的时候已经知道上层是什么报文了,直接交给相关的内核进程或协议栈处理就可以了。而在同一个协议内部端口号唯一。 247 | 248 | ## 刘超老师的课程 249 | 250 | 最后,我们看一下拥塞控制的问题,也是通过窗口的大小来控制的,前面的滑动窗口 rwnd 是怕发送方把接收方缓存塞满,而拥塞窗口 cwnd,是怕把网络塞满。 251 | 252 | 这里有一个公式 LastByteSent - LastByteAcked <= min {cwnd, rwnd} ,是拥塞窗口和滑动窗口共同控制发送的速度。 253 | 254 | 顺序问题、丢包问题、流量控制都是通过滑动窗口来解决的,这其实就相当于你领导和你的工作备忘录,布置过的工作要有编号,干完了有反馈,活不能派太多,也不能太少; 255 | 256 | 拥塞控制是通过拥塞窗口来解决的,相当于往管道里面倒水,快了容易溢出,慢了浪费带宽,要摸着石头过河,找到最优值。 257 | 258 | ## 参考资料 259 | 260 | https://time.geekbang.org/column/article/9141 -------------------------------------------------------------------------------- /专题整理/网络/考点整理/tcp粘包.md: -------------------------------------------------------------------------------- 1 | # tcp粘包 2 | 3 | ## tcp底层的粘包/拆包机制 4 | 5 | 业务上一个包可能被tcp拆成多个包发送 6 | 7 | 分析TCP粘包、拆包问题的产生原因: 8 | - 应用程序write写入的字节大小大于套接口发送缓冲区的大小 9 | - 进行MSS大小的TCP分段、以太网帧的payload大于MTU进行IP分片等 10 | 11 | ## Netty TCP拆包粘包问题的处理 12 | 13 | 粘包拆包问题的解决方案,根据业界主流协议,有三种方案: 14 | - 消息定长,例如每个报文的大小固定为200个字节,如果不够,空位补空格 15 | - 在包尾部增加特殊字符进行分割,例如加回车等 16 | - 消息分为消息头和消息体,在消息头中包含表示消息总长度的字段,然后进行业务处理 17 | 18 | (定长,特殊分割符,自定义协议栈) 19 | 20 | -------------------------------------------------------------------------------- /专题整理/网络/考点整理/url过程.md: -------------------------------------------------------------------------------- 1 | # url过程 2 | 3 | DNS查询——TCP握手——HTTP请求——反向代理Nginx——uwsgi/gunicorn——web app响应——TCP挥手 4 | 5 | -------------------------------------------------------------------------------- /专题整理/网络/考点整理/优化网络.md: -------------------------------------------------------------------------------- 1 | # 优化网络 2 | 3 | ## 如何优化网络瓶颈 4 | 5 | 来源于Java高级面试 6 | 7 | 网络瓶颈的根源 8 | - 公网:带宽,出口调用量 9 | - 内网:带宽,出口调用量 10 | 11 | 解决网络带宽的方法 12 | - 扩容 13 | - 分散(分布式) 14 | - 压缩(1k数据压缩) 15 | -------------------------------------------------------------------------------- /专题整理/网络/考点整理/零拷贝.md: -------------------------------------------------------------------------------- 1 | # 零拷贝 2 | 3 | DMA技术,直接内存访问(Direct Memory Access) 技术 4 | 5 | 在进⾏ I/O 设备和内存的数据传输的时候,数据搬运的⼯作全部交给DMA 控制器,⽽ CPU 不再参与任何与数据搬运相关的事情,这样 CPU 就可以去处理别的事务。 6 | 7 | 要想提⾼⽂件传输的性能,就需要减少「⽤户态与内核态的上下⽂切换」和「内存拷⻉」的次数。 8 | 9 | ⽽⼀次系统调⽤必然会发⽣ 2 次上下⽂切换:⾸先从⽤户态切换到内核态,当内核执⾏完任务后,再切换回⽤户态交由进程代码执⾏。 10 | 11 | 所以,要想减少上下⽂切换到次数,就要减少系统调⽤的次数。 12 | 13 | ⽤户的缓冲区是没有必要存在的。 14 | 15 | 零拷⻉(Zero-copy)技术,因为我们没有在内存层⾯去拷⻉数据,也就是说全程没有通过CPU 来搬运数据,所有的数据都是通过 DMA 来进⾏传输的。。 16 | 17 | 零拷⻉技术的⽂件传输⽅式相⽐传统⽂件传输的⽅式,减少了 2 次上下⽂切换和数据拷⻉次数,只需要 2次上下⽂切换和数据拷⻉次数,就可以完成⽂件的传输,⽽且 2 次的数据拷⻉过程,都不需要通过 CPU,2 次都是由 DMA 来搬运。 18 | 19 | 所以,总体来看,零拷⻉技术可以把⽂件传输的性能提⾼⾄少⼀倍以上。 20 | 21 | Kafka 这个开源项⽬,就利⽤了「零拷⻉」技术,从⽽⼤幅提升了 I/O 的吞吐率,这也是 Kafka 在处理海量数据为什么这么快的原因之⼀。 22 | 23 | Nginx 也⽀持零拷⻉技术,⼀般默认是开启零拷⻉技术,这样有利于提⾼⽂件传输的效率。 24 | 25 | ## 总结 26 | 27 | 早期 I/O 操作,内存与磁盘的数据传输的⼯作都是由 CPU 完成的,⽽此时 CPU 不能执⾏其他任务,会特别浪费 CPU 资源。 28 | 29 | 于是,为了解决这⼀问题,DMA 技术就出现了,每个 I/O 设备都有⾃⼰的 DMA 控制器,通过这个 DMA 控制器,CPU 只需要告诉 DMA 控制器,我们要传输什么数据,从哪⾥来,到哪⾥去,就可以放⼼离开了。后续的实际数据传输⼯作,都会由 DMA 控制器来完成,CPU 不需要参与数据传输的⼯作。 30 | 31 | 传统 IO 的⼯作⽅式,从硬盘读取数据,然后再通过⽹卡向外发送,我们需要进⾏ 4 上下⽂切换,和 4 次数据拷⻉,其中 2 次数据拷⻉发⽣在内存⾥的缓冲区和对应的硬件设备之间,这个是由 DMA 完成,另外2 次则发⽣在内核态和⽤户态之间,这个数据搬移⼯作是由 CPU 完成的。 32 | 33 | 为了提⾼⽂件传输的性能,于是就出现了零拷⻉技术,它通过⼀次系统调⽤( sendfile ⽅法)合并了磁盘读取与⽹络发送两个操作,降低了上下⽂切换次数。另外,拷⻉数据都是发⽣在内核中的,天然就降低了数据拷⻉的次数。 34 | 35 | Kafka 和 Nginx 都有实现零拷⻉技术,这将⼤⼤提⾼⽂件传输的性能。 36 | 37 | 38 | ## 参考资料 39 | 40 | 上面整理自小林coding -------------------------------------------------------------------------------- /专题整理/进程、线程与协程.md: -------------------------------------------------------------------------------- 1 | # 进程、线程与协程 2 | 3 | ## 文档参考 4 | 5 | https://www.cnblogs.com/melonjiang/p/5307705.html 6 | 7 | -------------------------------------------------------------------------------- /专题整理/面试复习资料.md: -------------------------------------------------------------------------------- 1 | # 面试复习资料 2 | 3 | ## github上牛逼的项目 4 | 5 | github上118kstar的项目 6 | 7 | https://github.com/CyC2018/CS-Notes 8 | 9 | ## 面经整理 10 | 11 | https://juejin.cn/post/6844904120676007950 12 | 13 | -------------------------------------------------------------------------------- /参考资料/博文系列.md: -------------------------------------------------------------------------------- 1 | https://gitbook.cn/books/5ca40fd11763103ff10b0e43/index.html -------------------------------------------------------------------------------- /天地大同/算法篇.md: -------------------------------------------------------------------------------- 1 | # 算法篇 -------------------------------------------------------------------------------- /天地大同/系统设计篇.md: -------------------------------------------------------------------------------- 1 | # 系统设计篇 2 | 3 | 四火老师的博客: 4 | https://www.raychase.net/6364 5 | 6 | 系统设计中快速估算技巧: 7 | https://www.raychase.net/6280 8 | 9 | 搞定面试中的系统设计题: 10 | https://jiajunhuang.com/articles/2019_04_29-system_design.md.html 11 | 12 | 一个国外的课程 13 | System Design for Tech Interviews 14 | https://www.hiredintech.com/courses/system-design 15 | 16 | 17 | 上面的课程的解读 18 | https://blog.csdn.net/u013007900/article/details/79008993 19 | https://blog.csdn.net/u013007900/article/details/79049187 20 | 21 | 22 | ResumeJob的知乎文章,比较具体 23 | https://zhuanlan.zhihu.com/p/77857433 24 | 25 | 九章算法的知乎文章 26 | https://zhuanlan.zhihu.com/p/70743436 27 | https://zhuanlan.zhihu.com/p/162937258 28 | 29 | guide哥的系统设计的文章 30 | https://mp.weixin.qq.com/s/1Jl8ee0jJoFdGMfIhl_qaQ 31 | 32 | 33 | ## 系统设计基础知识点 34 | 35 | 网站服务器 web server 36 | - 一台性能比较好的web server, 大概每秒可以服务1000次访问请求 37 | 38 | 数据库 Database 39 | - 系统设计中最重要的考点 40 | 41 | 文件系统 File System 42 | 43 | 缓存 Cache 44 | - Cache是一个相对概念 45 | - 可以在内存上 46 | - 可以在磁盘上 47 | - 可以在CPU里 48 | - 可以在服务端 49 | - 可以在客户端 50 | - 我们在系统设计中说的Cache的时候默认是Memcache,即内存中的Cache 51 | - 我们通常把经常访问的数据放在Cache里来加速访问速度 52 | - Cache因为空间受限制,因此需要淘汰掉一些不常用的数据,常见的淘汰算法有LRU 53 | 54 | ## 系统设计介绍 55 | 56 | ### 系统设计面试的形式 57 | 58 | 系统设计的题目往往是一个开放性的题目 59 | 60 | 比如:设计微博,设计微信 61 | 62 | 或者是设计某某系统中的某某功能。比如设计一个功能实现对用户访问频率的限制,设计一个功能实现统计某个具体事件的历史发生次数。 63 | 64 | 65 | ### 常见的系统设计面试问题 66 | 67 | 68 | ### 系统设计与面向对象设计的异同 69 | 70 | 形式上: 71 | 面向对象设计手把手的 Coding 72 | 系统设计高屋建瓴的“扯淡” 73 | 74 | 考察的知识点上: 75 | 面向对象设计:Class, Object, Method, Inheritance, Interface … 76 | 系统设计考的是:Database, Schema, SQL, NoSQL, Memcached, File System, Distributed System, Latency, 77 | Scalbility, Master Slave, Load Balancer, Web Server, Message Queue, Sharding, Consistent Hashing, QPS … 78 | 79 | 典型题: 80 | 面向对象设计:电梯设计,游戏设计 81 | 系统设计:短网址系统设计,新鲜事系统设计 82 | 83 | ### 从 News Feed Design 介绍什么是系统设计 84 | 85 | 系统设计中首先要明确需求,面试官的问题可能很大,但是让你设计的东西未必会很多,设计的难度也未必会很大。极有可能从易到难引导你先设计一些简单的结构。 86 | 87 | 第一步,先细化问题 88 | 89 | ### 系统设计面试的常见错误 90 | 91 | ### 系统设计面试的评分标准 92 | 93 | 可行解 Work Solution 25% 94 | 特定问题 Special Case 20% 95 | 分析能力 Analysis 25% 96 | 权衡 Tradeoff 15% 97 | 知识储备 Knowledge Base 15% 98 | 99 | ### 系统设计的九阴真经—— 4S 分析法 100 | 101 | Scenario, Service, Storage, Scale 102 | 103 | 没有思路时,可以采取这四个点 104 | 105 | - Scenario 场景 106 | - 说人话:需要设计哪些功能,设计得多牛 107 | - Ask / Features / QPS / DAU / Interfaces 108 | - DAU日活用户 109 | - Service 服务 110 | - 说人话:将大系统拆分为小服务 111 | - Split / Application / Module 112 | - Storage 存储 113 | - 说人话:数据如何存储与访问 114 | - Schema / Data / SQL / NoSQL / File System 115 | - Scale 升级 116 | - 说人话:解决缺陷,处理可能遇到的问题 117 | - Sharding / Optimize / Special Case 118 | 119 | #### scenairo 场景 120 | 121 | 需要设计哪些功能,设计得多牛 122 | 1. Ask 问面试官 123 | 2. Analysis 分析 124 | 125 | 哪些功能,QPS是多少 126 | 127 | **ASK** 128 | 129 | 询问面试官: 130 | - 需要设计哪些功能(也可以自己想) 131 | - 需要承受多大的访问量? 132 | - 日活跃用户 Daily Active Users (DAU) 133 | - Twitter: MAU 330M, DAU ~170M+ 134 | - Read more: http://bit.ly/1Kml0M7 135 | 136 | 通常MAU是DAU的两倍 137 | 138 | **MAU:月活跃用户** 139 | 140 | 需要设计哪些功能 141 | 142 | 第一步 Step 1:Enumerate 143 | - 说人话:把Twitter的功能一个个罗列出来 144 | - Register / Login 145 | - User Profile Display / Edit 146 | - Upload Image / Video * 147 | - Search * 148 | - Post / Share a tweet 149 | - Timeline / News Feed 150 | - Follow / Unfollow a user 151 | 152 | 第二步 Step 2:Sort 153 | - 说人话:选出核心功能,因为你不可能这么短的时间什么都设计 154 | - Post a Tweet 155 | - Timeline 156 | - News Feed 157 | - Follow / Unfollow a user 158 | - Register / Login 159 | 160 | Analysis & Predict 161 | 162 | 并发用户 Concurrent User 163 | - 日活跃 * 每个用户平均请求次数 / 一天多少秒 = 150M * 60 / 86400~ 100k 164 | - 峰值 Peak = Average Concurrent User * 3 ~ 300k 165 | - 快速增长的产品 Fast Growing 166 | - MAX peak users in 3 months = Peak users * 2 167 | 读频率 Read QPS (Queries Per Second) 168 | - 300k 169 | 写频率 Write QPS 170 | - 5k 171 | 172 | 除了算平均请求数,还要算峰值 173 | 174 | **分析出QPS有什么用** 175 | 176 | QPS = 100 177 | - 用你的笔记本做 Web 服务器就好了 178 | QPS = 1k 179 | - 用一台好点的 Web 服务器就差不多了 180 | - 需要考虑 Single Point Failure 181 | QPS = 1m 182 | - 需要建设一个1000台 Web 服务器的集群 183 | - 需要考虑如何 Maintainance(某一台挂了怎么办) 184 | 185 | QPS和 Web Server (服务器) / Database (数据库) 之间的关系 186 | - 一台 Web Server 约承受量是 1k 的 QPS (考虑到逻辑处理时间以及数据库查询的瓶颈) 187 | - 一台 SQL Database 约承受量是 1k 的 QPS(如果 JOIN 和 INDEX query比较多的话,这个值会更小) 188 | - 一台 NoSQL Database (Cassandra) 约承受量是 10k 的 QPS 189 | - 一台 NoSQL Database (Memcached) 约承受量是 1M 的 QPS 190 | 191 | 单台web server能到1k是非常理想的状况 192 | 写了Join会很慢,要写轻量级的SQL 193 | 194 | #### service 服务 195 | 196 | 将大系统拆分为小服务 197 | 1. Replay 重放需求 198 | 2. Merge 归并需求 199 | 200 | **将大系统拆分为小服务** 201 | 202 | User Service 203 | - Register 204 | - Login 205 | Tweet Service 206 | - Post a tweet 207 | - News Feed 208 | - Timeline 209 | Friendship Service 210 | - Follow 211 | - Unfollow 212 | Media Service 213 | - Upload Image 214 | - Upload Video 215 | 216 | 217 | 第一步 Step 1: Replay 218 | - 重新过一遍每个需求,为每个需求添加一个服务 219 | 第二步 Step 2: Merge 220 | - 归并相同的服务 221 | 什么是服务 Service? 222 | - 可以认为是逻辑处理的整合 223 | - 对于同一类问题的逻辑处理归并在一个 Service 中 224 | - 把整个 System 细分为若干个小的 Service 225 | 226 | #### Storage 存储 227 | 228 | 数据如何存储与访问 229 | 1. Select 为每个 Service 选择存储结构 230 | 2. Schema 细化表结构 231 | 232 | **数据如何存储与访问** 233 | 234 | 数据库系统 Database 235 | - 关系型数据库 SQL Database 236 | - 用户信息 User Table 237 | - 非关系型数据库 NoSQL Database 238 | - 推文 Tweets 239 | - 社交图谱 Social Graph (followers) 240 | - 例如存储在mongoDB 241 | 文件系统 File System 242 | - 图片、视频 Media Files 243 | - 例如S3 244 | 缓存系统 Cache 245 | - 不支持数据持久化 Nonpersistent 246 | - 效率高,内存级访问速度 247 | 248 | 249 | 第一步 Step 1: Select 250 | - 为每个 Application / Service 选择合适的存储结构 251 | 第二步 Step 2: Schema 252 | - 细化数据表结构 253 | 254 | - 程序 = 算法 + 数据结构 255 | - 系统 = 服务 + 数据存储 256 | 257 | SQL是精确的。它最适合于具有精确标准的定义明确的项目。典型的使用场景是在线商店和银行系统。 258 | NoSQL是多变的。它最适合于具有不确定需求的数据。典型的使用场景是社交网络,客户管理和网络分析系统。 259 | 260 | 你可以先这么记着,nosql就是类似json存储,sql就是类似excel存储 261 | 不同类别的数据 或 不相近属性的数据 分表存储,也可以是功能拆分。 262 | 263 | 264 | 265 | **数据系统vs文件系统** 266 | 267 | 关系:数据库系统是文件系统的一层包装,他们不是独立的关系,是依赖的关系。数据库系统依赖于 268 | 文件系统。 269 | 270 | 区别:数据库系统提供了更丰富的数据操作,很细;文件系统只提供了简单的文件操作接口,很粗。 271 | 以关系型数据库(Relational Database) 为例,提供了 SQL 语句这样的丰富的查询语言,可以一些复 272 | 杂的 filter,如快速找出学生信息表中,所有 20-24 岁的学生信息。如果直接在文件系统上,则需要 273 | 扫描完所有的学生数据后才能找到。 274 | 275 | 数据库系统中读取的数据,大部分情况下(除了被 cache 的),都还是会到文件系统上去读取出来的。 276 | 因此两个系统的读写效率(不考虑复杂查询)可以认为是差不多的。 277 | 278 | **请设计数据库的表结构** 279 | 280 | User Table 281 | id integer 282 | username varchar 283 | email varchar 284 | password varchar 285 | 286 | Friendship Table 287 | from_user_id Foreign Key 288 | to_user_id Foreign Key 289 | created_at timestamp 290 | 291 | Tweet Table 292 | id integer 293 | user_id Foreign Key 294 | content text 295 | created_at timestamp 296 | 297 | #### News Feed 如何存取? 298 | 299 | 什么是新鲜事 News Feed? 300 | - 你登陆 Facebook / Twitter / 朋友圈 之后看到的信息流 301 | - 你的所有朋友发的信息的集合 302 | 有哪些典型的新鲜事系统? 303 | - Facebook 304 | - Twitter 305 | - 朋友圈 306 | - RSS Reader 307 | 新鲜事系统的核心因素? 308 | - 关注与被关注 309 | - 每个人看到的新鲜事都是不同的 310 | 311 | **Pull Model** 312 | 313 | 算法 314 | - 在用户查看News Feed时,获取每个好友的前100条Tweets,合并出前100条News Feed 315 | - K路归并算法 Merge K Sorted Arrays 316 | 317 | 复杂度分析 318 | - News Feed => 假如有N个关注对象,则为N次DB Reads的时间 + N路归并时间(可忽略) 319 | - Post a tweet => 1次DB Write的时间 320 | 321 | 为什么 N 路归并算法的耗时可以忽略? 322 | 323 | 假设一共 N 个关注的好友,每个好友100条信息。多路归并的时间复杂度是 O(100NlogN),这里使用了一个大小为 N 的堆。初始化这个堆需要 O(N) 的时间,归并出 100N 条信息,每一条需要 O(logN) 的时间。值得注意的是,读 DB 的时间是获取了 O(100N) 条数据,这个从时间复杂度上来说要更低,但是因为 DB 的操作事实上是在对文件进行操作,而文件的访问比内存访问慢几百上千倍。 324 | 325 | 磁盘和内存的访问差异是1000倍 326 | 327 | **Pull模型有什么缺陷么?** 328 | 329 | 现算,比较慢 330 | 331 | N次DB Reads非常慢 332 | 且发生在用户获得News 333 | Feed的请求过程中 334 | 335 | #### Push Model 336 | 337 | 算法 338 | - 为每个用户建一个List存储他的News Feed信息 339 | - 用户发一个Tweet之后,将该推文逐个推送到每个用户的News Feed List中 340 | - 关键词:Fanout 341 | - 用户需要查看News Feed时,只需要从该News Feed List中读取最新的100条即可 342 | 343 | 复杂度分析 344 | - News Feed => 1次DB Read 345 | - Post a tweet => N个粉丝,需要N次DB Writes 346 | - 好处是可以用异步任务在后台执行,无需用户等待 347 | 348 | News Feed Table 349 | id integer 350 | owner_id Foreign Key 351 | tweet_id Foreign Key 352 | created_at timestamp 353 | 354 | **Push模型有缺陷么?** 355 | 356 | 记住一句话 "Disk is cheap",不要怕浪费数据库存储,为了加速查询,多存一些东西是没关系的。 357 | 358 | **Pull VS Push** 359 | 360 | 热门Social App的模型 361 | - Facebook – Pull 362 | - Instagram – Push + Pull 363 | - Twitter – Pull 364 | - 朋友圈 - ? 365 | 误区 366 | - 不坚定想法,摇摆不定 367 | - 不能表现出Tradeoff的能力 368 | - 无法解决特定的问题 369 | 370 | 广告是Pull模型: 371 | 广告的投放人群很多,Push 模式很费时间,无法实时生效。且广告主可以筛选和改变投放人群,因此 Push 模型并不能很好的支持这种动态的改动。 372 | 373 | #### 4S 374 | 375 | 用过前3个步骤的分析,我们已经得到了一个可行方案 376 | Scenario 场景 377 | - 和面试官讨论 378 | - 搞清楚需要设计哪些功能 379 | - 并分析出所设计的系统大概所需要支持的 Concurrent Users / QPS / Memory / Storage 等 380 | Service 服务 381 | - 合并需要设计功能,相似的功能整合为一个Service 382 | Storage 存储 383 | - 对每个 Service 选择合适的存储结构 384 | - 细化数据表单 385 | - 画图展示数据存储和读取的流程 386 | 得到一个 Work Solution 而不是 Perfect Solution 387 | 这个Work Solution 可以存在很多待解决的缺陷 388 | 389 | #### Scale 扩展 390 | 391 | How to Scale? 系统如何优化与维护 392 | 1. Optimize 优化 393 | 2. Maintenance 维护 394 | 395 | **如何优化系统** 396 | 397 | 第一步 Step 1: Optimize 398 | - 解决设计缺陷 Solve Problems 399 | - Pull vs Push 400 | - 更多功能设计 More Features 401 | - Like, Follow & Unfollow, Ads 402 | - 一些特殊情况 Special Cases 403 | - 鹿晗关晓彤搞挂微博, 僵尸粉 404 | 405 | 第二步 Step 2: Maintenance 406 | - 鲁棒性 Robust 407 | - 如果有一台服务器/数据库挂了怎么办 408 | - 扩展性 Scalability 409 | - 如果有流量暴增,如何扩展 410 | 411 | **解决Pull的缺陷** 412 | 413 | 最慢的部分发生在用户读请求时(需要耗费用户等待时间) 414 | - 在 DB 访问之前加入Cache 415 | - Cache 每个用户的 Timeline 416 | - N次DB请求 → N次Cache请求 (N是你关注的好友个数) 417 | - Trade off: Cache所有的?Cache最近的1000条? 418 | - Cache 每个用户的 News Feed 419 | - 没有Cache News Feed的用户:归并N个用户最近的100条Tweets,然后取出结果的前100条 420 | - 有Cache News Feed的用户༚ 归并N个用户的在某个时间戳之后的所有Tweets 421 | 422 | 课后作业:对比MySQL 和 Memcached 的 QPS 423 | - Memcached QPS / MySQL QPS ~ 100 ~ 1000 424 | 425 | DB和Cache之间相差一百倍到一千倍的差距 426 | 427 | **解决Push的缺陷** 428 | 429 | 浪费更多的存储空间 Disk 430 | - 与Pull模型将News Feed存在内存(Memory)中相比 431 | - Push模型将News Feed存在硬盘(Disk)里完全不是个事儿 432 | - Disk is cheap 433 | 不活跃用户 Inactive Users 434 | - 粉丝排序 Rank followers by weight (for example, last login time) 435 | 粉丝数目 followers >> 关注数目 following 436 | - Lady Gaga问题 437 | - 无解?完全切换回Pull? 438 | - rade off: Pull + Push vs Pull 439 | 440 | 粉丝 Followers 80 M 441 | - Justin Bieber 95 M on Instagram 442 | - 谢娜 100M on Weibo 443 | Push 的挑战 444 | - Fanout 的过程可能需要几个小时! 445 | 面试时错误的回答方案 446 | - 既然 Push 不行,那我们就切换到 Pull 吧! 447 | - 说起来好容易啊! 448 | - 正确的思路 449 | - 尝试在现有的模型下做最小的改动来优化 450 | - 比如多加几台用于做 Push 任务的机器,Problem Solved! 451 | - 对长期的增长进行估计,并评估是否值得转换整个模型 452 | 453 | **Push结合Pull的优化方案** 454 | 455 | Push 结合 Pull 的优化方案 456 | - 普通的用户仍然 Push 457 | - 将 Lady Gaga 这类的用户,标记为明星用户 458 | - 对于明星用户,不 Push 到用户的 News Feed 中 459 | - 当用户需要的时候,来明星用户的 Timeline 里取,并合并到 News Feed 里 460 | 461 | **如何定义明星** 462 | 463 | 不是明星不能在线动态计算,要离线计算 464 | - 为 User 增加一个 is_superstar 的属性 465 | - 一个用户被标记为 superstar 之后,就不能再被取消标记 466 | 467 | User Table 468 | id integer 469 | username varchar 470 | email varchar 471 | password varchar 472 | is_superstar boolean 473 | 474 | **Pull vs Push** 475 | 476 | 为什么既然大家都用Pull,我们仍然要学习Push? 477 | - 系统设计不是选择一个最好的方案 478 | - 而是选择一个最合适的方案 479 | - 如果你没有很大的流量,Push是最经济最省力的做法 480 | - 系统设计面试也并不是期望你答出最优的解决方法,而是从你的分析当中判断你对系统的理解和知识储备。 481 | 482 | 什么时候用 Push? 483 | - 资源少 484 | - 想偷懒,少写代码 485 | - 实时性要求不高 486 | - 用户发帖比较少 487 | - 双向好友关系,没有明星问题(比如朋友圈) 488 | 489 | 什么时候用 Pull ? 490 | - 资源充足 491 | - 实时性要求高 492 | - 用户发帖很多 493 | - 单向好友关系,有明星问题 494 | 495 | **通用问题** 496 | 497 | - 数据库服务器挂了怎么办?How to maintenance? 498 | - 用户逐渐怎么怎么办?How to scale? 499 | - 服务器顶不住压力怎么办? 500 | - 数据库顶不住压力怎么办? 501 | - 以上两个问题,将在第二节课 Database 的专题中涉及! 502 | 503 | ### 系统设计面试总结 504 | 505 | Ask before design 506 | 问清楚再动手设计 507 | 不要一上来就冲着一个巨牛的方案去设计 508 | 切忌不要做关键词大师 509 | 510 | No more no less 511 | 不要总想着设计最牛的系统 512 | 要设计够用的系统 513 | 514 | Work solution first 515 | 先设计一个基本能工作的系统,然后再逐步优化 516 | Done is better than perfect! —— Mark Zuckerberg 517 | 518 | Analysis is important than solution 519 | 系统设计没有标准答案 520 | 记住答案是没用的 521 | 通过分析过程展示知识储备 522 | 权衡各种设计方式的利弊 523 | 524 | ### 拓展问题 525 | 526 | **拓展问题1:果取关问题** 527 | 528 | 如何实现 follow & unfollow ? 529 | 除了在数据库中创建/删除记录,还需要做什么? 530 | 531 | 如何实现 follow 与 unfollow? 532 | - Follow 一个用户之后,异步地将他的 Timeline 合并到你的 News Feed 中 533 | - Merge timeline into news feed asynchronously. 534 | - Unfollow 一个用户之后,异步地将他发的 Tweets 从你的 News Feed 中移除 535 | - Pick out tweets from news feed asynchronously. 536 | 为什么需要异步 Async? 537 | - 因为这个过程一点都不快呀 538 | 异步的好处? 539 | - 用户迅速得到反馈,似乎马上就 follow / unfollow 成功了 540 | 异步的坏处? 541 | - Unfollow 之后刷新 News Feed,发现好像他的信息还在 542 | - 不过最终还是会被删掉的 543 | 544 | **拓展问题2: 如何存储likes** 545 | 546 | 如何在 News Feed 中同时得到每个帖子被点赞、评论和转发的次数? 547 | 548 | Tweet Table 549 | id integer 550 | user_id Foreign Key 551 | content text 552 | created_at timestamp 553 | num_of_likes * integer 554 | num_of_comments * integer 555 | num_of_retweets * integer 556 | 557 | Like Table * 558 | id integer 559 | user_id Foreign Key 560 | tweet_id Foreign Key 561 | created_at timestamp 562 | 563 | De-normalize 564 | 565 | Normalize vs Denormalize 566 | 567 | Normalize 获得点赞数的方式: 568 | SELECT COUNT * FROM like_table where tweet_id=xxx; 569 | 优点:标准化,最准确。 570 | 缺点:炒鸡慢,会增加 O(N) 个 SQL Queries(对于某一页的 Tweets,每个都得来这么一句查询) 571 | Denormalize 获得点赞数的方式: 572 | 573 | 当有人点赞的时候: 574 | UPDATE like_table SET num_of_likes = num_of_likes + 1 where tweet_id = xxx 575 | 当有人取消赞的时候: 576 | UPDATE like_table SET num_of_likes = num_of_likes - 1 where tweet_id = xxx 577 | 想要获得一个 Tweet 的点赞数时,因为 num_of_likes 就存在 tweet 里,故无需额外的 SQL Queries 578 | 579 | **鹿晗公布恋情会怎样** 580 | 581 | 惊群现象 Thundering Herd 582 | 583 | 数据分片机制不管用 584 | 因为访问的是同一个数据,sharding 机制无论如何都会 sharding 到同一个机器上。此时 sharding 不能做到分摊流量的作用。 585 | 586 | 什么是惊群? 587 | 588 | 我们通常会使用缓存来作为数据库的“挡箭牌”,优化一些经常读取的数据的访问速度。即,在访问这些 589 | 数据时,会先看看是否在缓存中,如果在,就直接读取缓存中的数据,如果不在,就从数据库中读取 590 | 之后,写入缓存并返回。 591 | 那么在高并发的情况下,如果一条非常热的数据,因为缓存过期或者被淘汰算法淘汰等原因,被踢出 592 | 缓存之后,会导致短时间内(<1s),大量的数据请求会出现缓存穿透 (Cache miss),因为数据从 DB 593 | 回填到 Cache 需要时间。从而这些请求都会去访问数据库,导致数据库处理不过来而崩溃,从而影响 594 | 到其他数据的访问而导致整个网站崩溃。 595 | 596 | (关于缓存的几种场景,我要额外去看下) 597 | 598 | 录屏+手写 599 | 600 | window:surface pencil 601 | Mac: iPad+apple pencil+airServer 602 | 603 | 604 | -------------------------------------------------------------------------------- /天地大同/系统设计面试指南.md: -------------------------------------------------------------------------------- 1 | # 系统设计面试指北 2 | 3 | 系统设计的问题是在国内外大厂面试的常考题,这类问题开放性比较强,没有标准答案,比如设计微博,设计微信,或者是设计某个特定功能,如限制用户的访问频率。 4 | 5 | 面试官的问题可能很大,但是让你设计的东西未必会很多,设计的难度也未必会很大,极有可能从易到难引导你先设计一些简单的结构。所以掌握这类问题的面试技巧很重要。 6 | 7 | 如何应对这类问题,我在一个教程上学习到了4S法,总结如下: 8 | 9 | 4S分析法中的4S是指Scenario(场景),Service(服务),Storage(存储),Scale(扩展)。 10 | 11 | ### 第一步:Scenario 场景 12 | 13 | 在这一步,你需要询问面试官:需要设计哪些功能(也可以自己想),需要承受多大的访问量。 14 | 15 | 就以设计微博为例,可以把微博的功能一一列出来,但显然无法在短短的一小时的面试里完成所有的功能设计,所以要筛选出核心的功能,比如发微博和浏览微博。 16 | 17 | 另外,需要考虑系统所承受的QPS(每秒查询次数)大概是多少,需要考虑并发用户,读写频率并掌握相关的计算方法,对一些经验性的数值要记得。 18 | 19 | 比如一台web服务器的承受量约为1k的QPS,一台关系型数据库的承受量约为1k的QPS,一台非关系型数据库的承受量是10k的QPS。 20 | 21 | ### 第二步:Service 服务 22 | 23 | 所谓服务可以理解为逻辑处理的整合,将同一类问题的逻辑处理处理的整合,对于同一类的问题的逻辑处理可以归并到一个服务。这一步实际上就是将整个系统细分为若干个小的服务。 24 | 25 | 比如以设计微博为例,我们可以拆分成用户服务,微博服务,关注关系服务,媒体资源服务等等。 26 | 27 | ### 第三步:Storage 存储 28 | 29 | 这一步是4S分析法中最重要的一部分,需要根据每个服务的数据特性选择合适的存储结构,然后细化数据表结构。 30 | 31 | 系统设计中可以选择的存储结构一般有三大类:数据库系统,文件系统,缓存系统。其中数据库又分为关系型数据库(例如MySQL)和非关系型数据库(NoSQL)。 32 | 33 | 以设计微博为例,根据前面拆分的服务的特点,用户服务适合用MySQL存储,而微博信息适合用文档型数据库MongoDB存储,多媒体资源可以借助文件系统,如AWS S3,对于热点数据,还可以用Redis做缓存。 34 | 35 | 另外,我们要向面试官展示数据存储和读取过程。就以微博的feed流为例,如何拿到新鲜事列表,可以采取pull和push两种方式,pull的方式实时的去获取关注人的微博,并做多路归并,push的方式会为每个用户维护自己的新鲜事的记录。 36 | 37 | 我们要在方案的取舍中体现出自己的专业性,以及tradeoff的能力。 38 | 39 | ### 第四步:Scale 扩展 40 | 41 | 经过前面3个步骤的分析,我们已经得到了一个解决方案,但这个方案还有很多的缺陷,所以需要4S分析法最后一步,扩展。 42 | 43 | 这一步分为两个部分,一个是优化,包括解决涉及缺陷,更多功能设计以及一些特殊情况如何处理。另一个是维护,包括系统的鲁棒性和扩展性,比如有一台服务器挂了怎么办,比如鹿晗公布恋情导致流量激增怎么办。 44 | 45 | ### 总结 46 | 47 | 最后总结一下系统设计面试过程中的注意点: 48 | - Ask before design. 先跟面试官明确需求再动手设计,不要一上来就冲着一个巨牛的方案去设计。 49 | - No more no less. 不要总想着去设计最牛的系统,而是设计够用的系统。 50 | - Work solution first. 先设计一个基本能工作的MVP产品,再逐步优化。 51 | - Analysis is import than soluton. 系统设计没有标准答案,记住答案是没用的。通过分析过程展示你的知识储备,并权衡各种设计方式的利弊。 52 | 53 | 54 | -------------------------------------------------------------------------------- /天地大同/面经.md: -------------------------------------------------------------------------------- 1 | # 面经 2 | 3 | 微软面试一定要表现出: 4 | 5 | 思考过程(分析过程)+沟通过程+良好的编码习惯(变量名,测试用例) 6 | 7 | 如果有,请一定要表现出来,就算算法写出来了,往往也没什么鸟用。 -------------------------------------------------------------------------------- /模拟面试/Java面试题整理.md: -------------------------------------------------------------------------------- 1 | # Java面试题整理 2 | 3 | ## 并发 4 | 5 | ### 1. 在java中守护线程和本地线程区别? 6 | 7 | java 中的线程分为两种:守护线程(Daemon)和用户线程(User)。 8 | 任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon(boolon);true 则把该线程设置为守护线程,反之则为用户线程。 9 | Thread.setDaemon()必须在 Thread.start()之前调用,否则运行时会抛出异常。 10 | 11 | 两者的区别: 12 | 13 | 唯一的区别是判断虚拟机(JVM)何时离开,Daemon 是为其他线程提供服务,如果全部的 User Thread 已经撤离,Daemon 没有可服务的线程,JVM 撤离。也可以理解为守护线程是 JVM 自动创建的线程(但不一定),用户线程是程序创建的线程;比如 JVM 的垃圾回收线程是一个守护线程,当所有线程已经撤离,不再产生垃圾,守护线程自然就没事可干了,当垃圾回收线程是 Java 虚拟机上仅剩的线程时,Java 虚拟机会自动离开。 14 | 15 | 扩展:Thread Dump 打印出来的线程信息,含有 daemon 字样的线程即为守护进程,可能会有:服务守护进程、编译守护进程、windows 下的监听Ctrl+break 的守护进程、Finalizer 守护进程、引用处理守护进程、GC 守护进程。 16 | 17 | ### 2. 线程与进程的区别 18 | 19 | 进程是操作系统分配资源的最小单元,线程是操作系统调度的最小单元。 20 | 一个程序至少有一个进程,一个进程至少有一个线程。 21 | 22 | ### 3. 什么是多线程中的上下文切换 23 | 24 | 多线程会共同使用一组计算机上的 CPU,而线程数大于给程序分配的 CPU 数量时,为了让各个线程都有执行的机会,就需要轮转使用 CPU。不同的线程切换使用 CPU 发生的切换数据等就是上下文切换。 25 | 26 | ### 4. 死锁与活锁的区别,死锁与饥饿的区别? 27 | 28 | 死锁:是指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。 29 | 30 | 产生死锁的必要条件: 31 | 32 | 1、互斥条件:所谓互斥就是进程在某一时间内独占资源。 33 | 2、请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 34 | 3、不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。 35 | 4、循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。 36 | 37 | 活锁:任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试,失败,尝试,失败。 38 | 39 | 活锁和死锁的区别在于,处于活锁的实体是在不断的改变状态,所谓的“活”, 而处于死锁的实体表现为等待;活锁有可能自行解开,死锁则不能。 40 | 41 | 饥饿:一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执行的状态。 42 | 43 | Java 中导致饥饿的原因: 44 | 1、高优先级线程吞噬所有的低优先级线程的 CPU 时间。 45 | 2、线程被永久堵塞在一个等待进入同步块的状态,因为其他线程总是能在它之 46 | 前持续地对该同步块进行访问。 47 | 3、线程在等待一个本身也处于永久等待完成的对象(比如调用这个对象的 wait 48 | 方法),因为其他线程总是被持续地获得唤醒。 49 | 50 | ### 5. Java 中用到的线程调度算法是什么? 51 | 52 | 采用时间片轮转的方式。可以设置线程的优先级,会映射到下层的系统上面的优先级上,如非特别需要,尽量不要用,防止线程饥饿。 53 | 54 | ### 6. 为什么使用 Executor 框架 55 | 56 | 每次执行任务创建线程 new Thread()比较消耗性能,创建一个线程是比较耗时、耗资源的。 57 | 58 | 调用 new Thread()创建的线程缺乏管理,被称为野线程,而且可以无限制的创建,线程之间的相互竞争会导致过多占用系统资源而导致系统瘫痪,还有线程之间的频繁交替也会消耗很多系统资源。 59 | 60 | 接使用 new Thread() 启动的线程不利于扩展,比如定时执行、定期执行、定时定期执行、线程中断等都不便实现。 61 | 62 | ### 7. 在 Java 中 Executor 和 Executors 的区别 63 | 64 | Executors 工具类的不同方法按照我们的需求创建了不同的线程池,来满足业务的需求。 65 | Executor 接口对象能执行我们的线程任务。 66 | ExecutorService 接口继承了 Executor 接口并进行了扩展,提供了更多的方法我们能获得任务执行的状态并且可以获取任务的返回值。 67 | 使用 ThreadPoolExecutor 可以创建自定义线程池。 68 | Future 表示异步计算的结果,他提供了检查计算是否完成的方法,以等待计算的完成,并可以使用 get()方法获取计算的结果。 69 | 70 | ### 8. 什么是原子操作?在 Java Concurrency API 中有哪些原子类(atomic classes)? 71 | 72 | 原子操作(atomic operation)意为”不可被中断的一个或一系列操作” 。 73 | 处理器使用基于对缓存加锁或总线加锁的方式来实现多处理器之间的原子操作。在 Java 中可以通过锁和循环 CAS 的方式来实现原子操作。 CAS 操作——Compare & Set,或是 Compare & Swap,现在几乎所有的 CPU 指令都支持CAS 的原子操作。 74 | 75 | 原子操作是指一个不受其他操作影响的操作任务单元。原子操作是在多线程环境下避免数据不一致必须的手段。 76 | 77 | int++并不是一个原子操作,所以当一个线程读取它的值并加 1 时,另外一个线程有可能会读到之前的值,这就会引发错误。 78 | 79 | 为了解决这个问题,必须保证增加操作是原子的,在 JDK1.5 之前我们可以使用同步技术来做到这一点。到 JDK1.5,java.util.concurrent.atomic 包提供了 int 和long 类型的原子包装类,它们可以自动的保证对于他们的操作是原子的并且不需要使用同步。 80 | 81 | java.util.concurrent 这个包里面提供了一组原子类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由 JVM 从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。 82 | 83 | 原子类:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference 84 | 原子数组:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray 85 | 原子属性更新器:AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater 86 | 解决 ABA 问题的原子类:AtomicMarkableReference(通过引入一个 boolean来反映中间有没有变过),AtomicStampedReference(通过引入一个 int 来累加来反映中间有没有变过) 87 | 88 | ### 9. -------------------------------------------------------------------------------- /模拟面试/ms.md: -------------------------------------------------------------------------------- 1 | # ms 2 | 3 | -------------------------------------------------------------------------------- /模拟面试/面试题1.md: -------------------------------------------------------------------------------- 1 | ## 模拟面试题1 2 | 3 | ### 1. 调整数组顺序使奇数位于偶数前面 4 | 5 | 输入一个int数组,返回一个数组,其中奇数都在左边,偶数都在右边。附加条件 不使用额外的空间 6 | 7 | 解答: 8 | 9 | 这道题是的leetcode地址地址:剑指offer21 10 | https://leetcode-cn.com/problems/diao-zheng-shu-zu-shun-xu-shi-qi-shu-wei-yu-ou-shu-qian-mian-lcof/ 11 | 12 | 这道题我首先想到的是排序,但是超时了 13 | 14 | 然后想到了双指针 15 | 16 | 双指针也可以用两种,一种是首尾指针 17 | 18 | ``` python 19 | class Solution: 20 | def exchange(self, nums: List[int]) -> List[int]: 21 | i, j = 0, len(nums) - 1 22 | while i < j: 23 | if nums[i] % 2 == 1: 24 | i+=1 25 | continue 26 | 27 | if nums[j] % 2 == 0: 28 | j -=1 29 | continue 30 | nums[i], nums[j] = nums[j], nums[i] 31 | return nums 32 | ``` 33 | 34 | 一种是快慢指针 35 | 36 | ``` python 37 | class Solution: 38 | def exchange(self, nums: List[int]) -> List[int]: 39 | i, j = 0, 0 40 | while j < len(nums): 41 | if nums[j] % 2 == 1: 42 | nums[i], nums[j] = nums[j], nums[i] 43 | i += 1 44 | j += 1 45 | return nums 46 | ``` 47 | 48 | #### 建议 49 | 50 | 1. 熟悉各类排序:冒泡,插入,归并,快速,桶 51 | 2. 熟悉双指针法,这在很多数组题里能派上用场 52 | 53 | ### 2. 数据库索引 54 | 55 | todo 56 | 57 | ### 3. 数据库分库分表 58 | 59 | 数据库如何分库分表? 60 | 61 | ### 4. 找到一个文件里出现次数最多的的数字,文件大小远大于内存容量 62 | 63 | ### 5. 两数之和 64 | 65 | 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 66 | 67 | 这道题的leetcode地址是:https://leetcode-cn.com/problems/two-sum/ 68 | 69 | 非常经典的一道题,在我的面试经历中,也出现过。 70 | 71 | 在面试过程中,尽可能逐渐深入,把解法由浅入深依次讲出来,并且比较出它们的优劣 72 | 73 | 首先是暴力法,双层循环,复杂度O(n^2) 74 | 75 | ``` python 76 | class Solution: 77 | def twoSum(self, nums: List[int], target: int) -> List[int]: 78 | for i in range(len(nums)-1): 79 | for j in range(i+1, len(nums)): 80 | if nums[i] + nums[j] == target: 81 | return [i, j] 82 | ``` 83 | 84 | 用字典存放之前的结果,空间换时间,复杂度O(n) 85 | 86 | ``` python 87 | 88 | class Solution: 89 | def twoSum(self, nums: List[int], target: int) -> List[int]: 90 | data_dict = dict() 91 | for i, element in enumerate(nums): 92 | if target - element in data_dict: 93 | return [data_dict[target-element], i] 94 | data_dict[element] = i 95 | ``` 96 | 97 | #### 建议 98 | 99 | 1. 算法题要经常刷,不要只刷一遍,过遍数,五毒神掌 100 | 2. 同一道题,可以多看看别人的解法,并且知道各个解法的优劣 101 | 102 | ### 6. MySQL两种存储引擎的差异 103 | 104 | 简单介绍区别: 105 | 106 | - MyISAM是非事务安全的,而InnoDB是事务安全的 107 | - MyISAM锁的粒度是表级的,而InnoDB支持行级锁 108 | - MyISAM支持全文类型索引,而InnoDB不支持全文索引 109 | - MyISAM相对简单,效率上要优于InnoDB,小型应用可以考虑使用MyISAM 110 | - MyISAM表保存成文件形式,跨平台使用更加方便 111 | 112 | 1、MyISAM管理非事务表,提供高速存储和检索以及全文搜索能力,如果再应用中执行大量select操作,应该选择MyISAM 113 | 2、InnoDB用于事务处理,具有ACID事务支持等特性,如果在应用中执行大量insert和update操作,应该选择InnoDB 114 | 115 | 参考资料:https://juejin.cn/post/6903101301429796871 116 | 117 | ### 7. where a>1 and b>1;where a = 1;where b = 2;如何为这种条件语句建立索引 118 | 119 | table(a,b) 120 | table(b) 121 | 122 | ### 8. LRU的实现 123 | 124 | leetcode原题地址:https://leetcode-cn.com/problems/lru-cache/ 125 | 126 | python可以用collections.OrderedDict轻松实现,但肯定不符合面试官要求。 127 | 128 | 正解是用哈希表+双向链表 129 | 130 | - 双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,而靠近尾部的键值对是最久未使用的。 131 | - 哈希表即为普通的哈希映射(HashMap),通过缓存数据的键映射到其在双向链表中的位置。 132 | 133 | ``` python 134 | class DLinkedNode: 135 | def __init__(self, key=0, value=0): 136 | self.key = key 137 | self.value = value 138 | self.prev = None 139 | self.next = None 140 | 141 | 142 | class LRUCache1: 143 | 144 | def __init__(self, capacity: int): 145 | self.cache = dict() 146 | # 使用伪头部和伪尾部节点 147 | self.head = DLinkedNode() 148 | self.tail = DLinkedNode() 149 | self.head.next = self.tail 150 | self.tail.prev = self.head 151 | self.capacity = capacity 152 | self.size = 0 153 | 154 | def get(self, key: int) -> int: 155 | if key not in self.cache: 156 | return -1 157 | # 如果 key 存在,先通过哈希表定位,再移到头部 158 | node = self.cache[key] 159 | self.moveToHead(node) 160 | return node.value 161 | 162 | def put(self, key: int, value: int) -> None: 163 | if key not in self.cache: 164 | # 如果 key 不存在,创建一个新的节点 165 | node = DLinkedNode(key, value) 166 | # 添加进哈希表 167 | self.cache[key] = node 168 | # 添加至双向链表的头部 169 | self.addToHead(node) 170 | self.size += 1 171 | if self.size > self.capacity: 172 | # 如果超出容量,删除双向链表的尾部节点 173 | removed = self.removeTail() 174 | # 删除哈希表中对应的项 175 | self.cache.pop(removed.key) 176 | self.size -= 1 177 | else: 178 | # 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部 179 | node = self.cache[key] 180 | node.value = value 181 | self.moveToHead(node) 182 | 183 | def addToHead(self, node): 184 | node.prev = self.head 185 | node.next = self.head.next 186 | self.head.next.prev = node 187 | self.head.next = node 188 | 189 | def removeNode(self, node): 190 | node.prev.next = node.next 191 | node.next.prev = node.prev 192 | 193 | def moveToHead(self, node): 194 | self.removeNode(node) 195 | self.addToHead(node) 196 | 197 | def removeTail(self): 198 | node = self.tail.prev 199 | self.removeNode(node) 200 | return node 201 | 202 | ``` 203 | 204 | ### 9. 介绍一个项目中遇到的问题 205 | 206 | 我好像没遇到特别有挑战性的难题,业务开发居多,很难说出亮点。 207 | 208 | 我整理了白海飞老师《面试现场》的“经历没有亮点可讲?你需要做份详历”这一节的思路,希望对你能有所启发。 209 | 210 | 如何从过去的经历中发现亮点: 211 | 212 | 对于面试官而言,亮点是你打动他的能力素质,以及有价值的成果。能力素质包括经验、技能、潜力和动机,成果包括做出的产品、服务,总结的方法、实践,培养的人才和团队,以及各种形式的认可和奖励。 213 | 214 | 很多人都说难于发现自己经历的亮点。这是个普遍的现象,类似于“知识的诅咒”:你对做过的项目虽然了如指掌,却不容易看到哪些是别人认为了不起的价值,也总结不出当初用了哪些醒目的技能。正所谓“不识庐山真面目,只缘身在此山中”。 215 | 216 | 下面我们就一起来看看如何跳出这座山,换一下视角,尝试找找那些有价值有亮点的地方。 217 | 218 | 1. 转换角度找价值。工作的价值,可以从公司、客户、团队和自己的角度去寻找。 219 | 2. 量化结果找提高。 220 | 3. 复盘过程找创新。 221 | 4. 回顾挑战找动机 222 | 223 | ### 10. 一个100G的文件,内存只有8G,如何给文件排序 224 | 225 | 运用归并排序的思想 226 | 227 | 多路归并 228 | 229 | 先拆成一个个小块,分别排序,然后使用归并排序的思想 230 | 231 | 232 | 参考链接: 233 | https://www.cnblogs.com/chengxiao/p/6194356.html 234 | http://blog.itpub.net/31561269/viewspace-2564096/ 235 | https://blog.csdn.net/sdmxdzb/article/details/104409250 -------------------------------------------------------------------------------- /模拟面试/面试题10.md: -------------------------------------------------------------------------------- 1 | ## 模拟面试题10(我亲自经历的面试题) 2 | 3 | ### 1. 排序 4 | 5 | **冒泡排序** 6 | 7 | ``` python 8 | class Solution: 9 | def sortArray(self, nums: List[int]) -> List[int]: 10 | flag = True 11 | while flag: 12 | flag = False 13 | for i in range(len(nums)-1): 14 | if nums[i] > nums[i+1]: 15 | nums[i], nums[i+1] = nums[i+1], nums[i] 16 | flag = True 17 | return nums 18 | ``` 19 | 20 | **选择排序** 21 | 22 | ``` python 23 | class Solution: 24 | def sortArray(self, nums: List[int]) -> List[int]: 25 | for i in range(len(nums)): 26 | lowest_index = i 27 | for j in range(i+1, len(nums)): 28 | if nums[j] < nums[lowest_index]: 29 | lowest_index = j 30 | nums[i], nums[lowest_index] = nums[lowest_index], nums[i] 31 | return nums 32 | ``` 33 | 34 | **快速排序** 35 | 36 | ``` python 37 | class Solution: 38 | def sortArray(self, nums: List[int]) -> List[int]: 39 | if len(nums) <= 1: 40 | return nums 41 | pivot = nums[len(nums) // 2] 42 | left = [x for x in nums if x < pivot] 43 | middle = [x for x in nums if x == pivot] 44 | right = [x for x in nums if x > pivot] 45 | return self.sortArray(left) + middle + self.sortArray(right) 46 | ``` 47 | 48 | ``` python 49 | class Solution: 50 | def randomized_partition(self, nums, l, r): 51 | pivot = random.randint(l, r) 52 | nums[pivot], nums[r] = nums[r], nums[pivot] 53 | i = l - 1 54 | for j in range(l, r): 55 | if nums[j] < nums[r]: 56 | i += 1 57 | nums[j], nums[i] = nums[i], nums[j] 58 | i += 1 59 | nums[i], nums[r] = nums[r], nums[i] 60 | return i 61 | 62 | def randomized_quicksort(self, nums, l, r): 63 | if r - l <= 0: 64 | return 65 | mid = self.randomized_partition(nums, l, r) 66 | self.randomized_quicksort(nums, l, mid - 1) 67 | self.randomized_quicksort(nums, mid + 1, r) 68 | 69 | def sortArray(self, nums: List[int]) -> List[int]: 70 | self.randomized_quicksort(nums, 0, len(nums) - 1) 71 | return nums 72 | 73 | ``` 74 | 75 | 冒泡 稳定 76 | 选择 不稳定 77 | 插入 稳定 78 | 79 | 80 | 81 | 参考资料: 82 | https://www.cnblogs.com/huang-yc/p/9774287.html 83 | https://blog.csdn.net/MobiusStrip/article/details/83785159?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task 84 | -------------------------------------------------------------------------------- /模拟面试/面试题2.md: -------------------------------------------------------------------------------- 1 | ## 模拟面试题2 2 | 3 | ### 1. 打印重复数字 4 | 5 | 6 | 7 | ### 2. 查找前TopK 8 | 9 | ### 3. 链表反转,每次反转5个node 10 | 11 | ### 4. 使用linux shell统计log中访问url的数量并排序 12 | 13 | todo 14 | 15 | ### 5. 遇到过哪些慢查询,如何优化 16 | 17 | ### 6. tcp3次握手个四次挥手,tcp time wait,tcp还有哪些状态 18 | 19 | ### 7. 线程的状态有几种 20 | 21 | ### 8. StackOverflowError和OutOfMemoryError的区别和分别处理 22 | 23 | ### 9. synchronized和volatile的区别 24 | 25 | ### 10. synchronized和static的作用范围 26 | 27 | -------------------------------------------------------------------------------- /模拟面试/面试题3.md: -------------------------------------------------------------------------------- 1 | ## 模拟面试题3 2 | 3 | ### 1. 500表示什么 4 | 5 | - 1xxs - 信息性:服务器正在处理请求。 6 | - 2xxs - 成功信息:请求已经完成,服务器向浏览器提供了预期的响应。 7 | - 3xxs - 重定向:你的请求被重定向到了其他地方。服务器收到了请求,但是有某种重定向。 8 | - 4xxs – 客户端错误:客户端发生错误,导致服务器无法处理请求。 9 | - 5xxs – 服务端错误:客户端发出了有效的请求,但是服务器未能正确处理请求。 10 | 11 | 500表示服务端错误,常见的有: 12 | 13 | - 500 Internal Server Error:服务器内部错误,服务器遇到了不知道如何处理的情况。比如后端同学写错了model啥的~ 14 | - 502 Bad Gateway:此错误响应表明服务器作为网关需要得到一个处理这个请求的响应,但是得到一个错误的响应。 15 | - 503 Service Unavailable:服务器没有准备好处理请求。常见的原因是服务器因维护或重载而停机。 16 | - 504 Gateway Timeout:网关超时,服务器未能快速的做出反应。请求接口返回pedding时间过长基本就是这个问题了,囧。 17 | 18 | ### 2. 长度为m的int64中找top n 19 | 20 | (todo topk问题) 21 | 22 | ### 3. 进程间通信方法有几种 23 | 24 | 1.管道:速度慢,容量有限,只有父子进程能通讯 25 | 26 | 2.FIFO:任何进程间都能通讯,但速度慢 27 | 28 | 3.消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题 29 | 30 | 4.信号量:不能传递复杂消息,只能用来同步 31 | 32 | 5.共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存 33 | 34 | 参考资料:https://www.cnblogs.com/zgq0/p/8780893.html 35 | 36 | ### 4. HashTable的实现原理 37 | 38 | 39 | 40 | ### 5. Oracle相对于MySQL有什么优势 41 | 42 | 略 43 | 44 | ### 6. 微信抢红包功能设计 45 | 46 | https://www.cnblogs.com/jiawen010/articles/11506391.html 47 | 48 | 49 | ### 7. qps限流 50 | 51 | 一个缓存,一个滑动窗口? 52 | 53 | ### 8. 排序数组中计算重复数字出现的次数 54 | 55 | 56 | 57 | ### 9. kafka 的原理,以及使用中遇到的问题 58 | 59 | ### 10. 手写堆排序 -------------------------------------------------------------------------------- /模拟面试/面试题4.md: -------------------------------------------------------------------------------- 1 | ## 模拟面试题4 2 | 3 | ### 1. 排序算法 4 | 5 | ### 2. 会问有没有消息队列 分布式 缓存 一系列后端架构的经验 6 | 7 | ### 3. 在一个有序但元素可能重复的数组中,输入一个目标值,返回这个目标值的左边界和右边界。[1,1,2,2,2,2,2,4,5],得到2的左右边界。 8 | 9 | ### 4. cookie与session区别; 没有cookie的话session是否有效 10 | 11 | ### 5. HTTP与HTTPS区别 12 | 13 | ### 6. HTTPS如何做到安全 14 | 15 | ### 7. MySQL存储引擎区别 16 | 17 | ### 8. MySQL索引结构优缺点 18 | 19 | ### 9. 讲volatile和可见性 20 | 21 | ### 10. 二叉搜索树转双向链表写代码 22 | -------------------------------------------------------------------------------- /模拟面试/面试题5.md: -------------------------------------------------------------------------------- 1 | ## 模拟面试题5 2 | 3 | ### 1. java乐观锁悲观锁 4 | 5 | ### 2. 消息可达性和唯一消费 6 | 7 | ### 3. mybatis的Mapper接口映射到sql文件的实现原理 8 | 9 | ### 4. mysql联合索引,实现,优点 10 | 11 | ### 5. 两数组a,b,把a数组中在b数组出现的数字,按照它在B数组中出现的顺序进行位置调整。 12 | 13 | ### 6. 微博等热门评论,在分页到很深的时候,如何进行优化 14 | 15 | ### 7. 对简历所写的技能深度层次有准确表述,熟悉不要写成精通 16 | 17 | ### 8. go语言的三大特色,goroutine 调度方式,接口的底层实现,select 的触发顺序(如果多个通道同时准备好)等底层实现方面 18 | 19 | ### 9. 二叉树的最大距离 20 | 21 | ### 10. go语言底层socket 的调度算法(epoll 相关的编程注意点) -------------------------------------------------------------------------------- /模拟面试/面试题6.md: -------------------------------------------------------------------------------- 1 | ## 模拟面试题6 2 | 3 | ### 1. 数据库的索引建立实例,where 子句和order by 子句同时存在如何建立索引以及为什么 4 | 5 | ### 2. 数据库的事物隔离及其各自产生的赃读或幻读现象 6 | 7 | ### 3. 批量插入数据库有啥优化点,文件导入或单句执行(原因锁表开销少) 8 | 9 | ### 4. 项目中遇到的难点及解决办法 10 | 11 | ### 5. 结合redis 和本地缓存如何实现黑名单id过滤功能(考虑海量请求、同时会有读写情况) 12 | 13 | ### 6. 套接字底层选项功能及阻塞和非阻塞场景应用注意事项 14 | 15 | ### 7. 一个数组中取第K大的数 16 | 17 | ### 8. redis Zset数据结构、新增数据的时间复杂度 18 | 19 | ### 9. mysql的事务是怎么实现的 20 | 21 | ### 10. mysql 联合索引在什么情况能用到 22 | 23 | -------------------------------------------------------------------------------- /模拟面试/面试题7.md: -------------------------------------------------------------------------------- 1 | ## 模拟面试题7 2 | 3 | ### 1. 写一个线程安全的单例 4 | 5 | ### 2. 写一个熟悉的排序 说出时间复杂度 6 | 7 | ### 3. 有一个分布式不安全的文件系统,如何保证每次只有一个请求进行读写 8 | 9 | ### 4. 如何实现音乐随机播放 10 | 11 | ### 5. 登录的过程,cookie如何写入的 12 | 13 | ### 6. 301、302、500、502、504是什么含义 14 | 15 | ### 7. mysql相关: 引擎innodb特性,锁,事务 16 | 17 | ### 8. mq:kafka,rocketmq,同步机制和事务机制 18 | 19 | ### 9. 编程实现的是泛型链表的添加和删除,主要是看敲代码的过程和怎样解释自己的代码 20 | 21 | ### 10. 删除链表中重复的结点 -------------------------------------------------------------------------------- /模拟面试/面试题8.md: -------------------------------------------------------------------------------- 1 | ## 模拟面试题8 2 | 3 | ### 1. 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。例如,链表1->2->3->3->4-4->5处理后为1->2->5 4 | 5 | ### 2. 输入一个二叉树,以及指定一个层次,从右向左输出这一层的所有节点 6 | 7 | ### 3. 设计一个LRU,尽可能的支持高并发 8 | 9 | ### 4. session 怎么用 cookie 实现的,session 和 cookie 的区别。 10 | 11 | ### 5. java 线程池是怎么工作的。 12 | 13 | ### 6. java bio nio 的区别 14 | 15 | ### 7. nio是怎么实现的 16 | 17 | ### 8. 线上的 GC 是怎么配置的? 18 | 19 | ### 9. redis 主从怎么实现的。 20 | 21 | ### 10. 链表逆序,设计一个王者的组队系统(人选一起反馈的 看不懂是不是同一道题......) -------------------------------------------------------------------------------- /模拟面试/面试题9.md: -------------------------------------------------------------------------------- 1 | ## 模拟面试题9 2 | 3 | ### 1. sql什么时候不适合用索引,索引太多会有啥问题, 4 | 5 | ### 2. 同步io和异步io区别, 6 | 7 | ### 3. coding考察的是多个数组合并,follow up 如果数组个数非常多但是每个里面的内容比较少 8 | 9 | ### 4. http和https的区别,https的实现 10 | 11 | ### 5. 单链表,每k个进行一次翻转 12 | 13 | ### 6. 设计一个限流工具,每5s允许一个请求,多的请求丢弃 14 | 15 | 设计一个限流工具,在第1,6,11,16秒允许一个请求,多的丢弃 16 | 设计一个限流工具,在第1,6,11,16秒允许一个请求,多的排队等待 17 | 18 | ### 7. 502 504 19 | 20 | ### 8. cookie,session 21 | 22 | ### 9. mysql优化 23 | 24 | 索引优化 25 | 26 | sql优化 27 | 28 | ### 10. mysql MyISAM与innodb的区别 --------------------------------------------------------------------------------