-
Notifications
You must be signed in to change notification settings - Fork 0
/
en.search-data.min.c1b3281eae89cd6b6b2b6c5bc4f8a3b8469fa707f24812f9d7c79e365b71673f.json
1 lines (1 loc) · 183 KB
/
en.search-data.min.c1b3281eae89cd6b6b2b6c5bc4f8a3b8469fa707f24812f9d7c79e365b71673f.json
1
[{"id":0,"href":"/blog/","title":"Blog","section":"","content":"编程伐木累成员博客 # 网站 GitHub 优秀开源项目 微信公众号 说明 机智的程序员小熊 minibear2333 go语言精进之路 机智的程序员小熊 涉及各种后端语言、OpenStack、DevOps、容器等,还有一些思考 老衲的博客 yann的小站 运维、go语言 好博客推荐 # 网站 说明 酷壳 左耳朵耗子的博客,无所谓技术,里面一些思考见解也是值得一看的,我没事就会看看 Jack Cui 剑指Offer全集题解,机器学习领域,建站文章写的比较全 wego 一个线上go语言组织内容很丰富 星光博客 wordpress建站比较6、其他文章没什么意思 "},{"id":1,"href":"/contribute/","title":"Contribute","section":"","content":"如何贡献 # "},{"id":2,"href":"/interview/cao-zuo-xi-tong/","title":"Cao Zuo Xi Tong","section":"Interviews","content":"操作系统 # 并发和并行的理解? # 并发:在一个时间段中多个程序都启动运行在同一个处理机中,比如线程 并行:假设目前A,B两个进程,两个进程分别由不同的 CPU 管理执行,两个进程不抢占 CPU 资源且可以同时运行,这叫做并行。单核CPU是伪并行 线程与进程的优缺点 # 多线程的优点:\n 更加高效的内存共享。多进程下内存共享不便; 较轻的上下文切换。因为不用切换地址空间,CR3寄存器和清空TLB。 多进程的优点:\n 各个进程有自己内存空间,所以具有更强的容错性,不至于一个集成crash导致系统崩溃 具有更好的伸缩性,因为进程将地址空间,页表等进行了隔离,可以在不同的服务器上进行水平伸缩 如何提升多线程的效率? # 答者:彬\n 使用线程池,减少线程的创建销毁带来的开销 根据不同的线程类型确定线程数量,例如cpu繁琐的,核线比1:2,I/O繁琐的1:1 压测,看具体项目能吃多少线程 在线程数无法减少的情况下,根据物理内存调整jvm堆大小,为线程提供足够内存空间 升级物理机的cpu和内存 单台机极限的情况下,使用集群 最基本的,优化代码,减低复杂度 小熊补充\n 尽量使用线程池,从而不用频繁的创建,销毁线程; 减少线程之间的同步和通信; 通过Huge Page的方式避免产生大量的缺页异常; 避免需要频繁共享写的数据。 进程的三个状态 # 就绪 —\u0026gt; 执行:准备就绪,调度器满足了的需求 执行 —\u0026gt; 阻塞:正在执行的进程由于发生某事件(例如请求I/O而等待I/O完成等)而暂时无法继续执行时,便放弃处理机而处于暂停状态,这时即使把处理机分配给进程也无法运行,故称该进程处于阻塞状态。 阻塞 —\u0026gt; 就绪:处于阻塞状态的进程,在其等待的事件已经发生,如输入/输出完成,资源得到满足或错误处理完毕时,处于等待状态的进程并不马上转入执行状态,而是先转入就绪状态,然后再由系统进程调度程序在适当的时候将该进程转为执行状态; 执行 —\u0026gt; 就绪:正在执行的进程,因时间片用完而被暂停执行,或在采用抢先式优先级调度算法的系统中, 当有更高优先级的进程要运行而被迫让出处理机时,该进程便由执行状态转变为就绪状态。 进程与线程有什么区别? # 进程是资源分配的最小单位,线程是进程的一个实体,也是 CPU 调度和分派的基本单位,它是比进程更小的能独立运行的基本单位,有时又被称为轻量级进程。 创建进程或撤销进程,系统都要为之分配或回收资源,操作系统开销远大于创建或撤销线程时的开销; 不同进程地址空间相互独立,同一进程内的线程共享同一地址空间。一个进程的线程在另一个进程内是不可见的; 进程间不会相互影响,而一个线程挂掉将可能导致整个进程挂掉; 所以\n 如果以多进程的方式运行,那么允许多个任务同时运行; 如果以多线程的方式运行,那么允许将单个任务分成不同的部分运行; 为了防止进程/线程之间产生冲突和允许进程之间的共享资源,需要提供一种协调机制。 多线程一定就比单线程效率高吗? # 多线程对 CPU,内存等都要求比较高,如果存在的上下文切换过于耗时,互斥时间太久,效率反而会低。 也就是说线程大于资源数容易出现抢占,少了又会出现浪费 进程间怎么通信? # linux中的管道,优点是简单,缺点是不适合频繁的通信。大小受限且只能承载无格式字节流的方式 消息队列,按照独立单元(消息体)进行发送,双方约定好约定好消息类型或者正文的格式,发送到消息队列的数据太大,需要拷贝的时间也就越多 共享内存,使用ipcs创建共享内存,不需要拷贝,可立即感知,但容易出现冲突和覆盖 怎么保证多写入不出现冲突? # 信号量(计数器)\n为了防止冲突,我们得有个约束或者说一种保护机制。使得同一份共享的资源只能一个进程使用\np操作为申请资源,会将数值减去M,表示这部分被他使用了,其它进程暂时不能用。v操作是归还资源操作,告知归还了资源可以用这部分。\n信号\n一旦进程发送某一个信号给另一个进程,另一进程将执行相应的函数进行处理。也就是说把可能出现的异常等问题准备好,一旦信号产生就执行相应的逻辑即可。\n套接字(远程通信)\n可以跨主机通信 acept socket\n同步、异步、阻塞、非阻塞的概念 # 同步:当一个同步调用发出后,调用者要一直等待返回结果。通知后,才能进行后续的执行。 异步:当一个异步过程调用发出后,调用者不能立刻得到返回结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。 阻塞:是指调用结果返回前,当前线程会被挂起,即阻塞。 非阻塞:是指即使调用结果没返回,也不会阻塞当前线程。 进程调度算法有哪些? # 先来先服务调度算法 每一次的调度都从队列中选择最先进入队列的投入运行。\n 时间片轮转调度算法\n按照进程到达的时间排序, 每个进程时间片大小相等,时间片不够用时等待。\n 短作业优先调度算法\n 缺点是长作业等待时间进程饥饿\n 最短剩余时间优先调度算法 同样的问题\n 高响应比优先调度算法 短作业优先+等待时间越长优先级越高\n 优先级调度算法 每次从后备作业队列中选择优先级最髙的一个或几个作业,将它们调入内存,分配必要的资源,创建进程并放入就绪队列。\n死锁是什么? # 彼此拥有对方正在申请的互斥量,解决方法:\n 按照一定的先后顺序申请这些互斥量。 产生死锁的必要条件 # 资源互斥;如果是共享不存在死锁等待 非抢占:如果可以抢占直接抢占 循环等待:必要不充分条件,同时等待时可以顺序执行,除非出现环路等待 解决死锁的基本方法 # 破坏互斥:使用类似CAS无锁结构 破坏不抢占:锁获取失败时自动放弃持有的锁,并重试 破坏环路等待:按顺序请求锁,给锁排序、给线程(安全序列) 怎么预防死锁? # 破坏请求条件:一次性分配所有资源,这样就不会再有请求了; 破坏请保持条件:只要有一个资源得不到分配,也不给这个进程分配其他的资源; 破坏不可剥夺条件:当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源; 破坏环路等待条件:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反。 避免死锁的算法 # 银行家算法\n 当进程首次申请资源时,要测试该进程对资源的最大需求量,如果系统现存的资源可以满足它的最大需求量则按当前的申请量分配资源,否则就推迟分配。 当进程在执行中继续申请资源时,先测试该进程已占用的资源数与本次申请资源数之和是否超过了该进程对资源的最大需求量。 若超过则拒绝分配资源。若没超过则再测试系统现存的资源能否满足该进程尚需的最大资源量,若满足则按当前的申请量分配资源,否则也要推迟分配。 解决死锁 # 资源剥夺:挂起某些死锁进程,并抢占它的资源,将这些资源分配给其它死锁进程(但应该防止被挂起的进程长时间得不到资源); 撤销进程:强制撤销部分、甚至全部死锁进程并剥夺这些进程的资源(撤销的原则可以按进程优先级和撤销进程代价的高低进行); 进程回退:让一个或多个进程回退到足以避免死锁的地步。进程回退时自愿释放资源而不是被剥夺。要求系统保持进程的历史信息,设置还原点。 什么是缓冲区溢出?有什么危害? # 缓冲区溢出是指当计算机向缓冲区内填充数据时超过了缓冲区本身的容量,溢出的数据覆盖在合法数据上。\n你怎么理解操作系统里的内存碎片,有什么解决办法? # 内存碎片通常分为内部碎片和外部碎片:\n 内部碎片是由于采用固定大小的内存分区,当一个进程不能完全使用分给它的固定内存区域时就会产生内部碎片,通常内部碎片难以完全避免; 外部碎片是由于某些未分配的连续内存区域太小,以至于不能满足任意进程的内存分配请求,从而不能被进程利用的内存区域。 现在普遍采取的内存分配方式是段页式内存分配。将内存分为不同的段,再将每一段分成固定大小的页。通过页表机制,使段内的页可以不必连续处于同一内存区域。\n最后 # 如果文中有误,欢迎提pr或者issue,一旦合并或采纳作为贡献奖励可以联系我直接无门槛加入 技术交流群\n我是小熊,关注我,知道更多不知道的技术\n "},{"id":3,"href":"/interview/docker/","title":"Docker","section":"Interviews","content":"Docker # 介绍docker # docker是go开发的,基于Apache2.0协议开源,可以把应用和他的依赖打包到一个轻量级、方便移植的系统中,主要解决的问题是快速发布、自动化构建、统一测试成功的环境和线上环境\ndocker分为镜像、容器、仓库,打包好的叫镜像,存储镜像的地方叫仓库,镜像运行起来叫容器\n容器也可以理解成进程,只不过做了隔离和资源限制,方便管理,对于容器内部的进程来说就好像是一个独立的操作系统\n因为镜像小,方便创建和效果所以伸缩、管理、部署起来更为容易\n隔离是怎么做的? # 用namespace做的\n资源限制是怎么做的? # 用cgroup做的,对cpu和内存做了限制\n听过镜像分层吗? # 按层构建,基于一个基础层添加新层,前一层是后一层的基础,构建完就变成只读的了,每一层都意味着不同的操作指令,比如初始化环境、程序入口等\ndocker网络模式 # Docker使用Linux的Namespaces技术来进行资源隔离\n container模式:和一个已经存在的容器共享Network namespace,不创建自己的网卡,也不配置自己的ip,共享同一个ip和共享的端口范围,但文件系统和进程列表还是隔离的,同样是用到veth设置拉到docker0上,docker0是默认网关,转发到宿主机上 host模式:和主机共享网络,但文件系统和进程列表还是隔离的,容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口 none模式:Docker容器拥有自己的Network Namespace,但不为Docker容器进行任何网络配置,没有网卡、IP、路由等信息 bridge模式:默认模式,在主机上创建一个名为 docker0 的虚拟网桥,此主机上启动的 Docker 容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中。 从 docker0 子网中分配一个 IP 给容器使用,并设置 docker0 的 IP 地址为容器的默认网关。在主机上创建一对虚拟网卡 veth pair 设备,Docker 将 veth pair 设备的一端放在新创建的容器中,并命名为 eth0 (容器的网卡),另一端放在主机中,以 vethxxx 这样类似的名字命名,并将这个网络设备加入到 docker0 网桥中。可以通过 brctl show 命令查看。 选择一个和宿主机不同的IP地址和子网分配给docker0,连接到docker0的容器就从这个子网中选择一个未占用的IP使用 "},{"id":4,"href":"/interview/go/","title":"Go","section":"Interviews","content":"Go # 基本数据类型 # goroutine # 框架 # gin # 线程和协程,有什么区别,为什么协程可以创建很多 # 答者:记事本\n线程由系统调度,协程由运行时调度\n而为什么协程可以做到同时创建上万个,是因为go的协程初始化资源是4KB空间,比线程轻量级\n网上:\n区别在于\n 一个线程可以多个协程,一个进程也可以单独拥有多个协程。 线程进程都是同步机制,而协程则是异步。 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态。 线程是抢占式,而协程是非抢占式的,所以需要用户自己释放使用权来切换到其他协程,因此同一时间其实只有一个协程拥有运行权,相当于单线程的能力。 协程并不是取代线程, 而且抽象于线程之上, 线程是被分割的CPU资源, 协程是组织好的代码流程, 协程需要线程来承载运行, 线程是协程的资源, 但协程不会直接使用线程, 协程直接利用的是执行器(Interceptor), 执行器可以关联任意线程或线程池, 可以使当前线程, UI线程, 或新建新程.。 线程是协程的资源。协程通过Interceptor来间接使用线程这个资源。 协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:\n协程的好处:\n 无需线程上下文切换的开销 无需原子操作锁定及同步的开销 方便切换控制流,简化编程模型 缺点:\n 无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。 进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序 最佳实践\n 线程和协程推荐在IO密集型的任务(比如网络调用)中使用,而在CPU密集型的任务中,表现较差。 对于CPU密集型的任务,则需要多个进程,绕开GIL的限制,利用所有可用的CPU核心,提高效率。 所以大并发下的最佳实践就是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。 go协程的GMP理论 # 首先协程和线程是多对多的关系,一般是多对一,只要不涉及多线程就不涉及抢占和线程上下文切换\nG指goroutine M thread(machine)、P(Processor处理器)\n 在 Go 中,线程是运行 goroutine 的实体,调度器的功能是把可运行的 goroutine 分配到工作线程上。 全局队列(Global Queue):存放等待运行的 G。 P 的本地队列:同全局队列类似,存放的也是等待运行的 G,存的数量有限,不超过 256 个。新建 G’时,G’优先加入到 P 的本地队列,如果队列满了,则会把本地队列中一半的 G 移动到全局队列。 P 列表:所有的 P 都在程序启动时创建,并保存在数组中,最多有 GOMAXPROCS(可配置) 个。 M:线程想运行任务就得获取 P,从 P 的本地队列获取 G,P 队列为空时,M 也会尝试从全局队列拿一批 G 放到 P 的本地队列,或从其他 P 的本地队列偷一半放到自己 P 的本地队列。M 运行 G,G 执行之后,M 会从 P 获取下一个 G,不断重复下去。 一句话:协程创建先放入P的队列,放满了把一半放G的全局队列,M顺序取P来运行,如果M没有取到(P为空)移动全局队列到P中,或者去其他P上取,所以M有调度的作用\n引用: Golang 调度器 GMP 原理与调度全分析\ngo的切片和数组有什么区别 # 定长声明的是数组,不定长是切片\nvar arr1 [3]int = [3]int{1, 2, 3} var slice1 []int = []int{1, 2, 3} 数组拷贝后可以随便改值,不会对原数组有影响,但切片拷贝是引用,修改新切片会同时修改原切片\n管道chan是什么 # 一个 channels 是一个通信机制,它可以让一个 goroutine 通过它给另一个 goroutine 发送值信息,可以理解为一个队列,遵循先入先出的原则,同时在代码级别线程安全\n管道比锁快?为什么 # go中的chan 是用锁实现的。所以肯定不会比锁块。\n如果协程A创建了协程B和C,B协程panic了,A协程有recover,会发生什么? # 无法在父协程中捕获子协程的panic\nfunc main() { // 希望捕获所有所有 panic defer func () { r := recover() fmt.Println(\u0026#34;捕获到子协程panic:\u0026#34;,r) }() // 启动新协程 go func () { panic(123) }() // 等待一下,不然协程可能来不及执行 time.Sleep(1 * time.Second) fmt.Println(\u0026#34;这条消息打印不出来\u0026#34;) } 输出:\npanic: 123 goroutine 6 [running]: main.main.func2() ... Process finished with exit code 2 可以看到recover没有成功执行,整个进程都退出了 所以开了新协程而且忘记了在协程中捕获panic的话,服务的进程就会因为某个未捕获的panic而退出。 解决方法,使用结构体维护两个通道来处理done和panic事件,外部使用select来维护处理抛出panic,这样外部就可以recover了 引用: Go协程这样用才安全\n最后 # 如果文中有误,欢迎提pr或者issue,一旦合并或采纳作为贡献奖励可以联系我直接无门槛加入 技术交流群\n我是小熊,关注我,知道更多不知道的技术\n "},{"id":5,"href":"/interview/java/","title":"Java","section":"Interviews","content":"Java # 基本数据类型 # 整型:byte(8)、short(16)、int(32)、long(64) 浮点型:float(32)、double(64) 布尔型:boolean(8) 字符型:char(16) 只能向上转型 += 或者 ++ 运算符会执行隐式类型转换 异常处理 # 三种类型的异常 # **检查性异常:**最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。 错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。 关键字 # try/catch:捕获异常,catch可以多重捕获(直到异常被捕获或者通过所有的 catch 块) throws/throw:方法使用 throws 关键字来声明(一个方法没有捕获到一个检查性异常),throw 关键字抛出一个异常 finally:在 try 代码块后面执行的代码块 自定义异常 # 所有异常都必须是 Throwable 的子类。 自定义检查性异常类,需要继承 Exception 类。 自定义运行时异常类,那么需要继承 RuntimeException 类。 hashMap # 底层数据结构,JDK 1.8 是数组 + 链表 + 红黑树,JDK 1.7 无红黑树。 初始容量为 16,通过 tableSizeFor 保证容量为 2 的幂次方。寻址方式,高位异或,(n-1)\u0026amp;h 取模,优化速度。 扩容机制,当元素数量大于容量 x 负载因子 0.75 时,容量扩大为原来的 2 倍,新建一个数组,然后转移到新数组。 基于 Map 实现。 线程不安全。 key的hashCode()做hash,然后再计算index(高位运算和取模运算) 指针数组,value为链表,长度大于 8 时,转化为红黑树(1.8),优化查询效率。 当 HashMap 中有大量的元素都存放到同一个桶中时,这个时候 HashMap 就相当于一个单链表,遍历时间复杂度就是 O(n) 转换为红黑树:时间复杂度为 O(logn) 当限制n为2的幂次方时,(n-1)\u0026amp;h和h%n结果一致,但效率高了很多倍 ConcurrentHashMap的实现原理 # 采用了数组+链表+红黑树的实现方式来设计 ConcurrentHashMap的主干是个Segment数组 Segment继承了ReentrantLock,重入锁 Segment类似于HashMap,一个Segment维护着一个HashEntry数组 分段锁 内部大量采用CAS操作 比较交换 乐观锁 包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B) 链表:保存key,value及key的hash值的数据结构,value和next都用volatile修饰 final关键字 # 修饰类不能被继承 修饰方法:不能被重写 修饰变量:基本数据类型不能被修改;引用类型初始化后不能指向另一个对象 String实现 # 属性value:char[] String不可变: 实现字符串池(String pool) 多线程安全 避免安全问题 加快字符串处理速度(hashMap的key方便计算hashcode) Equals: this == anObject anObject instanceof String 比较value属性 重写hashCode方法 clone # clone() 是 Object 的 protected 方法,不是 public,一个类不显式去重写 clone(),其它类就不能直接去调用该类实例的 clone() 方法。 Cloneable 接口规定:一个类没有实现 Cloneable 接口又调用了 clone() 方法,就会抛出 CloneNotSupportedException 浅拷贝:拷贝对象和原始对象的引用类型引用同一个对象 深拷贝:拷贝对象和原始对象的引用类型引用不同对象 使用 clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象 volatile # 在多线程环境下,保证变量的可见性。使用了 volatile 修饰变量后,在变量修改后会立即同步到主存中,每次用这个变量前会从主存刷新。 禁止 JVM 指令重排序。 synchronized # 三种应用方式: 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁 修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。 偏向锁,自旋锁,轻量级锁(乐观锁)重量级锁(悲观锁) # 通过 synchronized 加锁,第一个线程获取的锁为偏向锁,这时有其他线程参与锁竞争,升级为轻量级锁,其他线程通过循环的方式尝试获得锁,称自旋锁。若果自旋的次数达到一定的阈值,则升级为重量级锁。 需要注意的是,在第二个线程获取锁时,会先判断第一个线程是否仍然存活,如果不存活,不会升级为轻量级锁。 AQS(一个用来构建锁和同步器的框架) # 结构 用 volatile 修饰的整数类型的 state 状态(持有锁的次数),用于表示同步状态,提供 getState 和 setState 来操作同步状态; 提供了一个 FIFO 等待队列,实现线程间的竞争和等待,这是 AQS 的核心; AQS 内部提供了各种基于 CAS 原子操作方法,如 compareAndSetState 方法,并且提供了锁操作的acquire和release方法。 独占锁模式 用 state 值表示锁并且 0 表示无锁状态,0 -\u0026gt; 1 表示从无锁到有锁, 仅允许一条线程持有锁,其余的线程会被包装成一个 Node 节点放到队列中进行挂起 队列中的头节点表示当前正在执行的线程,当头节点释放后会唤醒后继节点 共享锁模式 当有一个线程获取到锁之后,那么它就会依次唤醒等待队列中可以跟它共享的节点 ReentrantLock # 基于 AQS (AbstractQueuedSynchronizer)实现,主要有 state (资源) + FIFO (线程等待队列) 组成。\n 公平锁与非公平锁:区别在于在获取锁时,公平锁会判断当前队列是否有正在等待的线程,如果有则进行排队。\n 使用 lock() 和 unLock() 方法来加锁解锁。\n 非公平锁吞吐量高\n在获取锁的阶段来分析,当某一线程要获取锁时,非公平锁可以直接尝试获取锁,而不是判断当前队列中是否有线程在等待。一定情况下可以避免线程频繁的上下文切换,这样,活跃的线程有可能获得锁,而在队列中的锁还要进行唤醒才能继续尝试获取锁,而且线程的执行顺序一般来说不影响程序的运行。\n 线程池 # 分类\n FixThreadPool 固定数量的线程池,适用于对线程管理,高负载的系统 SingleThreadPool 只有一个线程的线程池,适用于保证任务顺序执行 CacheThreadPool 创建一个不限制线程数量的线程池,适用于执行短期异步任务的小程序,低负载系统 ScheduledThreadPool 定时任务使用的线程池,适用于定时任务 重要参数\n int corePoolSize, 核心线程数 int maximumPoolSize, 最大线程数 long keepAliveTime, TimeUnit unit, 超过 corePoolSize 的线程的存活时长,超过这个时间,多余的线程会被回收。 BlockingQueue\u0026lt;Runnable\u0026gt; workQueue, 任务的排队队列 ThreadFactory threadFactory, 新线程的产生方式 RejectedExecutionHandler handler) 拒绝策略 线程池线程工作过程\ncorePoolSize -\u0026gt; 任务队列 -\u0026gt; maximumPoolSize -\u0026gt; 拒绝策略\n 核心线程在线程池中一直存活,当有任务需要执行时,直接使用核心线程执行任务。当任务数量大于核心线程数时,加入等待队列。当任务队列数量达到队列最大长度时,继续创建线程,最多达到最大线程数。当设置回收时间时,核心线程以外的空闲线程会被回收。如果达到了最大线程数还不能够满足任务执行需求,则根据拒绝策略做拒绝处理。\n 反射 # 反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法\n常用方法 # 获取反射中的Class对象Class clz 使用 Class.forName 静态方法Class.forName(\u0026quot;java.lang.String\u0026quot;) 使用 .class 方法String.class 使用类对象的 getClass() 方法new String(\u0026quot;Hello\u0026quot;).getClass() 通过反射创建类对象(通过 Constructor 对象创建类对象可以选择特定构造方法,通过 Class 对象则只能使用默认的无参数构造方法) 通过 Class 对象的 newInstance() 方法(Apple)clz.newInstance() 通过 Constructor 对象的 newInstance() 方法Constructor constructor = clz.getConstructor(); Apple apple = (Apple)constructor.newInstance(); 通过反射获取类属性、方法、构造器 Class 对象的 getFields() 方法可以获取 Class 类的属性(无法获取私有属性) Class 对象的 getDeclaredFields() 方法则可以获取包括私有属性在内的所有属性 注解 # 注解的作用 # 由编译器使用的注解:不会被编译进入.class文件,在编译后就被编译器扔掉(@Override、@SuppressWarnings) 由工具处理.class文件使用的注解:会被编译进入.class文件,但加载结束后并不会存在于内存中 在程序运行期能够读取的注解:在加载后一直存在于JVM中 配置参数 # 可以有默认值;大部分注解会有一个名为 value 的配置参数,可以省略value参数\n 所有基本类型; String; 枚举类型; 基本类型、String、Class以及枚举的数组 定义方式 # 使用@interface语法来定义注解 用default设定一个默认值(强烈推荐) 元注解 # 修饰其他注解的注解 Java标准库定义 常用 @Target:定义Annotation能够被应用于源码的哪些位置(数组) 类或接口:ElementType.TYPE; 字段:ElementType.FIELD; 方法:ElementType.METHOD; 构造方法:ElementType.CONSTRUCTOR; 方法参数:ElementType.PARAMETER @Retention:定义Annotation的生命周期(默认值CLASS) RetentionPolicy.SOURCE:在编译期就被丢掉(由编译器使用) RetentionPolicy.CLASS:仅保存在class文件中,它们不会被加载进JVM(主要由底层工具库使用) RetentionPolicy.RUNTIME`:会被加载进JVM,并且在运行期可以被程序读取 @Repeatabl:定义Annotation是否可重复 @Inherited:定义子类是否可继承父类定义的Annotation 使用方式 # 注解定义后也是一种 class ,所有的注解都继承自 java.lang.annotation.Annotation 。读取注解,需要使用反射API\n Class.isAnnotationPresent(Class) Field.isAnnotationPresent(Class) Method.isAnnotationPresent(Class) Constructor.isAnnotationPresent(Class) "},{"id":6,"href":"/interview/k8s/","title":"K8s","section":"Interviews","content":"k8s # 核心组件 # 主节点(Master):kube-controller-manager,kube-apiserver,kube-scheduler 工作节点(Node):kubelet和kube-proxy 只有apiserver使用etcd k8s的架构和组件是哪些,有哪些资源(api)类型? # Kubernetes主要由以下几个核心组件组成:\n etcd保存了整个集群的状态; apiserver提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制; controller manager负责维护集群的状态,比如故障检测、自动扩展、滚动更新等; scheduler负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上; kubelet负责维护容器的生命周期,同时也负责Volume(CVI)和网络(CNI)的管理; Container runtime负责镜像管理以及Pod和容器的真正运行(CRI); kube-proxy负责为Service提供cluster内部的服务发现和负载均衡; 除了核心组件,还有一些推荐的Add-ons:\n kube-dns负责为整个集群提供DNS服务 Ingress Controller为服务提供外网入口 Heapster提供资源监控 Dashboard提供GUI Federation提供跨可用区的集群 Fluentd-elasticsearch提供集群日志采集、存储与查询 资源类型:\n 类别 名称 资源对象 Pod、ReplicaSet、ReplicationController、Deployment、StatefulSet、DaemonSet、Job、CronJob、HorizontalPodAutoscaling、Node、Namespace、Service、Ingress、Label、CustomResourceDefinition 存储对象 Volume、PersistentVolume、Secret、ConfigMap 策略对象 SecurityContext、ResourceQuota、LimitRange 身份对象 ServiceAccount、Role、ClusterRole 哪些组件和etcd打交道? # etcd 保存了整个集群的状态,所以和操作资源相关的都要读写 etcd\n 注意:只有apiserver使用etcd,其他组件都是通过apiserver和etcd打交道\n apiserver 提供了资源操作的唯一入口,并提供认证、授权、访问控制、API注册和发现等机制; controller manager 负责维护集群的状态,比如故障检测、自动扩展、滚动更新等; scheduler 负责资源的调度,按照预定的调度策略将Pod调度到相应的机器上; pod里面的pause容器的作用? # k8s用pause容器来作为一个pod中所有容器的父容器。这个pause容器有两个核心的功能,第一,它提供整个pod的Linux命名空间的基础。第二,启用PID命名空间,它在每个pod中都作为PID为1进程,并回收僵尸进程。\n滚动升级 # Deployment已经内置了RollingUpdate strategy 升级的过程是先创建新版的pod将流量导入到新pod上后销毁原来的旧的pod StatefulSet 默认方式 从序号最大的 Pod 开始,逐个删除和更新每一个 Pod,直到序号最小的 Pod 被更新 当正在更新的 Pod 达到了 Running 和 Ready 的状态之后,才继续更新其前序 Pod 指定 .spec.updateStrategy.rollingUpdate.partition 字段,可以分片(partitioned)执行RollingUpdate 更新策略 序号大于或等于 .spec.updateStrategy.rollingUpdate.partition 的 Pod 将被删除重建 序号小于 .spec.updateStrategy.rollingUpdate.partition 的 Pod 将不会更新,及时手工删除该 Pod,kubernetes 也会使用前一个版本的 .spec.template 重建该 Pod 如果 .spec.updateStrategy.rollingUpdate.partition 大于 .spec.replicas,更新 .spec.tempalte 将不会影响到任何 Pod partition适用场景 预发布 金丝雀更新 按阶段的更新 亲和、反亲和 # 亲和性:应用A与应用B两个应用频繁交互,所以有必要利用亲和性让两个应用的尽可能的靠近,甚至在一个node上,以减少因网络通信而带来的性能损耗。 反亲和性:当应用的采用多副本部署时,有必要采用反亲和性让各个应用实例打散分布在各个node上,以提高HA。 node亲和性可以约束调度器基于node labels调度pod requiredDuringSchedulingIgnoredDuringExecution:严格执行,满足规则调度,否则不调度 preferredDuringSchedulingIgnoredDuringExecution:尽力执行,优先满足规则调度 "},{"id":7,"href":"/interview/linux/","title":"Linux","section":"Interviews","content":"Linux # VIM 三个模式 # 一般指令模式(默认模式) 编辑模式 指令列模式 文件属性 # 用户分为三种 文件拥有者 群组 其它人 文件类型 d:目录 -:文件 l:链接文件 文件权限: 三位一组 对文件拥有者、所属群组以及其它人的文件权限 3 位分别为 r、w、x 权限,表示可读、可写、可执行 文件时间 modification time (mtime):文件的内容更新就会更新; status time (ctime):文件的状态(权限、属性)更新就会更新; access time (atime):读取文件时就会更新。 修改权限 chmod 左到右每个位的权值为 4、2、1 默认权限 文件默认权限666 目录默认权限777 链接 # 实体链接:在目录下创建一个条目,记录着文件名与 inode 编号,这个 inode 就是源文件的 inode。 符号链接:文件保存着源文件所在的绝对路径,在读取时会定位到源文件上,可以理解为 Windows 的快捷方式 ln创建链接:默认是实体链接,加 -s 为符号链接 获取文件内容 # cat、tac(从最后一行打印) more(一页一页打印)、less(增加向前翻页) head(前n行)、tail(后几行) 数据流重定向 # 一个箭头的表示以覆盖的方式重定向,而有两个箭头的表示以追加的方式重定向 输出重定向到 /dev/null(扔进垃圾箱) 排序指令(sort) # $ sort [-fbMnrtuk] [file or stdin] -f :忽略大小写 -b :忽略最前面的空格 -M :以月份的名字来排序,例如 JAN,DEC -n :使用数字 -r :反向排序 -u :相当于 unique,重复的内容只出现一次 -t :分隔符,默认为 tab -k :指定排序的区间 $ uniq [-ic](可以将重复的数据只取一个) -i :忽略大小写 -c :进行计数 正则表达式(grep) # $ grep [-acinv] [--color=auto] 搜寻字符串 filename -c : 统计匹配到行的个数 -i : 忽略大小写 -n : 输出行号 -v : 反向选择,也就是显示出没有 搜寻字符串 内容的那一行 --color=auto :找到的关键字加颜色显示 查看进程 # ps -l 查看自己的进程 ps aux 查看系统所有进程 pstree 查看进程数 查看子进程的命令 # 这里的 722 是指进程 pid\npstree -p 722 查看线程号 # 这里的 722 是指进程 pid\nps -T -p 722 查看线程cpu使用率 # top -H -p 722 查询linux文件被哪些pid读写 # lsof abc.txt 显示开启文件abc.txt的进程 lsof -i :22 知道22端口现在运行什么程序 lsof -c nsd 显示nsd进程现在打开的文件 lsof -g gid 显示归属gid的进程情况 lsof +d /usr/local/ 显示目录下被进程开启的文件 lsof +D /usr/local/ 同上,但是会搜索目录下的目录,时间较长 lsof -d 4 显示使用fd为4的进程 lsof -i [i] 用以显示符合条件的进程情况 获取URL中协议、域名、端口和Path # #获取协议 echo \u0026#34;http://www.baidu.com:80/ABCD/a.txt\u0026#34; | awk -F\u0026#39;:\u0026#39; \u0026#39;{print $1}\u0026#39; # 输出http #获取域名 echo \u0026#34;http://www.baidu.com:80/ABCD/a.txt\u0026#34; | awk -F\u0026#39;[/:]\u0026#39; \u0026#39;{print $4}\u0026#39; # 输出:www.baidu.com #获取端口 echo \u0026#34;http://www.baidu.com:80/ABCD/a.txt\u0026#34; | awk -F\u0026#39;[/:]\u0026#39; \u0026#39;{print $5}\u0026#39; # 输出 80 #获取Path echo \u0026#34;http://www.baidu.com:80/ABCD/a.txt\u0026#34; | cut -d/ -f4- # 输出 ABCD/a.txt 内存 # 内存使用情况buff和cache有什么区别? # 都是为了解决内存和IO设备的读写速度不对等的中间缓存,两个都是内存的一部分 cached 把读取过的数据保存起来,重新读取的时候命中就不用读磁盘了,如果没有命中就会按频率更新cached buffers 把分散的写操作集中起来,缓存要输出到io设备的数据(写一次就存一下硬盘贼耗时间,都是缓冲一会再一起写硬盘) 拓展一个shared,是共享内存,可以ipcs来查看 虚拟地址和物理地址有什么区别,程序编译运行后首先申请到的是什么地址? # 因为位数代表最大寻址能力,32位最大寻址能力是4G所以超过4g的内存条会造成浪费\n我们知道线程是cpu调度的最小单元,进程是资源分配的最小单元,每个进程之间的资源是独立的,互不影响的,这是怎么实现的呢?\n每个进程启动的时候会有独立内存空间,称为虚拟内存,启动时为给每个进程维护一个独立的页表做虚拟内存和物理内存的映射\n所以不同进程之间的虚拟内存地址可能是相同的,这没关系,最终映射到的是物理内存不是一个\n假如不同进程都访问某个系统的库,就不需要加载两遍到物理内存上,只要映射到同一地址范围就可以\n用到了再分配这种机制叫内存的惰性加载。\n虚拟内存寻址是cpu到一个叫mmu的硬件,物理内存寻址是mmu到内存条,mmu相当于是个外包\n所以虚拟内存虽然大,不一定全部都存在映射,之前说的堆栈空间也是在虚拟内存中的\n运维 # 怎么定位进程cpu占用大是哪一个函数导致的? # perf top -g -p 246 这里推荐\n 什么是程序的堆空间和栈空间? # 回答者:海翔\n栈是用来保证程序顺序执行的,后入栈的函数先出,完整记录一个函数(方法)调用从开始到结束所做的一切操作。\n堆是用来保存变量和对象的,存储临时数据和部分运行时数据。包括函数调用期间产生的临时变量,程序加载启动时载入的全局变量等等。堆内存的分配,应该是在临时变量第一次被使用时分配,全局静态变量是在类加载时分配。不同的变量有不同的生命周期。而垃圾回收,主要也是针对堆内存空间的调整和释放\n栈和堆都有其空间大小。\n当递归层级过深时会出现栈溢出异常,就是因为要保存的方法栈超过了栈可保存的最大数量。而堆内存不足时常常会遇到OOM异常,堆内存不足以存放新生成的对象或变量了。\n日志归档和清空有哪些方式 # 归档:logrotate 支持归档和删除长期日志\n代码级别可以使用滚动日志组件\n如果是手动删除可以使用cat /dev/null \u0026gt; xxx.log\n如果linux系统卡死,操作系统比较慢,排查思路? # 先使用top实时查看一下进程占cpu以及内存的情况,因为top是实时刷新的,比较直观 有时候刷新太快,进一步使用ps -aux可以查看哪些进程占cpu或者内存较高,如果需要急切恢复,可以kill -9杀进程来恢复。 有可能此进程只是一个子进程,那要使用ps -ef | grep 进程ID查找父进程 看哪些日志\n messages日志,/var/log目录下,比较全,关键字failed、error、false,查看开机时间Runtime journal 服务日志 dmesg日志,该日志是记录系统最近一次开机时加载的驱动信息,以及开机后键鼠等硬件的一些响应信息 如果是图形化服务器,查看Xorg.0.log日志,该日志是记录的图形化服务相关的一些日志信息,看一下图形化服务方面有什么异常没 .xsession-errors日志,root用户在/root下,普通用户在/home/username/下),是隐藏文件,该日志信息是某用户环境下的图形化日志信息 lightdm.log日志,该日志是和登录界面相关的一些日志信息,如果系统卡死的时候是在用户登录界面的时候卡死了,这个日志就有必要也看一眼 最后有可能是硬件问题 发散问题 # Linux 打开文件句柄写入一个文件时,mv这个文件会发生什么 # mv操作,目标文件的inode将等于源文件的inode;因此正在写入的文件被mv,数据仍然被写入到mv后的文件里,除非重新open\n正在写入的文件被rm后,数据会被写入到系统缓存中,一直会耗尽所有可用的内存\n最后 # 如果文中有误,欢迎提pr或者issue,一旦合并或采纳作为贡献奖励可以联系我直接无门槛加入 技术交流群\n我是小熊,关注我,知道更多不知道的技术\n "},{"id":8,"href":"/interview/mongodb/","title":"Mongodb","section":"Interviews","content":"TODO-mongoDB # 请移步 面试高频问题-待解答\n"},{"id":9,"href":"/interview/mysql/","title":"Mysql","section":"Interviews","content":"Mysql # 数据类型 # 整型:TINYINT, SMALLINT, MEDIUMINT, INT, BIGINT 分别使用 8, 16, 24, 32, 64 位存储空间 浮点数:FLOAT 和 DOUBLE 为浮点类型,DECIMAL 为高精度小数类型 字符串: CHAR(定长) 和 VARCHAR(变长的) 时间日期:date、datetime、timestamp(比DATETIME 空间效率更高) 存储引擎:myisam和innodb的区别是什么 # 答者:狸追\n innodb 支持事务和外键,最小锁粒度是行级锁,myisam 不支持事务和外键,最小锁粒度是表级锁,间歇锁 innodb 的索引如果是聚簇索引,叶子节点上保存的是数据和索引,非聚簇索引,节点上保存的是id,而myisam保存的是数据的地址(相当于一个指针) myisam 的表可以没有索引,innodb一定要有索引 myisam 会保存总行数,innodb是全表扫描 总结:对于大量更新、插入、删除,innodb性能上更好,因为他具备的事务、行级锁、B+树等特点,更安全,因为回滚和崩溃恢复更适合大型应用 经过测试在单进程读的情况下myisam执行速度比innodb更快,但是多进程读的时候就失去优势了 mysql5.5版本之后默认innodb 展开说\n MyISAM: 拥有较高的插入,查询速度 不支持事务 支持表级锁 不支持MVCC 不支持外键 支持全文索引 内部维护了一个计数器,selectcount更快 InnoDB :插入缓冲(insert buffer)、二次写(double write)、自适应哈希索引(ahi)、预读(read ahead) 5.5版本后Mysql的默认数据库 支持ACID事务 支持行级锁定 支持MVCC 支持外键 不支持全文索引 不建议使用过长的字段作为主键:因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。 不建议用非单调的字段作为主键:因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。 特殊的功能“自适应哈希索引”:当某个索引值被使用的非常频繁时,会在 B+Tree 索引之上再创建一个哈希索引(B+Tree 索引具有哈希索引的一些优点) 主从复制 # 主:binlog线程——记录下所有改变了数据库数据的语句,放进master上的binlog中 从:io线程——在使用start slave 之后,负责从master上拉取 binlog 内容,放进自己的relay log中 从:sql执行线程——执行relay log中的语句 索引类型 # 普通索引 唯一索引:索引列的值必须唯一,但允许有空值 主键索引:特殊的唯一索引,一个表只能有一个主键,不允许有空值 组合索引:在查询条件中使用了创建索引时的第一个字段,索引才会被使用。遵循最左前缀集合 全文索引:主要用来查找文本中的关键字(MATCH AGAINST) Mysql8新特性降序索引 聚集索引(主键索引)和非聚集索引 # 索引按照数据结构来说主要包含B+树和Hash索引。\n MyISAM 索引文件和数据文件是分离的,索引文件仅保存数据记录的地址 聚簇索引:innodb中,表数据文件本身就是按B+Tree组织的一个索引结构,这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB表数据文件本身就是主索引,也就是说节点只包含id索引,叶子节点同时保存索引和数据,这种数据和索引在一起的方式就是聚簇索引有主键使用主键,没有主键就用唯一非空索引代替,如果没有会隐式定义一个主键,一张表只能有一个聚簇索引 非聚集索引:innodb的非聚集索引的叶子节点上的data是主键。(为什么存放的主键,而不是记录所在地址呢,理由相当简单,因为记录所在地址并不能保证一定不会变,但主键可以保证) 索引数据结构 # B+Tree 索引 # MySQL 存储引擎的默认索引类型 BTREE 索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问 B-tree 索引可以用于使用 =, \u0026gt;, \u0026gt;=, \u0026lt;, \u0026lt;= 或者 BETWEEN 运算符的列比较 B+ Tree 的有序性,可以用于排序和分组 InnoDB 的 B+Tree 索引分为主索引和辅助索引 主索引的叶子节点 data 域记录着完整的数据记录,这种索引方式被称为聚簇索引(一个表只有一个:数据行不能存放个) 辅助索引的叶子节点的 data 域记录着主键的值。使用辅助索引进行查找,先查找主键值,再到主索引中进行查找 MyISAM的 B+Tree 索引分为主索引和辅助索引 和InnoDB不同,data域保存数据记录的地址 主索引和辅助索引在结构上没有任何区别,只是主索引要求key是唯一的,而辅助索引的key可以重复 Hash 索引 # 检索效率非常高,索引的检索可以一次定位 仅仅能满足\u0026quot;=\u0026quot;,\u0026ldquo;IN\u0026quot;和\u0026rdquo;\u0026lt;=\u0026gt;\u0026ldquo;查询,不能使用范围查询。 无法用于排序与分组 Hash 索引不能利用部分索引键查询。 Hash 索引在任何时候都不能避免表扫描。 Hash 索引遇到大量Hash值相等的情况后性能并不一定就会比B-Tree索引高。 索引优化 # 独立的列:索引列不能是表达式的一部分,也不能是函数的参数 多列索引:在需要使用多个列作为条件进行查询时,使用多列索引比使用多个单列索引性能更好 索引列的顺序:让选择性(不重复的索引值和记录总数的比值)最强的索引列放在前面 前缀索引:对于 BLOB、TEXT 和 VARCHAR 类型的列,必须使用前缀索引,只索引开始的部分字符 覆盖索引:索引包含所有需要查询的字段的值 索引高度有多高 # 事务的实现 # 事务的原子性是通过 undo log(回滚日志) 来实现的 事务的持久性是通过 redo log(重做日志) 来实现的 事务的隔离性是通过 (读写锁+MVCC)来实现的 事务的一致性是通过原子性,持久性,隔离性来实现的 AUTOCOMMIT # MySQL 默认采用自动提交模式。也就是说,如果不显式使用START TRANSACTION语句来开始一个事务,那么每个查询操作都会被当做一个事务并自动提交。\nmysql锁技术 # 读写锁 共享锁(shared lock),又叫做\u0026quot;读锁 排他锁(exclusive lock),又叫做\u0026quot;写锁\u0026rdquo; MVCC基础 # MVCC (MultiVersion Concurrency Control) 叫做多版本并发控制\nInnoDB的 MVCC ,是通过在每行记录的后面保存两个隐藏的列来实现的。这两个列, 一个保存了行的创建时间,一个保存了行的过期时间, 当然存储的并不是实际的时间值,而是系统版本号。 通过数据多版本来做到读写分离。从而实现不加锁读进而做到读写并行\n并发一致性问题 # 丢失修改:一个事务的更新操作被另外一个事务的更新操作替换 脏读:在不同的事务下,当前事务可以读到另外事务未提交的数据 不可重复读:一个事务内多次读取同一数据集合 幻读:针对插入语句,一个update,一个insert,update之后发现有未更新数据 事务的隔离级别(级别由低到高) # READ UNCOMMITED (未提交读) :事务中的修改,即使没有提交,对其它事务也是可见的(读的过程中写,脏读,读写并行) READ COMMITED (提交读):一个事务只能读取已经提交的事务所做的修改(排它锁,读写分离机制,产生不可重读以及幻读问题) REPEATABLE READ (可重复读)(默认级别):保证在同一个事务中多次读取同一数据的结果是一样的(读写锁实现orMVCC实现) SERIALIZABLE (串行) 查询性能优化 # 使用 Explain分析SELECT语句 select_type : 查询类型,有简单查询、联合查询、子查询等 key : 使用的索引 rows : 扫描的行数 优化数据访问 减少请求的数据量 只返回必要的列:最好不要使用 SELECT * 语句。 只返回必要的行:使用 LIMIT 语句来限制返回的数据。 缓存重复查询的数据 减少服务器端扫描的行数:索引 重构查询方式 切分大查询 分解大连接查询:将一个大连接查询分解成对每一个表进行一次单表查询,然后在应用程序中进行关联 高并发架构 # 关键字:分布式、高可用、集群、负载均衡、正/反代理\n演进 # 应用与数据库分离:增加服务器资源,提高性能 本地缓存和分布式缓存:使用memcached作为本地缓存,使用Redis作为分布式缓存,减小数据库的压力 反向代理(Nginx)实现负载均衡 数据库的读写分离 数据分库(按业务) 把大表拆分为小表 使用LVS或F5来使多个Nginx负载均衡(四层的负载均衡解决方案) 通过DNS轮询实现机房间的负载均衡 引入NoSQL数据库和搜索引擎等技术 大应用拆分为小应用 复用的功能抽离成微服务 引入企业服务总线ESB屏蔽服务接口的访问差异 引入容器化技术实现运行环境隔离与动态服务管理 以云平台承载系统 数据库分库分表 # 为什么要分库分表? # 数据量达到几千万时查询时间变多,无论表级锁还是行级锁,联合查询大概率阻塞严重(索引膨胀、查询超时) 对于大表要进行表结构DDL几乎不可能 从Innodb本身来讲数据文件的Btree上只有两个锁, 叶子节点锁和子节点锁, 可以想而知道, 当发生页拆分或是添加新叶时都会造成表里不能写入数据 方式 # 垂直分表(大表拆小表):将不经常使用或者长度较大的字段拆分出去放到“扩展表”中(设计阶段考虑) 垂直分库:按照业务模块来划分出不同的数据库 水平分表(横向分表):降低单表数据量,优化查询性能(主键hash或取模) 水平分库分表:水平分表+分库 多久分一个表 # 大数据量并且访问频繁的表,应该拆分成多个表 经测试在单表1000万条记录以下,写入读取性能是比较好的,再留点buffer, 那么单表全是数据字型的保持在800万条记录以下, 有字符型的单表保持在500万以下。 最好是提前规划预估表数据量,方便做分页查询,减少数据清洗 多久分一个库? # 单台数据库服务器存储空间、cpu、内存、网络io等因素无法支撑,做水平拆分(分库) 用什么做分库分表? # 使用mycat做分库分表,现在金融级别的分布式数据库可以考虑使用tidb,对水平伸缩和数据一致性有保证\n分表策略(水平拆分) # 查询切分(不推荐),把ID和库的Mapping关系记录在一个单独的库中,又会引入新的单点压力问题 范围切分,按照id范围切分,单表大小可控,但集中写入会频繁操作单表 Hash切分,易于水平扩展, 例如:mod 32, 拆分32个库,每个库 div 32 mod 32 拆分32张表,就是32*32=1024张表,更多参考(32*2^n)*(32⁄2^n) 其他业务相关:地理位置、时间 引用 大众点评订单系统分库分表实践 - 美团技术团队\n产生的问题和解决思路 # 垂直分库-\u0026gt;跨库Join 全局表 字段冗余(数据一致性的问题) 数据同步 系统层组装(把不同的表放在不同的库,不同的库放在不同服务器上,但是联合查询无法从数据库层面做到) 水平分库 分布式全局唯一ID 分片规则 随机分片和连续分片 数据迁移,容量规划,扩容等问题 跨分片的排序分页(分别排序之后汇总再排序) 跨分片的函数处理 夸分片join 怎么跨表查询,还有排序和翻页怎么做的? # issue\n分库分表有什么缺点?怎么解决? # 联合查询困难,关联的表可能不在同一数据库中 避免在同一个事务中修改不同库中的表,操作复杂,效率也会有影响 尽量把同一数据放在同一db服务器上,单点故障时不会影响其他数据 select count(*) conunt(1)和count(字段)执行的效率有何不同? # count(primary key)。遍历整个表,把主键值拿出来,累加; count(1)。遍历整个表,但是不取值,累加; count(非空字段)。遍历整个表,读出这个字段,累加; count(可以为空的字段)。遍历整个表,读出这个字段,判断不为null累加; count(*)。遍历整个表,做了优化,不取值,累加。 来自 胡慢慢滚雪球\n什么是乐观锁和悲观锁 # 通常mysql中使用的都是悲观锁,分为共享锁和排他锁两种,共享锁也就是读锁,可以多个线程同时读,不能修改;排他锁是写锁,未获得锁的线程需要阻塞\n乐观锁:假设数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检测,如果发现冲突了,则返回给用户错误的信息,让用户决定如何去做。乐观锁适用于读操作多的场景,这样可以提高程序的吞吐量。一般有两种实现方式CAS和基于版本号\n引用: 什么是乐观锁,什么是悲观锁\nCAS与ABA问题 # CAS是乐观锁的实现方式之一,CAS操作包含三个操作数—— 内存位置的值(V)、预期原值(A)和新值(B),在更新的时候做检查,内存值必须与期望值相同。举个例子,内存值V、期望值A、更新值B,当V == A的时候将V更新为B。\nABA问题,某个值中间被改动又被改回来,A-B-A,用CAS是无法识别的,考虑通过控制变量值的版本号来保证CAS的正确性。具体解决思路就是在变量前追加上版本号,每次变量更新的时候把版本号加一,那么A - B - A就会变成1A - 2B - 3A。\n引用: 深入理解CAS\n最后 # 如果文中有误,欢迎提pr或者issue,一旦合并或采纳作为贡献奖励可以联系我直接无门槛加入 技术交流群\n我是小熊,关注我,知道更多不知道的技术\n "},{"id":10,"href":"/interview/network/","title":"Network","section":"Interviews","content":"网络 # 计算机网络网络 # IP协议:一种分组交换传输协议; TCP协议:一种面向连接,可靠传输的协议; UDP协议:一种无连接,不可靠传输的协议。 操作系统抽象出Socket接口,每个应用程序需要各自对应到不同的Socket,数据包才能根据Socket正确地发到对应的应用程序 一个Socket由IP地址和端口号 小于1024的端口属于_特权端口_,需要管理员权限 使用Socket进行网络编程时,本质上就是两个进程之间的网络通信 服务器端的Socket是指定的IP地址和指定的端口号;客户端的Socket是它所在计算机的IP地址和一个由操作系统分配的随机端口号 三次握手 TCP客户端最后还要发送一次确认的原因:主要防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误 四次挥手 A 发送连接释放报文,FIN=1。 B 收到之后发出确认,此时 TCP 属于半关闭状态,B 能向 A 发送数据但是 A 不能向 B 发送数据。 当 B 不再需要连接时,发送连接释放报文,FIN=1。 A 收到后发出确认,进入 TIME-WAIT 状态,等待 2 MSL(最大报文存活时间)后释放连接。 B 收到 A 的确认后释放连接。 underlay就是底层承载网,overlay就是基于底层网络互联互通的基础加上隧道技术去构建一个虚拟的网络。overlay的核心其实就是打隧道(tunnel) http 中的 4层与7层分别代表什么,中间那三层是为了满足什么样的需求呢? # 提问:小雨\n答者:海翔\n 一个ISO7层模型,一个是web四层模型,不是一个东西 7层模型分物理层,数据链路层,网络层,传输层,会话层,表示层,应用层 4层模型是TCP/IP协议的定义,数据链路层对应7层模型的物理层+数据链路层,网络层对应7层模型的网络层,传输层对应7层模型的传输层,应用层对应7层模型的会话层+表示层+应用层 TCP # TCP 三次握手、四次挥手(重点) # 三次握手:1. 客户端主动,服务端确认客户端发送,服务端接收正常 2 服务端发包,客户端确认全部正常;3. 客户端发包,服务端确认客户端收包,服务端发包正常\n四次挥手:某一端发出请求,a1发出终止信号,a2返回确认收到,a2发送终止信号,a1发送确认收到\n为什么要有三次握手、四次挥手, 为什么一定要是三次和四次 # 因为要保证通信的可靠性,握手是三次,保证两端都确认自己和对方的收发包能力,挥手是确认两端都发包结束,可以断开\nTCP的优点和缺点 # 优点:可靠、稳定性\n缺点:\n 占用系统资源高:建立连接需要耗时, 传递数据时,确认、重传、拥塞 易被攻击 :因为有确认机制,三次握手等机制,容易被人利用,实现DOS 、DDOS攻击 挥手的过程中有timeout的状态,是什么情况 # 超时重传:分为仅重传timeout的包或重传timeout后所有的数据,但都要等待timeout 快速重传:数据驱动,发送端每次发包会收到ack 包,如果数据包没有连续到达,就ack最后那个可能被丢了的包,如果发送端连续收到3次相同的ack就重传。缺点是不知道要重传哪些包 SACK方法:TCP头里加一个SACK的东西,可以精确的知道哪些包到了哪些没到,但比较耗费资源,因为可能要遍历已经传过的资源 Duplicate-SACK方法: HTTP # URL # HTTP 使用 URL( U niform Resource Locator,统一资源定位符)来定位资源,它是 URI(Uniform Resource Identifier,统一资源标识符)的子集,URL 在 URI 的基础上增加了定位能力。 HTTP状态码 # 短连接与长连接 # 当浏览器访问一个包含多张图片的 HTML 页面时,除了请求访问的 HTML 页面资源,还会请求图片资源。如果每进行一次 HTTP 通信就要新建一个 TCP 连接,那么开销会很大。\n长连接只需要建立一次 TCP 连接就能进行多次 HTTP 通信。\n 从 HTTP/1.1 开始默认是长连接的,如果要断开连接,需要由客户端或者服务器端提出断开,使用 Connection : close; 在 HTTP/1.1 之前默认是短连接的,如果需要使用长连接,则使用 Connection : Keep-Alive。 Cookie # Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器之后向同一服务器再次发起请求时被携带上,用于告知服务端两个请求是否来自同一浏览器。由于之后每次请求都会需要携带 Cookie 数据,因此会带来额外的性能开销(尤其是在移动环境下)\n用途 # 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息) 个性化设置(如用户自定义设置、主题等) 浏览器行为跟踪(如跟踪分析用户行为等) 创建过程 # 服务器发送的响应报文包含 Set-Cookie 首部字段,客户端得到响应报文后把 Cookie 内容保存到浏览器中。 客户端之后对同一个服务器发送请求时,会从浏览器中取出 Cookie 信息并通过 Cookie 请求首部字段发送给服务器。 分类 # 会话期 Cookie:浏览器关闭之后它会被自动删除,也就是说它仅在会话期内有效。 持久性 Cookie:指定过期时间(Expires)或有效期(max-age)之后就成为了持久性的 Cookie。 Session # Session 可以存储在服务器上的文件、数据库或者内存中。也可以将 Session 存储在 Redis 这种内存型数据库中,效率会更高。\n使用 Session 维护用户登录状态的过程如下:\n 用户进行登录时,用户提交包含用户名和密码的表单,放入 HTTP 请求报文中; 服务器验证该用户名和密码,如果正确则把用户信息存储到 Redis 中,它在 Redis 中的 Key 称为 Session ID; 服务器返回的响应报文的 Set-Cookie 首部字段包含了这个 Session ID,客户端收到响应报文之后将该 Cookie 值存入浏览器中; 客户端之后对同一个服务器进行请求时会包含该 Cookie 值,服务器收到之后提取出 Session ID,从 Redis 中取出用户信息,继续之前的业务操作。 HTTPS # HTTP 有以下安全性问题:\n 使用明文进行通信,内容可能会被窃听; 不验证通信方的身份,通信方的身份有可能遭遇伪装; 无法证明报文的完整性,报文有可能遭篡改。 HTTPS 并不是新协议,而是让 HTTP 先和 SSL(Secure Sockets Layer)通信,再由 SSL 和 TCP 通信,也就是说 HTTPS 使用了隧道进行通信。\n通过使用 SSL,HTTPS 具有了加密(防窃听)、认证(防伪装)和完整性保护(防篡改)。\n HTTPS 的缺点 # 因为需要进行加密解密等过程,因此速度会更慢; 需要支付证书授权的高额费用。 "},{"id":11,"href":"/interview/qian-duan/","title":"Qian Duan","section":"Interviews","content":"TODO-前端 # 请移步 面试高频问题-待解答\n"},{"id":12,"href":"/interview/queue/","title":"Queue","section":"Interviews","content":"消息队列 # 如何保证队列数据一致性,防止重复消费,幂等性 # 所有的消息中间件在实际的业务场景中都逃脱不了保证消息的一致性的问题\nkafka实际上有个offset的概念,就是每个消息写进去,都有一个offset,可以理解成一个自增持的序号,一个个排下来\n然后消费者consumer,每隔一段时间,就把自己消费过的消息提交一下,如果说出现宕机或者重启,则会继续从上次消费的序号接着往下排,继续消费\n但是有的时候,消费者consumer消费的消息,由于各种原因,比如网络、宕机、停电。。。都没来得及写offset,这个时候少数消息会再次消费一次\n这个时候,我们可以用一个唯一的id标识来区分,这不是消息中间件做的事,而是开发者要做的,比如你消费一个就往数据库插入一条记录,然后下次再去消费的时候,你去查一下,看看这个消息是否被消费了,消费了那就不要重复消费了。\n(补充一下:确认一条数据在百万级别海量数据里是否存在?\u0026ndash;可以用布隆过滤器)\n 根据主键查一下,如果这数据都有了,就别插入了,update一下(虽然重复插入会因为唯一键约束而报错,我觉得我们还是应该避免报错) 如果是写redis,反正每次都是set,天然幂等性 如果不是上面两个场景,那做的稍微复杂一点,你需要让生产者发送每条数据的时候,里面加一个全局唯一的id,类似订单id之类的东西,然后你这里消费到了之后,先根据这个id去比如redis里查一下,之前消费过吗?如果没有消费过,你就处理,然后这个id写redis。如果消费过了,就别处理了,保证别重复处理相同的消息即可。 引用:\n rabbitMq-kafka消息高可用,一致性 如何保证消息队列的高可用和幂等性以及数据丢失,顺序一致性 如何预防rabbitmq消息的丢失 # 情况1、生产者自己丢失了消息(网络故障/发送失败)\n解决方案:\nrabbitmq:一般这种都是采用回调接口的方案(confirm模式),就是说你扔一个消息过去了,对方给你一个回调接口,告诉你成功了或者失败了,失败了你可以选择继续扔消息, (重试机制等),来保证消息一定送达\n开启confirm模式(异步的)之后,你每次写的消息都会分配一个唯一的id,然后如果写入了rabbitmq中,rabbitmq会给你回传一个ack消息,告诉你说这个消息ok了。如果rabbitmq没能处理这个消息,会回调你一个nack接口,告诉你这个消息接收失败,你可以重试。而且你可以结合这个机制自己在内存里维护每个消息id的状态,如果超过一定时间还没接收到这个消息的回调,那么你可以重发。\n引用: 如何保证消息队列的高可用和幂等性以及数据丢失,顺序一致性\n情况2、消息中间件弄丢了消息\n解决方案:以rabbitmq来说,开启持久化就好了,当发生宕机的时候,queue会自动从磁盘恢复数据,除非极其罕见的是,rabbitmq还没持久化,自己就挂了,可能导致少量数据会丢失的,但是这个概率较小\n情况3、消费者弄丢了消息\nrabbitmq如果丢失了数据,主要是因为你消费的时候,刚消费到,还没处理,结果进程挂了,比如重启了,rabbitmq认为你都消费了,这数据就丢了。\n关闭rabbitmq自动ack机制,可以通过一个api来调用,每次代码里确保处理完的时候,再程序里ack。这样的话,如果还没处理完,就没有ack,那rabbitmq就认为你还没处理完,这个时候rabbitmq会把这个消费分配给别的consumer去处理,消息是不会丢的\n如何预防kafak消息丢失 # TODO\n如何处理消息积压 # 出现场景:消费能力被阻塞(消费者挂掉或者处理速度慢),生产者还不停的往队列里扔消息\n解决方法:\n 快速恢复consumer服务,慢慢消费,如果积压的数据量太大的话恢复较慢 临时写脚本快速的把这批消息给消费掉,或者增加消费者数量/消费速度,要避免负载把其他服务打挂 扩容:提高相同消息的队列数量,出现问题时写脚本分发到不同队列里,再给每个队列指定消费者,消费结束后再恢复 预防:\n 提前准备多个队列在投递时随机投递,存储同类型无顺序要求的消息 使用多个消费者 消息过期或者队列满了怎么办 # 消息队列TTL超时或者队列满了数据会丢失,这个时候可以自己再去找消息,然后临时写个代码,自己再手动的去把这些消息重新推送到队列里去。\n另一种解决方案\n 可以在 rabbitmq 中声明死信队列,死信队列为处理过期或不能正确路由的消息提供了驻留场所,可以防止消息丢失,便于分析无法消费的原因 写程序处理死信队列里的数据,并接入告警分析 如果投递不成功,需要把数据暂存内存或者暂存redis之类的数据库中,等待恢复时重试\n怎么实现一个延时队列? # rabbitmq 可以配置 延时队列插件\n消费者是无感知的,可以正常消费,生产者设置延迟时间,到了时间后队列里才会出现消息\n如果一定要手动实现,可以维护不同的队列指代不同的延迟时间,程序根据相应队列来保证最新消息的时间戳与当前时间延迟转发到实际的消费队列\n引用: 过期时间,死信队列,延迟队列,优先队列,持久化\n什么是优先队列,怎么实现 # 优先级高的消息先消费\n在rabbitmq中,手动开启设置队列支持的最大优先级(建议不要设置太大,会消耗资源),在投递消息的时间设置优先级数值,队列会自动排序,但是如果消费速度太快,没有任何数据积压的时候,不存在排序也不存在优先级的问题\n如何保证消息队列的顺序性 # 同一订单不同消息投递到不同位置,不同消费者消费了同一订单的不同消息,一般出现在\n 程序设计问题投递到不同队列 同一队列多个消费者并发消费,无法保证消费顺序 解决方法:\n 队列都是有顺序性保证的,在投递时,创建多个队列,hash投递,hash相同订单号投递同一个队列 消费时,保证同一个队列只允许一个消费者消费 多线程并发处理时,避免多进程,而是增加线程数,维护多个内存队列把消息归类 引用: 如何保证消息的顺序性\n"},{"id":13,"href":"/interview/redis/","title":"Redis","section":"Interviews","content":"Redis # 数据类型 # 键的类型只能为字符串 值支持五种数据类型: 字符串: set \u0026lt;key\u0026gt; \u0026lt;value\u0026gt; get \u0026lt;key\u0026gt; del \u0026lt;key\u0026gt; 列表 rpush \u0026lt;key\u0026gt; \u0026lt;item\u0026gt; lrange \u0026lt;key\u0026gt; i j(j可填-1) rindex \u0026lt;key\u0026gt; i lpop \u0026lt;key\u0026gt; 无序集合 sadd \u0026lt;key\u0026gt; \u0026lt;item\u0026gt; smembers \u0026lt;key\u0026gt; sismember \u0026lt;key\u0026gt; \u0026lt;item\u0026gt; srem \u0026lt;key\u0026gt; \u0026lt;item\u0026gt; 散列表 hset \u0026lt;key\u0026gt; \u0026lt;sub_key\u0026gt; \u0026lt;value\u0026gt; hgetall \u0026lt;key\u0026gt;(每条数据sub_key和value各占一行) hdel \u0026lt;key\u0026gt; \u0026lt;sub_key\u0026gt; 有序集合 zadd \u0026lt;key\u0026gt; \u0026lt;score\u0026gt; \u0026lt;item\u0026gt; zrange \u0026lt;key\u0026gt; i j withscores zrangebyscore \u0026lt;key\u0026gt; \u0026lt;score1\u0026gt; \u0026lt;score2\u0026gt; withscores zrem \u0026lt;key\u0026gt; \u0026lt;item\u0026gt; zset(sort list) 的数据结构是什么? # zset 有序且唯一,在跳表以空间换时间 以冗余的链表换取效率\n 各数据类型底层数据结构 # 字符串:int raw embstr 字典:hashtable(拉链法单链表)、ziplist 列表:ziplist(压缩链表)、linkedlist(双向链表) 集合:hashtable、inset 有序集合:ziplist和skiplist(跳表) 图片引用: Redis基础数据结构详解\n为什么要用跳表不用B+树的结构呢? # 答者:Shawn\nB+树的每个节点可以存储多个关键字,而Redis是 内存中读取数据,不涉及IO,因此使用了跳表\n使用场景 # 计数器:对 String 进行自增自减运算,从而实现计数器功能(Redis 这种内存型数据库的读写性能非常高) 缓存:将热点数据放到内存中,设置内存的最大使用量以及淘汰策略来保证缓存的命中率 查找表:查找表的内容不能失效(DNS) 消息队列或阻塞队列:List 是一个双向链表 会话缓存 分布式锁实现 SETNX RedLock 其他 SET可以实现交集、并集等操作 ZSet 可以实现有序性操作 redis事务 # Redis 事务可以一次执行多个命令,单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。\n事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。\n引用: Redis事务\nRedis 与 Memcached 对比 # 两者都是非关系型内存键值数据库 差异 数据类型: Memcached 仅支持字符串类型 Redis 支持五种不同的数据类型 数据持久化 Redis 支持两种持久化策略:RDB 快照和 AOF 日志 Memcached 不支持持久化 分布式 Memcached 不支持分布式,只能通过在客户端使用一致性哈希来实现分布式存储,这种方式在存储和查询时都需要先在客户端计算一次数据所在的节点。 Redis Cluster 实现了分布式的支持 内存管理机制 在 Redis 中,并不是所有数据都一直存储在内存中,可以将一些很久没用的 value 交换到磁盘,而 Memcached 的数据则会一直在内存中 Memcached 将内存分割成特定长度的块来存储数据,以完全解决内存碎片的问题。但是这种方式会使得内存的利用率不高,例如块的大小为 128 bytes,只存储 100 bytes 的数据,那么剩下的 28 bytes 就浪费掉了 数据淘汰策略 # lru和ttl,大量过期时会不会阻塞 # 不会,因为redis是闲时清理,可以设置最高占用cpu,清理是基于概率的,存在部分key总是无法清理的情况在,另外清理key的过程是不会fork子进程\nkey清理不干净会不会遇到什么业务上的问题,万一用到了会发生什么?通过什么办法解决? # 如果是lru的话,假如一个key值在以前都没有被访问到,然而最近一次被访问到了,那么就会认为它是热点数据,会更新ttl,不会被淘汰。\n优化的话就增大maxmemory-sample,增加每次lru数据的个数,淘汰起来更精确\n在redis\u0026gt;4.0版本,有LFU算法,访问不频繁的优先淘汰就好了\n另外redis有三种删除策略\n惰性删除,也就是在置换的时候删除\n定时删除,固定时间段执行删除操作\n定期删除,和定时删除一样,区别会时间期是根据业务来自动取的\n另外rdb和aof的持久化策略中,rdb读取时不会读取过期数据,aof有rewrite功能,执行行也不会存过期的策略\n太频繁的主动删除对cpu不友好,惰性删除对内存不友好,一旦插入大key,会出现cpu使用高峰\n缓存穿透与缓存雪崩 # 缓存穿透:用户想要查询一个数据,发现redis内存数据库没有,也就是缓存没有命中,于是向持久层数据库查询,数据库也没有 布隆过滤器:对所有可能查询的参数以hash形式存储,当用户想要查询的时候,使用布隆过滤器发现不在集合中,就直接丢弃,不再对持久层查询 缓存空对象:当存储层不命中后,即使返回的空对象也将其缓存起来,同时会设置一个过期时间,之后再访问这个数据将会从缓存中获取,保护了后端数据源 缓存雪崩:大量key同一时间点失效,同时又有大量请求打进来,导致流量直接打在DB上,造成DB不可用 设置key永不失效(热点数据); 设置key缓存失效时候尽可能错开; 使用多级缓存机制,比如同时使用redsi和memcache缓存,请求-\u0026gt;redis-\u0026gt;memcache-\u0026gt;db; redis高可用 缓存击穿:某个 key 非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库 可以将热点数据设置为永不过期 基于 redis or zookeeper 实现互斥锁,等待第一个请求构建完缓存之后,再释放锁,进而其它请求才能通过该 key 访问数据。 引用: Redis雪崩、穿透和击穿\nredis做缓存时,如何保证与mysql数据一致性 # 只要涉及到数据更新就会有一致性的问题,无论是先更新哪一端\n 采用延时双删策略,先删除缓存再删除数据库再更新缓存,弊端期间短暂不一致 订阅mysql的binlog,增删改增量更新,参考canal(阿里的一款开源框架),这种情况增删改都是操作MySQL,redis就变成只读了,需要加一些更细粒度的判断 对不敏感的数据做定时任务 引用: Redis和mysql数据怎么保持数据一致的\nredis主从同步,中途重连时如何识别同步点 # 从 redis 2.8 开始支持断点续传, master 会存储一个 backlog , master 和 slave 都会保存一个 replica offset 还有一个 master id , offset 就是保存在 backlog 中的, 如果 master 和 slave 网络连接断掉了, slave 会让 master 从上次的 replica offset 开始继续复制但是如果没有找到对应的 offset , 那么就会执行一次 resynchronization (重新同步).\n引用: redis主从复制原理, 断点续传, 无磁盘化复制, 过期key的处理\n分布式锁实现 # SETNX(set if not exists)(redis单例) # SETNX lock.foo \u0026lt;current Unix time + lock timeout + 1\u0026gt; 如果 SETNX 返回1,说明该进程获得锁,SETNX将键 lock.foo 的值设置为锁的超时时间(当前时间 + 锁的有效时间) 如果 SETNX 返回0,说明其他进程已经获得了锁,进程不能进入临界区。进程可以在一个循环中不断地尝试 SETNX 操作,以获得锁 解决死锁问题 SETNX lock.foo 返回0,获取锁失败 GET lock.foo 来检测锁是否已超时 GETSET lock.foo \u0026lt;current Unix timestamp + lock timeout + 1\u0026gt; 设置键的值的同时,还会返回键的旧值 通过比较键 lock.foo 的旧值是否小于当前时间,可以判断进程是否已获得锁 RedLock # 客户端获取当前的时间戳。 对 N 个 Redis 实例进行获取锁的操作,具体的操作同单机分布式锁。对 Redis 实例的操作时间需要远小于分布式锁的超时时间,这样可以保证在少数 Redis 节点 Down 掉的时候仍可快速对下一个节点进行操作。 客户端会记录所有实例返回加锁成功的时间,只有从多半的实例(在这里例子中 \u0026gt;= 3)获取到了锁,且操作的时间远小于分布式锁的超时时间,锁才被人为是正确获取。 如果锁被成功获取了,当前分布式锁的合法时间为初始设定的合法时间减去上锁所花的时间。 若分布式锁获取失败,会强制对所有实例进行锁释放的操作,即使这个实例上不存在相应的键值 什么时候会fork子进程 # rdb 、aof、主从无盘复制方式传输\nbigkey还会出现什么问题? # 网络阻塞、redis超时、分片内存不均匀导致某些节点占用内存多\n避免bigkey的方法,主要是对 bigkey 进行拆分,拆成多个 key,然后用MGET取回来,再在业务层做合并。\n集群模式没有mget命令怎么办? # 再加个map存在key列表,然后并行取\nredis 是单线程的(主要读写 io 操作 寻址等),为什么不设计成多线程的? # Redis的核心是快『基于内存』,主要有以下观点:由『避免了上下文切换和cpu的竞争,更加无需考虑各种锁操作,也不会和mysql一样存在死锁导致的问题』。\n因为数据是存储在内存中,内存中的运行非常快,但是如果存在上面的锁,和上下文切换,可能就不会那么快了。\n有利于开发人员规范代码,单线程的代码比多核异步更加清晰明了。\n单线程虽然有这些好处,但一定会浪费一些多核cpu的性能优势,如果是你设计会怎么考虑? # 还得看cpu的频率,如果cpu的频率低,并且访问redis的并发很大,那么单个redis线程分摊到每个cpu上的压力也是非常可观的。(一个线程并不是一直都bind到一个固定的核上面的, 其实这也是常遇到的错误的认知:单个线程就算用多核的机器也是浪费的观念)\n虽然redis是单线程,如果有需要可以使用多实例来模拟出多线程或者多进程\nredis使用架构设计 # 一致性hash算法中,怎么解决扩容缩容数据落点变化导致的问题? # 虚节点把数据落点更加均衡,减少单台机器下线导致的大量数据移动,导致的数据倾斜,也可以解决数据倾斜导致的新节点崩溃的缓存雪崩问题\n那崩溃的节点上的历史数据怎么找回呢? # jing\n历史数据找回的前提应该是本来数据就是副本或者纠删码形式存储\n彬\n历史数据归根有两种,后台的一般会回写数据库,这部分不会丢,用户体验可以做到无感。主要是用户的临时数据,比如登录过的账号,这部分要么使用第三方中间件,比如redis之类的存储,这样每次需要直接找redis查即可,要么直接放客户端,例如放cookie,token这些,这样也不会随着服务端变更影响。如果放的是服务端,那么只能做数据迁移后再扩容\n扩容的时候,会发生历史key失效吗 # 缩容万一还是产生了某个节点压力变大而崩溃,怎么设计兜底的方案? # 识别热key和解决热key # 热key一般在两种情况下出现\n 突发热点事件,比如促销、秒杀、社会热点场景 频繁访问某些数据 热key危害\n 分片集群,热key集中时,一旦超过单点访问极限容易出现问题 流量集中,超过网卡访问上限,影响其他业务 缓存雪崩 原生寻找热key方法:\n Redis 4.0 客户端可以通过 --hotkeys 选项快速找到业务中的热点key OBJECT 命令可以找到某个key的访问频率 其他方法\n 埋点,通过sdk,但多语言维护问题困难 代理层收集,但存在新组件维护及性能瓶颈 定时扫描,使用刚刚说到的原生方法,实时性比较差 监控抓包,有可能会增加网络流量和系统负载情况 饿了么的方案\n 所有的redis经过自己开发的代理组件 使用LFU算法, 概率计数,在代理层仅统计32个key 统计值会因时间变化,每分钟衰减一半 采样率可以根据服务器的配置来配置 超过阈值的key推送到远程监控端 引用: 如何快速定位 Redis 热 key\n最后 # 如果文中有误,欢迎提pr或者issue,一旦合并或采纳作为贡献奖励可以联系我直接无门槛加入 技术交流群\n我是小熊,关注我,知道更多不知道的技术\n "},{"id":14,"href":"/interview/xiang-mu-wen-shi-mo/","title":"Xiang Mu Wen Shi Mo","section":"Interviews","content":"项目一般问什么 # 项目问题因人而异,但是这些问题是共性的,可以思考一下 # 你最有成就感,或者最有挑战的项目经过,解决什么样的问题 数据量很大还是并发量很高,并发量体现在哪里?QPS是多少? 怎么提高可用性的? 技术难点体现在哪里? 你的项目有没有出现什么重大事故/故障,是怎么解决的,具体是什么原因 有没有什么印象深刻的Bug 分布式锁如何实现 # 分布式锁,一般为了达到分布式加锁需要通过投票的机制,依次向所有节点申请加锁,半数以上节点投票通过完成加锁,可以避免单点故障(Redis称为Redlock算法)\n 加锁的动作需要保证原子性,Redis通过Lua脚本来保证 谁加的锁谁来释放锁,所以需要标记锁来源 预防加锁程序挂掉导致的锁不释放,所以需要设置过期时间 加锁成功需要判断获取锁总耗时没有超过锁有效时间,这才判定为加锁成功 注意:假如程序处理速度比锁过期时间要长,是不合理的设计,超时时间的设置就很精细,一般都是远大于处理的时间,如果真的处理时间太长应该判定失败并告警\n见 redis\n如何实现一个分布式id生成器 # 首先要知道自增主键出现的问题\n 在高并发的情况下加入事务执行失败回滚,会跳过当前插入ID,使ID不连续 所有数据库中的自增字段或者自增序列都要记录日志,会产生磁盘IO,会成为性能瓶颈 假如数据库使用的是Range分片,自增ID可能会集中写入集群中的一个节点,出现数据访问热地世,性能退化成单机写入 解决方案\n 随机主键UUID方案(32 个的 16 进制数字,16^32 = 2^128 就是128位),虽然可以保证每次随机都不一样,但缺点是键值长度过长,存储和计算的代价增加,uuid只能保证不重复,但数据页可能会分裂,影响查询性能 号段模式,每个业务批量获取数据库中的号段,比如一次获取1000个,然后内存生成1000个自增ID,使用完再获取1000个;只需要插入一条记录,步长设置为1000,注意使用乐观锁(维护版本号),记录字段有业务类型、当前最大可用id、号段步长,version号;缺点服务重启时重新申请号段,不够随机有被猜到的风险 在TiDB 里提供了一种AutoRandom的算法,生成64位整型随机ID,1bit符号位、5bit事务开始时间,58bit自增序列号,还是有可能出现尾部热点 雪花算法Snowflake,时间戳精确到毫秒,10位长度机器码最大规模1024个节点(2^10), 12位序列代表1毫秒能产生的id数量最多4096个。所以 TPS 可以达到 419 万左右(2^22*1000), 每秒那么多大多系统都够了 注意雪花算法,对时间的要求比较高,如果时间不同步,时钟回拨时 ID 有可能出现重复\n引用: 分布式数据库30讲\n如何优化雪花算法的问题 # 雪花算法的问题主要在于时间回拨出现id重复、机器id有上限\n时钟回拨就是本机时间略快,完成时间服务器的校准(NTP或者闰秒回拨)以后,会出现时间倒退,导致生成ID重复\n时钟回拨解决办法:\n 继续在当前ID序列号最大基础上增加,方案来自 snowflake算法的时钟回拨问题如何解决 如果时间偏差比较小,\u0026lt;=5ms 可以等待2倍时间,牺牲很短时间的可用性,方案来自 SnowFlakeID原理和改进优化 时间回拨跨度太大时告警,并摘除本身节点,只会影响一个节点 也可以考虑直接关闭时间同步 机器id有上限的解决办法(雪花算法优化)\n 百度(uid-generator)的解决办法是可以自定义各部分的位数,工作机器ID需要数据库中创建一个表,插入机器相关信息(host和port),再根据表的自增ID作为workID,重启服务就另申请workID 美团使用Leaf算法,可以基于号段模式或雪花算法,对号段模式优化 双buffer方案,提前加载下一号段;雪花算法借助zookeeper的持久顺序节点的特性配置workID(我想上容器的话直接使用hostname或者使用k8s中的sts也不错) 注意雪花算法实际上是趋势递增,而不是绝对递增,这是为了保证性能\n如何实现秒杀系统 # 漏斗的思路,是架构上设计,客户端,网关,后台服务,层层限流,保证业务处理不被流量洪峰打挂了\n客户端侧降低服务端压力:\n 动静分离,静态资源放到cdn(某些服务为了更新及时不能放cdn)、前端文件webpack打包减少请求量 减少后端请求数量,只保留抢按钮的请求 时间使用客户端时间,不到时候无法点击 增加互动游戏再降低并发请求量 秒杀活动一旦发起,不允许修改详情等信息 保证web安全,防止xss与重放(随机数、时间戳、序列号)、CSRF等攻击方式 部署架构:\n 后端服务部署多个可用区,防止单可用区故障导致整体不可用 需要配置安全策略:防火墙、防DDOS、API网关、WAF;接入风控挡掉不合法请求 使用负载均衡SLB,根据不同节点负载情况分发流量 硬件上使用SSD 后端防护:\n 防止超卖,推动库存确认流程到支付阶段 库存信息放到内存中(redis) 使用另外的数据库集群 过载保护(有损保护):\n 服务降级:秒杀期间关闭某些服务,比如淘宝关闭退款流程,微信抢红包延迟到账 熔断:接入监控系统,根据系统节点的承载能力和服务质量有关,比如 CPU 的使用率超过 90%,请求错误率超过 5%,请求延迟超过 500ms, 它们中的任意一个满足条件就会出现熔断,主动拒绝请求; 限流:速度过快时加入验证码流程,接入API网关可以进行流量控制,请求过滤和控制,并过滤的请求,前端根据错误码返回友好的页面(已抢完之类)常见限流算法:漏桶\u0026gt;令牌桶\u0026gt;滑动窗口\u0026gt;计数器 容灾 # 假如某个节点无法拉起,或者量级大被打挂了\n追踪链的traceid是怎么生成的 # traceID 一般由请求经过的第一个服务器生成,参考 服务器 IP + 生成 ID 的时间 + 自增序列,它的作用是把各个服务器上的调用日志串联起来\n 前 8 位 0ad1348f 为生成 TraceId 的服务器 IP,这是一个十六进制的数字,每两位代表 IP 中的一段,把这个数字按每两位转成十进制即可得到常见的 IP 10.209.52.143,可以根据此规律来寻找请求经过的第一个服务器。 后 13 位 1403169275002 是生成 TraceId 的时间。 最后四位 1003 是一个自增序列,范围是 1000 到 9000,到达 9000 后回到 1000 再重新开始自增。 TraceID生成规则-蚂蚁集团方案\n"},{"id":15,"href":"/leetcode-vscode/","title":"Leetcode Vscode","section":"","content":"协作办法 # 下载插件 # 直接在vscode中搜索 LeetCode 插件完成下载,并登陆账号,修改配置如下\n找到修改配置位置 # 改为中国区 # 修改默认语言 # 修改文件夹目录 # 修改插件配置,设置文件夹目录为 LeetCode/all ,这样才会自动创建go文件在此\n\u0026#34;leetcode.filePath\u0026#34;: { \u0026#34;default\u0026#34;: { \u0026#34;folder\u0026#34;: \u0026#34;LeetCode/all\u0026#34;, \u0026#34;filename\u0026#34;: \u0026#34;${id}.${kebab-case-name}.${ext}\u0026#34; } } 修改工作目录 # 如果不是直接打开的需要修改工作目录\n 最后 # 如果文中有误,欢迎提pr或者issue,一旦合并或采纳作为贡献奖励可以联系我直接无门槛加入 技术交流群\n"},{"id":16,"href":"/leetcode/all/readme/","title":"Readme","section":"LeetCodes","content":"请使用 vscode 中的 LeetCode 插件,自动创建文件、手动提交测试\n"},{"id":17,"href":"/leetcode/difficult/","title":"Difficult","section":"LeetCodes","content":"LeetCode-hot100-difficult # 介绍 # 一句话总结算法思路,LeetCode hot100 difficult\n按频率排序,排序依据参考 字节跳动后端高频面试题目。\n全部源码可见我的GitHub interview-leetcode\n注:\n有下划线标志的都是超链接。 点击下列题目标题可以跳转到LeetCode中文官网直接阅读题目,提交代码。 点击下列代码链接,可以直接跳转到我的GitHub代码页面。每道题一般精选一种解法,我的GitHub中可能收录多种解法代码,请自行查看。\n题解 # 42.接雨水 # 题目: 接雨水,给定整数数组,把他想象成柱状图,凹槽部分接雨水总合\n题解:用栈的思路比较难理解,我写在代码里了,有兴趣可以自己看看,这里推荐双指针的思路\n 双指针下标l和r指向0和len-1,记录左侧最大值height[0]和右侧最大值height[len-1] 判断左侧和右侧最大值哪个更小,更小侧向内测移动,比如 if lMax\u0026lt;rMax then l++ 如果当前指针位置比lMax要小,又因为此时rMax\u0026gt;lMax说明此处一定会出现低洼,它被两侧包住了,一定不会渗水;水的高度是由更小的最大值决定,现在是lMax 统计水量res += lMax - height[l],如果当前指针比lMax大,说明左侧包不住,更新左侧最大值 上面1、2、3步骤的动作else 反过来,r--; if height[r]\u0026lt;rMax then res+= rMax-height[r] else rMax = height[r] 循环条件l\u0026lt;r,函数至少要3个值才有可能形成低洼,所以边界是len\u0026lt;3 return 0 代码: golang\n题目:\n题解:\n注意:\n代码: golang\n最后 # 如果文中有误,欢迎提pr或者issue,一旦合并或采纳作为贡献奖励可以联系我直接无门槛加入 技术交流群\n我是小熊,关注我,知道更多不知道的技术\n "},{"id":18,"href":"/leetcode/easy/","title":"Easy","section":"LeetCodes","content":"LeetCode-hot100-easy # 介绍 # 一句话总结算法思路,LeetCode hot100 easy\n总计21题,题目列表请戳 LeetCode Hot100 easy 列表。\n全部源码可见我的GitHub interview-leetcode\n注:\n有下划线标志的都是超链接。 点击下列题目标题可以跳转到LeetCode中文官网直接阅读题目,提交代码。 点击下列代码链接,可以直接跳转到我的GitHub代码页面。每道题一般精选一种解法,我的GitHub中可能收录多种解法代码,请自行查看。\n题解 # 1.两数之和(高频) # 题目: 数据中是否有两个数之和为目标值\n题解:遍历数组,用目标值减去当前值,判断HashMap是否有值存在,如果有则创建新数组返回两者,如果没有循环遍历完返回空数组\n时间复杂度:O(1) 代码: golang\n20.有效的括号(高频) # 题目: 存在[]{})(的字符串,判断是否合法\n题解: 存储左括号和右括号的映射,用栈统计左括号,出现左括号就入栈,出现右括号就和栈顶在map中映射的右括号比较,如果匹配就出栈,不匹配返回false,最后遍历完栈空为false\n注意:go语言可以用byte代表单个字符\n代码: golang\n21.合并两个有序链表(高频) # 题目: 两个升序链表,合并成一个\n题解:\n 需要一个空的头节点做辅助,head.Next就是结果 每次遍历始终维护上一个节点prev,初始值prev=head 循环遍历两个链表,循环条件都不为空,每次把当前节点更小的取出来即可 prev.Next = l1 prev = l1 l1 = l1.Next 最后加入有不为空的节点,则直接赋值 if l1!=nil{ prev.Next = l1 }else{ prev.Next = l2 } 代码: golang\n53.最大子序和(高频) # 题目: 求和加起来最大的连续子数组\n题解:\n 一次循环,数组里有可能出现负数,且只需要统计和即可 需要两个计数器,一个存储全局最大的子序列和,只要出现更大的就更新 另一个存储当前最大的子序和,判断当前最大子序和的方法是比较子序和与当前值哪个大 有可能当前值比子序列和大,就更新子序 max(nums[i],nums[i]+last) 核心代码\nlast = max(nums[i],nums[i]+last) resMax = max(resMax,last) 代码: golang\n70.爬楼梯 # 题目: 一次可以上一阶或者二阶,如果是n阶有多少种爬法\n题解:斐波那契数列,返回结果是前两个值的和,只需要保存前两个值和当前结果,递推赋值即可\n代码: golang\n101.对称二叉树 # 题目: 二叉树是不是镜像的\n题解:\n 递归,相当于使用了前序遍历和后续遍历相等的性质 函数签名isMirror(root,root),判断前序和后续相等 q.Val == p.Val \u0026amp;\u0026amp; isMirror(p.Left,q.Right) \u0026amp;\u0026amp; isMirror(p.Right,q.Left) 注意都为空时true,其中一个为空时false if p == nil \u0026amp;\u0026amp; q == nil { return true } if p==nil || q==nil{ return false } 代码: golang\n104.二叉树的最大深度 # 题目: 根节点到最远叶子节点的最长路径上的节点数\n题解:递归,前序遍历,返回值为左右节点最大深度+1,退出条件为null节点返回0,左右子树都为空返回1\n代码: golang\n121.买卖股票的最佳时机(高频) # 题目: 给定整数数组表示每天股票价格,买一次卖一次求最大收益,要求必须先买再卖\n题解:与目前最小值做差,得到当前最大值,更新最大值,一次循环。核心代码如下\nif v\u0026lt;minNum{ minNum = v }else if v - minNum \u0026gt; maxNum{ maxNum = v - minNum } 代码: golang\n136.只出现一次的数字 # 题目: 数组中某元素只出现一次,其余两次。找那个元素\n题解:\n 方法一、直接用map统计,超过一次就删掉 方法二、每个值都异或,最终得到的就是答案 异或的性质:a^a=0, a^0=a 代码: golang\n141.环形链表(高频) # 题目: 判断链表中是否有环\n题解:快慢指针,相等时退出,注意循环条件\np.Next != nil \u0026amp;\u0026amp; q.Next != nil \u0026amp;\u0026amp; q.Next.Next != nil 代码: golang\n155.最小栈(中频) # 题目: 设计一个栈,支持push、pop、top、getMin获取栈内最小值操作\n题解:要自定义结构体,结构体内两个栈,一个存储push的元素的栈1,另一个存储最小值栈2,栈2有一个性质,每个栈2内元素位置为栈顶时,始终表示当前栈1最小的元素,比如\n栈1 [1 2 3 4 0] 栈2 [1 1 1 1 0] 这样出栈同同步出栈,始终最小值就是栈2的栈顶,使用了一个当前最优解的思路\n代码: golang\n160.相交链表(高频) # 题目: 假定链表中没有循环,判断两个链表是否相交\n题解:\n法1(笨办法)\n 双指针同步走,肯定有一个先结束,假如另一个指针所指到结束位置的距离就是长链表比短链表多出来的部分 长链表跑到和短链表等长,然后同步走,找出相等节点(地址相同非值相同) 法2(最优)\n pA走过的路径为A链+B链, pB走过的路径为B链+A链 分别将另一个链表拼到尾部,相当于两个链表等长 pA和pB走过的长度都相同,都是A链和B链的长度之和,相当于将两条链从尾端对齐,如果相交,则会提前在相交点相遇,如果没有相交点,则会在最后相遇。 pA:1-\u0026gt;2-\u0026gt;3-\u0026gt;4-\u0026gt;5-\u0026gt;6-\u0026gt;null-\u0026gt;9-\u0026gt;5-\u0026gt;6-\u0026gt;null pB:9-\u0026gt;5-\u0026gt;6-\u0026gt;null-\u0026gt;1-\u0026gt;2-\u0026gt;3-\u0026gt;4-\u0026gt;5-\u0026gt;6-\u0026gt;null 代码: golang\n169.多数元素 # 题目: 数组中有一个数超过元素的一半,找出那个数\n题解:\n 最先想到的是hash表,更好的办法是投票 随便选个人当选,和它不同就反对,票数\u0026ndash; 和他相同就赞成,票数++ 票数为0则换届,最终票数肯定是正的,当选的肯定是众数,代码较短我直接贴上来了 func majorityElement(nums []int) int { var count,num int for _,v := range nums{ if count == 0 || v == num{ num = v count++ }else{ count-- } } return num } 代码: golang\n206.反转链表(高频) # 题目:\n题解:\n 其实是234. 回文链表 的一部分,就是在考基本功 方法1、递归\n 首先要确认递归返回链表头,所以退出条件:head为空直接退出,递归内部head.Next为空返回头节点 递归传入head.Next返回已经反转好的链表头 递归后动作,需要让最后的头节点指空,也就是head.Next = nil在指空前,需要让head.Next.Next=head 1-\u0026gt;2-\u0026gt;null 1\u0026lt;-2 nil\u0026lt;-1\u0026lt;-2 关键代码\nnewHead := reverseList(head.Next) head.Next.Next = head head.Next = nil return newHead 方法2、非递归(最快,省空间)\n 非递归两件套,prev,curr = nil,head 每次保存下一个节点,让当前节点指向上一个节点 然后向下走一位,prev=curr; curr=next,curr为空时停止,prev就是新的头节点 代码: golang\n226.翻转二叉树 # 题目: 翻转二叉树,每一层都要完全翻转\n题解:\n方法1 非递归 不推荐 较为麻烦,容易漏掉\n 层遍历保存每层的状态,反着读出来每层就是翻转结果 但不能改变层遍历的过程,所以要两个栈,栈1保存层遍历,栈2保存该层的反向结果作为下次的before层 遍历栈2,左右节点指向倒着遍历栈1 要注意有可能出现部分空节点的情况,栈1,空位要留出来,所以要用nil来占位 方法2 递归:其实子节点还是属于父节点,只要翻转左右节点位置就行了\n自身递归,“交换”左右子树时记得备份。 代码: golang\n234.回文链表 # 题目: 判断一个链表是否为回文链表\n题解: 法1、最简单,直接遍历一次转换成数组,判断数组是否回文即可 法2、快慢指针(整除器)\n 把剩下的一半变成逆序,再进行比较。注意奇偶情况讨论。递归非递归都行,想起来哪个用哪个,判断完后恢复链表\n 如果要快就边跑边让慢指针翻转链表,结束后也不用恢复\n代码: golang\n 283.移动零 # 题目: 把数组里的零全部移动到结尾\n题解:两个下标,使用类似于选择插入排序的方法,不断扩充非零列,剩余的元素用0填充\n代码: golang\n448.找到所有数组中消失的数字 # 题目: 1-n的数字存储在长度为n的数组里,有的数字重复出现了,所以有的数字没有出现,找出没有出现的数字\n题解: 法1、直接用hash表,比较简单,不过建议还是用法2可以体现水平 法2、用占位方法,遍历,出现的abs(数字)-1作为下标的数字改为负,如果已经是负的就不用改了,最后再遍历一次数组把存储数字为正位置的(下标+1)存储到结果集里\n代码: golang\n461.汉明距离 # 题目: 求两个数字二进制位不同的有多少个\n题解:先亦或,然后%2=1时统计,\u0026gt;\u0026gt;1代表/2去掉一位\n代码: golang\n543.二叉树的直径 # 题目: 求两个叶子之间最大距离\n题解:\n 深度优先dps,必须用递归,递归返回的是左右子树最深深度 维护一个最大值,递归返回后判断左右子树贡献的深度和,与最大值哪个大,更新最大值,这样可以保证直径是当前最大 max(x+y,maxRes) 返回当前子树最大深度return max(x,y)+1 代码: golang\n617.合并二叉树 # 题目: 合并二叉树,同位置都有值就加起来,有一个为空就只合并\n题解:\n 递归,返回条件:其中一个为空返回另一个节点 如果两个都不为空,加起来 处理递归函数,分别传入两棵树的左子树或右子树,赋值给当前节点左右子树 跟437 路径总和III的思想是一样的。 代码: golang\n最后 # 如果文中有误,欢迎提pr或者issue,一旦合并或采纳作为贡献奖励可以联系我直接无门槛加入 技术交流群\n我是小熊,关注我,知道更多不知道的技术\n "},{"id":19,"href":"/leetcode/hot100/readme/","title":"Readme","section":"LeetCodes","content":"介绍 # 题目列表请戳 LeetCode-Hot100列表\n算法 LeetCode Hot 100 思路与代码,请点击相应目录查看\n最后 # 如果文中有误,欢迎提pr或者issue,一旦合并或采纳作为贡献奖励可以联系我直接无门槛加入 技术交流群\n"},{"id":20,"href":"/leetcode/hua-wei-ji-shi/","title":"Hua Wei Ji Shi","section":"LeetCodes","content":"华为机试 # 字符串余 # 输入若干字符串和一个数字n,按8切分字符串,长度不足补0\n示例:\n输入:asdf 123456789 输出:asdf0000 12345678 90000000 func splitStr(str string,n int){ zeroStr := \u0026#34;00000000\u0026#34; for len(str)\u0026gt;8{ fmt.Println(str[:8]) str = str[8:] } fmt.Println(str+zeroStr[:8-len(str)]) } 货币汇率兑换算法 # 知道各货币兑换另一种货币的汇率,要求求最大兑换和最小可兑换货币\nUSD - RMB 1:6 RMB - HKD 1:2 HKD - JAN 1:20\n示例:\n输入HKD = 65 最大可兑换 USD=10,RMB=5 最小可兑换 JAN=2600 函数\n// 顺序输入UDB RMB HKD 汇率,比如上面的例子,输入[6 2 20] // moneyType = 0 1 2 3 对应 USD RMB HKD JAN,money对应货币数量 // isMax = true 时求最大可兑换 false 时求最小可兑换 // 返回兑换情况,最大[5,2,1,0],最小[0,0,0,1300] func xxx(rate []int,moneyType int,money int,isMax bool) []int{ } 解法,代码见 货币汇率兑换算法.go\nfunc xxx(rate []int,moneyType int,money int,isMax bool) []int{ res := make([]int,len(rate)+1) res[moneyType] = money rate = append(rate,1) if isMax{ for i:=moneyType;i\u0026gt;0;i--{ res[i-1] = res[i]/rate[i-1] res[i] = res[i]%rate[i-1] } return res } for i:=moneyType; i \u0026lt; len(res)-1; i++ { res[i+1] = res[i] * rate[i] res[i] = 0 } return res } 货币汇率兑换算法2 # 知道各货币兑换另一种货币的汇率,要求求最大兑换和最小可兑换货币(注意,有可能有的货币无汇率信息)\nUSD - RMB 1:6 HKD - JAN 1:20\n示例:\n输入HKD = 65 最大可兑换 HKD=65 最小可兑换 JAN=2600 输入RMB=65 最大可兑换 USD=10,RMB=5 最小可兑换 RMB=65 函数\n// 顺序输入UDB RMB HKD 汇率,比如上面的例子,输入[6 0 20],0表示不能兑换 // isMax = true 时求最大可兑换 false 时求最小可兑换 // moneyType = 0 1 2 3 对应 USD RMB HKD JAN,money对应货币数量 // 返回兑换情况,输入moneyType=2 money=65 输出最大[0,0,65,0],最小[0,0,0,1300] // 返回兑换情况,输入moneyType=1 money=65 输出最大[10,5,0,0],最小[0,65,0,0] func xxx(rate []int,moneyType int,money int,isMax bool) []int{ } 解法,代码见 货币汇率兑换算法2\n螺旋矩阵变体 # 输入一个坐标,直接输出对应的值,比如输入横坐标-4 纵坐标6输出什么(里面是个正方形,边长乘以边长就是结果)\n21 22…… 20 7 8 9 10 19 6 1 2 11 18 5 4 3 12 17 16 15 14 13\n示例\n输入样例 : 0 0 -1 -1 输出样例 : 1 5 先去掉内层面积,可以得到起始点值\n内层面积先求内层边长,side := (max-1) * 2 + 1\n内层面积:area := side*side 所以起始点为 begin := area + 1\n可以根据外层最大坐标值算出起始点坐标,(max,max-1)\n从起始点位置开始遍历坐标,规律为下左上右,对应 y- x- y+ x+,同时num++\n当达到边角以后需要转向,需要一个计数器统计当前方向 0 1 2 3 对应 y- x- y+ x+\n可以发现0 2是偶数改变y,1 3是奇数改变x,此时再确定符号\n0 2平移一位变成 -1 1 可以控制y-1或者y+1,1 3平移两位变成-1 1控制x-1或者x+1\n计数器数值变化需要在outSide-1的情况变化一次 所以 / (outSide-1)\n计数器需要在outSide-1的时候就要变化,所以到达outSide-1时必须计数为outSide-1的倍数时 num - begin = 0 所以 num - begin + 1\nfunc getNum(x,y int) int{ if x == y \u0026amp;\u0026amp; x == 0{ return 1 } maxNum := max(abs(x),abs(y)) side := (maxNum - 1) * 2 + 1 begin := side * side + 1 outSide := side + 2 num := begin for cnt,i,j:=0,maxNum,maxNum-1;cnt\u0026lt;4;{ if i == x \u0026amp;\u0026amp; j == y{ return num } if cnt%2 == 0{ j = j + cnt - 1 }else{ i = i + cnt - 2 } num++ cnt = (num - begin + 1) / (outSide - 1) } return num } 测试用例,输出1 2 3 4 5 6 7 8 9\nfmt.Println(getNum(0,0)) fmt.Println(getNum(1,0)) fmt.Println(getNum(1,-1)) fmt.Println(getNum(0,-1)) fmt.Println(getNum(-1,-1)) fmt.Println(getNum(-1,0)) fmt.Println(getNum(-1,1)) fmt.Println(getNum(0,1)) fmt.Println(getNum(1,1)) "},{"id":21,"href":"/leetcode/medium/","title":"Medium","section":"LeetCodes","content":"LeetCode-hot100-medium # 介绍 # 一句话总结算法思路,LeetCode hot100 medium\n按频率排序,排序依据参考 字节跳动后端高频面试题目。\n全部源码可见我的GitHub interview-leetcode\n注:\n有下划线标志的都是超链接。 点击下列题目标题可以跳转到LeetCode中文官网直接阅读题目,提交代码。 点击下列代码链接,可以直接跳转到我的GitHub代码页面。每道题一般精选一种解法,我的GitHub中可能收录多种解法代码,请自行查看。\n3.无重复字符的最长子串 # 题目: 在字符串中找一个子串,要求连续无重复字符且最长,只需要返回最大长度\n题解:\n 字符串长度为0直接返回 记录最大子串的长度,用来和新子串长度比较,维护子串的状态还需要记录当前子串的起始位置的下标 使用map来储存字符对应的下标, 只要当前字符出现在map里,同时map里的字符就是子串里的字符时(存储的字符下标大于等于起始位置下标)说明重复,更新子串起始位置为map中记录的重复点+1 else (没有出现在子串里),子串长度++,判断更新最大长度(注意更新时+1) 代码: golang\n215.数组中的第k个最大元素 # 题目: 数组中的第k个最大元素\n题解: 方法1,堆排序(不推荐)\n 求第K大的数,实际上就是取小根堆的根节点 小根堆的性质,根节点比所有叶子节点更小 注意:这里为什么用堆,是因为堆是一个完全二叉树,而二叉搜索树不自平衡,而且堆的话小根堆直接取根节点就是结果了\n方法2 快速排序变形(快速选择算法)\n 其实就是快排的思路,只是做了下剪枝 只要保证len-k这个位置右侧全部比k大,左侧全部小于等于k,那么len-k位置的数就是第k大 因为我们不知道是哪个数,所以随便取一个数x,最终达到左侧全部\u0026lt;=x,右侧全部\u0026gt;x的效果 把x的下标index和len-k比较,如果小,说明第k大数一定在[index+1,r];如果大说明第k大数一定在[l,index-1]中 缩小区间,继续随便取一个数,直到正好x的下标就是len-k为止 查找中枢的办法借助快排的思路\n 随机取一个数x,把他和r位置的数对调 维护左侧区间都比x小,所以初始化i为l-1 变量j遍历[l,r) 左闭右开区间,大于x无操作 小于x时候,i++,然后对调i和j位置的数字,这样又可以保证i左侧包括i位置的数都小于x 遍历结束以后i+1位置的数正好是最后一个比i大的数,把他和r对调 返回i+1,也就是中枢位置的下标 时间复杂度:运气好就是一次就找到了On运气不好每个数都找了一次ON^2,算法导论中把每次查找都使用一个随机数,可以显著提高效率,趋近于On,具体为什么可以自己去看\n为什么推荐用快选,因为空间O1,时间On~On^2比堆的时间Onlogn和Ologk更快,但是快排也有局限性\n 快选需要修改原数组,如果原数组不能修改的话,还需要拷贝一份数组,空间复杂度就上去了。 堆只需要保存 k 个元素的小根堆。快速排序变形的方法如果不允许修改原数组那就要保存下来所有的数据,所以数据量大时用堆更好 引用: 优劣比较\n代码: golang\n15.三数之和 # 题目: 数组里有没有三个数加起来为0,找出所有可能的情况\n题解:\n 先把数组排序 从小到大遍历这个数组,每次取一个元素,将这个元素的相反数设为target 在每次遍历中,使用双指针对当前元素的后面的所有元素进行处理,找到所有两个元素的和为target,这样,三个元素的和就是 0 双指针的具体处理方式为:头尾各一个指针,每次判断两个指针所指的元素的和与target的大小,如果和小了,左指针右移;如果和大了,右指针左移,直到两个指针相遇 注意:\n 因为不能有重复的结果,所以前后两次遍历取的元素如果相等,要采取跳过的操作 在第三步中,对当前元素的后面的所有元素进行处理的原因是,前面的元素已经找到了所有符合条件的可能,不需要再找 代码: golang 引用: 【LeetCode】15#三数之和\n22.括号生成 # 题目: 数字n代表生成括号的对数,请生成所有可能的并且有效的 括号组合3\n比如:\n输入:n = 3 输出:[\u0026#34;((()))\u0026#34;,\u0026#34;(()())\u0026#34;,\u0026#34;(())()\u0026#34;,\u0026#34;()(())\u0026#34;,\u0026#34;()()()\u0026#34;] 题解:\n 找不同可能性的题目,解题思路优先考虑深度优先dfs 既然是有效的括号组合而且只有小括号,那么只需要维护n个左括号,n个右括号的组合 深度优先,就是类似二叉树的遍历,使用模板 func dfs(根){ dfs(左子) dfs(右子) } 要维护左右括号的个数 因为是有效的括号,要先拼左括号再拼右括号,需要左右括号相等,所以 func dfs(左括号个数,右括号个数,当前字符串){ if 左\u0026gt;右{ return } if 左==右{ append(res,当前字符串) return } if 左\u0026gt;0{ dfs(左-1,右,当前+\u0026#34;(\u0026#34;) } if 右\u0026gt;0{ dfs(左,右-1,当前+\u0026#34;)\u0026#34;) } } 注意:边界n==0不需要查找\n代码: golang\n103.二叉树的锯齿形层序遍历 # 题目: 先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进\n题解:不改变原树结构,层遍历,维护正反bool条件,头插尾插,最终返回二维数组\n代码: golang\n39.组合总合 # 题目: 找出数组里所有和等于target的总合\n题解:回溯,边界是sum大于target剪枝,等于就拿到一个结果\n注意:append一维数组的时候要拷贝下\n拷贝函数参考\nvar tmp []int tmp = append(tmp,curr...) res = append(res,tmp) 代码: golang\n142. 环形链表 II # 题目: 找到交点坐标的下标,无环时返回null\n题解:\n 这不仅仅是返回是否相交,还要找到交点的下标;我们知道可以用双指针相遇时说明相交 找到交点时,快指针正好比慢指针多一圈 设链表共有 a+b+c 个节点,链表头部到链表入口有 a 个节点(不计链表入口节点),相遇时快指针走了a+b+c+b次,慢指针走了a+b次,发件人指针是慢指针的两倍速度,可得\na+b+c+b = 2(a+b) c=a 所以相遇时再加一个头指针一起跑,慢指针继续跑相遇点就是环的起始位置\n代码不写了,比较简单\n300.最长递增子序列 # 题目: 求最长递增子序列的长度,子序列是不连续的\n题解:求最值,不用保存子序列,我们记录数组里每个数作为终点的序列最长长度就好了\n 对每个数都遍历之前的数,只要比当前数小,说明他可以作为终点 因为已经记录了之前数作为终点的序列长度,让序列长度+1就可以 再做下对比,公式如下 max(dp[i],dp[j]+1) 得到所有序列作为终点的最长子序列长度数组以后,遍历下取最值就好了\n代码: golang\n1143.最长公共子序列 # 题目: 求两个数组的最长公共子序列长度\n题解:\n 用二维数组dp记录最长公共子序列长度,dp[i][j] 为当前位置最长公共子序列长度,状态转移方程\n// 其中因为当前字符相等,所以长度各减1的dp[i-1][j-1]表示没有当前字符时字符串的最长公共子序列长度 // 也就是加入text1长度为i,text2长度为j,i j 就是当前长度的最长公共子序列,i-1 j 代表长度为i-1的字符串与j的字符串公共子序列长度 if text1[i] == text2[j] then dp[i][j] = dp[i-1][j-1] + 1 // 长度不一样时对比各自长度-1,作为当前长度最长公共子序列 if text1[i] != text2[j] then dp[i][j] = max(dp[i-1][j],dp[i][j-1]) 注意:i-1 和 j-1 会越界,申明时让dp行列len+1\n代码: golang\n59.螺旋矩阵-ii # 题目: 给一个数字n,输出n*n的正方形螺旋矩阵\n题解:生成一个 n×n 空矩阵 mat,随后模拟整个向内环绕的填入过程:\n 定义当前左右上下边界 l,r,t,b,初始值 num = 1,迭代终止值 tar = n * n; 当 num \u0026lt;= tar 时,始终按照 从左到右 从上到下 从右到左 从下到上 填入顺序循环,每次填入后: 执行 num += 1:得到下一个需要填入的数字; 更新边界:例如从左到右填完后,上边界 t += 1,相当于上边界向内缩 1。 使用num \u0026lt;= tar而不是l \u0026lt; r || t \u0026lt; b作为迭代条件,是为了解决当n为奇数时,矩阵中心数字无法在迭代过程中被填充的问题。 最终返回 mat 即可。\n 题解来源\n代码: golang\n搜索二维矩阵 # 题目: 递增的二维矩阵,搜索值是否在内部\n题解:方法1,简单搜索\n 以二维数组左下角为原点,建立直角坐标轴。 若当前数字大于了查找数,查找往上移一位。 若当前数字小于了查找数,查找往右移一位。 题解来源\n方法2,二分搜索(独创)\n既然列递增,且行递增,下一行所有数字都比上一行大,可以把二维数组拼接起来,视为递增的一纬度数组来处理\n 取行列长度n1 n2,得到全局数字个数n1*n2 使用二分法,取数字时,mid/n2为行,mid%n2为列即可 我的题解\n代码: golang\n最后 # 如果文中有误,欢迎提pr或者issue,一旦合并或采纳作为贡献奖励可以联系我直接无门槛加入 技术交流群\n我是小熊,关注我,知道更多不知道的技术\n "},{"id":22,"href":"/leetcode/other/","title":"Other","section":"LeetCodes","content":"其他高频算法 # 其他高频 # medium # 120.三角形最小路径和 # 题目: 求三角形的最小路径和\n要求是每一步只能找下一行的相邻节点,也就是当前行的下标 i ,那么下一步可以移动到下一行的下标 i 或 i + 1\n输入:triangle = [[2],[3,4],[6,5,7],[4,1,8,3]] 输出:11 解释:如下面简图所示: 2 3 4 6 5 7 4 1 8 3 自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。 题解:从三角形底部向上遍历\n[ [2], [3,4], [6,5,7], [4,1,8,3] ] 相邻结点:与(i, j) 点相邻的结点为 (i + 1, j) 和 (i + 1, j + 1)。 用一个一维度数组dp [n+1]int来存储最终结果,保证每个值都是最优解 方程 min(dp[j],dp[j+1]) + triangle[i][j] 注意:\n代码: golang\n引用: 递归+记忆化+DP\ndifficult # 题目:\n题解:\n注意:\n代码: golang\n"},{"id":23,"href":"/leetcode/readme/","title":"Readme","section":"LeetCodes","content":"面试算法 # 介绍 # 这里是所有的算法题目,建议先刷hot100再刷剑指Offer\n请直接点击相应目录查看\n最后 # 如果文中有误,欢迎提pr或者issue,一旦合并或采纳作为贡献奖励可以联系我直接无门槛加入 技术交流群\n"},{"id":24,"href":"/leetcode/shu-ju-jie-gou/","title":"Shu Ju Jie Gou","section":"LeetCodes","content":"数据结构 # 树 # 基本概念 # 树有多个节点(node),用以储存元素 节点之间的连线称为边(edge) 边的上端节点称为父节点 下端称为子节点 树的深度(depth)是从根节点开始(其深度为1)自顶向下逐层累加的 高度的定义为:从结点x向下到某个叶结点最长简单路径中边的条数 深度是从根节点往下 二叉树 # 常见的二叉树:完全二叉树,满二叉树,二叉搜索树,二叉堆,AVL 树,红黑树,哈夫曼树\n 完全二叉树:若设二叉树的深度为 h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边(效率高) 满二叉树:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树 二叉搜索树(二叉排序树,二叉查找树) 树中每个节点最多有两个子树,通常称为左子树和右子树 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值 它的左右子树仍然是一棵二叉搜索树 (recursive) 平衡树(B树) 排序方式:所有节点关键字是按递增次序排列,并遵循左小右大原则; 子节点数:1\u0026lt;非叶节点的子节点数\u0026lt;=M ,且M\u0026gt;=2,空树除外(注:M阶代表一个树节点最多有多少个查找路径,M=M路, 当M=2则是2叉树, M=3则是3叉) 关键字数:ceil(m/2)-1\u0026lt;=枝节点的关键字数量\u0026lt;=M-1个(注:ceil()是个朝正无穷方向取整的函数 如ceil(1.1)结果为2); 所有叶子节点均在同一层、叶子节点除了包含了关键字和关键字记录的指针外也有指向其子节点的指针只不过其指针地址都为null对应下图最后一层节点的空格子 B+树 特点 B+树的非叶子节点不保存关键字记录的指针,只进行数据索引(非叶子节点所能保存的关键字大大增加) B+树叶子节点保存父节点的所有关键字记录的指针,数据地址必须要到叶子节点才能获取到(每次数据查询的次数都一样) B+树叶子节点的关键字从小到大有序排列,左边结尾数据都会保存右边节点开始数据的指针 非叶子节点的子节点数=关键字数(两种实现,或:非叶节点的关键字数=子节点数-1。Mysql 的B+树是第一种) 与红黑树的比较(访问磁盘数据有更高的性能) B+ 树有更低的树高\n 磁盘访问原理:操作系统一般将内存和磁盘分割成固定大小的块,每一块称为一页,内存与磁盘以页为单位交换数据。数据库系统将索引的一个节点的大小设置为页的大小,使得一次 I/O 就能完全载入一个节点。\n如果数据不在同一个磁盘块上,那么通常需要移动制动手臂进行寻道,而制动手臂因为其物理结构导致了移动效率低下,从而增加磁盘数据读取时间。B+ 树相对于红黑树有更低的树高,进行寻道的次数与树高成正比,在同一个磁盘块上进行访问只需要很短的磁盘旋转时间,所以 B+ 树更适合磁盘数据的读取\n 为了减少磁盘 I/O 操作,磁盘往往不是严格按需读取,而是每次都会预读。预读过程中,磁盘进行顺序读取,顺序读取不需要进行磁盘寻道,并且只需要很短的磁盘旋转时间,速度会非常快。并且可以利用预读特性,相邻的节点也能够被预先载入\n 平衡二叉树(AVL 树):它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树 红黑树 黑色完美平衡:任意一个结点到到每个叶子结点的路径都包含数量相同的黑结点 缓存淘汰策略LRU( Least Recently Used) # 如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。 哈希表+双向链表实现 使用哈希表存储 key,值为链表中的节点,节点中存储值,双向链表来记录节点的顺序,头部为最近访问节点。 **LRU**算法中有两种基本操作 get(key):查询key对应的节点,如果key存在,将节点移动至链表头部。 set(key, value): 设置key对应的节点的值。如果key不存在,则新建节点,置于链表开头。如果链表长度超标,则将处于尾部的最后一个节点去掉。如果节点存在,更新节点的值,同时将节点置于链表头部。 "},{"id":25,"href":"/leetcode/suan-fa-mian-shi-zhu-yi/","title":"Suan Fa Mian Shi Zhu Yi","section":"LeetCodes","content":"算法面试注意 # 算法思维 # 进入函数优先考虑边界 如果出现循环,进入循环时考虑break条件和continue条件 使用下标计算长度时,优先考虑区间的开闭 我们应该在做算法的过程总不断的思考和总结共性 面试注意 # 一定要记得常用的函数,现场是没有办法可以查的\n 字符串去左右空格 字符串切割 随机数种子,随机数生成 内置排序函数 int最小值最大值怎么取 以go为例\ns = strings.TrimSpace(s) arr := strings.Split(s,\u0026#34;\u0026#34;) rand.Seed(time.Now().UnixNano()) rand.Int() sort.Int() sort.Slice(x,func(i,j int)bool{ // 降序 return x[i]\u0026gt;x[j] }) math.MaxInt32 math.MinInt32 牛客网面试注意 # 牛客网比较坑,一切输入输出都要自己实现\n还要劳记链表创建代码, 完整代码\npackage main import( \u0026#34;fmt\u0026#34; ) type LinkNode struct{ Val int Next *LinkNode } func createNode(a []int) *LinkNode{ head :=\u0026amp;LinkNode{ 0, nil, } prev := head for i:=0;i\u0026lt;len(a);i++{ node := \u0026amp;LinkNode{ a[i], nil, } prev.Next = node prev = node } return head.Next } 同时要牢记二叉树的创建代码, 完整代码\ntype BTnode struct { Val int Left *BTnode Right *BTnode } func createTree(a []int) *BTnode { if len(a) == 0 || len(a)%2 == 0 { return nil } root := \u0026amp;BTnode{ a[0], nil, nil, } stack := []*BTnode{root} i := 1 for len(stack) \u0026gt; 0 \u0026amp;\u0026amp; i \u0026lt; len(a){ node := stack[0] stack = stack[1:] node.Left = \u0026amp;BTnode{a[i], nil, nil} node.Right = \u0026amp;BTnode{a[i+1], nil, nil} stack = append(stack, node.Left) stack = append(stack, node.Right) i = i + 2 } return root } 常用排序算法 # 常用排序算法在某些公司是会问到的,思路和时间复杂度如果都不知道,对结果的冲击是很大的\n 排序算法 平均时间复杂度 最坏时间复杂度 最好时间复杂度 空间复杂度 稳定性 冒泡排序 O(n²) O(n²) O(n) O(1) 稳定 直接选择排序 O(n²) O(n²) O(n) O(1) 不稳定 直接插入排序 O(n²) O(n²) O(n) O(1) 稳定 快速排序 O(nlogn) O(n²) O(nlogn) O(nlogn) 不稳定 堆排序 O(nlogn) O(nlogn) O(nlogn) O(1) 不稳定 希尔排序 O(nlogn) O(ns) O(n) O(1) 不稳定 归并排序 O(nlogn) O(nlogn) O(nlogn) O(n) 稳定 计数排序 O(n+k) O(n+k) O(n+k) O(n+k) 稳定 基数排序 O(N*M) O(N*M) O(N*M) O(M) 稳定 "},{"id":26,"href":"/mysql/%E4%B8%BB%E4%BB%8E/mysql%E7%9A%84expire_logs_days%E5%8F%82%E6%95%B0%E5%BC%95%E5%8F%91%E7%9A%84%E4%B8%BB%E4%BB%8E%E7%8A%B6%E6%80%81%E4%B8%A2%E5%A4%B1%E9%97%AE%E9%A2%98/","title":"Mysql的expire Logs Days参数引发的主从状态丢失问题","section":"Mysqls","content":"我在测试主从方案的时候发现状态丢失了,同步用的binlog也不见了(binlog doesn\u0026rsquo;t exist),非常奇怪,回顾解决以后写在这里供大家参考。\n报错与原因 # 发现错误信息类似于\nSlave: Table 'XXX' doesn't exist Error running query, ......,We stopped at log 'mysql-bin.000036' position 154. 赶快去找,本想看看主数据的这个文件的position 154是什么语句,使用语句\nshow binlog events in \u0026#39;mysql-bin.000036\u0026#39; 居然返回了502。\n又到主库的服务器查看了下binlog的存储情况,发现binlog的编号是从36开始的,前面的不见了!难道设置了参数定期删除binlog?\n于是又来到了my.cnf文件,查看文件之后找到了一个expire_logs_days。经搜索,确定了这个参数就是删除以前binlog文件的“罪魁祸首”。\n到这来,大概明白了为啥主从同步没有成功,因为这是基于binlog的(逐行扫描sql语句进行同步写入),如果binlog文件不全,就无法正确的进行主从同步。\n解决办法 # 这种情况从删除那一天起,至今所有的同步语句全部都丢失了,所以除非可以精确的知道执行了哪些语句,或者那些语句都不重要可以忽略,不然都必须要清理数据库,备份主库,重新手动更新从库来解决。可以参考我的 备份数据库这篇文章的。\n如果你精确的知识执行了哪些语句,需要先停止从库,执行丢失的语句,再进行从库同步设置。\nmysql\u0026gt; stop slave; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql\u0026gt; reset slave; Query OK, 0 rows affected (0.00 sec) .....执行你的语句 mysql\u0026gt; change master to master_host=\u0026#39;192.168.1.51\u0026#39;, master_user=\u0026#39;replslave\u0026#39;, master_password=\u0026#39;replslave\u0026#39;, master_log_file=\u0026#39;mysql-bin-000002\u0026#39;,master_log_pos=168; Query OK, 0 rows affected (0.11 sec) 如果不知道怎进行从库同步设置,请参考 mariadb/mysql建立主从\n小结 # 1.根据情况设置expire_logs_days,位于mariadb的配置文件中,意思是超时天数,超过这个数值就清理掉过期的binlog 2.还有一个参数叫max_binlog_size,默认是1G,如果设置的太小可能导致大事物被截断,保持默认就好。\n参考: mysql的expire_logs_days参数引发的问题\n"},{"id":27,"href":"/mysql/%E4%B8%BB%E4%BB%8E/relaylog/","title":"Relaylog","section":"Mysqls","content":"前言:MySQL进行主主复制或主从复制的时候会在home目录下面产生相应的relay log,本文档总结这些相关参数的定义及解释.\n1、什么是relay log # The relay log, like the binary log, consists of a set of numbered files containing events that describe database changes, and an index file that contains the names of all used relay log files. The term \u0026ldquo;relay log file\u0026rdquo; generally denotes an individual numbered file containing database events. The term\u0026quot;relay log\u0026quot; collectively denotes the set of numbered relay log files plus the index file\n 来源: 官网文档\n理解:relay log很多方面都跟binary log差不多。\n区别是:从服务器I/O线程将主服务器的二进制日志读取过来记录到从服务器本地文件,然后SQL线程会读取relay-log日志的内容并应用到从服务器,从而使从服务器和主服务器的数据保持一致\n2、relay log的相关参数说明 # 通过语句:show variables like '%relay%'``,查看查询到relay`的所有相关参数\nmysql\u0026gt; show variables like \u0026#39;%relay%\u0026#39;; +-----------------------+----------------+ | Variable_name | Value | +-----------------------+----------------+ | max_relay_log_size | relay_log | relay_log_basename | relay_log_index | relay_log_info_file | relay_log_info_repository | relay_log_purge | relay_log_recovery | relay_log_space_limit | sync_relay_log | sync_relay_log_info +-----------------------+----------------+ 参数详细解释:\nmax_relay_log_size\n标记relay log 允许的最大值,如果该值为0,则默认值为max_binlog_size(1G);如果不为0,则max_relay_log_size则为最大的relay_log文件大小;\nrelay_log\n定义relay_log的位置和名称,如果值为空,则默认位置在数据文件的目录(datadir),文件名为host_name-relay-bin.nnnnnn(By default, relay log file names have the form host_name-relay-bin.nnnnnn in the data directory);\nrelay_log_index\n同relay_log,定义relay_log的位置和名称;一般和relay-log在同一目录\nrelay_log_info_file\n设置relay-log.info的位置和名称(relay-log.info记录MASTER的binary_log的恢复位置和relay_log的位置)\nrelay_log_purge\n是否自动清空不再需要中继日志时。默认值为1(启用)。\nrelay_log_recovery\n当slave从库宕机后,假如relay-log损坏了,导致一部分中继日志没有处理,则自动放弃所有未执行的relay-log,并且重新从master上获取日志,这样就保证了relay-log的完整性。默认情况下该功能是关闭的,将relay_log_recovery的值设置为 1时,可在slave从库上开启该功能,建议开启。\nrelay_log_space_limit\n防止中继日志写满磁盘,这里设置中继日志最大限额。但此设置存在主库崩溃,从库中继日志不全的情况,不到万不得已,不推荐使用;\nsync_relay_log\n这个参数和sync_binlog是一样的,\n当设置为1时,slave的I/O线程每次接收到master发送过来的binlog日志都要写入系统缓冲区,然后刷入relay log中继日志里,这样是最安全的,因为在崩溃的时候,你最多会丢失一个事务,但会造成磁盘的大量I/O。\n当设置为0时,并不是马上就刷入中继日志里,而是由操作系统决定何时来写入,虽然安全性降低了,但减少了大量的磁盘I/O操作。这个值默认是0,可动态修改,建议采用默认值。\nsync_relay_log_info\n这个参数和sync_relay_log参数一样,当设置为1时,slave的I/O线程每次接收到master发送过来的binlog日志都要写入系统缓冲区,然后刷入relay-log.info里,这样是最安全的,因为在崩溃的时候,你最多会丢失一个事务,但会造成磁盘的大量I/O。当设置为0时,并不是马上就刷入relay-log.info里,而是由操作系统决定何时来写入,虽然安全性降低了,但减少了大量的磁盘I/O操作。这个值默认是0,可动态修改,建议采用默认值。\n总结 # 以上只是简单的介绍了每个参数的作用,这些参数具体的设置还是需要根据每个用户的实际系统情况进行设置的;\n推荐从库线上环境使用以下配置\n#slave replicate-do-db=cloud log-slave-updates replicate-wild-do-table=cloud.% binlog-ignore-db=mysql slave-skip-errors=1032,1062,1053,1146,2003 #relay log max_relay_log_size = 0; relay_log=$datadir/relay-bin relay_log_purge = 1; relay_log_recovery = 1; sync_relay_log =0; sync_relay_log_info = 0; cloud是库名,用于跨库同步,如果有多个用逗号隔开 如果是mha环境,则| relay_log_purge 不要开启,设置为0,可以使用 purge_relay_logs 来定期清除 本站整理自:https://blog.51cto.com/douya/1788753\n"},{"id":28,"href":"/mysql/%E4%B8%BB%E4%BB%8E/%E4%B8%BB%E4%BB%8E%E5%88%87%E6%8D%A2/","title":"主从切换","section":"Mysqls","content":"[TOC]\n1、主库停止应用,确认主库不再有数据生成 # 将主库改为 read_only 模式\nmysql\u0026gt; set global super_read_only=on; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; set global read_only=on; Query OK, 0 rows affected (0.00 sec) 2、查看主备库数据是否一致 # 主库:\nmysql\u0026gt; show master status; +------------------+----------+--------------+------------------+-----------------------------------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+----------+--------------+------------------+-----------------------------------------------+ | mysql-bin.000012 | 195 | | | 8876d804-9218-11e8-8eaf-0242ac110002:1-224227 | +------------------+----------+--------------+------------------+-----------------------------------------------+ 1 row in set (0.00 sec) mysql\u0026gt; select @@server_uuid; +--------------------------------------+ | @@server_uuid | +--------------------------------------+ | 8876d804-9218-11e8-8eaf-0242ac110002 | +--------------------------------------+ 1 row in set (0.00 sec) 备库\nmysql\u0026gt; show slave status \\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 172.17.0.2 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000012 Read_Master_Log_Pos: 195 Relay_Log_File: relaylog.000018 Relay_Log_Pos: 409 Relay_Master_Log_File: mysql-bin.000012 Slave_IO_Running: Yes Slave_SQL_Running: Yes Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 0 Last_Error: Skip_Counter: 0 Exec_Master_Log_Pos: 195 Relay_Log_Space: 672 Until_Condition: None Until_Log_File: Until_Log_Pos: 0 Master_SSL_Allowed: No Master_SSL_CA_File: Master_SSL_CA_Path: Master_SSL_Cert: Master_SSL_Cipher: Master_SSL_Key: Seconds_Behind_Master: 0 Master_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 0 Last_SQL_Error: Replicate_Ignore_Server_Ids: Master_Server_Id: 23306 Master_UUID: 8876d804-9218-11e8-8eaf-0242ac110002 Master_Info_File: mysql.slave_master_info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates Master_Retry_Count: 86400 Master_Bind: Last_IO_Error_Timestamp: Last_SQL_Error_Timestamp: Master_SSL_Crl: Master_SSL_Crlpath: Retrieved_Gtid_Set: 8876d804-9218-11e8-8eaf-0242ac110002:1-224227 Executed_Gtid_Set: 8876d804-9218-11e8-8eaf-0242ac110002:1-224227, febb1cb0-922f-11e8-ba72-0242ac110003:1-2 Auto_Position: 1 Replicate_Rewrite_DB: Channel_Name: mysq57-3306 Master_TLS_Version: Master_public_key_path: Get_master_public_key: 0 1 row in set (0.00 sec) #Retrieved_Gtid_Set ,Executed_Gtid_Set这2个值是否和主库的Executed_Gtid_Set 值相等,如果相等,则代表一致 3、备库: 停止 io_thread 和 sql_thread # mysql\u0026gt; stop slave; Query OK, 0 rows affected (0. 11 sec) 4、备库:记录 binlog 的 POS # mysql\u0026gt; show master status; +------------------+-----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+-----------+--------------+------------------+-------------------+ | master2-bin. 001 | 162644437 | | | | +------------------+-----------+--------------+------------------+-------------------+ 1 row in set (0. 00 sec) mysql\u0026gt; flush logs; mysql\u0026gt; show master status; +------------------+-----------+--------------+------------------+-------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set | +------------------+-----------+--------------+------------------+-------------------+ | master2-bin. 002 | 163266389 | | | | +------------------+-----------+--------------+------------------+-------------------+ 1 row in set (0. 00 sec) 5、将备库只读模式关闭 # mysql\u0026gt; show global variables like \u0026#39;%read_only%\u0026#39;; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | innodb_read_only | OFF | | read_only | ON | | super_read_only | ON | | transaction_read_only | OFF | +-----------------------+-------+ 4 rows in set (0. 01 sec) mysql\u0026gt; set global super_read_only=off; Query OK, 0 rows affected (0. 00 sec) mysql\u0026gt; set global read_only=off; Query OK, 0 rows affected (0. 00 sec) mysql\u0026gt; show global variables like \u0026#39;%read_only%\u0026#39;; +-----------------------+-------+ | Variable_name | Value | +-----------------------+-------+ | innodb_read_only | OFF | | read_only | OFF | | super_read_only | OFF | | transaction_read_only | OFF | +-----------------------+-------+ 4 rows in set (0. 00 sec) 6、新备库执行 change master to 语句, 指向新主库 # CHANGE MASTER 分 2 种情况,一种是开启了 GTID 模式,一种是未开启 GTID 模式\nGTID 模式:\nCHANGE MASTER TO MASTER_HOST=\u0026#39;oracle2.example.com\u0026#39;, MASTER_USER=\u0026#39;repl\u0026#39;, MASTER_PASSWORD=\u0026#39;password\u0026#39;, MASTER_PORT=3306, MASTER_AUTO_POSITION=1; 未开启 GTID 模式\nCHANGE MASTER TO MASTER_HOST=\u0026#39;oracle2.example.com\u0026#39;, MASTER_USER=\u0026#39;repl\u0026#39;, MASTER_PASSWORD=\u0026#39;password\u0026#39;, MASTER_PORT=3306, MASTER_LOG_FILE=\u0026#39;master2-bin. 002\u0026#39;, MASTER_LOG_POS=163266389; "},{"id":29,"href":"/mysql/%E4%B8%BB%E4%BB%8E/%E5%B8%B8%E8%A7%81%E6%95%85%E9%9A%9C/","title":"常见故障","section":"Mysqls","content":"【ERROR】1452:无法在外键的表插入或更新参考主键没有的数据。 # 主键在从库不存在时会发生这样的问题,报1452错误。此时可以检查参考的表的主键是否有主库对应的数据,如果有,则插入参考的表相应的数据,再开启复制恢复SQL线程。\n【ERROR】1032:删除或更新从库的数据,从库找不到记录。 # 此时,主库的数据是比从库新的,可以采取从库添加相同的数据再开启复制恢复SQL线程。\n【ERROR】1062:从库插入数据,发生唯一性冲突。 # 此时从库已经有相同主键的数据,如果再插入相同主键值的数据则会报错。可以查看主库的改行数据与从库的要插入数据是否一致,如一致则跳过错误,恢复SQL线程,如不一致,则以主库为准,将从库的该行记录删除,再开启复制。\n【ERROR】1201: Could not initialize master info structure. # 出现这个错误的原因是因为从库之前已经做过主从复制,所以需要先停止从库,再进行从库同步设置。\n具体的解决方法如下:\nmysql\u0026gt; change master to master_host=\u0026#39;192.168.1.51\u0026#39;, master_user=\u0026#39;replslave\u0026#39;, master_password=\u0026#39;replslave\u0026#39;, master_log_file=\u0026#39;mysql-bin-000002\u0026#39;,master_log_pos=168; ERROR 1201 (HY000): Could not initialize master info structure; more error messa ges can be found in the MySQL error log mysql\u0026gt; stop slave; Query OK, 0 rows affected, 1 warning (0.00 sec) mysql\u0026gt; reset slave; Query OK, 0 rows affected (0.00 sec) mysql\u0026gt; change master to master_host=\u0026#39;192.168.1.51\u0026#39;, master_user=\u0026#39;replslave\u0026#39;, master_password=\u0026#39;replslave\u0026#39;, master_log_file=\u0026#39;mysql-bin-000002\u0026#39;,master_log_pos=168; Query OK, 0 rows affected (0.11 sec) 方法来自: CSU-Max\n"},{"id":30,"href":"/mysql/%E4%B8%BB%E4%BB%8E/%E5%BB%BA%E7%AB%8B%E4%B8%BB%E4%BB%8E/","title":"建立主从","section":"Mysqls","content":"前提 # 本方案是两节点主从方案,只要建立好主从,及时数据库挂掉又拉起主从模式不会失效。\n 保证时间同步 保证都安装了mysql/mariadb 建立主从的过程 # 这里介绍的是两节点主从,如果是集群模式,至少需要三个节点,因为偶数个节点是导致脑裂高发的原因(无法确定该同步谁的)。\n 主从服务器节点设置不同的server-id 启用二进制日志和relaylog 主节点创建一个拥有复制权限的用户账号 查询主节点binlog信息 设置从节点同步主节点 停止所有写入 # 在所有服务器上执行此步\n在所有服务器上执行此步\n在所有服务器上执行此步\n停止所有写入是为了防止数据设置同步的过程中数据不一致。\n如果 mariadb 是通过 hosts 文件中的域名进行访问的,那么只需要编辑 /etc/hosts , 把mysql的域名解析删掉就可以停止所有读写,执行以下命令。\n关掉所有读写mysql的服务,你也可以直接用iptables来禁用端口通信(如果应用有自动重连机制的话,否则只能重启应用了)\n等待 1 分钟,依次进入集群中所有的 mariadb ,查看进程状态,确保没有额外的读写操作( command 列除了 show processlist 外没有多余的 sleep 和 query )。\nMariaDB [(none)]\u0026gt; show processlist; 备份与导入 # 首先,你需要保证所有的节点数据一致,在升级过程中万一升级失败能及时的恢复数据。\n请参考本小册 备份数据库\n添加一个专门用来同步的用户 # 在从节点中的 mariadb 执行以下命令,如果全部输出 ok,则继续。\n/usr/local/mariadb/bin/mysql -A -e \u0026#34;GRANT replication slave ON *.* TO \u0026#39;rep\u0026#39;@\u0026#39;%\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; flush privileges;\u0026#34; mysql -A -urep -p123456 -e \u0026#34;select \u0026#39;ok\u0026#39;;\u0026#34; 停止所有节点 # 执行以下命令停止\n/usr/local/mariadb/bin/mysqladmin shutdown 此时节点应该自动停止了,检查是否没有 mariadb 和 mysql 进程,如果有按需求判断是否停掉(kill)。\nps -ef | grep -E \u0026#34;mariadb|mysql\u0026#34; 更新 mysql 配置(从节点) # 先创建relaylog日志存储的目录,用来防止同步波动缓存同步信息。(注意,请设置成你自己的数据目录)\nmkdir /data/mariadb/relaylog chown -R mysql.mysql /data/mariadb/relaylog/ 添加配置到 [mysqld] 配置节中(注意现在不启动),该配置表示我们只同步 cloud 库和其下的表,如果要同步更多的库和表可以用逗号分隔,追加。\n如果想了解各个参数是什么含义可以到本小册 relaylog里看\nvim /etc/my.cnf.d/server.cnf # 添加内容如下 replicate-do-db=cloud #cloud是你想同步的库名,如果有多个请用逗号隔开 log-slave-updates replicate-wild-do-table=cloud.% #cloud是你想同步的库名,cloud.% 代表这个库下面的所有表,如果有多个请用逗号隔开 binlog-ignore-db=mysql # 忽略mysql库 max_relay_log_size = 0 relay_log=/data/mariadb/relaylog/relay-bin #请设置成正确的目录,上面刚刚创建的那个,最后的relay-bin是文件前缀 relay_log_purge = 1 relay_log_recovery = 1 sync_relay_log =0 sync_relay_log_info = 0 slave-skip-errors=1032,1062,1053,1146,2003 参考添加成功的图片\n 启动主节点并获取主节点信息 # 如果你的目录不同请自行修改\nmkdir -p /var/run/mariadb; chown -R mysql:mysql /var/run/mariadb; /usr/local/mariadb/bin/mysqld_safe --datadir=/data/mariadb/data --pid-file=/var/run/mariadb/mariadb.pid \u0026gt; /dev/null 2\u0026gt;\u0026amp;1 \u0026amp; 获取同步主节点的关键信息\n进入主节点数据库中,执行命令\nunlock tables; show master status; 得到同步主节点的关键信息\n 启动从节点并设置同步信息 # mkdir -p /var/run/mariadb; chown -R mysql:mysql /var/run/mariadb; /usr/local/mariadb/bin/mysqld_safe --datadir=/data/mariadb/data --pid-file=/var/run/mariadb/mariadb.pid \u0026gt; /dev/null 2\u0026gt;\u0026amp;1 \u0026amp; 建立主从结构\n进入从节点的数据库,指定主库信息,完成主从关系建立(注意:下面命令中的【主节点 ip 地址】别忘记替换,使用 eth0 本地网卡的 ip ,不要使用浮动 ip ,也不要使用 vip )\n账号和密码就是我们刚刚设置的,\nunlock tables; CHANGE MASTER TO MASTER_HOST=\u0026#39;主节点的ip地址\u0026#39;, MASTER_PORT=3306, MASTER_USER=\u0026#39;rep\u0026#39;, MASTER_PASSWORD=\u0026#39;123456\u0026#39;, MASTER_LOG_FILE=\u0026#39;mysql-bin.000171\u0026#39;, MASTER_LOG_POS=33105258; start slave; #开始同步 查看 slave 的状态,注意查看 slave 的进程状态,下面红色方框中圈起来的是两个 Yes 就表示状态正常了,注意等待主库复制的延迟秒数变为 0 Seconds_Behind_Master: 0\nshow slave status \\G the end\n可能遇到的坑 # 请根据你的业务写入速度和同步速度,设置好主节点的binlog大小和过期时间,具体设置方法请参考本小册 mysql 正确清理 binlog 日志的两种方法中的方法二、通过设置 binlog 过期的时间,使系统自动删除 binlog 文件。\n如果你想建立互为主从 # 如果你想建立互为主从,那么你可以把主节点当作从节点,把从节点当作主节点,从本文的\n 添加一个专门用来同步的用户开始重新执行,直到最后一步。\n"},{"id":31,"href":"/mysql/%E4%B8%BB%E4%BB%8E/%E9%9B%86%E7%BE%A4%E6%96%B9%E6%A1%88%E5%88%87%E6%8D%A2%E4%B8%BA%E4%BA%92%E4%B8%BA%E4%B8%BB%E4%BB%8E/","title":"集群方案切换为互为主从","section":"Mysqls","content":"文档目标 # 由于集群模式容易出现脑裂,恢复起来速度慢,难度大,不稳定,所以采用主从模式托管,容易恢复,更加稳定,同时建立成功主从模式以后,及时关机再重启也不会丢失主从状态。\n通过本文档可以把mariadb或mysql从三节点集群模式切换为互为主从(成环)\n 相对于集群的好处就是不会发生脑裂,故障恢复相对比较容易 坏处是如果挂掉一个节点时,剩下的两个节点就会变成主从模式,如果应用读写到从节点,那么数据就不同步了,需要等待恢复以后才能自动同步(所以我们要控制,挂掉一个节点后,让读写正确的切换到主节点上,可能要keepalived、haproxy的配合) [TOC]\n0、检查是否是集群状态 # ps: 监测当前是否是集群模式, 而且整个集群的所有服务器正常,如果不是 ON ,则停止操作,并联系管理员。\n在集群中所有服务器上执行\nMariaDB [(none)]\u0026gt; show global variables like \u0026#39;%wsrep_on%\u0026#39;; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | wsrep_on | ON | +---------------+-------+ 1 row in set (0.00 sec) 1、停止所有写入 # 在集群所有服务器上执行此步\n在集群所有服务器上执行此步\n在集群所有服务器上执行此步\n如果 mariadb 是通过 hosts 文件中的域名进行访问的,那么只需要编辑 /etc/hosts , 把 mysql.cloud.local 的域名解析删掉就可以停止所有读写,执行以下命令。\ncp /etc/hosts /etc/hosts.bak sed -i \u0026#34;/mysql.cloud.local/d\u0026#34; /etc/hosts 等待1分钟,依次进入集群中所有的 mariadb ,查看进程状态,确保没有额外的读写操作( command 列除了 show processlist 外没有多余的 sleep 和 query )。\nMariaDB [(none)]\u0026gt; show processlist; 2、添加一个专门用来同步的用户 # 在节点1中的 mariadb 执行以下命令,如果全部输出ok,则继续。\n/usr/local/mariadb/bin/mysql -A -e \u0026#34;GRANT replication slave ON *.* TO \u0026#39;rep\u0026#39;@\u0026#39;%\u0026#39; IDENTIFIED BY \u0026#39;123456\u0026#39;; flush privileges;\u0026#34; mysql -A -urep -p123456 -e \u0026#34;select \u0026#39;ok\u0026#39;;\u0026#34; 依次在第二台、第三台上重复此操作\n3、设置所有集群中的节点为只读。 # 进入节点1中的 mariadb 执行以下命令\nflush table with read lock; 依次在第二台、第三台上顺序重复此操作\n4、停止集群 # 如果你使用的是supervisor守护mariadb,则\nsupervisorctl stop mariadb 如果你使用的是systemctl托管,则\nsystemctl stop mariadb 此时节点应该自动停止了,检查是否没有 mariadb 和 mysql 进程,如果有按需求判断是否停掉。\nps -ef | grep -E \u0026#34;mariadb|mysql\u0026#34; 如果没有停止,则执行以下命令停止\n/usr/local/mariadb/bin/mysqladmin shutdown 依次在第二台、第三台上顺序重复 4、停止集群操作\n5、配置集群中每个节点为单点 # 执行下面命令改为单点(注意现在不启动),执行完检查是不是变成了 wsrep_on=OFF\nsed -i \u0026#34;s/^wsrep_on=.*/wsrep_on=OFF/g\u0026#34; /etc/my.cnf.d/server.cnf 创建relaylog日志存储的目录,用来防止同步波动缓存同步信息。\nmkdir /data/mariadb/relaylog chown -R mysql.mysql /data/mariadb/relaylog/ 添加配置到 [mysqld] 配置节中(注意现在不启动),该配置表示我们只同步 cloud 库和其下的表,如果要同步更多的库和表可以用逗号分隔,追加。\n如果想了解各个参数是什么含义可以到本小册 relaylog里看\nvim /etc/my.cnf.d/server.cnf # 添加内容如下 replicate-do-db=cloud #cloud是你想同步的库名,如果有多个请用逗号隔开 log-slave-updates replicate-wild-do-table=cloud.% #cloud是你想同步的库名,cloud.% 代表这个库下面的所有表,如果有多个请用逗号隔开 binlog-ignore-db=mysql # 忽略mysql库 max_relay_log_size = 0 relay_log=/data/mariadb/relaylog/relay-bin #请设置成正确的目录,上面刚刚创建的那个,最后的relay-bin是文件前缀 relay_log_purge = 1 relay_log_recovery = 1 sync_relay_log =0 sync_relay_log_info = 0 slave-skip-errors=1032,1062,1053,1146,2003 参考添加成功的图片\n 依次在第二台、第三台上顺序重复 5、配置集群中每个节点为单点操作\n6、建立 2同步3 主从结构 # 假如是三节点主从,我们使用 1同步2 , 2同步3 , 3同步1 的方案,现在三台机器上的 mariadb 都是停止状态的。\n2同步3 ,所以 3 是主节点, 2 是从节点。\n逆序启动,并建立主从\n启动节点3\nmkdir -p /var/run/mariadb; chown -R mysql:mysql /var/run/mariadb; /usr/local/mariadb/bin/mysqld_safe --datadir=/data/mariadb/data --pid-file=/var/run/mariadb/mariadb.pid \u0026gt; /dev/null 2\u0026gt;\u0026amp;1 \u0026amp; 获取同步主节点的关键信息\n进入主【节点3】数据库中,执行命令\nunlock tables; show master status; 得到同步主节点的关键信息\n 启动节点2\nmkdir -p /var/run/mariadb; chown -R mysql:mysql /var/run/mariadb; /usr/local/mariadb/bin/mysqld_safe --datadir=/data/mariadb/data --pid-file=/var/run/mariadb/mariadb.pid \u0026gt; /dev/null 2\u0026gt;\u0026amp;1 \u0026amp; 建立主从结构\n进入从【节点2】中的数据库,指定主库信息,完成主从关系建立(注意:节点3的ip地址别忘记替换,使用 eth0 本地网卡的 ip ,不要使用浮动 ip ,也不要使用 vip )\nunlock tables; CHANGE MASTER TO MASTER_HOST=\u0026#39;节点3的ip地址\u0026#39;, MASTER_PORT=3306, MASTER_USER=\u0026#39;rep\u0026#39;, MASTER_PASSWORD=\u0026#39;123456\u0026#39;, MASTER_LOG_FILE=\u0026#39;mysql-bin.000171\u0026#39;, MASTER_LOG_POS=33105258; start slave; #开始同步 查看slave的状态,注意查看slave的进程状态,下面红色方框中圈起来的是两个 Yes 就表示状态正常了,注意等待主库复制的延迟秒数变为0 Seconds_Behind_Master: 0\nshow slave status \\G 7、测试 2同步3 主从同步状态 # 进入主节点3的数据库中,执行sql命令,创建一个测试表。\nuse cloud; CREATE TABLE IF NOT EXISTS `testrep` ( `xx_id` INT UNSIGNED AUTO_INCREMENT, PRIMARY KEY ( `xx_id` ) )ENGINE=InnoDB DEFAULT CHARSET=utf8; show tables like \u0026#34;testrep\u0026#34;; 结果如下则创建成功\nMariaDB [cloud]\u0026gt; show tables like \u0026#34;testrep\u0026#34;; +---------------------------+ | Tables_in_cloud (testrep) | +---------------------------+ | testrep | +---------------------------+ 1 row in set (0.00 sec) 进入从节点2的数据库上,再执行查询命令查看同步状态,如果同上面结果一样则表示主从建立成功\nuse cloud; show tables like \u0026#34;testrep\u0026#34;; 8、建立 1同步2 主从结构 # 同上面类似,只不过是在 1 和 2 节点上执行而已。\n要注意四个点!!!\n 重复 6 和 7 两步,把【主节点3】和【从节点2】换成【主节点2】和【从节点1】 注意此时节点2已经启动了 不要再重复启动节点 unlock tables; 重复执行无所谓 步骤【 7 主从同步状态】不要再重复创建测试表了,主从建立成功后直接查看【从节点 1 】中是否有 testrep 表就可以了 9、建立 3同步1 主从结构 # 同上面类似,只不过是在 3 和 1 节点上执行而已。\n要注意四个点!!!\n 重复 1.6 即可,把【主节点3】和【从节点2】换成【主节点1】和【从节点3】 注意此时所有节点都已经启动了 不要再重复启动节点 测试同步状态\n在节点1上执行命令\nMariaDB [(none)]\u0026gt; use cloud; Database changed MariaDB [cloud]\u0026gt; drop table testrep; Query OK, 0 rows affected (0.01 sec) MariaDB [cloud]\u0026gt; show tables like \u0026#34;testrep\u0026#34;; Empty set (0.00 sec) 在节点3和节点2和执行以下命令,如果结果都是 Empty set ,表示三节点主从模式建立成功。\nMariaDB [cloud]\u0026gt; show tables like \u0026#34;testrep\u0026#34;; Empty set (0.00 sec) 10、恢复域名解析 # 在全部服务器上执行\ncp /etc/hosts.bak /etc/hosts "},{"id":32,"href":"/mysql/%E8%BF%90%E7%BB%B4/mysql%E6%AD%A3%E7%A1%AE%E6%B8%85%E7%90%86binlog%E6%97%A5%E5%BF%97%E7%9A%84%E4%B8%A4%E7%A7%8D%E6%96%B9%E6%B3%95/","title":"Mysql正确清理binlog日志的两种方法","section":"Mysqls","content":"前言 # MySQL 中的 binlog 日志记录了数据库中数据的变动,便于对数据的基于时间点和基于位置的恢复,但是 binlog 也会日渐增大,占用很大的磁盘空间,因此,要对 binlog 使用正确安全的方法清理掉一部分没用的日志\n方法一、手动清理 binlog # 清理前的准备:\n查看主库和从库正在使用的 binlog 是哪个文件\nshow master status \\G show slave status \\G 在删除 binlog 日志之前,首先对 binlog 日志备份,以防万一\n开始动手删除 binlog: 删除指定日期以前的日志索引中 binlog 日志文件\npurge master logs before\u0026#39;2016-09-01 17:20:00\u0026#39;; 或 删除指定日志文件的日志索引中 binlog 日志文件\npurge master logs to\u0026#39;mysql-bin.000022\u0026#39;; 注意:时间和文件名一定不可以写错,尤其是时间中的年和文件名中的序号,以防不小心将正在使用的 binlog 删除!!!\u0026gt; 切勿删除正在使用的 binlog!!! 使用该语法,会将对应的文件和 mysql-bin.index 中的对应路径删除。\n方法二、通过设置 binlog 过期的时间,使系统自动删除 binlog 文件 # 临时生效\nmysql\u0026gt; show variables like \u0026#39;expire_logs_days\u0026#39;; +------------------+-------+ | Variable_name | Value | +------------------+-------+ | expire_logs_days | 0 | +------------------+-------+ mysql\u0026gt; set global expire_logs_days = 30; #设置binlog多少天过期 长期生效需要修改配置文件\nvim /etc/my.cnf 内容 expire_logs_days=3 #代表3天 max_binlog_size = 1073741824 #默认是1G 注意:\n过期时间设置的要适当,对于主从复制,要看从库的延迟决定过期时间,避免主库 binlog 还未传到从库便因过期而删除,导致主从不一致!!!\n本站整理自: mysql 正确清理 binlog 日志的两种方法\n"},{"id":33,"href":"/mysql/%E8%BF%90%E7%BB%B4/%E5%A4%87%E4%BB%BD%E6%95%B0%E6%8D%AE%E5%BA%93/","title":"备份数据库","section":"Mysqls","content":"备份数据库 # 主库锁表\nflush table with read lock; 备份数据库(会有压缩,测试数据 11G,得到备份文件 227M)\n 其中 max_allowed_packet 和 net_buffer_length 两个参数是用来加速的,可以查看从库数据 如果 root 有密码请加上参数例如 -uroot -proot /usr/local/mariadb/bin/mysqldump --max_allowed_packet=33554432 --net_buffer_length=8192 --events -A -B |gzip \u0026gt;/server/backup/mysql_bak.$(date +%F).sql.gz PS: 如果上面的命令报错如下\nCouldn\u0026#39;t execute \u0026#39;show events\u0026#39;: Cannot proceed because system tables used by Event Scheduler were found damaged at server start (1577) 请执行命令(如果 root 有密码请加上参数例如 -uroot -proot )\n/usr/local/mariadb/bin/mysql_upgrade 备份结束后,解锁主库,恢复读写\nunlock tables; 拷贝到从库中\n 注意日期一定有所不同,如果端口不是默认的请叫参数 -P 端口号 注意确保 /root 目录有足够的空间,如果空间不够请切换目录 scp /server/backup/mysql_bak.2020-06-04.sql.gz monitor-host02:/root 从库 # 进入备份文件目录,解压(11G 的库,sql 文件只有 1.2G,注意空间比例)\ncd /root gzip -d mysql_bak.2020-06-04.sql.gz 导入数据\n 注意始终从库中不可以有写入 请先测试mysql -A是否可以进入数据库 mysql -A \u0026lt; mysql_bak.2020-06-04.sql \u0026amp; 实际上这种方式遇到特别大的数据还是很慢,可以考虑使用xtrabackup来自动备份数据库。\n"},{"id":34,"href":"/mysql/%E8%BF%90%E7%BB%B4/%E5%BF%98%E8%AE%B0%E5%AF%86%E7%A0%81/","title":"忘记密码","section":"Mysqls","content":"root 用户无法免密登陆,同时又忘记了密码?\n/usr/local/mariadb/bin/mysqladmin -u有权限的用户名 -p密码 shutdown /usr/local/mariadb/bin/mysqld_safe --skip-grant-tables \u0026amp; mysql -A -e \u0026#34;update mysql.user set password=password(\u0026#39;root\u0026#39;) where user=\u0026#39;root\u0026#39;\u0026#34;; mysql -A -e \u0026#34;flush privileges;\u0026#34; /usr/local/mariadb/bin/mysqladmin shutdown mkdir -p /var/run/mariadb; chown -R mysql:mysql /var/run/mariadb; /usr/local/mariadb/bin/mysqld_safe --datadir=/data/mariadb/data --pid-file=/var/run/mariadb/mariadb.pid \u0026gt; /dev/null 2\u0026gt;\u0026amp;1 \u0026amp; "},{"id":35,"href":"/mysql/%E8%BF%90%E7%BB%B4/%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9/","title":"注意事项","section":"Mysqls","content":"注意事项 # mysql.sock不要放在tmp目录下面 # 执行命令,更改mariadb.sock文件位置,防止/tmp目录下文件被删除导致挂掉\nsed -i \u0026#34;s/\\/tmp\\/mariadb.sock/\\/data\\/mariadb\\/mariadb.sock/g\u0026#34; /etc/my.cnf.d/client.cnf sed -i \u0026#34;s/\\/tmp\\/mariadb.sock/\\/data\\/mariadb\\/mariadb.sock/g\u0026#34; /etc/my.cnf 修改完确认上面命令中的两个文件是不是都成功修改了mariadb.sock的目录,为/data/mariadb/mariadb.sock,如果没有手动修改。\n"},{"id":36,"href":"/mysql/%E8%BF%90%E7%BB%B4/%E9%87%8D%E5%BB%BAmysql%E6%95%B0%E6%8D%AE%E5%BA%93%E7%9A%84%E6%96%B9%E6%B3%95/","title":"重建mysql数据库的方法","section":"Mysqls","content":"本操作是高危操作,会导致所有数据丢掉,用来恢复无法恢复的mysql,重建以后再导入备份好的数据。\n1、停止MySql数据库的运行 2、删除mysql/var里面的所有数据库,这里是数据目录,具体请查看/etc/my.cnf的配置 3、进入mysql/bin目录下,执行./mysql_install_db命令 此时会在mysql/var目录下创建两个目录文件mysql、test 4、修改mysql、test两个目录及目录下所有文件的权限:\nchown mysql:mysql -R mysql test 注意这里一定要加上-R参数,否则启动会报错 5、启动数据库\n./mysqld_safe --user=mysql \u0026amp; 6、修改root密码\nmysql/bin/mysqladmin -u root password \u0026#34;yourpasswd\u0026#34; 这样,就完成了MySql数据库的重建了。关于停止MySql的运行,直接用启动MYSQL服务命令也行,也可以用停止进程的方法。启动数据库也可以直接用启动MYSQL服务的命令来启动。\n"},{"id":37,"href":"/mysql/%E9%9B%86%E7%BE%A4/%E5%BF%AB%E9%80%9F%E6%8B%89%E8%B5%B7/","title":"快速拉起","section":"Mysqls","content":"当Mariadb集群因故障重启时,有时会遇到Mariadb Galera Cluster集群无法正常启动的情况。有很多方式能将数据库拉起,但是如何做到快速启动,又不丢失数据呢?\n找到数据最新的节点 # 对比三个节点日志中的New cluster view: global state状态,可知道那个节点中的数据是最新的。\n[mysql@test45 logs]$ grep \u0026#34;New cluster view\u0026#34; mariadb.log |awk -F: \u0026#39;END { print $1\u0026#34;:\u0026#34;$2\u0026#34;:\u0026#34;$3 $6\u0026#34;:\u0026#34;$7}\u0026#39; 180518 14:59:00 [Note] WSREP 874d8e7e-5980-11e8-8c23-83493ba049c2:\u0026lt;span style=\u0026#34;color:#ff0000;\u0026#34;\u0026gt;\u0026lt;strong\u0026gt;2840\u0026lt;/strong\u0026gt;\u0026lt;/span\u0026gt;, view# 3 [mysql@test44 logs]$ grep \u0026#34;New cluster view\u0026#34; mariadb.log |awk -F: \u0026#39;END { print $1\u0026#34;:\u0026#34;$2\u0026#34;:\u0026#34;$3 $6\u0026#34;:\u0026#34;$7}\u0026#39; 180518 15:55:03 [Note] WSREP 874d8e7e-5980-11e8-8c23-83493ba049c2:\u0026lt;strong\u0026gt;\u0026lt;span style=\u0026#34;color:#ff0000;\u0026#34;\u0026gt;3068\u0026lt;/span\u0026gt;\u0026lt;/strong\u0026gt;, view# 4 [mysql@test43 logs]$ grep \u0026#34;New cluster view\u0026#34; mariadb.log |awk -F: \u0026#39;END { print $1\u0026#34;:\u0026#34;$2\u0026#34;:\u0026#34;$3 $6\u0026#34;:\u0026#34;$7}\u0026#39; 180518 15:55:41 [Note] WSREP 874d8e7e-5980-11e8-8c23-83493ba049c2:\u0026lt;strong\u0026gt;\u0026lt;span style=\u0026#34;color:#ff0000;\u0026#34;\u0026gt;3111\u0026lt;/span\u0026gt;\u0026lt;/strong\u0026gt;, view# -1 得知三个节点数据的状态 :3111\u0026gt;3068\u0026gt;2840\n所以 [mysql@test43 logs] 上的节点上的数据是最新的。\n修改最新节点上的grastate.dat文件 # 修改这个最新节点上的grastate.dat文件,文件目录可能有所不同,他位于Mariadb的数据文件目录,即datadir\n[mysql@test43 script]$more grastate.dat # GALERA saved state version: 2.1 uuid: 874d8e7e-5980-11e8-8c23-83493ba049c2 seqno: -1 safe_to_bootstrap: 0 修改为:\n[mysql@test43 data]$ vim ../data/grastate.dat # GALERA saved state version: 2.1 uuid: 874d8e7e-5980-11e8-8c23-83493ba049c2 seqno: 3111 safe_to_bootstrap: 1 将seqno设置为3111(这是上面取到的,不是固定的)。\nsafe_to_bootstrap设置为 1\n注意:在启动数据库前,最好对数据目录做备份,以防万一出问题可以还原事故现场。\n按顺序启动节点 # 1. 在【mysql@test43】节点以 wsrep_new_cluster 的方式启动 mysql\nmysqld_safe --defaults-file=/data/conf/my.cnf --user=mysql --wsrep_new_cluster \u0026amp; 2.以正常方式启动其它两个节点\nmysqld_safe --defaults-file=/data/conf/my.cnf --user=mysql\u0026amp; 根据这两个节点数据的情况,会自动做SST 或IST.\ndocker 方式 # 如果数据库是以docker拉起的,可以修改my.cnf配置文件中的wsrep_cluster_address参数,启动mysql。\n如将\nwsrep_cluster_address = gcomm://172.x.x.100:4567,172.x.x.101::4567,172.x.x.102::4567 调整为:\nwsrep_cluster_address = gcomm:// 再\ndocker start mariadb 待集群正常启动后,切记还恢复wsrep_cluster_address 参数设置。\n引用: Mariadb Galera Cluster 故障快速拉起\n留给你的问题 # 如果要写一个集群自动恢复的脚本,你有什么思路吗?\n记得\n 免密 小心丢数据 快速恢复 "},{"id":38,"href":"/readme/","title":"Readme","section":"","content":"interview-leetcode🔥 # interview-leetcode 项目旨在快速复习找到工作,包括高频算法和真实面试问题问答,当然这些基本知识,也是工程师的学习素养,全部掌握对于工作来说也会有极大的帮助。\n \n 如果这个项目能为您带来帮助,我将感到非常荣幸!😁 如果您也对这个项目感兴趣,请 点此跳转项目🌟Star 以示鼓励, 项目会 持续更新,谢谢你的支持。 成员结构 # 这是一群爱学习的互联网人,联合起来完成了这个知识库的维护\n 编程伐木累线上组织的成员来源于各个公司和各大高校,在群里我们可以一起:\n 认识各大公司一线开发者,和他们进行交流分享 获得内推机会,我们的成员来自国内众多一线互联网公司、还有专业的人力资源在线答疑 讨论最新互联网前沿知识和目前的趋势 反馈本书遇到的问题,共同改善内容质量 成为本书的编辑者,为手册贡献更多优质的内容 日常分享算法、Go等语言、工程、架构、运维等技术内容 群内分享工作经验、职场感悟、学习方法、商业、投资、协作、管理等方面的想法 成为组织的一员,可在群内在线答疑交流、以及反馈知识库中的问题。\n算法 # 刷题平台: LeetCode中国\n 算法面试注意 数据结构 LeetCode-hot100-easy LeetCode-hot100-medium LeetCode-hot100-difficult 其他高频算法 华为机试 面试高频问题大全 # 面试高频问题,是技术交流群每日一问讨论并总结得出,复习的时候注意反思节奏,用过没,是什么,哪个更好,为什么可以这样,是怎么实现的\n 面试高频问题-操作系统 面试高频问题-linux 面试高频问题-go 面试高频问题-redis 面试高频问题-mysql 面试高频问题-网络 面试高频问题-docker 面试高频问题-k8s TODO面试高频问题-前端 TODO面试高频问题-mongoDB 项目一般问什么 面试高频问题-待解答 更多内容见电子书 leetcode.coding3min.com\n如何参与协作? # 写作规范参考: 中文技术文档的写作规范\n 如果你不会使用git,参考 每天三分钟玩转Git 如果有想法和创意,请提 issue或者进群提 如果想贡献代码,请提 PR 算法刷题请使用vscode LeetCode 插件,自动创建文件、手动提交测试 如果发现错误,可以在电子书右上角直接点击编辑,会自动fork,修改完毕后手动提出pr License # 本仓库的内容除了少量引用,引用内容会做标注,其余都是原创,在您引用本仓库内容或者对内容进行修改演绎时,请署名并以相同方式共享,谢谢。\n转载文章请在开头明显处标明该页面地址,公众号等其它转载请联系 [email protected],或加我微信。\n最后 # 如果文中有误,欢迎提pr或者issue,一旦合并或采纳作为贡献奖励可以联系我直接无门槛加入 技术交流群\n致谢 # 感谢以下人员对本仓库做出的贡献 💖\n"},{"id":39,"href":"/todo/","title":"Todo","section":"","content":"待解答 # 介绍 # 此处是未解答的问题列表,欢迎挑战,可以提 issue 和 pr , 解答时提issue直接粘贴题目发起起问题即可\n前端 # react中的diff算法是什么,为什么要做diff算法,key有什么用 # 提问:小雨\n回答-橙子: 如果有问题请更正我,谢谢! key 这个答案很简单就两个作用1. 为了高效,, key 相当于一个标记, 快速的能够比较出现在节点和之前节点的区别,比如你一个数组, 你有下标就能更快的找到,diff算法里面就提到了先比较key, 如果没有key , 那undefiend == undefined ,这样就会产生第二个问题, 容易就地复用所以key的第二个作用就是为了避免就地复用, 举个简单例子, 你写一个循环, 循环里面有每一个元素都有一个checkbox, 有一个他对应的文案, 这时候你进行一个排序, 没有key 你会发现内容排序变了,但是选中的checkbox 并没有跟着内容一起动\ndiff 算法我不会react 所以不能确定是否和vue 一样, 如果有需要vue的diff 算法解释请联系我\n闭包与Hooks设计有什么联系? # 提问:小雨\njava # hashmap和treemap的区别 # go # hashmap是怎么实现O1算法的时间复杂度的 # 为什么数组删除和插入是O(n) # redis # Redis 原理与调优经验 # 有用过哪些数据结构 # zset 是怎么使用的 # 介绍一下持久化aof rdb # 分片是如何存储 # mysql # mysql的事物隔离级别有哪些 默认的事物隔离级别是什么? # mysql 数据库 undolog和binlog的区别是什么? # mysql 回表和覆盖索引是什么? # 回表就是普通索引,定位id,再通过聚簇索引定位到数据,覆盖索引就是把单索引升级成联合索引,将多个列一起做索引,能防止回表\nmysql insert需不需要加锁? # mysql主从的实现原理 # 无主键插入重复记录可以成功吗? # 字符串作主键来建立索引和数字作主键有什么区别? # mysql用id自增好,还是字符串主键好,为什么,插入效率有什么区别 # 怎么保证重试时不重复插入 # 慢SQL优化 # 网络 # 网络模式 epoll sleep,哪种会比较快一点,为什么会快一点 # 一个TCP连接同一时间可以发起多少个HTTP1.1的请求?浏览器是怎么提高图片渲染并发 # 回答-橙子: 如果有问题请更正我,谢谢! 一个tcp 只能同时发起一个http请求,其他的要发需要等待, html , js, css 优先级高于图片, 在http 1.1 中同一个域名下可以建立6个持久连接(connect: keep-alive), 如果有n个域名资源, 那就可以 同时建立 n*6 个连接, 如果全部占用, 那后面的资源就需要等待, 浏览器是怎么提高图片渲染并发: 这里应该可以是提高所有资源的并发感觉可以貌似是一样的, 有两种解决方案1, 资源放在不同域名下 2. 升级http1.1 到2.0 , 2.0 中没有这种建立tcp 连接的限制\nHTTP常见状态码有哪些,都什么作用 # http中的keepalived是什么 # 回答-橙子: 如果有问题请更正我,谢谢! keep-alive 是http1.1 中出现的, 是为了减少建立tcp连接, 建立一次tcp 连接不断开中间可以有多个http 请求, 但必须是按顺序的\npost和put在语义上有什么区别? # 计算机网络http、spdy是什么? # 回答-橙子: 如果有问题请更正我,谢谢! http: 超文本传输协议, 解释来说就是在计算机世界里两点之间传输 图片, html , 视频等超文本数据的约定和规范, http 是一个协议。\n如何避免劫持 # 听说过哪些网络攻击方式,简单介绍一下 # 回答-橙子: 如果有问题请更正我,谢谢! 后续我自己用自己的话描述会补充全, xss csrf sql 注入\nTLS是哪一层 # HTTPS的连接流程是怎样;HTTPS的加密方式 # 对称加密和非对称加密都有 回答-橙子: 如果有问题请更正我,谢谢! 补充楼上 对称加密 是加密url , 非对称加密是加密key 后续会补充解释和一张图\nDNS的过程,DNS劫持是什么 # linux # 怎么看负载、网络流量、磁盘io、进程监控 # 为什么磁盘io会飙升 # 某个进程偶现CPU高,如何排查是哪里出现问题了 # linux文件权限有多少位,都是什么含义 # proc的存储都是文件吗? # proc是怎么实现的,如果让你实现一个你会怎么做? # k8s # overlay和underlay网络的区别? # pod里面存储的使用方式? # service的原理是什么? # stateful升级时要注意什么? # 对象存储 # 对象存储是怎么存储数据的? # 熟悉Linux系统、网络TCP/IP、QUIC等协议; 熟悉CDN原理和技术,调度系统架构,有CDN缓存与调度系统经验者优先; 熟悉TCP/IP和HTTP等协议栈,熟悉DNS/HTTPDNS/302调度原理\nnginx 的处理过程、工作在几层,怎么达到负载均衡的效果 ansible和saltstack的区别 获取子进程的ID docker的四种网络模式 k8s有哪个资源类型 k8s的滚动更新、亲和反亲和、自动伸缩 k8s中什么机制可以确保成功启动,但不一定是可用的(探针?) 什么是cdn? 怎么监控TCP网络,比如丢包、或者连接状态 网络监控有哪些指标 网络协议,比如说 HTTP对头阻塞,TCP阻塞控制 告警优先级排序,假如域名访问不通怎么判断是否宕机还是其他原因 如果这些告警是从多个不同的告警平台,怎么让告警收敛?(回调) cdn团队,保障SRE\n"},{"id":40,"href":"/tools/components/readme/","title":"Readme","section":"Tools","content":"这里存放了前端的各种组件\ncanvas # 20行代码实现代码雨效果 code-rain "},{"id":41,"href":"/tools/mac/readme/","title":"Readme","section":"Tools","content":"程序员的mac一些小技巧\n小问题 # brew install卡 暂时禁止自动更新,关闭窗口后失效\nexport HOMEBREW_NO_AUTO_UPDATE=true 命令替代 # tree命令\nalias tree=\u0026#34;find . -print | sed -e \u0026#39;s;[^/]*/;|____;g;s;____|; |;g\u0026#39;\u0026#34; "},{"id":42,"href":"/tools/mark-markdown-ppt/example/","title":"Example","section":"Tools","content":" Marp for VS Code方法 # 一种用markdown写ppt的vscode插件marp # 这款ppt就是我用“写”出来的,用来展示效果。\n 幻灯片1\n asdf asdf 幻灯片2\n asdf asdf 左中右组合方式 # 在其中一张图片后加入属性 vertical 将使图片纵向组合。 # 设置左图右文 # 文字 文字 命令 单独看图片 # 可以看到图片已经放到右边了,非常舒服\n prometheus # 此处的图片加了阴影drop-shadow\n 引用 # 技术分享之工具推荐-jeremyxu marp官方文档 Marp:用 Markdown「写」PPT 的新选择 官方github 谢谢 # "},{"id":43,"href":"/tools/mark-markdown-ppt/readme/","title":"Readme","section":"Tools","content":"一种用markdown写PPT的方法,再也不用费劲排版了 # 本文原创首发于 一种用markdown写PPT的方法,再也不用费劲排版了\n示例:\n 源文件 example 生成ppt example.pptx 前言 # 今天看 jeremyxu 的技术点滴,发现分享了一个 markdown 写 PPT 的插件,惊为天人,先来看看官方效果图。\n 再看看 jeremyxu 写的效果,我学完了都没学会是怎么写的,直到看了他 项目样例我才算是真的学会了。\n 参考 marp 官方文档可以很快学会用法,但是用的时候去翻比较麻烦,我提炼了常用的语法,最后做了一个 PPT 练手,才算是学会了,现在分享出来以便以后翻阅。\nmarp 是个什么? # 日常工作生活中常常会用到 ppt, 但是 ppt 有时候做起来非常浪费时间,如果不用关心排版,可以专注内容自动排版岂不妙哉?\n正好 markdown 就是解决排版的一种语言,有好心人自发开发了一个做 ppt 的利器,只用关注内容,简单分隔一下,稍微改一下样式就可以用了。\n安装和上手 # 下载个 VSCode, 天然支持 markdown ,然后在左侧的插件栏中搜索并安装 Marp for VS Code 就可以开始了。为了获得更好的 Markdown 编辑体验,大家不妨再安装一个叫做 Markdown All in One 的插件。\n使用 Markdown 输出一份最简单的幻灯片,只需要让编辑器知道两点即可:它是幻灯片(不是文档)以及它该在哪里分页,通过如下代码做到:\n--- marp: true --- 幻灯片1 1. asdf 2. asdf --- 幻灯片2 * asdf * asdf 效果如下\n 编辑完成后,通过编辑器右上角的 Marp 图标按钮就可以调出 Export slide deck... 命令并导出幻灯片了。 Marp 插件目前支持导出 HTML 和 PDF 格式,另外可以将首页导出为 PNG 或 JPEG 格式的图片。\n优化样式 # 当然没有漂亮是样式是不行的,好在可以简单设置呈现,为此我专门做了一个 ppt\n 有三款主题可以选择,可以参考 themes,有的主题只能居中,我选了一个可居中也可居左的主题。\n--- marp: true theme: gaia footer: \u0026#39;机智的小熊 2020-06-18\u0026#39; paginate: true style: | section a { font-size: 30px; } --- footer 代表是页尾, header 代表页首 paginate 是否在右下角标页码 style 自定义全局样式,插件所有的样式参考 官网提供的样式 首页配置 # 在当前页面头部,用 html 中的注释语法\n\u0026lt;!-- _class: lead gaia _paginate: false --\u0026gt; _class 当前页面设置 lead gaia 样式(居中),如果前面不加下划线会影响所有页面 _paginate 屏蔽右下角页码 其他更详细语法参考 官网手册 首页内容如下\n![w:160](图片链接) # Marp for VS Code方法 ## 一种用markdown写ppt的vscode插件marp 这款ppt就是我用“写”出来的,用来展示效果。 图片设置 # 更改长宽 # ![width:200px](image.jpg) ![height:300px](image.jpg) ![w:200px h:30cm](image.jpg) 图片滤镜(Image Filter) # 基于 CSS 的 filter 属性,Marp 可以对图片进行一些基于模糊、亮度、对比度等的操作,如:\n![blur:15px](image.png) ![brightness:0.5](image.png) ![contract:150%](image.png) 参考 更多 p 图命令\n背景图片 # 针对幻灯片的背景图片, Marp 提供了简单的方式将某张图片设为背景,在方括号中写入 bg 即可\n![bg](background.png) 同时通过在 bg 后追加图片的格式属性,如 [bg fit] ,可以具体设置背景图片的缩放方式。其中 cover 表示充满页面, fit 表示拉伸以适应页面, auto 为不做缩放使用原图片比例。\n其他图片详细语法, 参考 官网文档调整大小、滤镜,图片作背景的布局、尺寸、分割\n更改布局 # 背景图片布局 # ![bg](images/9BBDF9.png) ![bg](images/2EC0F9.png) ![bg](images/B95F89.png) 在其中一张图片后加入属性 vertical 将使图片纵向组合。\n ![bg vertical](images/9BBDF9.png) ![bg](images/2EC0F9.png) ![bg](images/B95F89.png) 更新图片与文字位置 # 有时候想左文右图,或者左图右文的布局,可以设置背景图片的位置\n 参考 更多背景图片文字排版命令\n假如你想加一张完整图片做展示,而不是要上面的样式,可以自行调整图片大小实现\n![bg right w:15cm](images/prometheuslogo.png) 如果是上下排布的长图就不需要加bg了,直接放上去就好了。\n### prometheus 此处的图片加阴影`drop-shadow` ![width:30cm height:9cm drop-shadow](images/prometheus.png) 常用语法汇总 # color: red 设置字体颜色 paginate: true 显示页码, _paginate: false 屏蔽当前页面页码 有三款主题可以选择,可以参考 themes 官网提供的样式 官网提供的语法汇总 更多 p 图命令 官网文档调整大小、滤镜,图片作背景的布局、尺寸、分割 更多背景图片文字排版命令 引用 # 技术分享之工具推荐-jeremyxu marp 官方文档 Marp:用 Markdown「写」PPT 的新选择 官方 github jeremyxu的github样例 "},{"id":44,"href":"/tools/readme/","title":"Readme","section":"Tools","content":"实用工具 # 工具名称 技术栈关键字 备注 markdown写PPT工具 vscode、markdown、ppt vscode的插件,从此不用再关心ppt排版 命令行自动演示工具 linux、demo 讲课、分享的时候假装在现场输入命令的作弊工具 运维工具 # 工具名称 技术栈关键字 备注 消耗CPU资源的脚本 shell、cpu 可设置占用多少cpu 前端组件库 # 分类 组件与效果 技术栈关键字 备注 背景 20行代码实现代码雨效果 canvas "},{"id":45,"href":"/tools/scripts/devops/killcpu/readme/","title":"Readme","section":"Tools","content":"使用方法很简单,参数3表示消耗3颗CPU的资源,运行后,会有一堆 kill 命令,方便 kill 进程:\n[root@test02 ~]# ./killcpu.sh 3 kill 30104 ; kill 30106 ; kill 30108 ; [root@test02 ~]# top top - 15:27:31 up 264 days, 23:39, 4 users, load average: 0.86, 0.25, 0.19 Tasks: 185 total, 5 running, 180 sleeping, 0 stopped, 0 zombie Cpu0 : 100.0% us, 0.0% sy, 0.0% ni, 0.0% id, 0.0% wa, 0.0% hi, 0.0% si Cpu1 : 0.0% us, 0.0% sy, 0.0% ni, 100.0% id, 0.0% wa, 0.0% hi, 0.0% si Cpu2 : 100.0% us, 0.0% sy, 0.0% ni, 0.0% id, 0.0% wa, 0.0% hi, 0.0% si Cpu3 : 100.0% us, 0.0% sy, 0.0% ni, 0.0% id, 0.0% wa, 0.0% hi, 0.0% si Mem: 8165004k total, 8095880k used, 69124k free, 53672k buffers Swap: 2031608k total, 103548k used, 1928060k free, 6801364k cached "},{"id":46,"href":"/tools/scripts/show-demo-magic/readme/","title":"Readme","section":"Tools","content":"报错?网卡?演示的时候别再尴尬,试试 demo-magic 假输入命令工具\n本文原创首发于 报错?网卡?演示的时候别再尴尬,试试 demo-magic 假输入命令工具\n简介 # 演示的时候要准确的输入很多命令,有时候会遭遇网比较卡,半天没反应,或者命令报错的尴尬,特别在大分享,众多人等待的情况下,只能面对面,干瞪眼。\n所以 demo-magic 就是解决这个问题的,他可以模拟你的输入输出,看起来就像现场打字一样,你还可以调整打字速度,可以随时暂定继续,满足演示的需要。\n如下图,就是全自动的,我特意让他在下载东西的时候停顿了1秒,在登陆的时候特意等待我按回车。\n 用法 # 先到 github上把demo-magic.sh下载下来,放到任意目录里。\n我的目录结构\n. |____tools | |____demo-magic.sh |____cache | |____ls-al-cache.txt | |____install-pv-cache.txt |____run.sh demo-magic.sh 就是下载好的脚本 cache 用来保存准备好的输出 run.sh 测试脚本(假演示脚本) 这个工具 用法非常非常简单,简单到一看就会的程度。\n#/bin/bash # include demo-magic . ./tools/demo-magic.sh -n # Will wait max 1 seconds until user presses PROMPT_TIMEOUT=1 TYPE_SPEED=10 # hide the evidence clear p \u0026#34;ls -al\u0026#34; cat cache/ls-al-cache.txt p \u0026#34;brew install pv\u0026#34; wait cat cache/install-pv-cache.txt # Will wait until user presses enter PROMPT_TIMEOUT=0 p \u0026#34;ssh 123.123.111.666\u0026#34; echo password\\(ENTER\\): wait echo login success "},{"id":47,"href":"/%E5%85%A5%E8%81%8C%E5%90%8E/","title":"入职后","section":"","content":"快速熟悉一个项目的方法 # 不知道你有没有经历过一个五年或者更长工作年限的开发人员半路加入团队的情况,可能第一两个星期他会问一些业务或者技术问题,不过一两个月他就可能在指导那些初级开发人员了。\n什么原因呢?因为他已经从过往经验里面总结出来一些套路了。\n项目的共性 # 绝大部分业务系统,核心功能都是由增删改查组成,然后通过通信、运算和人机交互串起来的,系统的复杂度主要体现在系统规模、性能、稳定性、业务流程、通信等方面。(部分工具类、基础架构类系统可能不一样) 绝大部份系统,都是遵循某种或几种设计模式分层进行开发的,最最常见的也就是MVC了。其他请参考一下设计模式教程。 快速熟悉新的项目的套路。 # 先搞清楚新的系统是搞什么的,就问简单几个问题,谁在用这个系统?用这个系统做什么?然后自己根据这些问题去文档找答案。 弄清楚系统是怎么分层、分模块的,每层、每个模块都用到了什么技术和框架,之间是怎么通信的。有架构设计文档的话学习一下最好,没用过的技术先查查资料知道个大概。 把开发环境搭起来,通过几个典型的功能弄清楚系统里面增删改查、通信、用户交互是怎么实现的。最简单的方法是根据系统的分层,先从前端到数据库把代码疏通一下,搞不清楚的话打开debug模式一步一步走一下。 经过上面三个步骤基本上就可以改几个bug和照葫芦画瓢做个功能了。后面重点关注那些没用过的技术和组件:先搞清它的目的、背景、实现原理和功能列表,再照着文档做几个demo,平常工作时把它的文档建个快捷方式,随手查询学习一下。 平常开发过程中如果遇到问题首先要相信: 1)绝大部分自己遇到的问题很多人已经遇到过并且解决了 。 2)绝大部分自己遇到的问题在当前系统里面已经有了答案。 3)绝大部分自己遇到的问题在你用的框架和组件里面都有现成的解决方案。\n对于规模比较大的系统或者系统集合,其实你平时工作接触到的也就是其中的一个系统或者模块,先把自己接触的部分搞定就行了。 对于老系统要注意 # 对于老系统,首先建议看一下 在感觉项目代码的构架不行的时候, 你们会怎么办?\n 老系统其实满是宝藏,里面有很多你可以借鉴和学习的东西。 老系统也满是坑,一个看起来毫不悬念的代码改了以后可能会引发地震。 很多你看着不爽的代码其实都是有道理的。 不要在老系统里面继续挖坑。 看不懂的代码不要动。 在你力所能及的范围内让老系统变的更美好。 上面这个套路应该符合百分之七八十的项目,可以试试看。\n引用: 程序员如何快速上手一个自己不太熟悉的新项目?有什么技巧?\n"}]