• 关于

    顺序程序设计出问题什么情况

    的搜索结果

回答

1.第一个问题通过数据版本,也就是所谓的乐观锁解决。 2.先写日志log,然后ack机制。其实很多这种方式被很多应用所用到比如mysql。 3.用户注册本身这个功能不属于高频调用,所以性能上不需要考虑太多,直接悲观锁实现即可。而且这种可能性非常低,就算失败,那么返回给用户一个能理解的失败信息即可。######回复 @sixliu:谢谢回复靠谱的回答真不多我再等等看...######回复 @花歌:第二个可能没太理解你的场景######谢谢咯~1和3可以,我看看还有什么别的方案,差不多也就这么做了,2的话再考虑考虑吧感觉还是有点不适用场景###### 三个问题,其实就是同一个并发的问题,###### 都是并发中会出现的问题。 1说的在内存里的情况,就是2。 1说的在数据库中的情况,就是3。 在数据库中,数据库自己会有锁来解决这个问题,遇到这种情况会修改失败,程序中捕获这种异常做处理返回给前台就可以了。 在内存中,单机单进程单线程,会有顺序,因此没有问题。多机或多进程或多线程操作同一数据,会出现此问题。一种实现方式是加锁,相当于仿照数据库那样的实现,内存正在被修改时,其他的修改会被阻塞或者异常终止。另一种方式是通过队列实现顺序操作,所有的修改都发送到一个程序修改。######让我想想,嗯差不多,回答比较靠谱,谢了先其实...我用的是nodejs全异步操作,前面的数据库操作没完成,后面的也可以进入函数,如果网络延迟,就会造成执行完成顺序和开始执行顺序不一致...等等想一会再问你哈###### 1.是设计上的问题 两个操作如果有先后顺序 就得先后执行  一个操作完了之后再下一个操作 不可能明知道有一前一后却还要非得一起 2.这个就是非常典型的数据库事务 就是保证多个不相关的操作的原子性 只要其中一个出问题就全部回滚 不存在有的成功有的失败 事务还是个挺复杂的东西 mongodb都还不支持事务 多服务器之间分布式的事务也是有些麻烦的  3.同时的操作数据库自己会进行锁的处理 对数据库来说还是一前一后  如果某个字段设置了唯一索引 那后面的那个必然会出错 代码里正常处理就可以了 所以用户名不唯一的处理有两个地方 一个是在插入之前 一个是在插入时抛出唯一索引异常   当然也可以在新建用户这一整个操作上加锁 全局同时只能有一个用户在新建 不过这样可能效率不高 ######问题1现实情况就是这样用户以为他的操作有顺序但基于连接池算是并发操作即时不用池那也是异步操作不能保证顺序所以只能考虑数据库锁时间戳问题2还没到数据库呢...只考虑多个内存中的对象操作问题3现在就是这样处理的###### 1.加锁 2.加事务控制 3.异常捕获与处理 工作不满一年吧######不好意思...工作6年多了开发经验10多年问题1暂时用乐观锁解决了问题2事务控制个毛线问题你可能是没读清内存中的几个对象而已和数据库无关就是事务也得自己实现这话谁都会说我想听的是备忘录模式这种...到底怎么做能优雅点还是我从需求设计上可能有问题问题3靠数据库唯一约束出错返回太暴力现在就是这么做的也可以数据库加锁怕影响性能###### 1,updateusersetstatus=2wherestatus=3andid=1; 2,用户名设置唯一索引。###### 可以用现在拷贝上操作,再合并的方法解决。1、按顺序合并。2、按状态合并。3、按索引合并。

优选2 2020-06-09 10:36:32 0 浏览量 回答数 0

回答

1.第一个问题通过数据版本,也就是所谓的乐观锁解决。 2.先写日志log,然后ack机制。其实很多这种方式被很多应用所用到比如mysql。 3.用户注册本身这个功能不属于高频调用,所以性能上不需要考虑太多,直接悲观锁实现即可。而且这种可能性非常低,就算失败,那么返回给用户一个能理解的失败信息即可。######回复 @sixliu : 谢谢回复 靠谱的回答真不多 我再等等看...######回复 @花歌 : 第二个 可能没太理解你的场景######谢谢咯~ 1和3可以,我看看还有什么别的方案,差不多也就这么做了,2的话 再考虑考虑吧 感觉还是有点不适用场景###### 三个问题,其实就是同一个并发的问题,###### 都是并发中会出现的问题。 1说的在内存里的情况,就是2。 1说的在数据库中的情况,就是3。 在数据库中,数据库自己会有锁来解决这个问题,遇到这种情况会修改失败,程序中捕获这种异常做处理返回给前台就可以了。 在内存中,单机单进程单线程,会有顺序,因此没有问题。多机或多进程或多线程操作同一数据,会出现此问题。一种实现方式是加锁,相当于仿照数据库那样的实现,内存正在被修改时,其他的修改会被阻塞或者异常终止。另一种方式是通过队列实现顺序操作,所有的修改都发送到一个程序修改。######让我想想,嗯 差不多,回答比较靠谱,谢了先 其实...我用的是nodejs 全异步操作,前面的数据库操作没完成,后面的也可以进入函数,如果网络延迟,就会造成执行完成顺序和开始执行顺序不一致... 等等想一会再问你哈###### 1. 是设计上的问题  两个操作如果有先后顺序  就得先后执行   一个操作完了之后再下一个操作   不可能明知道有一前一后 却还要非得一起 2. 这个就是非常典型的数据库事务   就是保证多个不相关的操作的原子性  只要其中一个出问题就全部回滚  不存在有的成功有的失败  事务还是个挺复杂的东西   mongodb都还不支持事务  多服务器之间分布式的事务也是有些麻烦的   3. 同时的操作 数据库自己会进行锁的处理  对数据库来说还是一前一后    如果某个字段设置了唯一索引  那后面的那个必然会出错  代码里正常处理就可以了   所以用户名不唯一的处理有两个地方  一个是在插入之前  一个是在插入时抛出唯一索引异常      当然也可以在新建用户这一整个操作上加锁   全局同时只能有一个用户在新建  不过这样可能效率不高  ######问题1 现实情况就是这样 用户以为他的操作有顺序 但基于连接池 算是并发操作 即时不用池 那也是异步操作 不能保证顺序 所以只能考虑数据库锁 时间戳 问题2 还没到数据库呢... 只考虑多个内存中的对象操作 问题3 现在就是这样处理的###### 1.加锁 2.加事务控制 3.异常捕获与处理 工作不满一年吧######不好意思... 工作6年多了 开发经验10多年 问题1 暂时用乐观锁解决了 问题2 事务控制个毛线 问题你可能是没读清 内存中的几个对象而已 和数据库无关 就是事务也得自己实现 这话谁都会说 我想听的是 备忘录模式 这种... 到底怎么做能优雅点 还是我从需求设计上可能有问题 问题3 靠数据库唯一约束出错返回太暴力 现在就是这么做的 也可以数据库加锁 怕影响性能###### 1,update user set status=2 where status=3 and id=1; 2,用户名设置唯一索引。###### 可以用现在拷贝上操作,再合并的方法解决。1、按顺序合并。2、按状态合并。3、按索引合并。

爱吃鱼的程序员 2020-05-29 20:15:24 0 浏览量 回答数 0

回答

“程序设计 = 算法 + 数据结构”是瑞士计算机科学家Niklaus Wirth于1976年出版的一本书的书名,很快就成了在计算机工作者之间流传的一句名言。斗转星移,尽管新技术方法不断涌现,这句名言依然焕发着无限的生命力,它借助面向对象知识的普及,使数据结构技术更加完善和易于使用。由此,也说明了数据结构在计算机学科中的地位和不可替代的独特作用。 然而,在可视化程序设计的今天,借助于集成开发环境我们可以很方便、快捷地开发部署应用程序,程序设计似乎不再只是计算机专业的人员的专利,很多人以为,只要掌握了几种开发工具就可以成为编程高手了,其实这是一个误区。纵然,我们可以很熟练地掌握一门程序设计语言、熟练地运用各种IDE开发应用程序,但是我们写出的代码是否是优良的。我们的设计是否合理。代码执行是否是高效的。代码风格是否是有美感的。更甚的说我们所写出代码的是否是艺术。 在长达几年的时间内,我总是陷在了一个误区里面:即认为工程能力和算法能力是不相干的两回事,我们似乎可以很轻松地完成一个工程项目,至少我在做一些MIS系统的时候一直都是这么认为的,甚至觉得根本不需要所谓的算法或数据结构。当时一直想不通的是为什么Google、百度这样牛的公司却对ACMer们如此青睐,对于这种招聘的标准感到疑惑不解。为什么他们不在技术(多线程、网络编程、分布式系统等)上做要求,却偏偏只关注这么一小块的算法设计。 我曾经反复地告诉自己“程序设计 = 算法 + 数据结构”在70年代提出是受限于计算机硬件,当时的内存不足、计算能力不强,程序需要设计足够精巧细致。再看当前主流的计算机配置,比70年代的大型机运算能力还要强大,我们好像完全不用担心算法设计的问题。报着这样的想法,我向来都不太重视算法,而且工程中对算法的需求并不多。 只是有一天,我突然发现我只是片面地关注其中一个方面,硬件能力是提升了,但同时人们所面对的信息、数据、运算任务的规模也是极大的膨胀了,而且膨胀的规模比硬件本身运算能力提升的规模还要大很多。算法和数据结构不仅没有贬值,反而比之前那个时代显得更为重要。试想,在互联网迅猛发展的今天,一个中等规模的企业每天所产生的数据量能达到GB级甚至TB级。要处理这样的海量数据不是说单纯的硬件运算能力上来就解决了的,设计优良的算法和数据结构设计能够在1分钟之内完成任务,而一个糟糕的设计则可能需要1个小时的运行。 一般认为,一个数据结构是由数据元素依据某种逻辑联系组织起来的,这种对数据元素间逻辑关系的描述称为数据结构。许多大型系统的构造经验表明,系统实现的困难程度和系统构造的质量都严重的依赖于是否选择了最优的数据结构。许多时候,确定了数据结构后,算法就容易得到了。当然,有些情况下事情也会反过来,我们根据特定算法来选择数据结构与之适应。算法则可以理解为有基本运算及规定的运算顺序所构成的完整的解题步骤,或者看成按照要求设计好的有限的确切的计算序列,并且这样的步骤和序列可以解决一类问题。 总的来说,数据结构和算法并不是一门教你编程的课,它们可以脱离任何的计算机程序设计语言,而只需要从抽象意义上去概括描述。说的简单一点,数据结构是一门告诉你数据在计算机里如何组织的课程,而算法是一门告诉你数据在计算机里如何运算的课程,前者是结构学、后者是数学。程序设计就像盖房子,数据结构是砖、瓦,而算法则是设计图纸。你若想盖房子首先必须要有原材料(数据结构),但这些原材料并不能自动地盖起你想要的房子,你必须按照设计图纸(算法)一砖一瓦地去砌,这样你才能拥有你想要的房子。数据结构是程序设计这座大厦的基础,没有基础,无论设计有多么高明,这座大厦不可能建造起来。算法则是程序设计之灵魂,它是程序设计的思想所在,没有灵魂没有思想那不叫程序,只是一堆杂乱无章的符号而已。在程序设计中,数据结构就像物质,而算法则是意识,这在哲学上可以理解为:意识是依赖与物质而存在的,物质是由意识而发展的。双方相互依赖,缺一不可。 当然最经典的数据结构是有限的,包括线性表、栈、队列、串、数组、二叉树、树、图、查找表等,而算法则是琳琅满目的,多种多样的。就好像数据结构是人体的各种组织、器官,算法则是人的思想。你可以用自己的思想去支配你的身体各个可以运动的器官随意运动。如果你想吃苹果,你可以削皮吃,可以带皮吃,只要你愿意,甚至你可以不洗就吃。但无论如何,你的器官还是你的器官,就那么几样,目的只有一个就是吃苹果,而方式却是随心所欲的。这就是算法的灵活性、不固定性。因此可以这样说:数据结构是死的,而算法是活的。 我花了四年时间才走出这个误区,值得庆幸的是不算太晚,而我的梦想是要做一名优秀的架构师,缺乏数据结构和算法的深厚功底,很难设计出高水平的具有专业水准的架构和应用,数据结构和算法则是我实现梦想最坚实的基石。现在,也正是我需要开始沉淀的时刻。程序设计这项伟大的工程,教授于我的将不仅仅是技术这么简单,我期待它能给我以更深的思考与感悟,激发我对生命的热爱,对理想的执着,对卓越的追求。

琴瑟 2019-12-02 01:22:02 0 浏览量 回答数 0

阿里云试用中心,为您提供0门槛上云实践机会!

0元试用32+款产品,最高免费12个月!拨打95187-1,咨询专业上云建议!

问题

如何解决消息队列的延时以及过期失效问题?【Java问答学堂】24期

剑曼红尘 2020-05-22 19:09:10 7 浏览量 回答数 1

回答

Kotlin的简介 Kotlin是由JetBrains公司(IDEA开发者)所开发的编程语言,其名称来自于开发团队附近的科特林岛。 多平台开发 JVM :Android; Server-Side Javascript:前端 Native(beta) :开发原生应用 windows、macos、linux Swift与Kotlin非常像 http://nilhcem.com/swift-is-like-kotlin/ kotlin发展历程 image.png java发展历程 image.png JVM语言的原理 image.png JVM规范与java规范是相互独立的 只要生成的编译文件匹配JVM字节码规范,任何语言都可以由JVM编译运行. Kotlin也是一种JVM语言,完全兼容java,可以与java相互调用;Kotlin语言的设计受到Java、C#、JavaScript、Scala、Groovy等语言的启发 kotlin的特性 下面不会罗列kotlin中具体的语法,会介绍我认为比较重要的特性,以及特性背后的东西。 类型推断 空类型设计 函数式编程 类型推断 image.png 类型推断是指编程语言中在编译期自动推导出值的数据类型。推断类型的能力让很多编程任务变得容易,让程序员可以忽略类型标注的同时仍然允许类型检查。 在开发环境中,我们往往写出表达式,然后可以用快捷键来生成变量声明,往往都是很准的,这说明了编译器其实是可以很准确的推断出来类型的。编程语言所具备的类型推断能力可以把类型声明的任务由开发者转到了编译器. java中声明变量的方式是类型写在最前面,后面跟着变量名,这就迫使开发者在声明变量时就要先思考变量的类型要定义成什么,而在一些情况下比如使用集合、泛型类型的变量,定义类型就会变得比较繁琐。 Kotlin中声明变量,类型可以省略,或者放到变量名后面,这可以降低类型的权重,从必选变为可选,降低开发者思维负担。java10中也引入了类型推断。 Javascript中声明变量也是用关键字var,但是还是有本质区别的,Kotlin中的类型推断并不是变成动态类型、弱类型,类型仍然是在编译期就已经决定了的,Kotlin仍然是静态类型、强类型的编程语言。javascript由于是弱类型语言,同一个变量可以不经过强制类型转换就被赋不同数据类型的值, 编程语言的一个趋势就是抽象程度越来越高,编译器做更多的事情。 空类型设计 空类型的由来 image.png 托尼·霍尔(Tony Hoare),图灵奖得主 托尼·霍尔是ALGOL语言的设计者,该语言在编程语言发展历史上非常重要,对其他编程语言产生重大影响,大多数近代编程语言(包括C语言)皆使用类似ALGOL的语法。他在一次大会上讨论了null应用的设计: “我把 null 引用称为自己的十亿美元错误。它的发明是在1965 年,那时我用一个面向对象语言( ALGOL W )设计了第一个全面的引用类型系统。我加入了null引用设计,仅仅是因为实现起来非常容易。它导致了数不清的错误、漏洞和系统崩溃,可能在之后 40 年中造成了十亿美元的损失。” null引用存在的问题 以java为例,看null引用的设计到底存在哪些问题 空指针问题NPE 编译时不能对空指针做出检查,运行时访问null对象就会出现错误,这个就是工程中常见的空指针异常。 null本身没有语义,会存在歧义 值未被初始化 值不存在 也许表示一种状态 逻辑上有漏洞 Java中,null可以赋值给任何引用,比如赋值给String类型变量,String a = null,但是null并不是String类型: a instanceof String 返回的是false,这个其实是有些矛盾的。所以当持有一个String类型的变量,就存在两种情况,null或者真正的String. 解决NPE的方式 防御式代码 在访问对象前判空,但会有冗余代码;会规避问题,而隐藏真正的问题 抛出异常给调用方处理 方法中传参传入的空值、无效值,抛出受检查异常给上层调用方 增加注解 Android中可以增加@NonNull注解,编译时做额外检查 空状态对象设计模式 空状态对象是一个实现接口但是不做任何业务逻辑的对象,可以取代判空检查;这样的空状态对象也可以在数据不可用的时候提供默认的行为 java8 Optional类 java8中引入了Optional类,来解决广泛存在的null引用问题.官方javadoc文档介绍 A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value. Additional methods that depend on the presence or absence of a contained value are provided, such as orElse() (return a default value if value not present) and ifPresent() (execute a block of code if the value is present). 来看一下是如何实现的。 举一个访问对象读取熟悉的例子 java 8 之前 : image.png java 8: image.png 总结: 1.用Optional还是会比较繁琐,这个也说明了设计一个替代null的方案还是比较难的。 optional的耗时大约是普通判空的数十倍,主要是涉及泛型、使用时多创键了一个对象的创建;数据比较大时,会造成性能损失。 java8 引入Optional的意义在于提示调用者,用特殊类型包装的变量可能为空,在使用取出时需要判断 Kotlin的空类型设计 Kotlin中引入了可空类型和不可空类型的区分,可以区分一个引用可以容纳null,还是不能容纳null。 String vs String? String 类型表示变量不能为空,String?则表示变量可以为空 String?含义是String or null.这两种是不同的类型. 比如: var a:String = “abc” //ok var a:String = null //不允许 var b :String? = null //ok a=b // 不允许 String?类型的值不能给String类型的值赋值 这样就将类型分成了可空类型和不可能类型,每一个类型都有这样的处理;Kotlin中访问非空类型变量永远不会出现空指针异常。 同样上面的例子,采用Kotlin去写,就会简洁很多 image.png 编程范式-函数式编程 编程范式是什么? 编程范式是程序员看待程序和写程序的观点 主要的类型 非结构化编程 结构化编程 面向对象编程 命令式编程 函数式编程 这些类型并不是彼此互斥的,而是按照不同的维度做的划分,一种编程语言可能都支持多个编程范式 非结构化编程 第一代的高级语言往往是非结构化编程 比如 BASIC语言 每一行的代码前面都有一个数字作为行号,通常使用GOTO的跳跃指令来实现判断和循环. 看一下下面这段代码是做什么的: image.png 实际上做的是:程序在屏幕上显示数字 1 到 10 及其对应的平方 采用这种方式写程序,大量的使用goto实现逻辑的跳转,代码一长,可读性和维护性就比较差了,形成“面条式代码” 结构化编程 采用顺序、分支、循环结构来表达,禁用或者少用GOTO; 并用子程序来组织代码,采用自顶向下的方式来写程序 代表语言是C语言 实现同样的逻辑: image.png 可见采用结构化编程,代码的逻辑会更清晰。 面向对象编程 思想: 将计算机程序视为一组对象的集合,而每个对象都可以接收其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在各个对象之间传递。 特性: 封装性、继承性、多态性。 命令式编程 把计算机程序视为一系列的命令集合 主要思想是关注计算机执行的步骤,即一步一步告诉计算机先做什么再做什么。 “先做这,再做那”,强调“怎么做” 实现: 用变量来储存数据,用语句来执行指令,改变变量状态。 基本所有的常见的编程语言都具有此范式 函数式编程 声明式语法,描述要什么,而不是怎么做 类似于SQL语句 语言: kotlin swift python javascript scala 函数是第一等公民 可以赋值给变量,可作为参数传入另一个函数,也可作为函数的返回值 纯函数 y=f(x) 只要输入相同,返回值不变 没有副作用:不修改函数的外部状态 举个栗子 公司部门要进行outing,去哪里是个问题,要考虑多个因素,比如花费、距离、天数等等,有多个备选地点进行选择。 定义一个数据类: image.png 要进行筛选了,分别用sql,kotlin,java来实现 找出花费低于2000元的outing地点信息 SQL image.png Kotlin image.png java 7 image.png 可见kotin的写法还是比较接近于sql的思想的,声明式的写法,而不管具体如何实现;其中的:place->place.money<2000 就是函数,可以作为参数传递给fliter这个高阶函数;而且这个函数没有副作用,不改变外部状态。 再来一个复杂一点的: 找出花费低于5000元,时间不多于4天,按照距离排序的outing地点名称 SQL image.png Kotlin: image.png java 7 image.png 由此可见用kotlin的函数式写法,会更简洁,逻辑也更清晰,这段代码的目标一目了然,这种清晰在于实现了业务逻辑与控制逻辑的分离,业务逻辑就是由函数实现的,比如place->place.money<500,而控制逻辑是由filter,sorterBy等高阶函数实现的。 而java的传统写法是基于对数据的操作,避免不了遍历的操作,业务逻辑与控制逻辑交织在了一起,这段代码的目的就不是那么容易清晰看到的了。 总结 kotlin是实用的现代编程语言,吸收了众多编程语言的优点,支持类型推断、空类型安全、函数式编程、DSL等特性,非常值得学习和使用。

问问小秘 2020-04-30 16:33:40 0 浏览量 回答数 0

问题

Redis 和 Memcached 的区别?Redis 的线程模型是什么?【Java问答学堂】31期

剑曼红尘 2020-06-03 20:28:14 28 浏览量 回答数 1

问题

【Java问答学堂】13期 redis 和 memcached 有什么区别?

剑曼红尘 2020-05-06 14:37:41 0 浏览量 回答数 1

回答

【Java问答学堂】13期 redis 和 memcached 有什么区别?redis 的线程模型是什么?为什么 redis 单线程却能支撑高并发? 面试官心理分析 这个是问 redis 的时候,最基本的问题吧,redis 最基本的一个内部原理和特点,就是 redis 实际上是个单线程工作模型,你要是这个都不知道,那后面玩儿 redis 的时候,出了问题岂不是什么都不知道? 还有可能面试官会问问你 redis 和 memcached 的区别,但是 memcached 是早些年各大互联网公司常用的缓存方案,但是现在近几年基本都是 redis,没什么公司用 memcached 了。 面试题剖析 redis 和 memcached 有啥区别? redis 支持复杂的数据结构 redis 相比 memcached 来说,拥有更多的数据结构,能支持更丰富的数据操作。如果需要缓存能够支持更复杂的结构和操作, redis 会是不错的选择。 redis 原生支持集群模式 在 redis3.x 版本中,便能支持 cluster 模式,而 memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据。 性能对比 由于 redis 只使用单核,而 memcached 可以使用多核,所以平均每一个核上 redis 在存储小数据时比 memcached 性能更高。而在 100k 以上的数据中,memcached 性能要高于 redis。虽然 redis 最近也在存储大数据的性能上进行优化,但是比起 memcached,还是稍有逊色。 redis 的线程模型 redis 内部使用文件事件处理器 file event handler,这个文件事件处理器是单线程的,所以 redis 才叫做单线程的模型。它采用 IO 多路复用机制同时监听多个 socket,将产生事件的 socket 压入内存队列中,事件分派器根据 socket 上的事件类型来选择对应的事件处理器进行处理。 文件事件处理器的结构包含 4 个部分: 多个 socketIO 多路复用程序文件事件分派器事件处理器(连接应答处理器、命令请求处理器、命令回复处理器) 多个 socket 可能会并发产生不同的操作,每个操作对应不同的文件事件,但是 IO 多路复用程序会监听多个 socket,会将产生事件的 socket 放入队列中排队,事件分派器每次从队列中取出一个 socket,根据 socket 的事件类型交给对应的事件处理器进行处理。 来看客户端与 redis 的一次通信过程: 要明白,通信是通过 socket 来完成的,不懂的同学可以先去看一看 socket 网络编程。 首先,redis 服务端进程初始化的时候,会将 server socket 的 AE_READABLE 事件与连接应答处理器关联。 客户端 socket01 向 redis 进程的 server socket 请求建立连接,此时 server socket 会产生一个 AE_READABLE 事件,IO 多路复用程序监听到 server socket 产生的事件后,将该 socket 压入队列中。文件事件分派器从队列中获取 socket,交给连接应答处理器。连接应答处理器会创建一个能与客户端通信的 socket01,并将该 socket01 的 AE_READABLE 事件与命令请求处理器关联。 假设此时客户端发送了一个 set key value 请求,此时 redis 中的 socket01 会产生 AE_READABLE 事件,IO 多路复用程序将 socket01 压入队列,此时事件分派器从队列中获取到 socket01 产生的 AE_READABLE 事件,由于前面 socket01 的 AE_READABLE 事件已经与命令请求处理器关联,因此事件分派器将事件交给命令请求处理器来处理。命令请求处理器读取 socket01 的 key value 并在自己内存中完成 key value 的设置。操作完成后,它会将 socket01 的 AE_WRITABLE 事件与命令回复处理器关联。 如果此时客户端准备好接收返回结果了,那么 redis 中的 socket01 会产生一个 AE_WRITABLE 事件,同样压入队列中,事件分派器找到相关联的命令回复处理器,由命令回复处理器对 socket01 输入本次操作的一个结果,比如 ok,之后解除 socket01 的 AE_WRITABLE 事件与命令回复处理器的关联。 这样便完成了一次通信。关于 Redis 的一次通信过程,推荐读者阅读《Redis 设计与实现——黄健宏》进行系统学习。 为啥 redis 单线程模型也能效率这么高? 纯内存操作。核心是基于非阻塞的 IO 多路复用机制。C 语言实现,一般来说,C 语言实现的程序“距离”操作系统更近,执行速度相对会更快。单线程反而避免了多线程的频繁上下文切换问题,预防了多线程可能产生的竞争问题。 往期回顾: 【Java问答学堂】1期 为什么使用消息队列?消息队列有什么优点和缺点?Kafka、ActiveMQ、RabbitMQ、RocketMQ 都有什么区别,以及适合哪些场景? 【Java问答学堂】2期 如何保证消息队列的高可用? 【Java问答学堂】3期 如何保证消息不被重复消费?或者说,如何保证消息消费的幂等性? 【Java问答学堂】4期 如何保证消息的可靠性传输?(如何处理消息丢失的问题?) 【Java问答学堂】5期 如何保证消息的顺序性? 【Java问答学堂】6期 如何解决消息队列的延时以及过期失效问题? 【Java问答学堂】7期 如果让你写一个消息队列,该如何进行架构设计? 【Java问答学堂】8期 es 的分布式架构原理能说一下么(es 是如何实现分布式的啊)? 【Java问答学堂】9期 es 写入数据的工作原理是什么啊?es 查询数据的工作原理是什么啊? 【Java问答学堂】10期 es 在数据量很大的情况下(数十亿级别)如何提高查询效率啊? 【Java问答学堂】11期 es 生产集群的部署架构是什么?每个索引的数据量大概有多少? 【Java问答学堂】12期 项目中缓存是如何使用的?为什么要用缓存?缓存使用不当会造成什么后果?

剑曼红尘 2020-05-06 14:37:53 0 浏览量 回答数 0

回答

  算法,数据结构是关键,另外还有组合数学,特别是集合与图论,概率论也重要。推荐买一本《算法导论》,那本书行,看起来超爽。。。基本掌握语法还不行啊,语法的超熟练掌握,不然出了错误很难调试的。。。最重要的是超牛皮的头脑啦,分析能力,逻辑推理能力很重要。ACM很好玩啦,祝你成功。。。   acm是3人一组的,以学校为单位报名的,也就是说要得到学校同意,还要有2个一起搞的。其实可能是你不知道你们学校搞acm的地方,建议你好好询问下你们学校管科技创新方面的人。建议你找几个兴趣相同的一起做,互相探讨效果好多了,团队合作也是acm要求的3大能力之一。   数据结构远远不够的,建议你看算法导论,黑书,oj的话个人觉得还是poj好,有水题有好题,而且做的人多,要解题报告什么的也好找。我们就是一些做acm的学生一起搞,也没老师,这样肯定能行的。   基础的话是语言,然后数据结构,然后算法。   ACM有三个方向:算法,数学,实现   要求三种能力:英文,自学,团队协作   简单的说,你要能读懂英文的题意描述,要有一门acm能使用的编程语言,要会数据结构,有一点数学基础,一点编程方面天赋,要有兴趣和毅力(最重要),就具有做ACM的基本条件了。   做acm我推荐c,c++也可以,java在某些情况下好用,但是大多数情况的效率和代码量都不大好,所以建议主用c++,有些题目用java   还有什么问题,可以问我啊。   不好意思,没见过用java描述的acm书籍,大多数是用伪命令,其他有的用的c,c++,老一些的用pascal。java只是用来做高精度的一些题的,个人觉得不用专门看这方面的书,java的基本部分学好就够用了。所以我还是推荐主用c++,在高精度和个别题再用java。你可以找找java描述的算法设计与分析,这个好像有   数据结构:C语言版 清华大学出版社 严蔚敏 《数据结构》   算法:清华大学出版社 王晓东 《算法设计与分析》   麻省理工大学 中译本:机械工业出版社 《算法导论》   基本上这三本书就已经足够了,建议一般水平的人先不要看算法导论,待另外两本书看的差不多的时候,再看算法导论加深理解。   另外还有很多针对性更强的书籍,不过针对性太强,这里就不多介绍了。   以上一些都是些算法方面的书,最好的方式就是做题与看书相结合,很多在线做题的网站,PKU,ZOJ很多,推荐PKU,题目比较多,参与的人比较多。做一段时间的题,然后看书,研究算法,再做题,这样进步比较快。   还有关于ACM竞赛,我有自己的一点话说。   首先说下ACM/ICPC是个团队项目,最后的参赛名额是按照学校为单位的,所以找到志同道合的队友和学校的支持是很重要的。   刚刚接触信息学领域的同学往往存在很多困惑,不知道从何入手学习,在这篇文章里,我希望能将自己不多的经验与大家分享,希望对各位有所帮助。   一、语言是最重要的基本功   无论侧重于什么方面,只要是通过计算机程序去最终实现的竞赛,语言都是大家要过的第一道关。亚洲赛区的比赛支持的语言包括C/C++与JAVA。笔者首先说说JAVA,众所周知,作为面向对象的王牌语言,JAVA在大型工程的组织与安全性方面有着自己独特的优势,但是对于信息学比赛的具体场合,JAVA则显得不那么合适,它对于输入输出流的操作相比于C++要繁杂很多,更为重要的是JAVA程序的运行速度要比C++慢10倍以上,而竞赛中对于JAVA程序的运行时限却往往得不到同等比例的放宽,这无疑对算法设计提出了更高的要求,是相当不利的。其实,笔者并不主张大家在这种场合过多地运用面向对象的程序设计思维,因为对于小程序来说这不旦需要花费更多的时间去编写代码,也会降低程序的执行效率。   接着说C和C++。许多现在参加讲座的同学还在上大一,C的基础知识刚刚学完,还没有接触过C++,其实在赛场上使用纯C的选手还是大有人在的,它们主要是看重了纯C在效率上的优势,所以这部分同学如果时间有限,并不需要急着去学习新的语言,只要提高了自己在算法设计上的造诣,纯C一样能发挥巨大的威力。   而C++相对于C,在输入输出流上的封装大大方便了我们的操作,同时降低了出错的可能性,并且能够很好地实现标准流与文件流的切换,方便了调试的工作。如果有些同学比较在意这点,可以尝试C和C++的混编,毕竟仅仅学习C++的流操作还是不花什么时间的。   C++的另一个支持来源于标准模版库(STL),库中提供的对于基本数据结构的统一接口操作和基本算法的实现可以缩减我们编写代码的长度,这可以节省一些时间。但是,与此相对的,使用STL要在效率上做出一些牺牲,对于输入规模很大的题目,有时候必须放弃STL,这意味着我们不能存在“有了STL就可以不去管基本算法的实现”的想法;另外,熟练和恰当地使用STL必须经过一定时间的积累,准确地了解各种操作的时间复杂度,切忌对STL中不熟悉的部分滥用,因为这其中蕴涵着许多初学者不易发现的陷阱。   通过以上的分析,我们可以看出仅就信息学竞赛而言,对语言的掌握并不要求十分全面,但是对于经常用到的部分,必须十分熟练,不允许有半点不清楚的地方,下面我举个真实的例子来说明这个道理——即使是一点很细微的语言障碍,都有可能酿成错误:   在去年清华的赛区上,有一个队在做F题的时候使用了cout和printf的混合输出,由于一个带缓冲一个不带,所以输出一长就混乱了。只是因为当时judge team中负责F题的人眼睛尖,看出答案没错只是顺序不对(答案有一页多,是所有题目中最长的一个输出),又看了看程序发现只是输出问题就给了个Presentation error(格式错)。如果审题的人不是这样而是直接给一个 Wrong Answer,相信这个队是很难查到自己错在什么地方的。   现在我们转入第二个方面的讨论,基础学科知识的积累。   二、以数学为主的基础知识十分重要   虽然被定性为程序设计竞赛,但是参赛选手所遇到的问题更多的是没有解决问题的思路,而不是有了思路却死活不能实现,这就是平时积累的基础知识不够。今年World Final的总冠军是波兰华沙大学,其成员出自于数学系而非计算机系,这就是一个鲜活的例子。竞赛中对于基础学科的涉及主要集中于数学,此外对于物理、电路等等也可能有一定应用,但是不多。因此,大一的同学也不必为自己还没学数据结构而感到不知从何入手提高,把数学捡起来吧。下面我来谈谈在竞赛中应用的数学的主要分支。   1、离散数学——作为计算机学科的基础,离散数学是竞赛中涉及最多的数学分支,其重中之重又在于图论和组合数学,尤其是图论。   图论之所以运用最多是因为它的变化最多,而且可以轻易地结合基本数据结构和许多算法的基本思想,较多用到的知识包括连通性判断、DFS和BFS,关节点和关键路径、欧拉回路、最小生成树、最短路径、二部图匹配和网络流等等。虽然这部分的比重很大,但是往往也是竞赛中的难题所在,如果有初学者对于这部分的某些具体内容暂时感到力不从心,也不必着急,可以慢慢积累。   竞赛中设计的组合计数问题大都需要用组合数学来解决,组合数学中的知识相比于图论要简单一些,很多知识对于小学上过奥校的同学来说已经十分熟悉,但是也有一些部分需要先对代数结构中的群论有初步了解才能进行学习。组合数学在竞赛中很少以难题的形式出现,但是如果积累不够,任何一道这方面的题目却都有可能成为难题。   2、数论——以素数判断和同余为模型构造出来的题目往往需要较多的数论知识来解决,这部分在竞赛中的比重并不大,但只要来上一道,也足以使知识不足的人冥思苦想上一阵时间。素数判断和同余最常见的是在以密码学为背景的题目中出现,在运用密码学常识确定大概的过程之后,核心算法往往要涉及数论的内容。   3、计算几何——计算几何相比于其它部分来说是比较独立的,就是说它和其它的知识点很少有过多的结合,较常用到的部分包括——线段相交的判断、多边形面积的计算、内点外点的判断、凸包等等。计算几何的题目难度不会很大,但也永远不会成为最弱的题。   4、线性代数——对线性代数的应用都是围绕矩阵展开的,一些表面上是模拟的题目往往可以借助于矩阵来找到更好的算法。   5、概率论——竞赛是以黑箱来判卷的,这就是说你几乎不能动使用概率算法的念头,但这也并不是说概率就没有用。关于这一点,只有通过一定的练习才能体会。   6、初等数学与解析几何——这主要就是中学的知识了,用的不多,但是至少比高等数学多,我觉得熟悉一下数学手册上的相关内容,至少要知道在哪儿能查到,还是必要的。   7、高等数学——纯粹运用高等数学来解决的题目我接触的只有一道,但是一些题目的叙述背景往往需要和这部分有一定联系,掌握得牢固一些总归没有坏处。   以上就是竞赛所涉及的数学领域,可以说范围是相当广的。我认识的许多人去搞信息学的竞赛就是为了逼着自己多学一点数学,因为数学是一切一切的基础。   三、数据结构与算法是真正的核心   虽然数学十分十分重要,但是如果让三个只会数学的人参加比赛,我相信多数情况下会比三个只会数据结构与算法的人得到更为悲惨的结局。   先说说数据结构。掌握队列、堆栈和图的基本表达与操作是必需的,至于树,我个人觉得需要建树的问题有但是并不多。(但是树往往是很重要的分析工具)除此之外,排序和查找并不需要对所有方式都能很熟练的掌握,但你必须保证自己对于各种情况都有一个在时间复杂度上满足最低要求的解决方案。说到时间复杂度,就又该说说哈希表了,竞赛时对时间的限制远远多于对空间的限制,这要求大家尽快掌握“以空间换时间”的原则策略,能用哈希表来存储的数据一定不要到时候再去查找,如果实在不能建哈希表,再看看能否建二叉查找树等等——这都是争取时间的策略,掌握这些技巧需要大家对数据结构尤其是算法复杂度有比较全面的理性和感性认识。   接着说说算法。算法中最基本和常用的是搜索,主要是回溯和分支限界法的使用。这里要说的是,有些初学者在学习这些搜索基本算法是不太注意剪枝,这是十分不可取的,因为所有搜索的题目给你的测试用例都不会有很大的规模,你往往察觉不出程序运行的时间问题,但是真正的测试数据一定能过滤出那些没有剪枝的算法。实际上参赛选手基本上都会使用常用的搜索算法,题目的区分度往往就是建立在诸如剪枝之类的优化上了。   常用算法中的另一类是以“相似或相同子问题”为核心的,包括递推、递归、贪心法和动态规划。这其中比较难于掌握的就是动态规划,如何抽象出重复的子问题是很多题目的难点所在,笔者建议初学者仔细理解图论中一些以动态规划为基本思想所建立起来的基本算法(比如Floyd-Warshall算法),并且多阅读一些定理的证明,这虽然不能有什么直接的帮助,但是长期坚持就会对思维很有帮助。   四、团队配合   通过以上的介绍大家也可以看出,信息学竞赛对于知识面覆盖的非常广,想凭一己之力全部消化这些东西实在是相当困难的,这就要求我们尽可能地发挥团队协作的精神。同组成员之间的熟练配合和默契的形成需要时间,具体的情况因成员的组成不同而不同,这里我就不再多说了。   五、练习、练习、再练习   知识的积累固然重要,但是信息学终究不是看出来的,而是练出来的,这是多少前人最深的一点体会,只有通过具体题目的分析和实践,才能真正掌握数学的使用和算法的应用,并在不断的练习中增加编程经验和技巧,提高对时间复杂度的感性认识,优化时间的分配,加强团队的配合。总之,在这里光有纸上谈兵是绝对不行的,必须要通过实战来锻炼自己。   大家一定要问,我们去哪里找题做,又如何检验程序是否正确呢。这大可不必担心,现在已经有了很多网上做题的站点,这些站点提供了大量的题库并支持在线判卷,你只需要把程序源码提交上去,马上就可以知道自己的程序是否正确,运行所使用的时间以及消耗的内存等等状况。下面我给大家推荐几个站点,笔者不建议大家在所有这些站点上做题,选择一个就可以了,因为每个站点的题都有一定的难易比例,系统地做一套题库可以使你对各种难度、各种类型的题都有所认识。   1、Ural:   Ural是中国学生对俄罗斯的Ural州立大学的简称 ,那里设立了一个Ural Online Problem Set,并且支持Online Judge。Ural的不少题目算法性和趣闻性都很强,得到了国内广大学生的厚爱。根据“信息学初学者之家”网站的统计,Ural的题目类型大概呈如下的分布:   题型   搜索   动态规划   贪心   构造   图论   计算几何   纯数学问题   数据结构   其它   所占比例   约10%   约15%   约5%   约5%   约10%   约5%   约20%   约5%   约25%   这和实际比赛中的题型分布也是大体相当的。有兴趣的朋友可以去看看。   2、UVA:   UVA代表西班牙Valladolid大学(University de Valladolid)。该大学有一个那里设立了一个PROBLEM SET ARCHIVE with ONLINE JUDGE ,并且支持ONLINE JUDGE,形式和Ural大学的题库类似。不过和Ural不同的是,UVA题目多的多,而且比较杂,而且有些题目的测试数据比较刁钻。这使得刚到那里做题的朋友往往感觉到无所适从,要么难以找到合适的题目,要么Wrong Answer了很多次以后仍然不知道错在那里。 如果说做Ural题目主要是为了训练算法,那么UVA题目可以训练全方位的基本功和一些必要的编程素质。UVA和许多世界知名大学联合办有同步网上比赛,因此那里强人无数,不过你先要使自己具有听懂他们在说什么的素质:)   3、ZOJ:   ZOJ是浙江大学建立的ONLINE JUDGE,是中国大学建立的第一个同类站点,也是最好和人气最高的一个,笔者和许多班里的同学就是在这里练习。ZOJ虽然也定位为一个英文网站,但是这里的中国学生比较多,因此让人觉得很亲切。这里目前有500多道题目,难易分配适中,且涵盖了各大洲的题目类型并配有索引,除此之外,ZOJ的JUDGE系统是几个网站中表现得比较好的一个,很少出现Wrong Answer和Presentation error混淆的情况。这里每月也办有一次网上比赛,只要是注册的用户都可以参加。   说起中国的ONLINE JUDGE,去年才开始参加ACM竞赛的北京大学现在也建立了自己的提交系统;而我们学校也是去年开始参加比赛,现在也有可能推出自己的提交系统,如果能够做成,到时候大家就可以去上面做题了。同类网站的飞速发展标志着有越来越多的同学有兴趣进入信息学的领域探索,这是一件好事,同时也意味着更激烈的竞争。

小旋风柴进 2019-12-02 01:20:20 0 浏览量 回答数 0

回答

首先,如何理解单元,还是那个分层测试的思想。 你的ui,business,dao肯定有自己的单元测试,每一个测试都是有必要的。 为了提高测试的效率引入了automation,如果能提高自动化测试的覆盖率,是不是就提高了测试效率。 当然还是那句话,你的模块是否合理,测试框架的选择,持续集成都需要考虑. 所以你的问题是很多公司都面临的问题,单元作还是不做?做到什么程度?怎么做? 说白了这个不简单,需要从最基本的开发流程开始修改,你们的领导能在多大程度支持这个改进,这才是关键。 最终的目的还是质量保证,我们大部分时候都是在偿还自己欠下的技术债务罢了。 ###### 引用来自“jeffsui”的评论 首先,如何理解单元,还是那个分层测试的思想。 你的ui,business,dao肯定有自己的单元测试,每一个测试都是有必要的。 为了提高测试的效率引入了automation,如果能提高自动化测试的覆盖率,是不是就提高了测试效率。 当然还是那句话,你的模块是否合理,测试框架的选择,持续集成都需要考虑. 所以你的问题是很多公司都面临的问题,单元作还是不做?做到什么程度?怎么做? 说白了这个不简单,需要从最基本的开发流程开始修改,你们的领导能在多大程度支持这个改进,这才是关键。 最终的目的还是质量保证,我们大部分时候都是在偿还自己欠下的技术债务罢了。 我最近在做的项目就是没有单元测试的,接手以后弄了一些bug,有严重的也有不严重的。严重的直接导致了 App 的崩溃。 最近计划做单元测试。项目的分层不是很严格的,基本上只有 dao 和 controller 两层。计划对新业务编写时引入业务层,然后只对业务层代码测试。这样就只对最复杂,最容易出问题的代码做单元测试。 对新逻辑,严格遵守分层,特别是控制层代码,只放入参校验和数据返回,不混入任何业务。业务层在逻辑复杂的模块里,会比较庞大,大时候再考虑抽象。dao 层本身比较单纯也容易界定,测试压力会比较小。 ######这就是还债######一般都是对dao接口、service接口分别做单元测试,考虑各种情况下所期待的结果,做好对应的处理方式###### 针对数据操作的 单元测试的两种测试方式: 1、直接在数据库内测试,这样的话需要分离数据初始化和数据清楚的逻辑,如果需要自动化运行单元测试,还需要控制好单元测试的执行顺序。如果抽象得好,我觉得还是可以接受的,特别如果使用了ORM框架,这个操作应该还是可以抽象得比较好的。 2、使用内存数据库,把所有的CURD操作抽象出接口,然后测试的时候创建CURD操作的mock对象,mock对象操作内存数据库。这个需要你的程序CURD操作和业务逻辑操作完全分离出两层出来,对系统架构设计是有一定要求的。

kun坤 2020-06-09 14:00:53 0 浏览量 回答数 0

问题

【精品问答】Java技术1000问(1)

问问小秘 2019-12-01 21:57:43 39155 浏览量 回答数 15

问题

【精品问答】python技术1000问(1)

问问小秘 2019-12-01 21:57:48 455812 浏览量 回答数 21

问题

荆门开诊断证明-scc

游客5k2abgdj3m2ti 2019-12-01 22:09:00 1 浏览量 回答数 0

回答

基础:比如计算机系统、算法、编译原理等等 Web开发: 主要是Web开发相关的内容,包括HTML/CSS/JS(前端页面)、Servlet/JSP(J2EE)以及Mysql(数据库)相关的知识。它们的学习顺序应该是从前到后,因此最先学习的应该是HTML/CSS/JS(前端页面),这部分内容你可以去上面的那个runoob网站上找。J2EE:你需要学习的是Servlet/JSP(J2EE)部分,这部分是Java后端开发必须非常精通的部分,因此这部分是这三部分中最需要花精力的。关于Servlet/Jsp部分视频的选择,业界比较认可马士兵的视频 。最后一步,你需要学会使用数据库,mysql是个不错的入门选择,而且Java领域里主流的关系型数据库就是mysql。这部分一般在你学习Servlet/Jsp的时候,就会接触到的,其中的JDBC部分就是数据库相关的部分。你不仅要学会使用JDBC操作数据库,还要学会使用数据库客户端工具,比如navicat,sqlyog,二选一即可。开发框架:目前比较主流的是SSM框架,即spring、springmvc、mybatis。你需要学会这三个框架的搭建,并用它们做出一个简单的增删改查的Web项目。你可以不理解那些配置都是什么含义,以及为什么要这么做,这些留着后面你去了解。但你一定要可以快速的利用它们三个搭建出一个Web框架,你可以记录下你第一次搭建的过程,相信我,你一定会用到的。还要提一句的是,你在搭建SSM的过程中,可能会经常接触到一个叫maven的工具。这个工具也是你以后工作当中几乎是必须要使用的工具,所以你在搭建SSM的过程中,也可以顺便了解一下maven的知识。在你目前这个阶段,你只需要在网络上了解一下maven基本的使用方法即可,一些高端的用法随着你工作经验的增加,会逐渐接触到的。在这一年里,你至少需要看完《Java编程思想》这本书。这本书的内容是帮助你对于Java有一个更加深入的了解,是Java基础的升级版。 总而言之,这个阶段的核心学习思想就是,在工作中实践,并且更加深入的了解Java基础。对于参加工作1年到2年的同学。这部分时间段的同学,已经对Java有了一个更加深入的了解。但是对于面向对象的体会可能还不够深刻,编程的时候还停留在完成功能的层次,很少会去考虑设计的问题。于是这个时候,设计模式就来了。我当时看的是《大话设计模式》这本书,并且写了完整版的设计模式博客。因此,我要求大家,最多在你工作一年的时候,必须开始写博客,而设计模式就是你博客的开端。此外,设计模式并不是你这一年唯一的任务,你还需要看一些关于代码编写优化的书。比如《重构 改善既有代码的设计》,《effective java》。总而言之,这个阶段,你的核心任务就是提高你的代码能力,要能写出一手优雅的代码。对于参加工作2年到3年的同学有的同学在这个时候觉得自己已经很牛逼了,于是忍不住开始慢慢松懈。请记住,你还嫩的多。这个阶段,有一本书是你必须看的,它叫做《深入理解Java虚拟机》。这本书绝对是Java开发者最重要的书,没有之一。在我眼里,这本书的重要性还要高于《Java编程思想》。这本书的内容是帮助你全面的了解Java虚拟机,在这个阶段,你一定已经知道Java是运行在JVM之上的。所以,对于JVM,你没有任何理由不了解它。这个时候,你应该去更加深入的了解并发相关的知识,而这部分内容,我比较推荐《Java并发编程实战》这本书。只要你把这本书啃下来了,并发的部分基本已经了解了十之六七。与此同时,这个阶段你要做的事情还远不止如此。这个时候,你应该对于你所使用的框架应该有了更深入的了解,对于Java的类库也有了更深入的了解。因此,你需要去看一些JDK中的类的源码,也包括你所使用的框架的源码。这些源码能看懂的前提是,你必须对设计模式非常了解。否则的话,你看源码的过程中,永远会有这样那样的疑问,这段代码为什么要这么写?为什么要定义这个接口,它看起来好像很多余?由此也可以看出,这些学习的过程是环环相扣的,如果你任何一个阶段拉下来了,那么你就真的跟不上了,或者说是一步慢步步慢。而且我很负责的告诉你,我在这个阶段的时候,所学习的东西远多于这里所罗列出来的。总而言之,这个阶段,你需要做的是深入了解Java底层和Java类库(比如并发那本书就是Java并发包java.concurrent的内容),也就是JVM和JDK的相关内容。而且还要更深入的去了解你所使用的框架,方式比较推荐看源码或者看官方文档。另外,还有一种学习的方式,在2年这个阶段,也应该启用了,那就是造轮子。不要听信那套“不要重复造轮子”的论调,那是公司为了节省时间成本编造出来的。重复造轮子或许对别人没有价值,因为你造的轮子可能早就有了,而且一般情况下你造出来的轮子还没有现存的好。  但是对别人没有价值,不代表对你自己没有价值。一个造轮子的过程,是一个从无到有的过程。这个过程可以对你进行系统的锻炼,它不仅考察你的编码能力,还考察你的框架设计能力,你需要让你的轮子拥有足够好的扩展性、健壮性。而且在造轮子的过程中,你会遇到各种各样的难题,这些难题往往又是你学习的契机。当你把轮子造好的时候,你一定会发现,其实你自己收获了很多。所以,这个阶段,除了上面提到的了解JVM、JDK和框架源码以外,也请你根据别人优秀的源码,去造一个任何你能够想象出来的轮子。第四部分:参加工作3年到4年的同学这个阶段的同学,提升已经是很难了,而且这个阶段的学习往往会比较多样化。因为在前3年的过程中,你肯定或多或少接触过一些其它的技术,比如大数据、分布式缓存、分布式消息服务、分布式计算、软负载均衡等等。这些技术,你能精通任何一项,都将是你未来面试时巨大的优势,因此如果你对某一项技术感兴趣的话,  这个时候可以深入去研究一下。这项技术不一定是你工作所用到的,但一定是相关的。而且在研究一门新技术时,切忌朝三暮四。有的同学今天去整整大数据,搞搞Hadoop、hbase一类的东西。过不了一段时间,就觉得没意思,又去研究分布式缓存,比如redis。然后又过不了一段时间,又去研究分布式计算,比如整整Mapreduce或者storm。结果到最后,搞得自己好像什么都会一样,在简历上大言不惭的写上大数据、分布式缓存、分布式计算都了解,其实任何一个都只是浮于表面。到时候面试官随便一问,就把你给识破了。我比较推崇的基础书籍有三本,分别是《深入理解计算机系统》,《tcp/ip详解 卷一、二、三》,《数据结构与算法》。其中TCP/IP有三本书,但我们这里把这三本看成是一本大书。这三本分别适合三种人,《深入理解计算机系统》比较适合一直从事Java Web开发和APP后端开发工作的人群。《tcp/ip详解 卷一、二、三》比较适合做网络编程的人群,比如你使用netty去开发的话,那么就要对TCP/IP有更深入的了解。而《数据结构与算法》这本书,则比较适合做计算研究工作的人,比如刚才提到的分布式计算。另外,我要强调的是,这里所说的适合,并不是其它两本对你就没有用。比如你做Java Web和APP后端开发,《tcp/ip详解 卷一、二、三》这本书对你的作用也是很大的。这里只是分出个主次关系而已,你要是时间足够的话,能把三本都精读那当然最好不过了。第五部分:参加工作4年到5年的同学经过前面一年的历练,相信你在自己所钻研的领域已经有了自己一定的见解,这个时候,技术上你应该已经遇到瓶颈了。这个时候不要着急提高自己的技术,已经是时候提高你的影响力了,你可以尝试去一些知名的公司去提高你的背景,你可以发表一些文章去影响更多的人。当然,你也可以去Github创建一个属于你的开源项目,去打造自己的产品。  这次的开源项目不同于之前的造轮子,你这个时候是真的要去尽量尝试造出来真正对别人有价值的轮子。技术学到这个阶段,很容易遇到瓶颈,而且往往达到一定程度后,你再深入下去的收效就真的微乎其微了,除非你是专门搞学术研究的。然而很可惜,大部分程序猿做不到这一步,那是科学家做的事情。这个时候提高影响力不仅仅是因为技术上容易遇到瓶颈,更多的是影响力可以给你创造更多的机会。程序猿在某种程度上和明星很像,一个好的电视剧和电影就可以成就一批明星,程序猿有的时候也是,一个好的项目就可以成就一群程序猿。比如国内几个脍炙人口的项目,像淘宝、支付宝、QQ、百度、微信等等。这每一个项目,都成就了一批程序猿。我敢说,这里面任何一个项目,如果你是它的核心开发,光是这样一个Title,就已经是你非常大的优势。更何况还不止如此,Title说到底也是个名头,更重要的是,这种项目在做的时候,对你的历练一定也是非常给力的。

hiekay 2019-12-02 01:40:04 0 浏览量 回答数 0

回答

基础:比如计算机系统、算法、编译原理等等 Web开发: 主要是Web开发相关的内容,包括HTML/CSS/JS(前端页面)、Servlet/JSP(J2EE)以及Mysql(数据库)相关的知识。它们的学习顺序应该是从前到后,因此最先学习的应该是HTML/CSS/JS(前端页面),这部分内容你可以去上面的那个runoob网站上找。J2EE:你需要学习的是Servlet/JSP(J2EE)部分,这部分是Java后端开发必须非常精通的部分,因此这部分是这三部分中最需要花精力的。关于Servlet/Jsp部分视频的选择,业界比较认可马士兵的视频 。最后一步,你需要学会使用数据库,mysql是个不错的入门选择,而且Java领域里主流的关系型数据库就是mysql。这部分一般在你学习Servlet/Jsp的时候,就会接触到的,其中的JDBC部分就是数据库相关的部分。你不仅要学会使用JDBC操作数据库,还要学会使用数据库客户端工具,比如navicat,sqlyog,二选一即可。开发框架:目前比较主流的是SSM框架,即spring、springmvc、mybatis。你需要学会这三个框架的搭建,并用它们做出一个简单的增删改查的Web项目。你可以不理解那些配置都是什么含义,以及为什么要这么做,这些留着后面你去了解。但你一定要可以快速的利用它们三个搭建出一个Web框架,你可以记录下你第一次搭建的过程,相信我,你一定会用到的。还要提一句的是,你在搭建SSM的过程中,可能会经常接触到一个叫maven的工具。这个工具也是你以后工作当中几乎是必须要使用的工具,所以你在搭建SSM的过程中,也可以顺便了解一下maven的知识。在你目前这个阶段,你只需要在网络上了解一下maven基本的使用方法即可,一些高端的用法随着你工作经验的增加,会逐渐接触到的。在这一年里,你至少需要看完《Java编程思想》这本书。这本书的内容是帮助你对于Java有一个更加深入的了解,是Java基础的升级版。 总而言之,这个阶段的核心学习思想就是,在工作中实践,并且更加深入的了解Java基础。对于参加工作1年到2年的同学。这部分时间段的同学,已经对Java有了一个更加深入的了解。但是对于面向对象的体会可能还不够深刻,编程的时候还停留在完成功能的层次,很少会去考虑设计的问题。于是这个时候,设计模式就来了。我当时看的是《大话设计模式》这本书,并且写了完整版的设计模式博客。因此,我要求大家,最多在你工作一年的时候,必须开始写博客,而设计模式就是你博客的开端。此外,设计模式并不是你这一年唯一的任务,你还需要看一些关于代码编写优化的书。比如《重构 改善既有代码的设计》,《effective java》。总而言之,这个阶段,你的核心任务就是提高你的代码能力,要能写出一手优雅的代码。对于参加工作2年到3年的同学有的同学在这个时候觉得自己已经很牛逼了,于是忍不住开始慢慢松懈。请记住,你还嫩的多。这个阶段,有一本书是你必须看的,它叫做《深入理解Java虚拟机》。这本书绝对是Java开发者最重要的书,没有之一。在我眼里,这本书的重要性还要高于《Java编程思想》。这本书的内容是帮助你全面的了解Java虚拟机,在这个阶段,你一定已经知道Java是运行在JVM之上的。所以,对于JVM,你没有任何理由不了解它。这个时候,你应该去更加深入的了解并发相关的知识,而这部分内容,我比较推荐《Java并发编程实战》这本书。只要你把这本书啃下来了,并发的部分基本已经了解了十之六七。与此同时,这个阶段你要做的事情还远不止如此。这个时候,你应该对于你所使用的框架应该有了更深入的了解,对于Java的类库也有了更深入的了解。因此,你需要去看一些JDK中的类的源码,也包括你所使用的框架的源码。这些源码能看懂的前提是,你必须对设计模式非常了解。否则的话,你看源码的过程中,永远会有这样那样的疑问,这段代码为什么要这么写?为什么要定义这个接口,它看起来好像很多余?由此也可以看出,这些学习的过程是环环相扣的,如果你任何一个阶段拉下来了,那么你就真的跟不上了,或者说是一步慢步步慢。而且我很负责的告诉你,我在这个阶段的时候,所学习的东西远多于这里所罗列出来的。总而言之,这个阶段,你需要做的是深入了解Java底层和Java类库(比如并发那本书就是Java并发包java.concurrent的内容),也就是JVM和JDK的相关内容。而且还要更深入的去了解你所使用的框架,方式比较推荐看源码或者看官方文档。另外,还有一种学习的方式,在2年这个阶段,也应该启用了,那就是造轮子。不要听信那套“不要重复造轮子”的论调,那是公司为了节省时间成本编造出来的。重复造轮子或许对别人没有价值,因为你造的轮子可能早就有了,而且一般情况下你造出来的轮子还没有现存的好。  但是对别人没有价值,不代表对你自己没有价值。一个造轮子的过程,是一个从无到有的过程。这个过程可以对你进行系统的锻炼,它不仅考察你的编码能力,还考察你的框架设计能力,你需要让你的轮子拥有足够好的扩展性、健壮性。而且在造轮子的过程中,你会遇到各种各样的难题,这些难题往往又是你学习的契机。当你把轮子造好的时候,你一定会发现,其实你自己收获了很多。所以,这个阶段,除了上面提到的了解JVM、JDK和框架源码以外,也请你根据别人优秀的源码,去造一个任何你能够想象出来的轮子。第四部分:参加工作3年到4年的同学这个阶段的同学,提升已经是很难了,而且这个阶段的学习往往会比较多样化。因为在前3年的过程中,你肯定或多或少接触过一些其它的技术,比如大数据、分布式缓存、分布式消息服务、分布式计算、软负载均衡等等。这些技术,你能精通任何一项,都将是你未来面试时巨大的优势,因此如果你对某一项技术感兴趣的话,  这个时候可以深入去研究一下。这项技术不一定是你工作所用到的,但一定是相关的。而且在研究一门新技术时,切忌朝三暮四。有的同学今天去整整大数据,搞搞Hadoop、hbase一类的东西。过不了一段时间,就觉得没意思,又去研究分布式缓存,比如redis。然后又过不了一段时间,又去研究分布式计算,比如整整Mapreduce或者storm。结果到最后,搞得自己好像什么都会一样,在简历上大言不惭的写上大数据、分布式缓存、分布式计算都了解,其实任何一个都只是浮于表面。到时候面试官随便一问,就把你给识破了。我比较推崇的基础书籍有三本,分别是《深入理解计算机系统》,《tcp/ip详解 卷一、二、三》,《数据结构与算法》。其中TCP/IP有三本书,但我们这里把这三本看成是一本大书。这三本分别适合三种人,《深入理解计算机系统》比较适合一直从事Java Web开发和APP后端开发工作的人群。《tcp/ip详解 卷一、二、三》比较适合做网络编程的人群,比如你使用netty去开发的话,那么就要对TCP/IP有更深入的了解。而《数据结构与算法》这本书,则比较适合做计算研究工作的人,比如刚才提到的分布式计算。另外,我要强调的是,这里所说的适合,并不是其它两本对你就没有用。比如你做Java Web和APP后端开发,《tcp/ip详解 卷一、二、三》这本书对你的作用也是很大的。这里只是分出个主次关系而已,你要是时间足够的话,能把三本都精读那当然最好不过了。第五部分:参加工作4年到5年的同学经过前面一年的历练,相信你在自己所钻研的领域已经有了自己一定的见解,这个时候,技术上你应该已经遇到瓶颈了。这个时候不要着急提高自己的技术,已经是时候提高你的影响力了,你可以尝试去一些知名的公司去提高你的背景,你可以发表一些文章去影响更多的人。当然,你也可以去Github创建一个属于你的开源项目,去打造自己的产品。  这次的开源项目不同于之前的造轮子,你这个时候是真的要去尽量尝试造出来真正对别人有价值的轮子。技术学到这个阶段,很容易遇到瓶颈,而且往往达到一定程度后,你再深入下去的收效就真的微乎其微了,除非你是专门搞学术研究的。然而很可惜,大部分程序猿做不到这一步,那是科学家做的事情。这个时候提高影响力不仅仅是因为技术上容易遇到瓶颈,更多的是影响力可以给你创造更多的机会。程序猿在某种程度上和明星很像,一个好的电视剧和电影就可以成就一批明星,程序猿有的时候也是,一个好的项目就可以成就一群程序猿。比如国内几个脍炙人口的项目,像淘宝、支付宝、QQ、百度、微信等等。这每一个项目,都成就了一批程序猿。我敢说,这里面任何一个项目,如果你是它的核心开发,光是这样一个Title,就已经是你非常大的优势。更何况还不止如此,Title说到底也是个名头,更重要的是,这种项目在做的时候,对你的历练一定也是非常给力的。

hiekay 2019-12-02 01:38:44 0 浏览量 回答数 0

问题

【精品问答】Python二级考试题库

珍宝珠 2019-12-01 22:03:38 1146 浏览量 回答数 2

问题

阿里云RDS实例平滑升级步骤

小咖秀 2019-12-01 21:23:12 9574 浏览量 回答数 2

回答

如何掌握牢靠Go语言的容器? 容器相对来说更偏重细节一些,如果想掌握的更牢靠的话呢,还是要多看一下代码,重点给大家几个提示 Go语言的并发初步有哪两个特别重要的特点? **GO语言的协程并发操作或者说协程的资源池,其调度策略有两个: ** 1、没有优先级,没有API能设置优先级,正是因为它一切都是靠Go语言自身的一个调度器来听调度,才能保证它的高效率,这点非常重要。 2、调度的策略是可抢占的,假如说一个任务它长时间的占用CPU,那么它是有可能被购入天的这个调度器给其抢占过来,让其其的任务来做运行,这是两个最重要的特点。 GO语言调度的单元goroutine的应用场景是什么? 使用JAVA或者C编写网络程序时,一个线程来处理一个http请求, 但是对于资源的利用率不高。而Go语言实现了轻量级线程的机制,GO语言在底层封装了所有的系统调用,自己实现了一个调度器,这种设计在操作系统的代码中非常多见。比如现代的操作系统基本都会封装一个软件的Timer,同时可以提供上万个软Timer同时工作,而这只是基于数量很少的硬件timer实现的,而GO语言中的并发也是如此,他是基于线程的调度池,这种调度的单元在Go语言中被称为goroutine。 GO语言与其它并发模型最大的区别是什么? 宏观GO语言与其它并发模型最大的不同,就是其推荐使用通信的这种方式来替代共享内存。当资源需要在goroutine之间进行共享的时候,实际上就是这个资源,或者说这个信息通过通道在goroutine之间进行通信的过程。因为这个锁,一般来说都是用在这个共享内存当中的,因为如果说大家阅读GO语言的相关代码,就可以看到这个channel,它实际上是基于锁来保证并发安全。 然而,这也不代表GO语言当中只能使用channel来进行一些操作,其也具备锁这方面的知识。因为现实当中,这个锁还是有一定它现实的意义和现实的要求,因为这个锁它最关键的一个意义就是它能保证资源能在并发的操作当中有一个合理的调度情况和调度策略。其中跟这个最重要,或者说最关联性最强的一个概念就是原子操作。 GO语言中的原子操作具体实现过程是怎样的? 对于原子操作,在其逻辑下,按照它书面的定义上来讲,是指不会被调度器打断的操作。对原子操作实际上就是不存在中间状态的一种操作,要不就全成功,要不全失败,这个在我们在用并发方式来调动某任务,或者说来设计某种并发系统的情况下,这种名字操作我发现是非常重要的设计理念之一。 并发与并行具体概念及实际区分是怎样的? 有一个比较重要的一个概念,就是并发与并行,其实并发与并行,它实际上具体的含义是不一样的,并发实际上是把任务在不同的时间点交给同样一个处理器来进行处理,在同一个时间点,任务不会同时进行,只是任务感觉自己正在执行,因为其那会儿可能正在堵塞状态或者说是就绪状态,其不知道自己被暂停了,以为已经被调度走了,可能自己没有感知,但是实际上CPU所有权已经不在这个任务身上了。 并行比并发更高级一些,它实际上是把每个任务都交给独立的处理器去进行完成,但同一时间点,任务在一定程度上实际上是同时在执行的。一般来说,并发的性能是要比并行更重要一些,在1.5版本之前,我们需要人工去设置GO调度器最多能运行在多少个CPU上,但是在最新的GO版本当中,已经不需要这个相关的操作。 详细介绍一下并发程序中的竞争态? 并发系统设计最初始的这一个概念就是并发程序设计当中一个竞合的概念,或者也叫竞争态。假如说我要记录一个文件的阅读量,但是这个文件或者说这个网页,可能它的阅读渠道有非常多,有可能通过引擎通过微信通过APP等等这些渠道,这些渠道的话呢,它的阅读也都是并发的,这就会涉及到同样一个变量,被多个协程的所共同访问的情况。具体代码如下: 对于GO语言并发体系中的主推的通信机制是什么? channel是GO语言并发体系中的主推的通信机制,它可以让一个 goroutine 通过它给另一个 goroutine 发送值信息。每个 channel 都有一个特殊的类型,也就是 channels 可发送数据的类型。一个可以发送 int 类型数据的 channel 一般写为 chan int。 GO语言当中,它实际上是大家协同的机制,通过这种方式让几个goroutine之间做达到一个协调的效果,那么每个goroutine当中,实际上channel都是一个特殊的类型,它实际上是可以发送数据。比如现在想发送一个int类型的数据,那么channel就要定义一个发送int数据的一个管道。 那么GO语言当中,提倡使用通讯的方式来代替共享内存的方式来做goroutine,或者说并发之间的一个协同。channel如果我们后续阅读它的代码就会知道,它是保证协程安全,并且它遵循这个先入先出的原则来让这个储蓄方读取获得数据,而且它能保证顺序,正是这两个特性,可以让这个channel替代共享内存,因为它的如果顺序有所改变的话,它实际上也是有会有问题。 详细介绍GO语言中关于通道的声明涉及哪些方面? 1.经典方式声明 通过使用chan类型,其声明方式如下: var name chan type 其中type表示通道内的数据类型;name:通道的变量名称,不过这样创建的通道只是空值 nil,一般来说都是通道都是通过make函数创建的。 2.make方式 make函数可以创建通道格式如下: name := make(chan type) 3.创建带有缓冲的通道 后面会讲到缓冲通道的概念,这里先说他的定义方式 name := make(chan type, size) 其中type表示通道内的数据类型;name:通道的变量名称,size代表缓冲的长度。 具体介绍通道数据收发的详细过程有哪些? 通道的数据发送 通道当中发送数据的操作服务是这样的这样的一个大于号加上一个减号。 chan <- value 注意,如果是发送给一个没有缓冲的一个通道。假如说数据没有被接收的话,那么这个发送操作将持续被注册,也就是说就是channel这个语句就直接被注册到这,假如说没有任何的协程去读到他或者其他语句去读到这个产品,那么这个语句就被注册掉了。但GO语言是能发现的,如果其一直在堵塞的话,那实际上就造成死锁,GO语言的编译器实际上能发现的有点错误。 假如说,首先创建一个int型的通道,然后直接尝试发送一个数据给它,编译会报错,然后呢,数据的这个数据的接收的话,实际上就是把这个点号的位置跟那个大于号的位置做了一个调换。其实把这个双方的位置做了一个调换之后,是实际上就是都做了一个允许的操作。这其中的话呢,还有一种比较特殊的一个读取操作是其可以忽略到接收到的数据,因为不管管道中发出的数据,如果没读的话就堵塞到这,那么如果你觉得这个语句你也不需要,那么你可以把那个变量给它忽略掉。 2.通道的数据接收 通道接收数据的操作符也是<-,具体有以下几种方式 - 1) 阻塞接收数据 阻塞模式接收数据时,将接收变量作为<-操作符的左值,格式如下: data := <-ch 执行该语句时将会阻塞,直到接收到数据并赋值给 data 变量。 如需要忽略接收的数据,则将data变量省略,具体格式如下: <-ch - 2) 非阻塞接收数据 使用非阻塞方式从通道接收数据时,语句不会发生阻塞,格式如下: data, ok := <-ch 非阻塞的通道接收方法可能造成高的 CPU 占用,因此使用非常少。一般只配合select语句配合定时器做超时检测时使用。 关于通道数据收发有哪些需要注意的事项? 通道数据在进行输入收发的时候,必须要在两个不同的goroutine当中进行,因在同一个goroutine当中,收发的这些语句实际上都是堵塞的,你可能在同一个goroutine当中,它的这个函数已经在那边阻塞住了,或者说程序已经在那边阻塞住了,它已经停在那了,你后面有一句你能执行不到,所以说通道的收发必须在两个不同的goroutine之间来进行,在同一个goroutine之间的这个收发操作的话,实际上是没有意义的。 接收将持续堵塞,直到发送方发送出去,如果接收方接收,然后通道中没有发送方数据时,接收方也会发送,直到发送方到发送数据为止。就是刚才说的这个一体两面,这个发送方假如说没有人读的话,发送方会堵塞,假如说没有人写的话,那么接收方也会发生堵塞,这两边实际上都会有一个堵塞的情况。那么这个通道的收发的话呢,一般来说一次只能收一一个元素,假如说这个是一个有缓冲的一个通道,我通过一次不操作的话,实际上也只不过读出一个元素。不能把它一些缓冲区所有元素都读出来。 聊一下生产者消费者模式具体内容有哪些? 介绍一下生产者消费者模式,从GO语言的这个并发模型来看,也就是说假如说咱们站在一个比较高的一个高度来看,其实利用channel的确能达到共享内存的目的。这个channel的性质与在读写状态且保证顺序的共享内存并无不同。甚至我们可以说这个是基于消息队列的封装程度可以比共享内存来的更安全,所以说呢,这个在这个GO语言当中,或者说在GO语言的这个设计风格当中的话呢,其这个生产者消费者模式实现起来会相对来说比较简单一些。我们先介绍一下什么是生产者消费者。 就这个这这张图当中的话呢,就是一个典型的那种消费的问题, 就是说我是生产者的话我会生产一些产品,然后放到这个仓库当中,消费者的话会从那个仓库当中去取商品,这个可以说是消息队列,还有包括卡夫卡那些比较经典的相应队列当中,都会用到的这么一个设计模式,或者说其们从本质上来说的话,都是基于这样一个设计模式,交易的生产者是谁?消费者是谁?这个消息队列的话是。这个生产者消费者模式的话呢,实际上也成为有缓冲有限缓冲问题,它是一个并发的一个经典的案例,因为我们知道这个商品仓库的库房大小是有限的,也就是说生产者不能无限的去生产商品,一旦这个库房爆掉的话,它是它是必须要中止自己的生产,消费者也是不能无限地获取消息。 假如仓库是空的话,那这个消费者的这个相关的情况也需要被阻塞。那么怎么在这个生产者跟消费者之间保证商品不丢失。这就是生产者与消费者之间最核心的内容。先来看一下这个Java当中生产者消费者的这种实现到底是什么样的。这个可以说是一个最经典的这么样一个实现。这个Java当中是没有channel,那么它只能通过什么呢,只能通过信号量和一个一个log,也就是说一个忽视服务态度,这两个这两个配合信号量和所配合才能共同完成,这样一个生产者消费者这么一个相关的工作。 GO语言并发实战详细过程梳理 在现在这个远程办公的这一个大的背景下,积累了大量重复的文件,因为很可能大家都不断的在不同的群里发相同的文件,发相同的这个报表,以及一些相同的视频等等这些需要学习的材料,那么怎么把这些文件都找出来,然后把这些相同文件都给删掉了,这实际上是并发课的一个实践的一个内容,因为这个创业型的这个方案的话,它的代码相对来说比较长。 如何使用GO语言清理PC机中的文件,详细代码及注释如下: package main import ( // "fmt" // fmt 包使用函数实现 I/O 格式化(类似于 C 的 printf 和 scanf 的函数), 格式化参数源自C,但更简单 "io/ioutil" //"sync" //"time" ) func PrintRepreatFile(path string, fileNameSizeMap map[string]int64, exFileList []string) { fs, _ := ioutil.ReadDir(path) for _, file := range fs { if file.IsDir() { PrintRepreatFile(path+"/"+file.Name(), fileNameSizeMap, exFileList)//遍历整个文件系统,如果是目录则递归调用 } else { if file.Size() > 1000000 {//设定文件清理阈值,如果大于一定大小再进行清理 fileSize := fileNameSizeMap[file.Name()]//通过查哈希表的方式来确定,有无重名且大小相同的文件。 if fileSize == file.Size() { fmt.Println(path + "/" + file.Name())//如果有则打印出来 exFileList = append(exFileList, path+file.Name())//将结果记入切片当中 } else { fileNameSizeMap[file.Name()] = file.Size() } } } } } func main() { //方式一 fileNameSizeMap := make(map[string]int64, 10000) exFileList := make([]string, 100, 1000) PrintRepreatFile("E:/test", fileNameSizeMap, exFileList) } 这个程序在GO语言的环境下可以直接运行使用,其中有几个知识点,也是咱们前文提到过的,首先是切片的大小一定要设定的相对合适一些,如果容量不够大造成频繁扩容非常浪费资源。二是哈希表也就是map没有并发安全的属于,在我们这个未引入并发的程序中可以使用,如果有并发操作,那么map不再适用了。 可能很多人被GO语言的在并发性能所吸引入坑的,GO语言之父也就是UNIX之父Ken Thompson明显给出了很多建议,根据笔者在操作系统方面的相关经验来看,GO语言设计中经常参考UNIX内核的设计思路。比如硬定时器的数量有限,无法满足系统实际运行需要,所以在内核代码中就会看到基于硬件定时器的软件定时器的方案,而软件定时器的数量可以比硬件定时器多几百倍。 这样的理念明显融合到了 goroutine之中,由于其它编程语言往往直接通过系统级别的线程来实现并发功能,但是这样的方式往往会是大马拉小车,造成系统资源的浪费。因此GO语言封装了所有的系统操作,实现了更加轻量级的协程-goroutine。只要使用关键字(go)就可以启动协程,对比C++、JAVA的多线程并发模型,GO的协程更简单明了。 当然协程之间的消息通信与并发控制也是非常重要的一环。在GO语言借鉴了Message Queue的消息队列机制替代共享内存的方式进行协程间通信,其中管道channel作为基本的数据类型,保证并发时的操作安全。而且管道的引入还带来很多实践中非常实用的功能,比如可以方便实现生产者、消费者等并发设计模式,而这些设计模式在其它使用共享存内存的并发模型中实现起相关功能来非常的繁锁。 在GO语言中在调用函数前加入go 关键字,就能启动一个协程,也就是一个并发,但是我们上面的程序如果把调用方式改为: go PrintRepreatFile("E:/test", fileNameSizeMap, exFileList) 你会发现程序会直接退出,什么都没做,所以GO语言的并发对于初学者来说还是有一定门槛的,比如上例中如果想设计成一个并行的程序,如何让多个协程共同来帮忙找出重复的文件其实还是要费一番周折的。

剑曼红尘 2020-04-13 11:06:46 0 浏览量 回答数 0

问题

如何保证缓存与数据库的双写一致性?【Java问答】38期

剑曼红尘 2020-06-16 12:58:57 36 浏览量 回答数 1

问题

最大限度利用 JavaScript 和 Ajax 性能:报错

kun坤 2020-06-05 22:56:50 0 浏览量 回答数 1

回答

回 2楼(zc_0101) 的帖子 您好,       您的问题非常好,SQL SERVER提供了很多关于I/O压力的性能计数器,请选择性能计算器PhysicalDisk(LogicalDisk),根据我们的经验,如下指标的阈值可以帮助你判断IO是否存在压力: 1.  % Disk Time :这个是磁盘时间百分比,这个平均值应该在85%以下 2.  Current Disk Queue Length:未完成磁盘请求数量,这个每个磁盘平均值应该小于2. 3.  Avg. Disk Queue Length:磁盘请求队列的平均长度,这个每个磁盘平均值也应该小于2 4.  Disk Transfers/sec:每次磁盘传输数量,这个每个磁盘的最大值应该小于100 5.  Disk Bytes/sec:每次磁盘传入字节数,这个在普通的磁盘上应该在10M左右 6.  Avg. Disk Sec/Read:从磁盘读取的平均时间,这个平均值应该小于10ms(毫秒) 7.  Avg. Disk Sec/Write:磁盘写入的平均时间,这个平均值也应该小于10ms(毫秒) 以上,请根据自己的磁盘系统判断,比如传统的机械臂磁盘和SSD有所不同。 一般磁盘的优化方向是: 1. 硬件优化:比如使用更合理的RAID阵列,使用更快的磁盘驱动器,添加更多的内存 2. 数据库设置优化:比如创建多个文件和文件组,表的INDEX和数据放到不同的DISK上,将数据库的日志放到单独的物理驱动器,使用分区表 3. 数据库应用优化:包括应用程序的设计,SQL语句的调整,表的设计的合理性,INDEX创建的合理性,涉及的范围很广 希望对您有所帮助,谢谢! ------------------------- 回 3楼(鹰舞) 的帖子 您好,      根据您的描述,由于查询产生了副本REDO LOG延迟,出现了架构锁。我们知道SQL SERVER 2012 AlwaysOn在某些数据库行为上有较多变化。我们先看看架构锁: 架构锁分成两类: 1. SCH-M:架构更改锁,主要发生在数据库SCHEMA的修改上,从你的描述看,没有更改SCHEMA,那么可以排除这个因素 2. SCH-S:架构稳定锁,主要发生在数据库的查询编译等活动 根据你的情况,应该属于SCH-S导致的。查询编译活动主要发生有新增加了INDEX, 更新了统计信息,未参数化的SQL语句等等 对于INDEX和SQL语句方面应,我想应该不会有太多问题。 我们重点关注一下统计信息:SQL SERVER 2012 AG副本的统计信息维护有两种: 1. 主体下发到副本 2. 临时统计信息存储在TEMPDB 对于主体下发的,我们可以设置统计信息的更新行为,自动更新时,可以设置为异步的(自动更新统计信息必须首先打开): USE [master] GO ALTER DATABASE [Test_01]     SET AUTO_UPDATE_STATISTICS_ASYNC ON WITH NO_WAIT GO 这样的话查询优化器不等待统计信息更新完成即编译查询。可以优化一下你的BLOCK。 对于临时统计信息存储在TEMPDB里面也是很重要的,再加上ALWAYSON的副本数据库默认是快照隔离,优化TEMPDB也是必要的,关于优化TEPDB这个我想大部分都知道,这里只是提醒一下。 除了从统计信息本身来解决,在查询过程中,可以降低查询的时间,以尽量减少LOCK的时间和范围,这需要优化你的SQL语句或者应用程序。 以上,希望对您有所帮助。谢谢! ------------------------- 回 4楼(leamonjxl) 的帖子 这是一个关于死锁的问题,为了能够提供帮助一些。请根据下列建议进行: 1.    跟踪死锁 2.    分析死锁链和原因 3.    一些解决办法 关于跟踪死锁,我们首先需要打开1222标记,例如DBCC TRACEON(1222,-1), 他将收集的信息写入到死锁事件发生的服务器上的日志文件中。同时建议打开Profiler的跟踪信息: 如果发生了死锁,需要分析死锁发生的根源在哪里?我们不是很清楚你的具体发生死锁的形态是怎么样的。 关于死锁的实例也多,这里不再举例。 这里只是提出一些可以解决的思路: 1.    减少锁的争用 2.    减少资源的访问数 3.    按照相同的时间顺序访问资源 减少锁的争用,可以从几个方面入手 1.    使用锁提示,比如为查询语句添加WITH (NOLOCK), 但这还取决于你的应用是否允许,大部分分布式的系统都是可以加WITH (NOLOCK), 金融行业可能需要慎重。 2.    调整隔离级别,使用MVCC,我们的数据库默认级别是READ COMMITED. 建议修改为读提交快照隔离级别,这样的话可以尽量读写不阻塞,只不过MVCC的ROW VERSION保存到TEMPDB下面,需要维护好TEMPDB。当然如果你的整个数据库隔离级别可以设置为READUNCOMMINTED,这些就不必了。 减少资源的访问数,可以从如下几个方面入手: 1.    使用聚集索引,非聚集INDEX的叶子页面与堆或者聚集INDEX的数据页面分离。因此,如果对非聚集INDEX 操作的话,会产生两个锁,一个是基本表,一个是非聚集INDEX。而聚集INDEX就不一样,聚集INDEX的叶子页面和表的数据页面相同,他只需要一个LOCK。 2.    查询语句尽量使用覆盖INDEX, 使用全覆盖INDEX,就不需要访问基本表。如果没有全覆盖,还会通过RID或者CLUSTER INDEX访问基本表,这样产生的LOCK可能会与其他SESSION争用。 按照相同的时间顺序访问资源: 确保每个事务按照相同的物理顺序访问资源。两个事务按照相同的物理顺序访问,第一个事务会获得资源上的锁而不会被第二个事务阻塞。第二个事务想获得第一个事务上的LOCK,但被第一个事务阻塞。这样的话就不会导致循环阻塞的情况。 ------------------------- 回 4楼(leamonjxl) 的帖子 两种方式看你的业务怎么应用。这里不仅是分表的问题,还可能存在分库,分服务器的问题。取决与你的架构方案。 物理分表+视图,这是一种典型的冷热数据分离的方案,大致的做法如下: 1.    保留最近3个月的数据为当前表,也即就是我们说的热数据 2.    将其他数据按照某种规则分表,比如按照年或者季度或者月,这部分是相对冷的数据 分表后,涉及到几个问题: 第一问题是,转移数据的过程,一般是晚上业务比较闲来转移,转移按照一定的规则来做,始终保持3个月,这个定时任务本身也很消耗时间 再者,关于查询部分,我想你们的数据库服务器应该通过REPLICATION做了读写分离的吧,主库我觉得压力不会太大,主要是插入或者更新,只读需要做视图来包含全部的数据,但通过UNION ALL所有分表的数据,最后可能还是非常大,在某些情况下,性能不一定好。这个是不是业务上可以解决。比如,对于1年前的历史数据,放在单独的只读上,相对热的数据放在一起,这样压力也会减少。 分区表的话,因为涉及到10亿数据,要有好的分区方案,相对比较简单一点。但对于10亿的大表,始终是个棘手的问题,无论分多少个分区,单个服务器的资源也是有限的。可扩展性方面也存在问题,比如在只读上你没有办法做服务器级别的拆分了。这可能也会造成瓶颈。 现在很多企业都在做分库分表,这些的要解决一些高并发,数据量大的问题。不知是否考虑过类似于中间件的方案,比如阿里巴巴的TDDL类似的方案,如果你有兴趣,可以查询相关资料。 ------------------------- 回 9楼(jiangnii) 的帖子 阿里云数据库不仅提供一个数据库,还提供数据库一种服务。阿里云数据库不仅简化了基础架构的部署,还提供了数据库高可用性架构,备份服务,性能诊断服务,监控服务,专家服务等等,保证用户放心、方便、省心地使用数据库,就像水电一样。以前的运维繁琐的事,全部由阿里云接管,用户只需要关注数据库的使用和具体的业务就好。 关于优化和在云数据库上处理大数据量或复杂的数据操作方面,在云数据库上是一样的,没有什么特别的地方,不过我们的云数据库是使用SSD磁盘,这个比普通的磁盘要快很多,IO上有很大的优势。目前单个实例支持1T的数据量大小。陆续我们会推出更多的服务,比如索引诊断,连接诊断,容量分析,空间诊断等等,这些工作可能是专业的DBA才能完成的,以后我们会提供自动化的服务来为客户创造价值,希望能帮助到客户。 谢谢! ------------------------- 回 12楼(daniellin17) 的帖子 这个问题我不知道是否是两个问题,一个是并行度,另一个是并发,我更多理解是吞吐量,单就并行度而言。 提高并行度需要考虑的因素有: 1.    可用于SQL SERVER的CPU数量 2.    SQL SERVER的版本(32位/64位) 3.    可用内存 4.    执行的查询类型 5.    给定的流中处理的行数 6.    活动的并发连接数量 7.    sys.configurations参数:affinity mask/max server memory (MB)/ max degree of parallelism/ cost threshold for parallelism 以DOP的参数控制并行度为例,设置如下: SELECT * FROM sys.configurations WITH (NOLOCK) WHERE name = 'max degree of parallelism' EXEC sp_configure 'max degree of parallelism',2 RECONFIGURE WITH OVERRIDE 经过测试,DOP设置为2是一个比较适中的状态,特别是OLTP应用。如果设置高了,会产生较多的SUSPEND进程。我们可以观察到资源等待资源类型是:CXPACKET 你可以用下列语句去测试: DBCC SQLPERF('sys.dm_os_wait_stats',CLEAR) SELECT * FROM sys.dm_os_wait_stats WITH (NOLOCK) ORDER BY 2 DESC ,3 DESC 如果是吞吐量的话。优化的范围就很广了。优化是系统性的。硬件配置我们选择的话,大多根据业务量来预估,然后考虑以下: 1.    RAID的划分,RAID1适合存放事务日志文件(顺序写),RAID10/RAID5适合做数据盘,RAID10是条带化并镜像,RAID5条带化并奇偶校验 2.    数据库设置,比如并行度,连接数,BUFFER POOL 3.    数据库文件和日志文件的存放规则,数据库文件的多文件设置规则 4.    TEMPDB的优化原则,这个很重要的 5.    表的设计方面根据业务类型而定 6.    CLUSTERED INDEX和NONCLUSTERED INDEX的设计 7.    阻塞分析 8.    锁和死锁分析 9.    执行计划缓冲分析 10.    存储过程重编译 11.    碎片分析 12.    查询性能分析,这个有很多可以优化的方式,比如OR/UNION/类型转换/列上使用函数等等 我这里列举一个高并发的场景: 比如,我们的订单,比如搞活动的时候,订单刷刷刷地增长,单个实例可能每秒达到很高很高,我们分析到最后最常见的问题是HOT PAGE问题,其等待类型是PAGE LATCH竞争。这个过程可以这么来处理,简单列几点,可以参考很多涉及高并发的案例: 1.    数据库文件和日志文件分开,存放在不同的物理驱动器磁盘上 2.    数据库文件需要与CPU个数形成一定的比例 3.    表设计可以使用HASH来作为表分区 4.    表可以设置无序的KEY/INDEX,比如使用GUID/HASH VALUE来定义PRIMARY KEY CLUSTER INDEX 5.    我们不能将自增列设计为聚集INDEX 这个场景只是针对高并发的插入。对于查询而言,是不适合的。但这些也可能导致大量的页拆分。只是在不同的场景有不同的设计思路。这里抛砖引玉。 ------------------------- 回 13楼(zuijh) 的帖子 ECS上现在有两种磁盘,一种是传统的机械臂磁盘,另一种是SSD,请先诊断你的IO是否出现了问题,本帖中有提到如何判断磁盘出现问题的相关话题,请参考。如果确定IO出现问题,可以尝试使用ECS LOCAL SSD。当然,我们欢迎你使用云数据库的产品,云数据库提供了很多有用的功能,比如高可用性,灵活的备份方案,灵活的弹性方案,实用的监控报警等等。 ------------------------- 回 17楼(豪杰本疯子) 的帖子 我们单个主机或者单个实例的资源总是有限的,因为涉及到很大的数据量,对于存储而言是个瓶颈,我曾使用过SAN和SAS存储,SAN存储的优势确实可以解决数据的灵活扩展,但是SAN也分IPSAN和FIBER SAN,如果IPSAN的话,性能会差一些。即使是FIBER SAN,也不是很好解决性能问题,这不是它的优势,同时,我们所有DB SERVER都连接到SAN上,如果SAN有问题,问题涉及的面就很广。但是SAS毕竟空间也是有限的。最终也会到瓶颈。数据量大,是造成性能问题的直接原因,因为我们不管怎么优化,一旦数据量太大,优化的能力总是有限的,所以这个时候更多从架构上考虑。单个主机单个实例肯定是抗不过来的。 所以现在很多企业在向分布式系统发展,对于数据库而言,其实有很多形式。我们最常见的是读写分离,比如SQL SERVER而言,我们可以通过复制来完成读写分离,SQL SERVER 2012及以后的版本,我们可以使用ALWAYSON来实现读写分离,但这只能解决性能问题,那空间问题怎么解决。我们就涉及到分库分表,这个分库分表跟应用结合得紧密,现在很多公司通过中间件来实现,比如TDDL。但是中间件不是每个公司都可以玩得转的。因此可以将业务垂直拆分,那么DB也可以由此拆分开来。举个简单例子,我们一个典型的电子商务系统,有订单,有促销,有仓库,有配送,有财务,有秒杀,有商品等等,很多公司在初期,都是将这些放在一个主机一个实例上。但是这些到了一定规模或者一定数据量后,就会出现性能和硬件资源问题,这时我们可以将它们独立一部分获完全独立出来。这些都是一些好的方向。希望对你有所帮助。 ------------------------- 回 21楼(dt) 的帖子 问: 求大数据量下mysql存储,优化方案 分区好还是分表好,分的过程中需要考虑事项 mysql高并发读写的一些解决办法 答: 分区:对于应用来说比较简单,改造较少 分表: 应用需较多改造,优点是数据量太大的情况下,分表可以拆分到多个实例上,而分区不可以。 高并发优化,有两个建议: 1.    优化事务逻辑 2.    解决mysql高并发热点,这个可以看看阿里的一个热点补丁: http://www.open-open.com/doc/view/d58cadb4fb68429587634a77f93aa13f ------------------------- 回 23楼(aelven) 的帖子 对于第一个问题.需要看看你的数据库架构是什么样的?比如你的架构具有高可用行?具有读写分离的架构?具有群集的架构.数据库应用是否有较冷门的功能。高并发应该不是什么问题。可扩展性方面需要考虑。阿里云数据库提供了很多优势,比如磁盘是性能超好的SSD,自动转移的高可用性,没有任何单点,自动灵活的备份方案,实用的监控报警,性能监控服务等等,省去DBA很多基础性工作。 你第二个问题,看起来是一个高并发的场景,这种高并发的场景容易出现大量的LOCK甚至死锁,我不是很清楚你的业务,但可以建议一下,首先可以考虑快照隔离级别,实现行多版本控制,让读写不要阻塞。至于写写过程,需要加锁的粒度降低最低,同时这种高并发也容易出现死锁,关于死锁的分析,本帖有提到,请关注。 第三个问题,你用ECS搭建自己的应用也是可以的,RDS数据库提供了很多功能,上面已经讲到了。安全问题一直是我们最看重的问题,肯定有超好的防护的。 ------------------------- 回 26楼(板砖大叔) 的帖子 我曾经整理的关于索引的设计与规范,可以供你参考: ----------------------------------------------------------------------- 索引设计与规范 1.1    使用索引 SQL SERVER没有索引也可以检索数据,只不过检索数据时扫描这个表而异。存储数据的目的,绝大多数都是为了再次使用,而一般数据检索都是带条件的检索,数据查询在数据库操作中会占用较大的比例,提高查询的效率往往意味着整个数据库性能的提升。索引是特定列的有序集合。索引使用B-树结构,最小优化了定位所需要的键值的访问页面量,包含聚集索引和非聚集索引两大类。聚集索引与数据存放在一起,它决定表中数据存储的物理顺序,其叶子节点为数据行。 1.2    聚集索引 1.2.1    关于聚集索引 没聚集索引的表叫堆。堆是一种没有加工的数据,以行标示符作为指向数据存储位置的指针,数据没有顺序。聚集索引的叶子页面和表的数据页面相同,因此表行物理上按照聚集索引列排序,表数据的物理顺序只有一种,所以一个表只有一个聚集索引。 1.2.2    与非聚集索引关系 非聚集索引的一个索引行包含指向表对应行的指针,这个指针称为行定位器,行定位器的值取决于数据页保存为堆还是被聚集。若是堆,行定位器指向的堆中数据行的行号指针,若是聚集索引表,行定位器是聚集索引键值。 1.2.3    设计聚集索引注意事项     首先创建聚集索引     聚集索引上的列需要足够短     一步重建索引,不要使用先DROP再CREATE,可使用DROP_EXISTING     检索一定范围和预先排序数据时使用,因为聚集索引的叶子与数据页面相同,索引顺序也是数据物理顺序,读取数据时,磁头是按照顺序读取,而不是随机定位读取数据。     在频繁更新的列上不要设计聚集索引,他将导致所有的非聚集所有的更新,阻塞非聚集索引的查询     不要使用太长的关键字,因为非聚集索引实际包含了聚集索引值     不要在太多并发度高的顺序插入,这将导致页面分割,设置合理的填充因子是个不错的选择 1.3    非聚集索引 1.3.1    关于非聚集索引 非聚集索引不影响表页面中数据的顺序,其叶子页面和表的数据页面时分离的,需要一个行定位器来导航数据,在将聚集索引时已经有说明,非聚集索引在读取少量数据行时特别有效。非聚集索引所有可以有多个。同时非聚集有很多其他衍生出来的索引类型,比如覆盖索引,过滤索引等。 1.3.2    设计非聚集索引     频繁更新的列,不适合做聚集索引,但可以做非聚集索引     宽关键字,例如很宽的一列或者一组列,不适合做聚集索引的列可作非聚集索引列     检索大量的行不宜做非聚集索引,但是可以使用覆盖索引来消除这种影响 1.3.3    优化书签查找 书签会访问索引之外的数据,在堆表,书签查找会根据RID号去访问数据,若是聚集索引表,一般根据聚集索引去查找。在查询数据时,要分两个部分来完成,增加了读取数据的开销,增加了CPU的压力。在大表中,索引页面和数据页面一般不会临近,若数据只存在磁盘,产生直接随机从磁盘读取,这导致更多的消耗。因此,根据实际需要优化书签查找。解决书签查找有如下方法:     使用聚集索引避免书签查找     使用覆盖索引避免书签查找     使用索引连接避免数据查找 1.4    聚集与非聚集之比较 1.4.1    检索的数据行 一般地,检索数据量大的一般使用聚集索引,因为聚集索引的叶子页面与数据页面在相同。相反,检索少量的数据可能非聚集索引更有利,但注意书签查找消耗资源的力度,不过可考虑覆盖索引解决这个问题。 1.4.2    数据是否排序 如果数据需要预先排序,需要使用聚集索引,若不需要预先排序就那就选择聚集索引。 1.4.3    索引键的宽度 索引键如果太宽,不仅会影响数据查询性能,还影响非聚集索引,因此,若索引键比较小,可以作为聚集索引,如果索引键够大,考虑非聚集索引,如果很大的话,可以用INCLUDE创建覆盖索引。 1.4.4    列更新的频度 列更新频率高的话,应该避免考虑所用非聚集索引,否则可考虑聚集索引。 1.4.5    书签查找开销 如果书签查找开销较大,应该考虑聚集索引,否则可使用非聚集索引,更佳是使用覆盖索引,不过得根据具体的查询语句而看。 1.5    覆盖索引 覆盖索引可显著减少查询的逻辑读次数,使用INCLUDE语句添加列的方式更容易实现,他不仅减小索引中索引列的数据,还可以减少索引键的大小,原因是包含列只保存在索引的叶子级别上,而不是索引的叶子页面。覆盖索引充当一个伪的聚集索引。覆盖索引还能够有效的减少阻塞和死锁的发生,与聚集索引类似,因为聚集索引值发生一次锁,非覆盖索引可能发生两次,一次锁数据,一次锁索引,以确保数据的一致性。覆盖索引相当于数据的一个拷贝,与数据页面隔离,因此也只发生一次锁。 1.6    索引交叉 如果一个表有多个索引,那么可以拥有多个索引来执行一个查询,根据每个索引检索小的结果集,然后就将子结果集做一个交叉,得到满足条件的那些数据行。这种技术可以解决覆盖索引中没有包含的数据。 1.7    索引连接 几乎是跟索引交叉类似,是一个衍生品种。他将覆盖索引应用到交叉索引。如果没有单个覆盖索引查询的索引而多个索引一起覆盖查询,SQL SERVER可以使用索引连接来完全满足查询而不需要查询基础表。 1.8    过滤索引 用来在可能没有好的选择性的一个或者多个列上创建一个高选择性的关键字组。例如在处理NULL问题比较有效,创建索引时,可以像写T-SQL语句一样加个WHERE条件,以排除某部分数据而检索。 1.9    索引视图 索引视图在OLAP系统上可能有胜算,在OLTP会产生过大的开销和不可操作性,比如索引视图要求引用当前数据库的表。索引视图需要绑定基础表的架构,索引视图要求企业版,这些限制导致不可操作性。 1.10    索引设计建议 1.10.1    检查WHERE字句和连接条件列 检查WHERE条件列的可选择性和数据密度,根据条件创建索引。一般地,连接条件上应当考虑创建索引,这个涉及到连接技术,暂时不说明。 1.10.2    使用窄的索引 窄的索引有可减少IO开销,读取更少量的数据页。并且缓存更少的索引页面,减少内存中索引页面的逻辑读取大小。当然,磁盘空间也会相应地减少。 1.10.3    检查列的唯一性 数据分布比较集中的列,种类比较少的列上创建索引的有效性比较差,如果性别只有男女之分,最多还有个UNKNOWN,单独在上面创建索引可能效果不好,但是他们可以为覆盖索引做出贡献。 1.10.4    检查列的数据类型 索引的数据类型是很重要的,在整数类型上创建的索引比在字符类型上创建索引更有效。同一类型,在数据长度较小的类型上创建又比在长度较长的类型上更有效。 1.10.5    考虑列的顺序 对于包含多个列的索引,列顺序很重要。索引键值在索引上的第一上排序,然后在前一列的每个值的下一列做子排序,符合索引的第一列通常为该索引的前沿。同时要考虑列的唯一性,列宽度,列的数据类型来做权衡。 1.10.6    考虑索引的类型 使用索引类型前面已经有较多的介绍,怎么选择已经给出。不再累述。 ------------------------- 回 27楼(板砖大叔) 的帖子 这两种都可以吧。看个人的喜好,不过微软现在的统一风格是下划线,比如表sys.all_columns/sys.tables,然后你再看他的列全是下划线连接,name     /object_id    /principal_id    /schema_id    /parent_object_id      /type    /type_desc    /create_date    /modify_date 我个人的喜好也是喜欢下划线。    

石沫 2019-12-02 01:34:30 0 浏览量 回答数 0

回答

我们都知道JVM的内存管理是自动化的,Java语言的程序指针也不需要开发人员手工释放,JVM的GC会自动的进行回收,但是,如果编程不当,JVM仍然会发生内存泄露,导致Java程序产生了OutOfMemoryError(OOM)错误。 产生OutOfMemoryError错误的原因包括: java.lang.OutOfMemoryError: Java heap spacejava.lang.OutOfMemoryError: PermGen space及其解决方法java.lang.OutOfMemoryError: unable to create new native threadjava.lang.OutOfMemoryError:GC overhead limit exceeded对于第1种异常,表示Java堆空间不够,当应用程序申请更多的内存,而Java堆内存已经无法满足应用程序对内存的需要,将抛出这种异常。 对于第2种异常,表示Java永久带(方法区)空间不够,永久带用于存放类的字节码和长常量池,类的字节码加载后存放在这个区域,这和存放对象实例的堆区是不同的,大多数JVM的实现都不会对永久带进行垃圾回收,因此,只要类加载的过多就会出现这个问题。一般的应用程序都不会产生这个错误,然而,对于Web服务器来讲,会产生有大量的JSP,JSP在运行时被动态的编译成Java Servlet类,然后加载到方法区,因此,太多的JSP的Web工程可能产生这个异常。 对于第3种异常,本质原因是创建了太多的线程,而能创建的线程数是有限制的,导致了这种异常的发生。 对于第4种异常,是在并行或者并发回收器在GC回收时间过长、超过98%的时间用来做GC并且回收了不到2%的堆内存,然后抛出这种异常进行提前预警,用来避免内存过小造成应用不能正常工作。 下面两个异常与OOM有关系,但是,又没有绝对关系。 java.lang.StackOverflowError ...java.net.SocketException: Too many open files对于第1种异常,是JVM的线程由于递归或者方法调用层次太多,占满了线程堆栈而导致的,线程堆栈默认大小为1M。 对于第2种异常,是由于系统对文件句柄的使用是有限制的,而某个应用程序使用的文件句柄超过了这个限制,就会导致这个问题。 上面介绍了OOM相关的基础知识,接下来我们开始讲述笔者经历的一次OOM问题的定位和解决的过程。 产生问题的现象 在某一段时间内,我们发现不同的业务服务开始偶发的报OOM的异常,有的时候是白天发生,有的时候是晚上发生,有的时候是基础服务A发生,有的时候是上层服务B发生,有的时候是上层服务C发生,有的时候是下层服务D发生,丝毫看不到一点规律。 产生问题的异常如下: Caused by: java.lang.OutOfMemoryError: unable to create new native thread at java.lang.Thread.start0(Native Method)at java.lang.Thread.start(Thread.java:597)at java.util.Timer.(Timer.java:154) 解决问题的思路和过程 经过细心观察发现,产生问题虽然在不同的时间发生在不同的服务池,但是,晚上0点发生的时候概率较大,也有其他时间偶发,但是都在整点。 这个规律很重要,虽然不是一个时间,但是基本都在整点左右发生,并且晚上0点居多。从这个角度思考,整点或者0点系统是否有定时,与出问题的每个业务系统技术负责人核实,0点没有定时任务,其他时间的整点有定时任务,但是与发生问题的时间不吻合,这个思路行不通。 到现在为止,从现象的规律上我们已经没法继续分析下去了,那我们回顾一下错误本身: java.lang.OutOfMemoryError: unable to create new native thread 顾名思义,错误产生的原因就是应用不能创建线程了,但是,应用还需要创建线程。为什么程序不能创建线程呢? 有两个具体原因造成这个异常: 由于线程使用的资源过多,操作系统已经不能再提供给应用资源了。操作系统设置了应用创建线程的最大数量,并且已经达到了最大允许数量。上面第1条资源指的是内存,而第2条中,在Linux下线程使用轻量级进程实现的,因此线程的最大数量也是操作系统允许的进程的最大数量。 内存计算 操作系统中的最大可用内存除去操作系统本身使用的部分,剩下的都可以为某一个进程服务,在JVM进程中,内存又被分为堆、本地内存和栈等三大块,Java堆是JVM自动管理的内存,应用的对象的创建和销毁、类的装载等都发生在这里,本地内存是Java应用使用的一种特殊内存,JVM并不直接管理其生命周期,每个线程也会有一个栈,是用来存储线程工作过程中产生的方法局部变量、方法参数和返回值的,每个线程对应的栈的默认大小为1M。 Linux和JVM的内存管理示意图如下: 内存结构模型因此,从内存角度来看创建线程需要内存空间,如果JVM进程正当一个应用创建线程,而操作系统没有剩余的内存分配给此JVM进程,则会抛出问题中的OOM异常:unable to create new native thread。 如下公式可以用来从内存角度计算允许创建的最大线程数: 最大线程数 = (操作系统最大可用内存 - JVM内存 - 操作系统预留内存)/ 线程栈大小 根据这个公式,我们可以通过剩余内存计算可以创建线程的数量。 下面是问题出现的时候,从生产机器上执行前面小节介绍的Linux命令free的输出: free -m >> /tmp/free.log total used free shared buffers cached Mem: 7872 7163 709 0 31 3807-/+ buffers/cache: 3324 4547Swap: 4095 173 3922Tue Jul 5 00:27:51 CST 2016从上面输出可以得出,生产机器8G内存,使用了7G,剩余700M可用,其中操作系统cache使用3.8G。操作系统cache使用的3.8G是用来缓存IO数据的,如果进程内存不够用,这些内存是可以释放出来优先分配给进程使用。然而,我们暂时并不需要考虑这块内存,剩余的700M空间完全可以继续用来创建线程数: 700M / 1M = 700个线程 因此,根据内存可用计算,当OOM异常:unable to create new native thread问题发生的时候,还有700M可用内存,可以创建700个线程。 到现在为止可以证明此次OOM异常不是因为线程吃光所有的内存而导致的。 线程数对比 上面提到,有两个具体原因造成这个异常,我们上面已经排除了第1个原因,那我们现在从第2个原因入手,评估是否操作系统设置了应用创建线程的最大数量,并且已经达到了最大允许数量。 在问题出现的生产机器上使用ulimit -a来显示当前的各种系统对用户使用资源的限制: robert@robert-ubuntu1410:~$ ulimit -acore file size (blocks, -c) 0data seg size (kbytes, -d) unlimitedscheduling priority (-e) 0file size (blocks, -f) unlimitedpending signals (-i) 62819max locked memory (kbytes, -l) 64max memory size (kbytes, -m) unlimitedopen files (-n) 65535pipe size (512 bytes, -p) 8POSIX message queues (bytes, -q) 819200real-time priority (-r) 0stack size (kbytes, -s) 10240cpu time (seconds, -t) unlimitedmax user processes (-u) 1024virtual memory (kbytes, -v) unlimitedfile locks (-x) unlimited这里面我们看到生产机器设置的允许使用的最大用户进程数为1024: max user processes (-u) 1024现在,我们必须获得问题出现的时候,用户下创建的线程情况。 在问题产生的时候,我们使用前面小结介绍的JVM监控命令jstack命令打印出了Java线程情况,jstack命令的示例输出如下: robert@robert-ubuntu1410:~$ jstack 27432017-04-09 12:06:51Full thread dump Java HotSpot(TM) Server VM (25.20-b23 mixed mode): "Attach Listener" #23 daemon prio=9 os_prio=0 tid=0xc09adc00 nid=0xb4c waiting on condition [0x00000000] java.lang.Thread.State: RUNNABLE "http-nio-8080-Acceptor-0" #22 daemon prio=5 os_prio=0 tid=0xc3341000 nid=0xb02 runnable [0xbf1bd000] java.lang.Thread.State: RUNNABLE at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method) at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:241) - locked <0xcf8938d8> (a java.lang.Object) at org.apache.tomcat.util.net.NioEndpoint$Acceptor.run(NioEndpoint.java:688) at java.lang.Thread.run(Thread.java:745) "http-nio-8080-ClientPoller-1" #21 daemon prio=5 os_prio=0 tid=0xc35bc400 nid=0xb01 runnable [0xbf1fe000] java.lang.Thread.State: RUNNABLE at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method) at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:269) at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:79) at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:86) - locked <0xcf99b100> (a sun.nio.ch.Util$2) - locked <0xcf99b0f0> (a java.util.Collections$UnmodifiableSet) - locked <0xcf99aff8> (a sun.nio.ch.EPollSelectorImpl) at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:97) at org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:1052) at java.lang.Thread.run(Thread.java:745) ......从jstack命令的输出并统计后,我们得知,JVM一共创建了904个线程,但是,这还没有到最大的进程限制1024。 robert@robert-ubuntu1410:~$ grep "Thread " js.log | wc -l 904 这是我们思考,除了JVM创建的应用层线程,JVM本身可能会有一些管理线程存在,而且操作系统内用户下可能也会有守护线程在运行。 我们继续从操作系统的角度来统计线程数,我们使用上面小结介绍的Linux操作系统命令pstack,并得到如下的输出: PID LWP USER %CPU %MEM CMD 1 1 root 0.0 0.0 /sbin/init 2 2 root 0.0 0.0 [kthreadd] 3 3 root 0.0 0.0 [migration/0] 4 4 root 0.0 0.0 [ksoftirqd/0] 5 5 root 0.0 0.0 [migration/0] 6 6 root 0.0 0.0 [watchdog/0] 7 7 root 0.0 0.0 [migration/1] 8 8 root 0.0 0.0 [migration/1] 9 9 root 0.0 0.0 [ksoftirqd/1] 10 10 root 0.0 0.0 [watchdog/1] 11 11 root 0.0 0.0 [migration/2] 12 12 root 0.0 0.0 [migration/2] 13 13 root 0.0 0.0 [ksoftirqd/2] 14 14 root 0.0 0.0 [watchdog/2] 15 15 root 0.0 0.0 [migration/3] 16 16 root 0.0 0.0 [migration/3] 17 17 root 0.0 0.0 [ksoftirqd/3] 18 18 root 0.0 0.0 [watchdog/3] 19 19 root 0.0 0.0 [events/0] 20 20 root 0.0 0.0 [events/1] 21 21 root 0.0 0.0 [events/2] 22 22 root 0.0 0.0 [events/3] 23 23 root 0.0 0.0 [cgroup] 24 24 root 0.0 0.0 [khelper] ...... 7257 7257 zabbix 0.0 0.0 /usr/local/zabbix/sbin/zabbix_agentd: active checks #2 [idle 1 sec] 7258 7258 zabbix 0.0 0.0 /usr/local/zabbix/sbin/zabbix_agentd: active checks #3 [idle 1 sec] 7259 7259 zabbix 0.0 0.0 /usr/local/zabbix/sbin/zabbix_agentd: active checks #4 [idle 1 sec] ...... 9040 9040 app 0.0 30.5 /apps/prod/jdk1.6.0_24/bin/java -Dnop -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Ddbconfigpath=/apps/dbconfig/ -Djava.io.tmpdir=/apps/data/java-tmpdir -server -Xms2048m -Xmx2048m -XX:PermSize=128m -XX:MaxPermSize=512m -Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=192.168.10.194 -Dcom.sun.management.jmxremote.port=6969 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp -Xshare:off -Dhostname=sjsa-trade04 -Djute.maxbuffer=41943040 -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8 -Dworkdir=/apps/data/tomcat-work -Djava.endorsed.dirs=/apps/product/tomcat-trade/endorsed -classpath commonlib:/apps/product/tomcat-trade/bin/bootstrap.jar:/apps/product/tomcat-trade/bin/tomcat-juli.jar -Dcatalina.base=/apps/product/tomcat-trade -Dcatalina.home=/apps/product/tomcat-trade -Djava.io.tmpdir=/apps/data/tomcat-temp/ org.apache.catalina.startup.Bootstrap start 9040 9041 app 0.0 30.5 /apps/prod/jdk1.6.0_24/bin/java -Dnop -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Ddbconfigpath=/apps/dbconfig/ -Djava.io.tmpdir=/apps/data/java-tmpdir -server -Xms2048m -Xmx2048m -XX:PermSize=128m -XX:MaxPermSize=512m -Dcom.sun.management.jmxremote -Djava.rmi.server.hostname=192.168.10.194 -Dcom.sun.management.jmxremote.port=6969 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp -Xshare:off -Dhostname=sjsa-trade04 -Djute.maxbuffer=41943040 -Djava.net.preferIPv4Stack=true -Dfile.encoding=UTF-8 -Dworkdir=/apps/data/tomcat-work -Djava.endorsed.dirs=/apps/product/tomcat-trade/endorsed -classpath commonlib:/apps/product/tomcat-trade/bin/bootstrap.jar:/apps/product/tomcat-trade/bin/tomcat-juli.jar -Dcatalina.base=/apps/product/tomcat-trade -Dcatalina.home=/apps/product/tomcat-trade -Djava.io.tmpdir=/apps/data/tomcat-temp/ org.apache.catalina.startup.Bootstrap start ......通过命令统计用户下已经创建的线程数为1021。 $ grep app pthreads.log | wc -l 1021 现在我们确定,1021的数字已经相当的接近1021的最大进程数了,正如前面我们提到,在Linux操作系统里,线程是通过轻量级的进程实现的,因此,限制用户的最大进程数,就是限制用户的最大线程数,至于为什么没有精确达到1024这个最大值就已经报出异常,应该是系统的自我保护功能,在还剩下3个线程的前提下,就开始报错。 到此为止,我们已经通过分析来找到问题的原因,但是,我们还是不知道为什么会创建这么多的线程,从第一个输出得知,JVM已经创建的应用线程有907个,那么他们都在做什么事情呢? 于是,在问题发生的时候,我们又使用JVM的jstack命令,查看输出得知,每个线程都阻塞在打印日志的语句上,log4j中打印日志的代码实现如下: public void callAppenders(LoggingEvent event) { int writes = 0; for(Category c = this; c != null; c=c.parent) { // Protected against simultaneous call to addAppender, removeAppender,... synchronized(c) { if(c.aai != null) { writes += c.aai.appendLoopOnAppenders(event); } if(!c.additive) { break; } } } if(writes == 0) { repository.emitNoAppenderWarning(this); } }在log4j中,打印日志有一个锁,锁的作用是让打印日志可以串行,保证日志在日志文件中的正确性和顺序性。 那么,新的问题又来了,为什么只有凌晨0点会出现打印日志阻塞,其他时间会偶尔发生呢?这时,我们带着新的线索又回到问题开始的思路,凌晨12点应用没有定时任务,系统会不会有其他的IO密集型的任务,比如说归档日志、磁盘备份等? 经过与运维部门碰头,基本确定是每天凌晨0点日志切割导致磁盘IO被占用,于是堵塞打印日志,日志是每个工作任务都必须的,日志阻塞,线程池就阻塞,线程池阻塞就导致线程池被撑大,线程池里面的线程数超过1024就会报错。 到这里,我们基本确定了问题的原因,但是还需要对日志切割导致IO增大进行分析和论证。 首先我们使用前面小结介绍的vmstat查看问题发生时IO等待数据: vmstat 2 1 >> /tmp/vm.logprocs -----------memory---------- ---swap-- -----io---- --system-- -----cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 3 0 177608 725636 31856 3899144 0 0 2 10 0 0 39 1 1 59 0 Tue Jul 5 00:27:51 CST 2016可见,问题发生的时候,CPU的IO等待为59%,同时又与运维部门同事复盘,运维同事确认,脚本切割通过cat命令方法,先把日志文件cat后,通过管道打印到另外一个文件,再清空原文件,因此,一定会导致IO的上升。 其实,问题的过程中,还有一个疑惑,我们认为线程被IO阻塞,线程池被撑开,导致线程增多,于是,我们查看了一下Tomcat线程池的设置,我们发现Tomcat线程池设置了800,按理说,永远不会超过1024。 maxThreads="800" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" redirectPort="8443" acceptCount="100" debug="0" connectionTimeout="20000" disableUploadTimeout="true" /> 关键在于,笔者所在的支付平台服务化架构中,使用了两套服务化框架,一个是基于dubbo的框架,一个是点对点的RPC,用来紧急情况下dubbo服务出现问题,服务降级使用。 每个服务都配置了点对点的RPC服务,并且独享一个线程池: maxThreads="800" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" redirectPort="8443" acceptCount="100" debug="0" connectionTimeout="20000" disableUploadTimeout="true" /> 由于我们在对dubbo服务框架进行定制化的时候,设计了自动降级原则,如果dubbo服务负载变高,会自动切换到点对点的RPC框架,这也符合微服务的失效转移原则,但是设计中没有进行全面的考虑,一旦一部分服务切换到了点对点的RPC,而一部分的服务没有切换,就导致两个现场池都被撑满,于是超过了1024的限制,就出了问题。 到这里,我们基本可以验证,问题的根源是日志切割导致IO负载增加,然后阻塞线程池,最后发生OOM:unable to create new native thread。 剩下的任务就是最小化重现的问题,通过实践来验证问题的原因。我们与性能压测部门沟通,提出压测需求: Tomcat线程池最大设置为1500.操作系统允许的最大用户进程数1024.在给服务加压的过程中,需要人工制造繁忙的IO操作,IO等待不得低于50%。经过压测压测部门的一下午努力,环境搞定,结果证明完全可以重现此问题。 最后,与所有相关部门讨论和复盘,应用解决方案,解决方案包括: 全部应用改成按照小时切割,或者直接使用log4j的日志滚动功能。Tomcat线程池的线程数设置与操作系统的线程数设置不合理,适当的减少Tomcat线程池线程数量的大小。升级log4j日志,使用logback或者log4j2。这次OOM问题的可以归结为“多个因、多个果、多台机器、多个服务池、不同时间”,针对这个问题,与运维部、监控部和性能压测部门的同事奋斗了几天几夜,终于通过在线上抓取信息、分析问题、在性能压测部门同事的帮助下,最小化重现问题并找到问题的根源原因,最后,针对问题产生的根源提供了有效的方案。 与监控同事现场编写的脚本 本节提供一个笔者在实践过程中解决OOM问题的一个简单脚本,这个脚本是为了解决OOM(unable to create native thread)的问题而在问题机器上临时编写,并临时使用的,脚本并没有写的很专业,笔者也没有进行优化,保持原汁原味的风格,这样能让读者有种身临其境的感觉,只是为了抓取需要的信息并解决问题,但是在线上问题十分火急的情况下,这个脚本会有大用处。 !/bin/bash ps -Leo pid,lwp,user,pcpu,pmem,cmd >> /tmp/pthreads.logecho "ps -Leo pid,lwp,user,pcpu,pmem,cmd >> /tmp/pthreads.log" >> /tmp/pthreads.logecho date >> /tmp/pthreads.logecho 1 pid=ps aux|grep tomcat|grep cwh|awk -F ' ' '{print $2}'echo 2 echo "pstack $pid >> /tmp/pstack.log" >> /tmp/pstack.logpstack $pid >> /tmp/pstack.logecho date >> /tmp/pstack.logecho 3 echo "lsof >> /tmp/sys-o-files.log" >> /tmp/sys-o-files.loglsof >> /tmp/sys-o-files.logecho date >> /tmp/sys-o-files.logecho 4 echo "lsof -p $pid >> /tmp/service-o-files.log" >> /tmp/service-o-files.loglsof -p $pid >> /tmp/service-o-files.logecho date >> /tmp/service-o-files.logecho 5 echo "jstack -l $pid >> /tmp/js.log" >> /tmp/js.logjstack -l -F $pid >> /tmp/js.logecho date >> /tmp/js.logecho 6 echo "free -m >> /tmp/free.log" >> /tmp/free.logfree -m >> /tmp/free.logecho date >> /tmp/free.logecho 7 echo "vmstat 2 1 >> /tmp/vm.log" >> /tmp/vm.logvmstat 2 1 >> /tmp/vm.logecho date >> /tmp/vm.logecho 8 echo "jmap -dump:format=b,file=/tmp/heap.hprof 2743" >> /tmp/jmap.logjmap -dump:format=b,file=/tmp/heap.hprof >> /tmp/jmap.logecho date >> /tmp/jmap.logecho 9 echo end

hiekay 2019-12-02 01:39:43 0 浏览量 回答数 0

问题

不搞清这8大算法思想,刷再多题效果也不好的 7月23日 【今日算法】

游客ih62co2qqq5ww 2020-07-29 11:10:09 3 浏览量 回答数 1

回答

在开始谈我对架构本质的理解之前,先谈谈对今天技术沙龙主题的个人见解,千万级规模的网站感觉数量级是非常大的,对这个数量级我们战略上 要重 视 它 , 战术上又 要 藐 视 它。先举个例子感受一下千万级到底是什么数量级?现在很流行的优步(Uber),从媒体公布的信息看,它每天接单量平均在百万左右, 假如每天有10个小时的服务时间,平均QPS只有30左右。对于一个后台服务器,单机的平均QPS可以到达800-1000,单独看写的业务量很简单 。为什么我们又不能说轻视它?第一,我们看它的数据存储,每天一百万的话,一年数据量的规模是多少?其次,刚才说的订单量,每一个订单要推送给附近的司机、司机要并发抢单,后面业务场景的访问量往往是前者的上百倍,轻松就超过上亿级别了。 今天我想从架构的本质谈起之后,希望大家理解在做一些建构设计的时候,它的出发点以及它解决的问题是什么。 架构,刚开始的解释是我从知乎上看到的。什么是架构?有人讲, 说架构并不是一 个很 悬 乎的 东西 , 实际 上就是一个架子 , 放一些 业务 和算法,跟我们的生活中的晾衣架很像。更抽象一点,说架构其 实 是 对 我 们 重复性业务 的抽象和我 们 未来 业务 拓展的前瞻,强调过去的经验和你对整个行业的预见。 我们要想做一个架构的话需要哪些能力?我觉得最重要的是架构师一个最重要的能力就是你要有 战 略分解能力。这个怎么来看呢: 第一,你必须要有抽象的能力,抽象的能力最基本就是去重,去重在整个架构中体现在方方面面,从定义一个函数,到定义一个类,到提供的一个服务,以及模板,背后都是要去重提高可复用率。 第二, 分类能力。做软件需要做对象的解耦,要定义对象的属性和方法,做分布式系统的时候要做服务的拆分和模块化,要定义服务的接口和规范。 第三, 算法(性能),它的价值体现在提升系统的性能,所有性能的提升,最终都会落到CPU,内存,IO和网络这4大块上。 这一页PPT举了一些例子来更深入的理解常见技术背后的架构理念。 第一个例子,在分布式系统我们会做 MySQL分 库 分表,我们要从不同的库和表中读取数据,这样的抽象最直观就是使用模板,因为绝大多数SQL语义是相同的,除了路由到哪个库哪个表,如果不使用Proxy中间件,模板就是性价比最高的方法。 第二看一下加速网络的CDN,它是做速度方面的性能提升,刚才我们也提到从CPU、内存、IO、网络四个方面来考虑,CDN本质上一个是做网络智能调度优化,另一个是多级缓存优化。 第三个看一下服务化,刚才已经提到了,各个大网站转型过程中一定会做服务化,其实它就是做抽象和做服务的拆分。第四个看一下消息队列,本质上还是做分类,只不过不是两个边际清晰的类,而是把两个边际不清晰的子系统通过队列解构并且异步化。新浪微博整体架构是什么样的 接下我们看一下微博整体架构,到一定量级的系统整个架构都会变成三层,客户端包括WEB、安卓和IOS,这里就不说了。接着还都会有一个接口层, 有三个主要作用: 第一个作用,要做 安全隔离,因为前端节点都是直接和用户交互,需要防范各种恶意攻击; 第二个还充当着一个 流量控制的作用,大家知道,在2014年春节的时候,微信红包,每分钟8亿多次的请求,其实真正到它后台的请求量,只有十万左右的数量级(这里的数据可能不准),剩余的流量在接口层就被挡住了; 第三,我们看对 PC 端和移 动 端的需求不一样的,所以我们可以进行拆分。接口层之后是后台,可以看到微博后台有三大块: 一个是 平台服 务, 第二, 搜索, 第三, 大数据。到了后台的各种服务其实都是处理的数据。 像平台的业务部门,做的就是 数据存储和读 取,对搜索来说做的是 数据的 检 索,对大数据来说是做的数据的 挖掘。微博其实和淘宝是很类似 微博其实和淘宝是很类似的。一般来说,第一代架构,基本上能支撑到用户到 百万 级别,到第二代架构基本能支撑到 千万 级别都没什么问题,当业务规模到 亿级别时,需要第三代的架构。 从 LAMP 的架构到面向服 务 的架构,有几个地方是非常难的,首先不可能在第一代基础上通过简单的修修补补满足用户量快速增长的,同时线上业务又不能停, 这是我们常说的 在 飞 机上 换 引擎的 问题。前两天我有一个朋友问我,说他在内部推行服务化的时候,把一个模块服务化做完了,其他部门就是不接。我建议在做服务化的时候,首先更多是偏向业务的梳理,同时要找准一个很好的切入点,既有架构和服务化上的提升,业务方也要有收益,比如提升性能或者降低维护成本同时升级过程要平滑,建议开始从原子化服务切入,比如基础的用户服务, 基础的短消息服务,基础的推送服务。 第二,就是可 以做无状 态 服 务,后面会详细讲,还有数据量大了后需要做数据Sharding,后面会将。 第三代 架构 要解决的 问题,就是用户量和业务趋于稳步增加(相对爆发期的指数级增长),更多考虑技术框架的稳定性, 提升系统整体的性能,降低成本,还有对整个系统监控的完善和升级。 大型网站的系统架构是如何演变的 我们通过通过数据看一下它的挑战,PV是在10亿级别,QPS在百万,数据量在千亿级别。我们可用性,就是SLA要求4个9,接口响应最多不能超过150毫秒,线上所有的故障必须得在5分钟内解决完。如果说5分钟没处理呢?那会影响你年终的绩效考核。2015年微博DAU已经过亿。我们系统有上百个微服务,每周会有两次的常规上线和不限次数的紧急上线。我们的挑战都一样,就是数据量,bigger and bigger,用户体验是faster and faster,业务是more and more。互联网业务更多是产品体验驱动, 技 术 在 产 品 体验上最有效的贡献 , 就是你的性能 越来越好 。 每次降低加载一个页面的时间,都可以间接的降低这个页面上用户的流失率。微博的技术挑战和正交分解法解析架构 下面看一下 第三代的 架构 图 以及 我 们 怎么用正交分解法 阐 述。 我们可以看到我们从两个维度,横轴和纵轴可以看到。 一个 维 度 是 水平的 分层 拆分,第二从垂直的维度会做拆分。水平的维度从接口层、到服务层到数据存储层。垂直怎么拆分,会用业务架构、技术架构、监控平台、服务治理等等来处理。我相信到第二代的时候很多架构已经有了业务架构和技术架构的拆分。我们看一下, 接口层有feed、用户关系、通讯接口;服务层,SOA里有基层服务、原子服务和组合服务,在微博我们只有原子服务和组合服务。原子服务不依赖于任何其他服务,组合服务由几个原子服务和自己的业务逻辑构建而成 ,资源层负责海量数据的存储(后面例子会详细讲)。技 术框架解决 独立于 业务 的海量高并发场景下的技术难题,由众多的技术组件共同构建而成 。在接口层,微博使用JERSY框架,帮助你做参数的解析,参数的验证,序列化和反序列化;资源层,主要是缓存、DB相关的各类组件,比如Cache组件和对象库组件。监 控平台和服 务 治理 , 完成系统服务的像素级监控,对分布式系统做提前诊断、预警以及治理。包含了SLA规则的制定、服务监控、服务调用链监控、流量监控、错误异常监控、线上灰度发布上线系统、线上扩容缩容调度系统等。 下面我们讲一下常见的设计原则。 第一个,首先是系统架构三个利器: 一个, 我 们 RPC 服 务组 件 (这里不讲了), 第二个,我们 消息中 间 件 。消息中间件起的作用:可以把两个模块之间的交互异步化,其次可以把不均匀请求流量输出为匀速的输出流量,所以说消息中间件 异步化 解耦 和流量削峰的利器。 第三个是配置管理,它是 代码级灰度发布以及 保障系统降级的利器。 第二个 , 无状态 , 接口 层 最重要的就是无状 态。我们在电商网站购物,在这个过程中很多情况下是有状态的,比如我浏览了哪些商品,为什么大家又常说接口层是无状态的,其实我们把状态从接口层剥离到了数据层。像用户在电商网站购物,选了几件商品,到了哪一步,接口无状态后,状态要么放在缓存中,要么放在数据库中, 其 实 它并不是没有状 态 , 只是在 这 个 过 程中我 们 要把一些有状 态 的 东 西抽离出来 到了数据层。 第三个, 数据 层 比服 务层 更需要 设计,这是一条非常重要的经验。对于服务层来说,可以拿PHP写,明天你可以拿JAVA来写,但是如果你的数据结构开始设计不合理,将来数据结构的改变会花费你数倍的代价,老的数据格式向新的数据格式迁移会让你痛不欲生,既有工作量上的,又有数据迁移跨越的时间周期,有一些甚至需要半年以上。 第四,物理结构与逻辑结构的映射,上一张图看到两个维度切成十二个区间,每个区间代表一个技术领域,这个可以看做我们的逻辑结构。另外,不论后台还是应用层的开发团队,一般都会分几个垂直的业务组加上一个基础技术架构组,这就是从物理组织架构到逻辑的技术架构的完美的映射,精细化团队分工,有利于提高沟通协作的效率 。 第五, www .sanhao.com 的访问过程,我们这个架构图里没有涉及到的,举个例子,比如当你在浏览器输入www.sanhao网址的时候,这个请求在接口层之前发生了什么?首先会查看你本机DNS以及DNS服务,查找域名对应的IP地址,然后发送HTTP请求过去。这个请求首先会到前端的VIP地址(公网服务IP地址),VIP之后还要经过负载均衡器(Nginx服务器),之后才到你的应用接口层。在接口层之前发生了这么多事,可能有用户报一个问题的时候,你通过在接口层查日志根本发现不了问题,原因就是问题可能发生在到达接口层之前了。 第六,我们说分布式系统,它最终的瓶颈会落在哪里呢?前端时间有一个网友跟我讨论的时候,说他们的系统遇到了一个瓶颈, 查遍了CPU,内存,网络,存储,都没有问题。我说你再查一遍,因为最终你不论用上千台服务器还是上万台服务器,最终系统出瓶颈的一定会落在某一台机(可能是叶子节点也可能是核心的节点),一定落在CPU、内存、存储和网络上,最后查出来问题出在一台服务器的网卡带宽上。微博多级双机房缓存架构 接下来我们看一下微博的Feed多级缓存。我们做业务的时候,经常很少做业务分析,技术大会上的分享又都偏向技术架构。其实大家更多的日常工作是需要花费更多时间在业务优化上。这张图是统计微博的信息流前几页的访问比例,像前三页占了97%,在做缓存设计的时候,我们最多只存最近的M条数据。 这里强调的就是做系统设计 要基于用 户 的 场 景 , 越细致越好 。举了一个例子,大家都会用电商,电商在双十一会做全国范围内的活动,他们做设计的时候也会考虑场景的,一个就是购物车,我曾经跟相关开发讨论过,购物车是在双十一之前用户的访问量非常大,就是不停地往里加商品。在真正到双十一那天他不会往购物车加东西了,但是他会频繁的浏览购物车。针对这个场景,活动之前重点设计优化购物车的写场景, 活动开始后优化购物车的读场景。 你看到的微博是由哪些部分聚合而成的呢?最右边的是Feed,就是微博所有关注的人,他们的微博所组成的。微博我们会按照时间顺序把所有关注人的顺序做一个排序。随着业务的发展,除了跟时间序相关的微博还有非时间序的微博,就是会有广告的要求,增加一些广告,还有粉丝头条,就是拿钱买的,热门微博,都会插在其中。分发控制,就是说和一些推荐相关的,我推荐一些相关的好友的微博,我推荐一些你可能没有读过的微博,我推荐一些其他类型的微博。 当然对非时序的微博和分发控制微博,实际会起多个并行的程序来读取,最后同步做统一的聚合。这里稍微分享一下, 从SNS社交领域来看,国内现在做的比较好的三个信息流: 微博 是 基于弱关系的媒体信息流 ; 朋友圈是基于 强 关系的信息流 ; 另外一个做的比 较 好的就是今日 头 条 , 它并不是基于关系来构建信息流 , 而是基于 兴趣和相关性的个性化推荐 信息流 。 信息流的聚合,体现在很多很多的产品之中,除了SNS,电商里也有信息流的聚合的影子。比如搜索一个商品后出来的列表页,它的信息流基本由几部分组成:第一,打广告的;第二个,做一些推荐,热门的商品,其次,才是关键字相关的搜索结果。 信息流 开始的时候 很 简单 , 但是到后期会 发现 , 你的 这 个流 如何做控制分发 , 非常复杂, 微博在最近一两年一直在做 这样 的工作。刚才我们是从业务上分析,那么技术上怎么解决高并发,高性能的问题?微博访问量很大的时候,底层存储是用MySQL数据库,当然也会有其他的。对于查询请求量大的时候,大家知道一定有缓存,可以复用可重用的计算结果。可以看到,发一条微博,我有很多粉丝,他们都会来看我发的内容,所以 微博是最适合使用 缓 存 的系统,微博的读写比例基本在几十比一。微博使用了 双 层缓 存,上面是L1,每个L1上都是一组(包含4-6台机器),左边的框相当于一个机房,右边又是一个机房。在这个系统中L1缓存所起的作用是什么? 首先,L1 缓 存增加整个系 统 的 QPS, 其次 以低成本灵活扩容的方式 增加 系统 的 带宽 。想象一个极端场景,只有一篇博文,但是它的访问量无限增长,其实我们不需要影响L2缓存,因为它的内容存储的量小,但它就是访问量大。这种场景下,你就需要使用L1来扩容提升QPS和带宽瓶颈。另外一个场景,就是L2级缓存发生作用,比如我有一千万个用户,去访问的是一百万个用户的微博 ,这个时候,他不只是说你的吞吐量和访问带宽,就是你要缓存的博文的内容也很多了,这个时候你要考虑缓存的容量, 第二 级缓 存更多的是从容量上来 规划,保证请求以较小的比例 穿透到 后端的 数据 库 中 ,根据你的用户模型你可以估出来,到底有百分之多少的请求不能穿透到DB, 评估这个容量之后,才能更好的评估DB需要多少库,需要承担多大的访问的压力。另外,我们看双机房的话,左边一个,右边一个。 两个机房是互 为 主 备 , 或者互 为热备 。如果两个用户在不同地域,他们访问两个不同机房的时候,假设用户从IDC1过来,因为就近原理,他会访问L1,没有的话才会跑到Master,当在IDC1没找到的时候才会跑到IDC2来找。同时有用户从IDC2访问,也会有请求从L1和Master返回或者到IDC1去查找。 IDC1 和 IDC2 ,两个机房都有全量的用户数据,同时在线提供服务,但是缓存查询又遵循最近访问原理。还有哪些多级缓存的例子呢?CDN是典型的多级缓存。CDN在国内各个地区做了很多节点,比如在杭州市部署一个节点时,在机房里肯定不止一台机器,那么对于一个地区来说,只有几台服务器到源站回源,其他节点都到这几台服务器回源即可,这么看CDN至少也有两级。Local Cache+ 分布式 缓 存,这也是常见的一种策略。有一种场景,分布式缓存并不适用, 比如 单 点 资 源 的爆发性峰值流量,这个时候使用Local Cache + 分布式缓存,Local Cache 在 应用 服 务 器 上用很小的 内存资源 挡住少量的 极端峰值流量,长尾的流量仍然访问分布式缓存,这样的Hybrid缓存架构通过复用众多的应用服务器节点,降低了系统的整体成本。 我们来看一下 Feed 的存 储 架构,微博的博文主要存在MySQL中。首先来看内容表,这个比较简单,每条内容一个索引,每天建一张表,其次看索引表,一共建了两级索引。首先想象一下用户场景,大部分用户刷微博的时候,看的是他关注所有人的微博,然后按时间来排序。仔细分析发现在这个场景下, 跟一个用户的自己的相关性很小了。所以在一级索引的时候会先根据关注的用户,取他们的前条微博ID,然后聚合排序。我们在做哈希(分库分表)的时候,同时考虑了按照UID哈希和按照时间维度。很业务和时间相关性很高的,今天的热点新闻,明天就没热度了,数据的冷热非常明显,这种场景就需要按照时间维度做分表,首先冷热数据做了分离(可以对冷热数据采用不同的存储方案来降低成本),其次, 很容止控制我数据库表的爆炸。像微博如果只按照用户维度区分,那么这个用户所有数据都在一张表里,这张表就是无限增长的,时间长了查询会越来越慢。二级索引,是我们里面一个比较特殊的场景,就是我要快速找到这个人所要发布的某一时段的微博时,通过二级索引快速定位。 分布式服务追踪系统 分布式追踪服务系统,当系统到千万级以后的时候,越来越庞杂,所解决的问题更偏向稳定性,性能和监控。刚才说用户只要有一个请求过来,你可以依赖你的服务RPC1、RPC2,你会发现RPC2又依赖RPC3、RPC4。分布式服务的时候一个痛点,就是说一个请求从用户过来之后,在后台不同的机器之间不停的调用并返回。 当你发现一个问题的时候,这些日志落在不同的机器上,你也不知道问题到底出在哪儿,各个服务之间互相隔离,互相之间没有建立关联。所以导致排查问题基本没有任何手段,就是出了问题没法儿解决。 我们要解决的问题,我们刚才说日志互相隔离,我们就要把它建立联系。建立联系我们就有一个请求ID,然后结合RPC框架, 服务治理功能。假设请求从客户端过来,其中包含一个ID 101,到服务A时仍然带有ID 101,然后调用RPC1的时候也会标识这是101 ,所以需要 一个唯一的 请求 ID 标识 递归迭代的传递到每一个 相关 节点。第二个,你做的时候,你不能说每个地方都加,对业务系统来说需要一个框架来完成这个工作, 这 个框架要 对业务 系 统 是最低侵入原 则 , 用 JAVA 的 话 就可以用 AOP,要做到零侵入的原则,就是对所有相关的中间件打点,从接口层组件(HTTP Client、HTTP Server)至到服务层组件(RPC Client、RPC Server),还有数据访问中间件的,这样业务系统只需要少量的配置信息就可以实现全链路监控 。为什么要用日志?服务化以后,每个服务可以用不同的开发语言, 考虑多种开发语言的兼容性 , 内部定 义标 准化的日志 是唯一且有效的办法。最后,如何构建基于GPS导航的路况监控?我们刚才讲分布式服务追踪。分布式服务追踪能解决的问题, 如果 单一用 户发现问题 后 , 可以通 过请 求 ID 快速找到 发 生 问题 的 节 点在什么,但是并没有解决如何发现问题。我们看现实中比较容易理解的道路监控,每辆车有GPS定位,我想看北京哪儿拥堵的时候,怎么做? 第一个 , 你肯定要知道每个 车 在什么位置,它走到哪儿了。其实可以说每个车上只要有一个标识,加上每一次流动的信息,就可以看到每个车流的位置和方向。 其次如何做 监 控和 报 警,我们怎么能了解道路的流量状况和负载,并及时报警。我们要定义这条街道多宽多高,单位时间可以通行多少辆车,这就是道路的容量。有了道路容量,再有道路的实时流量,我们就可以基于实习路况做预警? 对应于 分布式系 统 的话如何构建? 第一 , 你要 定义 每个服 务节 点它的 SLA A 是多少 ?SLA可以从系统的CPU占用率、内存占用率、磁盘占用率、QPS请求数等来定义,相当于定义系统的容量。 第二个 , 统计 线 上 动态 的流量,你要知道服务的平均QPS、最低QPS和最大QPS,有了流量和容量,就可以对系统做全面的监控和报警。 刚才讲的是理论,实际情况肯定比这个复杂。微博在春节的时候做许多活动,必须保障系统稳定,理论上你只要定义容量和流量就可以。但实际远远不行,为什么?有技术的因素,有人为的因素,因为不同的开发定义的流量和容量指标有主观性,很难全局量化标准,所以真正流量来了以后,你预先评估的系统瓶颈往往不正确。实际中我们在春节前主要采取了三个措施:第一,最简单的就是有降 级 的 预 案,流量超过系统容量后,先把哪些功能砍掉,需要有明确的优先级 。第二个, 线上全链路压测,就是把现在的流量放大到我们平常流量的五倍甚至十倍(比如下线一半的服务器,缩容而不是扩容),看看系统瓶颈最先发生在哪里。我们之前有一些例子,推测系统数据库会先出现瓶颈,但是实测发现是前端的程序先遇到瓶颈。第三,搭建在线 Docker 集群 , 所有业务共享备用的 Docker集群资源,这样可以极大的避免每个业务都预留资源,但是实际上流量没有增长造成的浪费。 总结 接下来说的是如何不停的学习和提升,这里以Java语言为例,首先, 一定要 理解 JAVA;第二步,JAVA完了以后,一定要 理 解 JVM;其次,还要 理解 操作系统;再次还是要了解一下 Design Pattern,这将告诉你怎么把过去的经验抽象沉淀供将来借鉴;还要学习 TCP/IP、 分布式系 统、数据结构和算法。

hiekay 2019-12-02 01:39:25 0 浏览量 回答数 0

回答

摘要:面试也是一门学问,在面试之前做好充分的准备则是成功的必须条件,而程序员在代码面试时,常会遇到编写算法的相关问题,比如排序、二叉树遍历等等。 在程序员的职业生涯中,算法亦算是一门基础课程,尤其是在面试的时候,很多公司都会让程序员编写一些算法实例,例如快速排序、二叉树查找等等。 本文总结了程序员在代码面试中最常遇到的10大算法类型,想要真正了解这些算法的原理,还需程序员们花些功夫。 1.String/Array/Matrix 在Java中,String是一个包含char数组和其它字段、方法的类。如果没有IDE自动完成代码,下面这个方法大家应该记住: String/arrays很容易理解,但与它们有关的问题常常需要高级的算法去解决,例如动态编程、递归等。 下面列出一些需要高级算法才能解决的经典问题: Evaluate Reverse Polish Notation Longest Palindromic Substring 单词分割 字梯 Median of Two Sorted Arrays 正则表达式匹配 合并间隔 插入间隔 Two Sum 3Sum 4Sum 3Sum Closest String to Integer 合并排序数组 Valid Parentheses 实现strStr() Set Matrix Zeroes 搜索插入位置 Longest Consecutive Sequence Valid Palindrome 螺旋矩阵 搜索一个二维矩阵 旋转图像 三角形 Distinct Subsequences Total Maximum Subarray 删除重复的排序数组 删除重复的排序数组2 查找没有重复的最长子串 包含两个独特字符的最长子串 Palindrome Partitioning 2.链表 在Java中实现链表是非常简单的,每个节点都有一个值,然后把它链接到下一个节点。 class Node { int val; Node next; Node(int x) { val = x; next = null; } } 比较流行的两个链表例子就是栈和队列。 栈(Stack) class Stack{ Node top; public Node peek(){ if(top != null){ return top; } return null; } public Node pop(){ if(top == null){ return null; }else{ Node temp = new Node(top.val); top = top.next; return temp; } } public void push(Node n){ if(n != null){ n.next = top; top = n; } } } 队列(Queue) class Queue{ Node first, last;   public void enqueue(Node n){ if(first == null){ first = n; last = first; }else{ last.next = n; last = n; } }   public Node dequeue(){ if(first == null){ return null; }else{ Node temp = new Node(first.val); first = first.next; return temp; } } } 值得一提的是,Java标准库中已经包含一个叫做Stack的类,链表也可以作为一个队列使用(add()和remove())。(链表实现队列接口)如果你在面试过程中,需要用到栈或队列解决问题时,你可以直接使用它们。 在实际中,需要用到链表的算法有: 插入两个数字 重新排序列表 链表周期 Copy List with Random Pointer 合并两个有序列表 合并多个排序列表 从排序列表中删除重复的 分区列表 LRU缓存 3.树&堆 这里的树通常是指二叉树。 class TreeNode{ int value; TreeNode left; TreeNode right; } 下面是一些与二叉树有关的概念: 二叉树搜索:对于所有节点,顺序是:left children <= current node <= right children; 平衡vs.非平衡:它是一 棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树; 满二叉树:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点; 完美二叉树(Perfect Binary Tree):一个满二叉树,所有叶子都在同一个深度或同一级,并且每个父节点都有两个子节点; 完全二叉树:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。 堆(Heap)是一个基于树的数据结构,也可以称为优先队列( PriorityQueue),在队列中,调度程序反复提取队列中第一个作业并运行,因而实际情况中某些时间较短的任务将等待很长时间才能结束,或者某些不短小,但具有重要性的作业,同样应当具有优先权。堆即为解决此类问题设计的一种数据结构。 下面列出一些基于二叉树和堆的算法: 二叉树前序遍历 二叉树中序遍历 二叉树后序遍历 字梯 验证二叉查找树 把二叉树变平放到链表里 二叉树路径和 从前序和后序构建二叉树 把有序数组转换为二叉查找树 把有序列表转为二叉查找树 最小深度二叉树 二叉树最大路径和 平衡二叉树 4.Graph 与Graph相关的问题主要集中在深度优先搜索和宽度优先搜索。深度优先搜索非常简单,你可以从根节点开始循环整个邻居节点。下面是一个非常简单的宽度优先搜索例子,核心是用队列去存储节点。 第一步,定义一个GraphNode class GraphNode{ int val; GraphNode next; GraphNode[] neighbors; boolean visited; GraphNode(int x) { val = x; } GraphNode(int x, GraphNode[] n){ val = x; neighbors = n; } public String toString(){ return "value: "+ this.val; } } 第二步,定义一个队列 class Queue{ GraphNode first, last; public void enqueue(GraphNode n){ if(first == null){ first = n; last = first; }else{ last.next = n; last = n; } } public GraphNode dequeue(){ if(first == null){ return null; }else{ GraphNode temp = new GraphNode(first.val, first.neighbors); first = first.next; return temp; } } } 第三步,使用队列进行宽度优先搜索 public class GraphTest { public static void main(String[] args) { GraphNode n1 = new GraphNode(1); GraphNode n2 = new GraphNode(2); GraphNode n3 = new GraphNode(3); GraphNode n4 = new GraphNode(4); GraphNode n5 = new GraphNode(5); n1.neighbors = new GraphNode[]{n2,n3,n5}; n2.neighbors = new GraphNode[]{n1,n4}; n3.neighbors = new GraphNode[]{n1,n4,n5}; n4.neighbors = new GraphNode[]{n2,n3,n5}; n5.neighbors = new GraphNode[]{n1,n3,n4}; breathFirstSearch(n1, 5); } public static void breathFirstSearch(GraphNode root, int x){ if(root.val == x) System.out.println("find in root"); Queue queue = new Queue(); root.visited = true; queue.enqueue(root); while(queue.first != null){ GraphNode c = (GraphNode) queue.dequeue(); for(GraphNode n: c.neighbors){ if(!n.visited){ System.out.print(n + " "); n.visited = true; if(n.val == x) System.out.println("Find "+n); queue.enqueue(n); } } } } } 输出结果: value: 2 value: 3 value: 5 Find value: 5 value: 4 实际中,基于Graph需要经常用到的算法: 克隆Graph 15 2014-04-24 18:55:03回复数 293 只看楼主 引用 举报 楼主 柔软的胖纸 Bbs1 5.排序 不同排序算法的时间复杂度,大家可以到wiki上查看它们的基本思想。 BinSort、Radix Sort和CountSort使用了不同的假设,所有,它们不是一般的排序方法。 下面是这些算法的具体实例,另外,你还可以阅读:Java开发者在实际操作中是如何排序的。 归并排序 快速排序 插入排序 6.递归和迭代 下面通过一个例子来说明什么是递归。 问题: 这里有n个台阶,每次能爬1或2节,请问有多少种爬法? 步骤1:查找n和n-1之间的关系 为了获得n,这里有两种方法:一个是从第一节台阶到n-1或者从2到n-2。如果f(n)种爬法刚好是爬到n节,那么f(n)=f(n-1)+f(n-2)。 步骤2:确保开始条件是正确的 f(0) = 0; f(1) = 1; public static int f(int n){ if(n <= 2) return n; int x = f(n-1) + f(n-2); return x; } 递归方法的时间复杂度指数为n,这里会有很多冗余计算。 f(5) f(4) + f(3) f(3) + f(2) + f(2) + f(1) f(2) + f(1) + f(2) + f(2) + f(1) 该递归可以很简单地转换为迭代。 public static int f(int n) { if (n <= 2){ return n; } int first = 1, second = 2; int third = 0; for (int i = 3; i <= n; i++) { third = first + second; first = second; second = third; } return third; } 在这个例子中,迭代花费的时间要少些。关于迭代和递归,你可以去 这里看看。 7.动态规划 动态规划主要用来解决如下技术问题: 通过较小的子例来解决一个实例; 对于一个较小的实例,可能需要许多个解决方案; 把较小实例的解决方案存储在一个表中,一旦遇上,就很容易解决; 附加空间用来节省时间。 上面所列的爬台阶问题完全符合这四个属性,因此,可以使用动态规划来解决: public static int[] A = new int[100]; public static int f3(int n) { if (n <= 2) A[n]= n; if(A[n] > 0) return A[n]; else A[n] = f3(n-1) + f3(n-2);//store results so only calculate once! return A[n]; } 一些基于动态规划的算法: 编辑距离 最长回文子串 单词分割 最大的子数组 8.位操作 位操作符: 从一个给定的数n中找位i(i从0开始,然后向右开始) public static boolean getBit(int num, int i){ int result = num & (1<<i); if(result == 0){ return false; }else{ return true; } } 例如,获取10的第二位: i=1, n=10 1<<1= 10 1010&10=10 10 is not 0, so return true; 典型的位算法: Find Single Number Maximum Binary Gap 9.概率 通常要解决概率相关问题,都需要很好地格式化问题,下面提供一个简单的例子: 有50个人在一个房间,那么有两个人是同一天生日的可能性有多大?(忽略闰年,即一年有365天) 算法: public static double caculateProbability(int n){ double x = 1; for(int i=0; i<n; i++){ x *= (365.0-i)/365.0; } double pro = Math.round((1-x) * 100); return pro/100; } 结果:calculateProbability(50) = 0.97 10.组合和排列 组合和排列的主要差别在于顺序是否重要。 例1: 1、2、3、4、5这5个数字,输出不同的顺序,其中4不可以排在第三位,3和5不能相邻,请问有多少种组合? 例2: 有5个香蕉、4个梨、3个苹果,假设每种水果都是一样的,请问有多少种不同的组合? 基于它们的一些常见算法 排列 排列2 排列顺序 来自: ProgramCreek 转载于:https://bbs.csdn.net/topics/390768965

养狐狸的猫 2019-12-02 02:11:29 0 浏览量 回答数 0

问题

【Java学习全家桶】1460道Java热门问题,阿里百位技术专家答疑解惑

管理贝贝 2019-12-01 20:07:15 27612 浏览量 回答数 19

问题

线性表 7月8日 【今日算法】

游客ih62co2qqq5ww 2020-07-09 07:47:37 504 浏览量 回答数 1

回答

PHP面试干货 1、进程和线程 进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。进程和线程的区别在于: 简而言之,一个程序至少有一个进程,一个进程至少有一个线程. 线程的划分尺度小于进程,使得多线程程序的并发性高。 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。 2、apache默认使用进程管理还是线程管理?如何判断并设置最大连接数? 一个进程可以开多个线程 默认是进程管理 默认有一个主进程 Linux: ps -aux | grep httpd | more 一个子进程代表一个用户的连接 Conf/extra/httpd-mpm.conf 多路功能模块 http -l 查询当前apache处于什么模式下 3、单例模式 单例模式需求:只能实例化产生一个对象 如何实现: 私有化构造函数 禁止克隆对象 提供一个访问这个实例的公共的静态方法(通常为getInstance方法),从而返回唯一对象 需要一个保存类的静态属性 class demo { private static $MyObject; //保存对象的静态属性 private function __construct(){ //私有化构造函数 } private function __clone(){ //禁止克隆 } public static function getInstance(){ if(! (self::$MyObject instanceof self)){ self::$MyObject = new self; } return self::$MyObject; } } 4、安装完Apache后,在http.conf中配置加载PHP文件以Apache模块的方式安装PHP,在文件http.conf中首先要用语句LoadModule php5_module "e:/php/php5apache2.dll"动态装载PHP模块,然后再用语句AddType application/x-httpd-php .php 使得Apache把所有扩展名为PHP的文件都作为PHP脚本处理 5、debug_backtrace()函数能返回脚本里的任意行中调用的函数的名称。该函数同时还经常被用在调试中,用来判断错误是如何发生的 function one($str1, $str2) { two("Glenn", "Quagmire"); } function two($str1, $str2) { three("Cleveland", "Brown"); } function three($str1, $str2) { print_r(debug_backtrace()); } one("Peter", "Griffin"); Array ( [0] => Array ( [file] => D:\www\test\result.php [line] => 9 [function] => three [args] => Array ( [0] => Cleveland [1] => Brown ) ) [1] => Array ( [file] => D:\www\test\result.php [line] => 5 [function] => two [args] => Array ( [0] => Glenn [1] => Quagmire ) ) [2] => Array ( [file] => D:\www\test\result.php [line] => 16 [function] => one [args] => Array ( [0] => Peter [1] => Griffin ) ) ) 6、输出用户的IP地址,并且判断用户的IP地址是否在192.168.1.100 — 192.168.1.150之间 echo $ip=getenv('REMOTE_ADDR'); $ip=str_replace('.','',$ip); if($ip<1921681150 && $ip>1921681100) { echo 'ip在192.168.1.100—–192.168.1.150之间'; } else { echo 'ip不在192.168.1.100—–192.168.1.150之间'; } 7、请将2维数组按照name的长度进行重新排序,按照顺序将id赋值 $tarray = array( array('id' => 0, 'name' => '123'), array('id' => 0, 'name' => '1234'), array('id' => 0, 'name' => '1235'), array('id' => 0, 'name' => '12356'), array('id' => 0, 'name' => '123abc') ); foreach($tarray as $key=>$val) { $c[]=$val['name']; } function aa($a,$b) { if(strlen($a)==strlen($b)) return 0; return strlen($a)>strlen($b)?-1:1; } usort($c,'aa'); $len=count($c); for($i=0;$i<$len;$i++) { $t[$i]['id']=$i+1; $t[$i]['name']=$c[$i]; } print_r($t); 8、表单数据提交方式POST和GET的区别,URL地址传递的数据最大长度是多少? POST方式提交数据用户不可见,是数据更安全,最大长度不受限制,而GET方式传值在URL地址可以看到,相对不安全,对大长度是2048字节。 9、SESSION和COOKIE的作用和区别,SESSION信息的存储方式,如何进行遍历 SESSION和COOKIE都能够使值在页面之间进行传递,SESSION存储在服务器端,数据更安全,COOKIE保存在客户端,用户使用手段可以进行修改,SESSION依赖于COOKIE进行传递的。Session遍历使用$_SESSION[]取值,cookie遍历使用$_COOKIE[]取值。 10、什么是数据库索引,主键索引,唯一索引的区别,索引的缺点是什么 索引用来快速地寻找那些具有特定值的记录。 主键索引和唯一索引的区别:主键是一种唯一性索引,但它必须指定为“PRIMARY KEY”,每个表只能有一个主键。唯一索引索引列的所有值都只能出现一次,即必须唯一。 索引的缺点: 1、创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。 2、索引需要占用物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,需要的空间就会更大。 3、当对表中的数据进行增加、删除、修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。 11、数据库设计时,常遇到的性能瓶颈有哪些,常有的解决方案 瓶颈主要有: 1、磁盘搜索 优化方法是:将数据分布在多个磁盘上 2、磁盘读/写 优化方法是:从多个磁盘并行读写。 3、CPU周期 优化方法:扩充内存 4、内存带宽 12、include和require区别 include引入文件的时候,如果碰到错误,会给出提示,并继续运行下边的代码。 require引入文件的时候,如果碰到错误,会给出提示,并停止运行下边的代码。 13、文件上传时设计到点 和文件上传有关的php.ini配置选项(File Uploads): file_uploads=On/Off:文件是否允许上传 upload_max_filesize上传文件时,单个文件的最大大小 post_max_size:提交表单时,整个post表单的最大大小 max_file_uploads =20上传文件的个数 内存占用,脚本最大执行时间也间接影响到文件的上传 14、header常见状态 //200 正常状态 header('HTTP/1.1 200 OK'); // 301 永久重定向,记得在后面要加重定向地址 Location:$url header('HTTP/1.1 301 Moved Permanently'); // 重定向,其实就是302 暂时重定向 header('Location: http://www.maiyoule.com/'); // 设置页面304 没有修改 header('HTTP/1.1 304 Not Modified'); // 显示登录框, header('HTTP/1.1 401 Unauthorized'); header('WWW-Authenticate: Basic realm="登录信息"'); echo '显示的信息!'; // 403 禁止访问 header('HTTP/1.1 403 Forbidden'); // 404 错误 header('HTTP/1.1 404 Not Found'); // 500 服务器错误 header('HTTP/1.1 500 Internal Server Error'); // 3秒后重定向指定地址(也就是刷新到新页面与 <meta http-equiv="refresh" content="10;http://www.maiyoule.com/ /> 相同) header('Refresh: 3; url=http://www.maiyoule.com/'); echo '10后跳转到http://www.maiyoule.com'; // 重写 X-Powered-By 值 header('X-Powered-By: PHP/5.3.0'); header('X-Powered-By: Brain/0.6b'); //设置上下文语言 header('Content-language: en'); // 设置页面最后修改时间(多用于防缓存) $time = time() - 60; //建议使用filetime函数来设置页面缓存时间 header('Last-Modified: '.gmdate('D, d M Y H:i:s', $time).' GMT'); // 设置内容长度 header('Content-Length: 39344'); // 设置头文件类型,可以用于流文件或者文件下载 header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="example.zip"'); header('Content-Transfer-Encoding: binary'); readfile('example.zip');//读取文件到客户端 //禁用页面缓存 header('Cache-Control: no-cache, no-store, max-age=0, must-revalidate'); header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); header('Pragma: no-cache'); //设置页面头信息 header('Content-Type: text/html; charset=iso-8859-1'); header('Content-Type: text/html; charset=utf-8'); header('Content-Type: text/plain'); header('Content-Type: image/jpeg'); header('Content-Type: application/zip'); header('Content-Type: application/pdf'); header('Content-Type: audio/mpeg'); header('Content-Type: application/x-shockwave-flash'); //.... 至于Content-Type 的值 可以去查查 w3c 的文档库,那里很丰富 15、ORM和ActiveRecord ORM:object relation mapping,即对象关系映射,简单的说就是对象模型和关系模型的一种映射。为什么要有这么一个映射?很简单,因为现在的开发语言基本都是oop的,但是传统的数据库却是关系型的。为了可以靠贴近面向对象开发,我们想要像操作对象一样操作数据库。还可以隔离底层数据库层,我们不需要关心我们使用的是mysql还是其他的关系型数据库 ActiveRecord也属于ORM层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而且简洁易懂。 ActiveRecord的主要思想是: 1. 每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录;通常表的每个字段在类中都有相应的Field; 2. ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD;; 3. ActiveRecord是一种领域模型(Domain Model),封装了部分业务逻辑; ActiveRecord比较适用于: 1. 业务逻辑比较简单,当你的类基本上和数据库中的表一一对应时, ActiveRecord是非常方便的,即你的业务逻辑大多数是对单表操作; 2. 当发生跨表的操作时, 往往会配合使用事务脚本(Transaction Script),把跨表事务提升到事务脚本中; 3. ActiveRecord最大优点是简单, 直观。 一个类就包括了数据访问和业务逻辑. 如果配合代码生成器使用就更方便了; 这些优点使ActiveRecord特别适合WEB快速开发。 16、斐波那契方法,也就是1 1 2 3 5 8 ……,这里给出两种方法,大家可以对比下,看看哪种快,以及为什么 function fibonacci($n){ if($n == 0){ return 0; } if($n == 1){ return 1; } return fibonacci($n-1)+fibonacci($n-2); } function fibonacci($n){ for($i=0; $i<$n; $i++){ $r[] = $i<2 ? 1 : $r[$i-1]+$r[$i-2]; } return $r[--$i]; } 17、约瑟夫环,也就是常见的数猴子,n只猴子围成一圈,每只猴子下面标了编号,从1开始数起,数到m那么第m只猴子便退出,依次类推,每数到m,那么那个位置的猴子退出,那么最后剩下的猴子下的编号是啥。 function yuesefu($n,$m) { $r=0; for($i=2; $i<=$n; $i++) { $r=($r+$m)%$i; } return $r+1; } 18、冒泡排序,大致是临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换,这样一趟过去后,最大或最小的数字被交换到了最后一位,然后再从头开始进行两两比较交换,直到倒数第二位时结束 function bubbleSort($arr){ for($i=0, $len=count($arr); $i<$len; $i++){ for($j=0; $j<$len; $j++){ if($arr[$i]<$arr[$j]){ $tmp = $arr[$j]; $arr[$j] = $arr[$i]; $arr[$i] = $tmp; } } } return $arr; } 19、快速排序,也就是找出一个元素(理论上可以随便找一个)作为基准,然后对数组进行分区操作,使基准左边元素的值都不大于基准值,基准右边的元素值 都不小于基准值,如此作为基准的元素调整到排序后的正确位置。递归快速排序,将其他n-1个元素也调整到排序后的正确位置。最后每个元素都是在排序后的正 确位置,排序完成。所以快速排序算法的核心算法是分区操作,即如何调整基准的位置以及调整返回基准的最终位置以便分治递归。 function quickSort($arr){ $len = count($arr); if($len <=1){ return $arr; } $key = $arr[0]; $leftArr = $rightArr= array(); for($i=1; $i<$len; $i++){ if($arr[$i] <= $key){ $leftArr[] = $arr[$i]; } else{ $rightArr[] = $arr[$i]; } } $leftArr = quickSort($leftArr); $rightArr = quickSort($rightArr); return array_merge($leftArr, array($key), $rightArr); } 20、(递归的)列出目录下所有文件及目录,这里也有两种方法 function listDir($path){ $res = dir($path); while($file = $res->read()){ if($file == '.' || $file == '..'){ continue; } if(is_dir($path . '/' .$file)){ echo $path . '/' .$file . "\r\n"; listDir($path . '/' .$file); } else{ echo $path . '/' .$file . "\r\n"; } } $res->close(); } function listDir($path){ if(is_dir($path)){ if(FALSE !== ($res = opendir($path))){ while(FALSE !== ($file = readdir($res))){ if($file == '.' || $file == '..'){ continue; } $subPath = $path . '/' . $file; if(is_dir($subPath)){ echo $subPath . "\r\n"; listDir($subPath); } else{ echo $subPath . "\r\n"; } } } } } 21、找出相对的目录,比如/a/b/c/d/e.php相对于/a/b/13/34/c.php是/c/d/ function ralativePath($a, $b){ $a = explode('/', dirname($a)); $b = explode('/', dirname($b)); $c = '/'; foreach ($a as $k=> $v){ if($v != $b[$k]){ $c .= $v . '/'; } } echo $c; } 22、快速找出url中php后缀 function get_ext($url){ $data = parse_url($url); return pathinfo($data['path'], PATHINFO_EXTENSION); } 23、正则题,使用正则抓取网页,以网页meta为utf8为准,若是抓取的网页编码为big5之类的,需要转化为utf8再收录 function preg_meta($meta){ $replacement = "\\1utf8\\6\\7"; $pattern = '#(<meta\s+http-equiv=(\'|"|)Content-Type(\'|"|)\s+content=(\'|"|)text/html; charset=)(\w+)(\'|"|)(>)#i'; return preg_replace($pattern, $replacement, $meta); } echo preg_meta("<meta http-equiv=Content-Type content='text/html; charset=big5'><META http-equiv=\"Content-Type\" content='text/html; charset=big5'>"); 24、不用php的反转函数倒序输出字符串,如abc,反序输出cba function revstring($str){ for($i=strlen($str)-1; $i>=0; $i--){ echo $str{$i}; } } revstring('abc'); 25、常见端口 TCP 21端口:FTP 文件传输服务 SSH 22端口:SSH连接linux服务器,通过SSH连接可以远程管理Linux等设备 TCP 23端口:TELNET 终端仿真服务 TCP 25端口:SMTP 简单邮件传输服务 UDP 53端口:DNS 域名解析服务 TCP 80端口:HTTP 超文本传输服务 TCP 110端口:POP3 “邮局协议版本3”使用的端口 TCP 443端口:HTTPS 加密的超文本传输服务 TCP 1521端口:Oracle数据库服务 TCP 1863端口:MSN Messenger的文件传输功能所使用的端口 TCP 3389端口:Microsoft RDP 微软远程桌面使用的端口 TCP 5631端口:Symantec pcAnywhere 远程控制数据传输时使用的端口 UDP 5632端口:Symantec pcAnywhere 主控端扫描被控端时使用的端口 TCP 5000端口:MS SQL Server使用的端口 UDP 8000端口:腾讯QQ 26、linux常用的命令 top linux进程实时监控 ps 在Linux中是查看进程的命令。ps查看正处于Running的进程 mv 为文件或目录改名或将文件由一个目录移入另一个目录中。 find 查找文件 df 可显示所有文件系统对i节点和磁盘块的使用情况。 cat 打印文件类容 chmod 变更文件或目录的权限 chgrp 文件或目录的权限的掌控以拥有者及所诉群组来管理。可以使用chgrp指令取变更文件与目录所属群组 grep 是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹 配的行打印出来。 wc 为统计指定文件中的字节数、字数、行数,并将统计结果显示输出 27、对于大流量的网站,您采用什么样的方法来解决访问量问题 首先,确认服务器硬件是否足够支持当前的流量 其次,优化数据库访问。 第三,禁止外部的盗链。 第四,控制大文件的下载。 第五,使用不同主机分流主要流量 第六,使用流量分析统计软件 28、$_SERVER常用的字段 $_SERVER['PHP_SELF'] #当前正在执行脚本的文件名 $_SERVER['SERVER_NAME'] #当前运行脚本所在服务器主机的名称 $_SERVER['REQUEST_METHOD'] #访问页面时的请求方法。例如:“GET”、“HEAD”,“POST”,“PUT” $_SERVER['QUERY_STRING'] #查询(query)的字符串 $_SERVER['HTTP_HOST'] #当前请求的 Host: 头部的内容 $_SERVER['HTTP_REFERER'] #链接到当前页面的前一页面的 URL 地址 $_SERVER['REMOTE_ADDR'] #正在浏览当前页面用户的 IP 地址 $_SERVER['REMOTE_HOST'] #正在浏览当前页面用户的主机名 $_SERVER['SCRIPT_FILENAME'] #当前执行脚本的绝对路径名 $_SERVER['SCRIPT_NAME'] #包含当前脚本的路径。这在页面需要指向自己时非常有用 $_SERVER['REQUEST_URI'] #访问此页面所需的 URI。例如,“/index.html” 29、安装php扩展 进入扩展的目录 phpize命令得到configure文件 ./configure --with-php-config=/usr/local/php/bin/php-config make & make install 在php.ini中加入扩展名称.so 重启web服务器(nginx/apache) 30、php-fpm与nginx PHP-FPM也是一个第三方的FastCGI进程管理器,它是作为PHP的一个补丁来开发的,在安装的时候也需要和PHP源码一起编译,也就是说PHP-FPM被编译到PHP内核中,因此在处理性能方面更加优秀;同时它在处理高并发方面也比spawn-fcgi引擎好很多,因此,推荐Nginx+PHP/PHP-FPM这个组合对PHP进行解析。 FastCGI 的主要优点是把动态语言和HTTP Server分离开来,所以Nginx与PHP/PHP-FPM经常被部署在不同的服务器上,以分担前端Nginx服务器的压力,使Nginx专一处理静态请求和转发动态请求,而PHP/PHP-FPM服务器专一解析PHP动态请求 #fastcgi FastCGI是一个可伸缩地、高速地在HTTP server和动态脚本语言间通信的接口。多数流行的HTTP server都支持FastCGI,包括Apache、Nginx和lighttpd等,同时,FastCGI也被许多脚本语言所支持,其中就有PHP。 FastCGI是从CGI发展改进而来的。传统CGI接口方式的主要缺点是性能很差,因为每次HTTP服务器遇到动态程序时都需要重新启动脚本解析器来执行解析,然后结果被返回给HTTP服务器。这在处理高并发访问时,几乎是不可用的。另外传统的CGI接口方式安全性也很差,现在已经很少被使用了。 FastCGI接口方式采用C/S结构,可以将HTTP服务器和脚本解析服务器分开,同时在脚本解析服务器上启动一个或者多个脚本解析守护进程。当HTTP服务器每次遇到动态程序时,可以将其直接交付给FastCGI进程来执行,然后将得到的结果返回给浏览器。这种方式可以让HTTP服务器专一地处理静态请求或者将动态脚本服务器的结果返回给客户端,这在很大程度上提高了整个应用系统的性能。 Nginx+FastCGI运行原理 Nginx不支持对外部程序的直接调用或者解析,所有的外部程序(包括PHP)必须通过FastCGI接口来调用。FastCGI接口在Linux下是socket,(这个socket可以是文件socket,也可以是ip socket)。为了调用CGI程序,还需要一个FastCGI的wrapper(wrapper可以理解为用于启动另一个程序的程序),这个wrapper绑定在某个固定socket上,如端口或者文件socket。当Nginx将CGI请求发送给这个socket的时候,通过FastCGI接口,wrapper接纳到请求,然后派生出一个新的线程,这个线程调用解释器或者外部程序处理脚本并读取返回数据;接着,wrapper再将返回的数据通过FastCGI接口,沿着固定的socket传递给Nginx;最后,Nginx将返回的数据发送给客户端,这就是Nginx+FastCGI的整个运作过程。 31、ajax全称“Asynchronous Javascript And XML”(异步JavaScript和XML)

小川游鱼 2019-12-02 01:41:29 0 浏览量 回答数 0

回答

PHP面试干货 1、进程和线程 进程和线程都是由操作系统所体会的程序运行的基本单元,系统利用该基本单元实现系统对应用的并发性。进程和线程的区别在于: 简而言之,一个程序至少有一个进程,一个进程至少有一个线程. 线程的划分尺度小于进程,使得多线程程序的并发性高。 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。 2、apache默认使用进程管理还是线程管理?如何判断并设置最大连接数? 一个进程可以开多个线程 默认是进程管理 默认有一个主进程 Linux: ps -aux | grep httpd | more 一个子进程代表一个用户的连接 Conf/extra/httpd-mpm.conf 多路功能模块 http -l 查询当前apache处于什么模式下 3、单例模式 单例模式需求:只能实例化产生一个对象 如何实现: 私有化构造函数 禁止克隆对象 提供一个访问这个实例的公共的静态方法(通常为getInstance方法),从而返回唯一对象 需要一个保存类的静态属性 class demo { private static $MyObject; //保存对象的静态属性 private function __construct(){ //私有化构造函数 } private function __clone(){ //禁止克隆 } public static function getInstance(){ if(! (self::$MyObject instanceof self)){ self::$MyObject = new self; } return self::$MyObject; } } 4、安装完Apache后,在http.conf中配置加载PHP文件以Apache模块的方式安装PHP,在文件http.conf中首先要用语句LoadModule php5_module "e:/php/php5apache2.dll"动态装载PHP模块,然后再用语句AddType application/x-httpd-php .php 使得Apache把所有扩展名为PHP的文件都作为PHP脚本处理 5、debug_backtrace()函数能返回脚本里的任意行中调用的函数的名称。该函数同时还经常被用在调试中,用来判断错误是如何发生的 function one($str1, $str2) { two("Glenn", "Quagmire"); } function two($str1, $str2) { three("Cleveland", "Brown"); } function three($str1, $str2) { print_r(debug_backtrace()); } one("Peter", "Griffin"); Array ( [0] => Array ( [file] => D:\www\test\result.php [line] => 9 [function] => three [args] => Array ( [0] => Cleveland [1] => Brown ) ) [1] => Array ( [file] => D:\www\test\result.php [line] => 5 [function] => two [args] => Array ( [0] => Glenn [1] => Quagmire ) ) [2] => Array ( [file] => D:\www\test\result.php [line] => 16 [function] => one [args] => Array ( [0] => Peter [1] => Griffin ) ) ) 6、输出用户的IP地址,并且判断用户的IP地址是否在192.168.1.100 — 192.168.1.150之间 echo $ip=getenv('REMOTE_ADDR'); $ip=str_replace('.','',$ip); if($ip<1921681150 && $ip>1921681100) { echo 'ip在192.168.1.100—–192.168.1.150之间'; } else { echo 'ip不在192.168.1.100—–192.168.1.150之间'; } 7、请将2维数组按照name的长度进行重新排序,按照顺序将id赋值 $tarray = array( array('id' => 0, 'name' => '123'), array('id' => 0, 'name' => '1234'), array('id' => 0, 'name' => '1235'), array('id' => 0, 'name' => '12356'), array('id' => 0, 'name' => '123abc') ); foreach($tarray as $key=>$val) { $c[]=$val['name']; } function aa($a,$b) { if(strlen($a)==strlen($b)) return 0; return strlen($a)>strlen($b)?-1:1; } usort($c,'aa'); $len=count($c); for($i=0;$i<$len;$i++) { $t[$i]['id']=$i+1; $t[$i]['name']=$c[$i]; } print_r($t); 8、表单数据提交方式POST和GET的区别,URL地址传递的数据最大长度是多少? POST方式提交数据用户不可见,是数据更安全,最大长度不受限制,而GET方式传值在URL地址可以看到,相对不安全,对大长度是2048字节。 9、SESSION和COOKIE的作用和区别,SESSION信息的存储方式,如何进行遍历 SESSION和COOKIE都能够使值在页面之间进行传递,SESSION存储在服务器端,数据更安全,COOKIE保存在客户端,用户使用手段可以进行修改,SESSION依赖于COOKIE进行传递的。Session遍历使用$_SESSION[]取值,cookie遍历使用$_COOKIE[]取值。 10、什么是数据库索引,主键索引,唯一索引的区别,索引的缺点是什么 索引用来快速地寻找那些具有特定值的记录。 主键索引和唯一索引的区别:主键是一种唯一性索引,但它必须指定为“PRIMARY KEY”,每个表只能有一个主键。唯一索引索引列的所有值都只能出现一次,即必须唯一。 索引的缺点: 1、创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。 2、索引需要占用物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,需要的空间就会更大。 3、当对表中的数据进行增加、删除、修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。 11、数据库设计时,常遇到的性能瓶颈有哪些,常有的解决方案 瓶颈主要有: 1、磁盘搜索 优化方法是:将数据分布在多个磁盘上 2、磁盘读/写 优化方法是:从多个磁盘并行读写。 3、CPU周期 优化方法:扩充内存 4、内存带宽 12、include和require区别 include引入文件的时候,如果碰到错误,会给出提示,并继续运行下边的代码。 require引入文件的时候,如果碰到错误,会给出提示,并停止运行下边的代码。 13、文件上传时设计到点 和文件上传有关的php.ini配置选项(File Uploads): file_uploads=On/Off:文件是否允许上传 upload_max_filesize上传文件时,单个文件的最大大小 post_max_size:提交表单时,整个post表单的最大大小 max_file_uploads =20上传文件的个数 内存占用,脚本最大执行时间也间接影响到文件的上传 14、header常见状态 //200 正常状态 header('HTTP/1.1 200 OK'); // 301 永久重定向,记得在后面要加重定向地址 Location:$url header('HTTP/1.1 301 Moved Permanently'); // 重定向,其实就是302 暂时重定向 header('Location: http://www.maiyoule.com/'); // 设置页面304 没有修改 header('HTTP/1.1 304 Not Modified'); // 显示登录框, header('HTTP/1.1 401 Unauthorized'); header('WWW-Authenticate: Basic realm="登录信息"'); echo '显示的信息!'; // 403 禁止访问 header('HTTP/1.1 403 Forbidden'); // 404 错误 header('HTTP/1.1 404 Not Found'); // 500 服务器错误 header('HTTP/1.1 500 Internal Server Error'); // 3秒后重定向指定地址(也就是刷新到新页面与 <meta http-equiv="refresh" content="10;http://www.maiyoule.com/ /> 相同) header('Refresh: 3; url=http://www.maiyoule.com/'); echo '10后跳转到http://www.maiyoule.com'; // 重写 X-Powered-By 值 header('X-Powered-By: PHP/5.3.0'); header('X-Powered-By: Brain/0.6b'); //设置上下文语言 header('Content-language: en'); // 设置页面最后修改时间(多用于防缓存) $time = time() - 60; //建议使用filetime函数来设置页面缓存时间 header('Last-Modified: '.gmdate('D, d M Y H:i:s', $time).' GMT'); // 设置内容长度 header('Content-Length: 39344'); // 设置头文件类型,可以用于流文件或者文件下载 header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="example.zip"'); header('Content-Transfer-Encoding: binary'); readfile('example.zip');//读取文件到客户端 //禁用页面缓存 header('Cache-Control: no-cache, no-store, max-age=0, must-revalidate'); header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); header('Pragma: no-cache'); //设置页面头信息 header('Content-Type: text/html; charset=iso-8859-1'); header('Content-Type: text/html; charset=utf-8'); header('Content-Type: text/plain'); header('Content-Type: image/jpeg'); header('Content-Type: application/zip'); header('Content-Type: application/pdf'); header('Content-Type: audio/mpeg'); header('Content-Type: application/x-shockwave-flash'); //.... 至于Content-Type 的值 可以去查查 w3c 的文档库,那里很丰富 15、ORM和ActiveRecord ORM:object relation mapping,即对象关系映射,简单的说就是对象模型和关系模型的一种映射。为什么要有这么一个映射?很简单,因为现在的开发语言基本都是oop的,但是传统的数据库却是关系型的。为了可以靠贴近面向对象开发,我们想要像操作对象一样操作数据库。还可以隔离底层数据库层,我们不需要关心我们使用的是mysql还是其他的关系型数据库 ActiveRecord也属于ORM层,由Rails最早提出,遵循标准的ORM模型:表映射到记录,记录映射到对象,字段映射到对象属性。配合遵循的命名和配置惯例,能够很大程度的快速实现模型的操作,而且简洁易懂。 ActiveRecord的主要思想是: 1. 每一个数据库表对应创建一个类,类的每一个对象实例对应于数据库中表的一行记录;通常表的每个字段在类中都有相应的Field; 2. ActiveRecord同时负责把自己持久化,在ActiveRecord中封装了对数据库的访问,即CURD;; 3. ActiveRecord是一种领域模型(Domain Model),封装了部分业务逻辑; ActiveRecord比较适用于: 1. 业务逻辑比较简单,当你的类基本上和数据库中的表一一对应时, ActiveRecord是非常方便的,即你的业务逻辑大多数是对单表操作; 2. 当发生跨表的操作时, 往往会配合使用事务脚本(Transaction Script),把跨表事务提升到事务脚本中; 3. ActiveRecord最大优点是简单, 直观。 一个类就包括了数据访问和业务逻辑. 如果配合代码生成器使用就更方便了; 这些优点使ActiveRecord特别适合WEB快速开发。 16、斐波那契方法,也就是1 1 2 3 5 8 ……,这里给出两种方法,大家可以对比下,看看哪种快,以及为什么 function fibonacci($n){ if($n == 0){ return 0; } if($n == 1){ return 1; } return fibonacci($n-1)+fibonacci($n-2); } function fibonacci($n){ for($i=0; $i<$n; $i++){ $r[] = $i<2 ? 1 : $r[$i-1]+$r[$i-2]; } return $r[--$i]; } 17、约瑟夫环,也就是常见的数猴子,n只猴子围成一圈,每只猴子下面标了编号,从1开始数起,数到m那么第m只猴子便退出,依次类推,每数到m,那么那个位置的猴子退出,那么最后剩下的猴子下的编号是啥。 function yuesefu($n,$m) { $r=0; for($i=2; $i<=$n; $i++) { $r=($r+$m)%$i; } return $r+1; } 18、冒泡排序,大致是临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换,这样一趟过去后,最大或最小的数字被交换到了最后一位,然后再从头开始进行两两比较交换,直到倒数第二位时结束 function bubbleSort($arr){ for($i=0, $len=count($arr); $i<$len; $i++){ for($j=0; $j<$len; $j++){ if($arr[$i]<$arr[$j]){ $tmp = $arr[$j]; $arr[$j] = $arr[$i]; $arr[$i] = $tmp; } } } return $arr; } 19、快速排序,也就是找出一个元素(理论上可以随便找一个)作为基准,然后对数组进行分区操作,使基准左边元素的值都不大于基准值,基准右边的元素值 都不小于基准值,如此作为基准的元素调整到排序后的正确位置。递归快速排序,将其他n-1个元素也调整到排序后的正确位置。最后每个元素都是在排序后的正 确位置,排序完成。所以快速排序算法的核心算法是分区操作,即如何调整基准的位置以及调整返回基准的最终位置以便分治递归。 function quickSort($arr){ $len = count($arr); if($len <=1){ return $arr; } $key = $arr[0]; $leftArr = $rightArr= array(); for($i=1; $i<$len; $i++){ if($arr[$i] <= $key){ $leftArr[] = $arr[$i]; } else{ $rightArr[] = $arr[$i]; } } $leftArr = quickSort($leftArr); $rightArr = quickSort($rightArr); return array_merge($leftArr, array($key), $rightArr); } 20、(递归的)列出目录下所有文件及目录,这里也有两种方法 function listDir($path){ $res = dir($path); while($file = $res->read()){ if($file == '.' || $file == '..'){ continue; } if(is_dir($path . '/' .$file)){ echo $path . '/' .$file . "\r\n"; listDir($path . '/' .$file); } else{ echo $path . '/' .$file . "\r\n"; } } $res->close(); } function listDir($path){ if(is_dir($path)){ if(FALSE !== ($res = opendir($path))){ while(FALSE !== ($file = readdir($res))){ if($file == '.' || $file == '..'){ continue; } $subPath = $path . '/' . $file; if(is_dir($subPath)){ echo $subPath . "\r\n"; listDir($subPath); } else{ echo $subPath . "\r\n"; } } } } } 21、找出相对的目录,比如/a/b/c/d/e.php相对于/a/b/13/34/c.php是/c/d/ function ralativePath($a, $b){ $a = explode('/', dirname($a)); $b = explode('/', dirname($b)); $c = '/'; foreach ($a as $k=> $v){ if($v != $b[$k]){ $c .= $v . '/'; } } echo $c; } 22、快速找出url中php后缀 function get_ext($url){ $data = parse_url($url); return pathinfo($data['path'], PATHINFO_EXTENSION); } 23、正则题,使用正则抓取网页,以网页meta为utf8为准,若是抓取的网页编码为big5之类的,需要转化为utf8再收录 function preg_meta($meta){ $replacement = "\\1utf8\\6\\7"; $pattern = '#(<meta\s+http-equiv=(\'|"|)Content-Type(\'|"|)\s+content=(\'|"|)text/html; charset=)(\w+)(\'|"|)(>)#i'; return preg_replace($pattern, $replacement, $meta); } echo preg_meta("<meta http-equiv=Content-Type content='text/html; charset=big5'><META http-equiv=\"Content-Type\" content='text/html; charset=big5'>"); 24、不用php的反转函数倒序输出字符串,如abc,反序输出cba function revstring($str){ for($i=strlen($str)-1; $i>=0; $i--){ echo $str{$i}; } } revstring('abc'); 25、常见端口 TCP 21端口:FTP 文件传输服务 SSH 22端口:SSH连接linux服务器,通过SSH连接可以远程管理Linux等设备 TCP 23端口:TELNET 终端仿真服务 TCP 25端口:SMTP 简单邮件传输服务 UDP 53端口:DNS 域名解析服务 TCP 80端口:HTTP 超文本传输服务 TCP 110端口:POP3 “邮局协议版本3”使用的端口 TCP 443端口:HTTPS 加密的超文本传输服务 TCP 1521端口:Oracle数据库服务 TCP 1863端口:MSN Messenger的文件传输功能所使用的端口 TCP 3389端口:Microsoft RDP 微软远程桌面使用的端口 TCP 5631端口:Symantec pcAnywhere 远程控制数据传输时使用的端口 UDP 5632端口:Symantec pcAnywhere 主控端扫描被控端时使用的端口 TCP 5000端口:MS SQL Server使用的端口 UDP 8000端口:腾讯QQ 26、linux常用的命令 top linux进程实时监控 ps 在Linux中是查看进程的命令。ps查看正处于Running的进程 mv 为文件或目录改名或将文件由一个目录移入另一个目录中。 find 查找文件 df 可显示所有文件系统对i节点和磁盘块的使用情况。 cat 打印文件类容 chmod 变更文件或目录的权限 chgrp 文件或目录的权限的掌控以拥有者及所诉群组来管理。可以使用chgrp指令取变更文件与目录所属群组 grep 是一种强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹 配的行打印出来。 wc 为统计指定文件中的字节数、字数、行数,并将统计结果显示输出 27、对于大流量的网站,您采用什么样的方法来解决访问量问题 首先,确认服务器硬件是否足够支持当前的流量 其次,优化数据库访问。 第三,禁止外部的盗链。 第四,控制大文件的下载。 第五,使用不同主机分流主要流量 第六,使用流量分析统计软件 28、$_SERVER常用的字段 $_SERVER['PHP_SELF'] #当前正在执行脚本的文件名 $_SERVER['SERVER_NAME'] #当前运行脚本所在服务器主机的名称 $_SERVER['REQUEST_METHOD'] #访问页面时的请求方法。例如:“GET”、“HEAD”,“POST”,“PUT” $_SERVER['QUERY_STRING'] #查询(query)的字符串 $_SERVER['HTTP_HOST'] #当前请求的 Host: 头部的内容 $_SERVER['HTTP_REFERER'] #链接到当前页面的前一页面的 URL 地址 $_SERVER['REMOTE_ADDR'] #正在浏览当前页面用户的 IP 地址 $_SERVER['REMOTE_HOST'] #正在浏览当前页面用户的主机名 $_SERVER['SCRIPT_FILENAME'] #当前执行脚本的绝对路径名 $_SERVER['SCRIPT_NAME'] #包含当前脚本的路径。这在页面需要指向自己时非常有用 $_SERVER['REQUEST_URI'] #访问此页面所需的 URI。例如,“/index.html” 29、安装php扩展 进入扩展的目录 phpize命令得到configure文件 ./configure --with-php-config=/usr/local/php/bin/php-config make & make install 在php.ini中加入扩展名称.so 重启web服务器(nginx/apache) 30、php-fpm与nginx PHP-FPM也是一个第三方的FastCGI进程管理器,它是作为PHP的一个补丁来开发的,在安装的时候也需要和PHP源码一起编译,也就是说PHP-FPM被编译到PHP内核中,因此在处理性能方面更加优秀;同时它在处理高并发方面也比spawn-fcgi引擎好很多,因此,推荐Nginx+PHP/PHP-FPM这个组合对PHP进行解析。 FastCGI 的主要优点是把动态语言和HTTP Server分离开来,所以Nginx与PHP/PHP-FPM经常被部署在不同的服务器上,以分担前端Nginx服务器的压力,使Nginx专一处理静态请求和转发动态请求,而PHP/PHP-FPM服务器专一解析PHP动态请求 #fastcgi FastCGI是一个可伸缩地、高速地在HTTP server和动态脚本语言间通信的接口。多数流行的HTTP server都支持FastCGI,包括Apache、Nginx和lighttpd等,同时,FastCGI也被许多脚本语言所支持,其中就有PHP。 FastCGI是从CGI发展改进而来的。传统CGI接口方式的主要缺点是性能很差,因为每次HTTP服务器遇到动态程序时都需要重新启动脚本解析器来执行解析,然后结果被返回给HTTP服务器。这在处理高并发访问时,几乎是不可用的。另外传统的CGI接口方式安全性也很差,现在已经很少被使用了。 FastCGI接口方式采用C/S结构,可以将HTTP服务器和脚本解析服务器分开,同时在脚本解析服务器上启动一个或者多个脚本解析守护进程。当HTTP服务器每次遇到动态程序时,可以将其直接交付给FastCGI进程来执行,然后将得到的结果返回给浏览器。这种方式可以让HTTP服务器专一地处理静态请求或者将动态脚本服务器的结果返回给客户端,这在很大程度上提高了整个应用系统的性能。 Nginx+FastCGI运行原理 Nginx不支持对外部程序的直接调用或者解析,所有的外部程序(包括PHP)必须通过FastCGI接口来调用。FastCGI接口在Linux下是socket,(这个socket可以是文件socket,也可以是ip socket)。为了调用CGI程序,还需要一个FastCGI的wrapper(wrapper可以理解为用于启动另一个程序的程序),这个wrapper绑定在某个固定socket上,如端口或者文件socket。当Nginx将CGI请求发送给这个socket的时候,通过FastCGI接口,wrapper接纳到请求,然后派生出一个新的线程,这个线程调用解释器或者外部程序处理脚本并读取返回数据;接着,wrapper再将返回的数据通过FastCGI接口,沿着固定的socket传递给Nginx;最后,Nginx将返回的数据发送给客户端,这就是Nginx+FastCGI的整个运作过程。 31、ajax全称“Asynchronous Javascript And XML”(异步JavaScript和XML)

小川游鱼 2019-12-02 01:41:29 0 浏览量 回答数 0

问题

ES 在数据量很大的情况下(数十亿级别)如何提高查询效率啊?【Java问答学堂】28期

剑曼红尘 2020-05-28 09:45:28 15 浏览量 回答数 1
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 企业信息查询 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 2020阿里巴巴研发效能峰会 企业建站模板 云效成长地图 高端建站 阿里云双十一主会场 阿里云双十一新人会场 1024程序员加油包 阿里云双十一拼团会场 场景化解决方案 阿里云双十一直播大厅