/** Spark SQL源码分析系列文章*/

前面几篇文章讲解了Spark SQL的核心执行流程和Spark SQL的Catalyst框架的Sql Parser是怎样接受用户输入sql,经过解析生成Unresolved Logical Plan的。我们记得Spark SQL的执行流程中另一个核心的组件式Analyzer,本文将会介绍Analyzer在Spark SQL里起到了什么作用。

Analyzer位于Catalyst的analysis package下,主要职责是将Sql Parser 未能Resolved的Logical Plan 给Resolved掉。

一、Analyzer构造

Analyzer会使用Catalog和FunctionRegistry将UnresolvedAttribute和UnresolvedRelation转换为catalyst里全类型的对象。

Analyzer里面有fixedPoint对象,一个Seq[Batch].

  1. class Analyzer(catalog: Catalog, registry: FunctionRegistry, caseSensitive: Boolean)
  2. extends RuleExecutor[LogicalPlan] with HiveTypeCoercion {
  3. // TODO: pass this in as a parameter.
  4. val fixedPoint = FixedPoint(100)
  5. val batches: Seq[Batch] = Seq(
  6. Batch("MultiInstanceRelations", Once,
  7. NewRelationInstances),
  8. Batch("CaseInsensitiveAttributeReferences", Once,
  9. (if (caseSensitive) Nil else LowercaseAttributeReferences :: Nil) : _*),
  10. Batch("Resolution", fixedPoint,
  11. ResolveReferences ::
  12. ResolveRelations ::
  13. NewRelationInstances ::
  14. ImplicitGenerate ::
  15. StarExpansion ::
  16. ResolveFunctions ::
  17. GlobalAggregates ::
  18. typeCoercionRules :_*),
  19. Batch("AnalysisOperators", fixedPoint,
  20. EliminateAnalysisOperators)
  21. )

Analyzer里的一些对象解释:

FixedPoint:相当于迭代次数的上限。

  1. /** A strategy that runs until fix point or maxIterations times, whichever comes first. */
  2. case class FixedPoint(maxIterations: Int) extends Strategy

Batch: 批次,这个对象是由一系列Rule组成的,采用一个策略(策略其实是迭代几次的别名吧,eg:Once)

  1. /** A batch of rules. */,
  2. protected case class Batch(name: String, strategy: Strategy, rules: Rule[TreeType]*)

Rule:理解为一种规则,这种规则会应用到Logical Plan 从而将UnResolved 转变为Resolved

  1. abstract class Rule[TreeType <: TreeNode[_]] extends Logging {
  2. /** Name for this rule, automatically inferred based on class name. */
  3. val ruleName: String = {
  4. val className = getClass.getName
  5. if (className endsWith "$") className.dropRight(1) else className
  6. }
  7. def apply(plan: TreeType): TreeType
  8. }

Strategy:最大的执行次数,如果执行次数在最大迭代次数之前就达到了fix point,策略就会停止,不再应用了。

  1. /**
  2. * An execution strategy for rules that indicates the maximum number of executions. If the
  3. * execution reaches fix point (i.e. converge) before maxIterations, it will stop.
  4. */
  5. abstract class Strategy { def maxIterations: Int }

Analyzer解析主要是根据这些Batch里面定义的策略和Rule来对Unresolved的逻辑计划进行解析的。

这里Analyzer类本身并没有定义执行的方法,而是要从它的父类RuleExecutor[LogicalPlan]寻找,Analyzer也实现了HiveTypeCosercion,这个类是参考Hive的类型自动兼容转换的原理。如图:

RuleExecutor:执行Rule的执行环境,它会将包含了一系列的Rule的Batch进行执行,这个过程都是串行的。

具体的执行方法定义在apply里:

可以看到这里是一个while循环,每个batch下的rules都对当前的plan进行作用,这个过程是迭代的,直到达到Fix Point或者最大迭代次数。

  1. def apply(plan: TreeType): TreeType = {
  2. var curPlan = plan
  3. batches.foreach { batch =>
  4. val batchStartPlan = curPlan
  5. var iteration = 1
  6. var lastPlan = curPlan
  7. var continue = true
  8. // Run until fix point (or the max number of iterations as specified in the strategy.
  9. while (continue) {
  10. curPlan = batch.rules.foldLeft(curPlan) {
  11. case (plan, rule) =>
  12. val result = rule(plan) //这里将调用各个不同Rule的apply方法,将UnResolved Relations,Attrubute和Function进行Resolve
  13. if (!result.fastEquals(plan)) {
  14. logger.trace(
  15. s"""
  16. |=== Applying Rule ${rule.ruleName} ===
  17. |${sideBySide(plan.treeString, result.treeString).mkString("\n")}
  18. """.stripMargin)
  19. }
  20. result //返回作用后的result plan
  21. }
  22. iteration += 1
  23. if (iteration > batch.strategy.maxIterations) { //如果迭代次数已经大于该策略的最大迭代次数,就停止循环
  24. logger.info(s"Max iterations ($iteration) reached for batch ${batch.name}")
  25. continue = false
  26. }
  27. if (curPlan.fastEquals(lastPlan)) { //如果在多次迭代中不再变化,因为plan有个unique id,就停止循环。
  28. logger.trace(s"Fixed point reached for batch ${batch.name} after $iteration iterations.")
  29. continue = false
  30. }
  31. lastPlan = curPlan
  32. }
  33. if (!batchStartPlan.fastEquals(curPlan)) {
  34. logger.debug(
  35. s"""
  36. |=== Result of Batch ${batch.name} ===
  37. |${sideBySide(plan.treeString, curPlan.treeString).mkString("\n")}
  38. """.stripMargin)
  39. } else {
  40. logger.trace(s"Batch ${batch.name} has no effect.")
  41. }
  42. }
  43. curPlan //返回Resolved的Logical Plan
  44. }

二、Rules介绍

    目前Spark SQL 1.0.0的Rule都定义在了Analyzer.scala的内部类。
    在batches里面定义了4个Batch。
    MultiInstanceRelations、CaseInsensitiveAttributeReferences、Resolution、AnalysisOperators 四个。
    这4个Batch是将不同的Rule进行归类,每种类别采用不同的策略来进行Resolve。
    

2.1、MultiInstanceRelation

如果一个实例在Logical Plan里出现了多次,则会应用NewRelationInstances这儿Rule
  1. Batch("MultiInstanceRelations", Once,
  2. NewRelationInstances)
  1. trait MultiInstanceRelation {
  2. def newInstance: this.type
  3. }
  1. object NewRelationInstances extends Rule[LogicalPlan] {
  2. def apply(plan: LogicalPlan): LogicalPlan = {
  3. val localRelations = plan collect { case l: MultiInstanceRelation => l} //将logical plan应用partial function得到所有MultiInstanceRelation的plan的集合
  4. val multiAppearance = localRelations
  5. .groupBy(identity[MultiInstanceRelation]) //group by操作
  6. .filter { case (_, ls) => ls.size > 1 } //如果只取size大于1的进行后续操作
  7. .map(_._1)
  8. .toSet
  9. //更新plan,使得每个实例的expId是唯一的。
  10. plan transform {
  11. case l: MultiInstanceRelation if multiAppearance contains l => l.newInstance
  12. }
  13. }
  14. }

2.2、LowercaseAttributeReferences

同样是partital function,对当前plan应用,将所有匹配的如UnresolvedRelation的别名alise转换为小写,将Subquery的别名也转换为小写。
总结:这是一个使属性名大小写不敏感的Rule,因为它将所有属性都to lower case了。
  1. object LowercaseAttributeReferences extends Rule[LogicalPlan] {
  2. def apply(plan: LogicalPlan): LogicalPlan = plan transform {
  3. case UnresolvedRelation(databaseName, name, alias) =>
  4. UnresolvedRelation(databaseName, name, alias.map(_.toLowerCase))
  5. case Subquery(alias, child) => Subquery(alias.toLowerCase, child)
  6. case q: LogicalPlan => q transformExpressions {
  7. case s: Star => s.copy(table = s.table.map(_.toLowerCase))
  8. case UnresolvedAttribute(name) => UnresolvedAttribute(name.toLowerCase)
  9. case Alias(c, name) => Alias(c, name.toLowerCase)()
  10. case GetField(c, name) => GetField(c, name.toLowerCase)
  11. }
  12. }
  13. }

2.3、ResolveReferences

将Sql parser解析出来的UnresolvedAttribute全部都转为对应的实际的catalyst.expressions.AttributeReference AttributeReferences
这里调用了logical plan 的resolve方法,将属性转为NamedExepression。
  1. object ResolveReferences extends Rule[LogicalPlan] {
  2. def apply(plan: LogicalPlan): LogicalPlan = plan transformUp {
  3. case q: LogicalPlan if q.childrenResolved =>
  4. logger.trace(s"Attempting to resolve ${q.simpleString}")
  5. q transformExpressions {
  6. case u @ UnresolvedAttribute(name) =>
  7. // Leave unchanged if resolution fails.  Hopefully will be resolved next round.
  8. val result = q.resolve(name).getOrElse(u)//转化为NamedExpression
  9. logger.debug(s"Resolving $u to $result")
  10. result
  11. }
  12. }
  13. }

2.4、 ResolveRelations

这个比较好理解,还记得前面Sql parser吗,比如select * from src,这个src表parse后就是一个UnresolvedRelation节点。
这一步ResolveRelations调用了catalog这个对象。Catalog对象里面维护了一个tableName,Logical Plan的HashMap结果。
通过这个Catalog目录来寻找当前表的结构,从而从中解析出这个表的字段,如UnResolvedRelations 会得到一个tableWithQualifiers。(即表和字段) 
这也解释了为什么流程图那,我会画一个catalog在上面,因为它是Analyzer工作时需要的meta data。
  1. object ResolveRelations extends Rule[LogicalPlan] {
  2. def apply(plan: LogicalPlan): LogicalPlan = plan transform {
  3. case UnresolvedRelation(databaseName, name, alias) =>
  4. catalog.lookupRelation(databaseName, name, alias)
  5. }
  6. }

2.5、ImplicitGenerate

如果在select语句里只有一个表达式,而且这个表达式是一个Generator(Generator是一个1条记录生成到N条记录的映射)
当在解析逻辑计划时,遇到Project节点的时候,就可以将它转换为Generate类(Generate类是将输入流应用一个函数,从而生成一个新的流)。
  1. object ImplicitGenerate extends Rule[LogicalPlan] {
  2. def apply(plan: LogicalPlan): LogicalPlan = plan transform {
  3. case Project(Seq(Alias(g: Generator, _)), child) =>
  4. Generate(g, join = false, outer = false, None, child)
  5. }
  6. }

2.6 StarExpansion

在Project操作符里,如果是*符号,即select * 语句,可以将所有的references都展开,即将select * 中的*展开成实际的字段。
  1. object StarExpansion extends Rule[LogicalPlan] {
  2. def apply(plan: LogicalPlan): LogicalPlan = plan transform {
  3. // Wait until children are resolved
  4. case p: LogicalPlan if !p.childrenResolved => p
  5. // If the projection list contains Stars, expand it.
  6. case p @ Project(projectList, child) if containsStar(projectList) =>
  7. Project(
  8. projectList.flatMap {
  9. case s: Star => s.expand(child.output) //展开,将输入的Attributeexpand(input: Seq[Attribute]) 转化为Seq[NamedExpression]
  10. case o => o :: Nil
  11. },
  12. child)
  13. case t: ScriptTransformation if containsStar(t.input) =>
  14. t.copy(
  15. input = t.input.flatMap {
  16. case s: Star => s.expand(t.child.output)
  17. case o => o :: Nil
  18. }
  19. )
  20. // If the aggregate function argument contains Stars, expand it.
  21. case a: Aggregate if containsStar(a.aggregateExpressions) =>
  22. a.copy(
  23. aggregateExpressions = a.aggregateExpressions.flatMap {
  24. case s: Star => s.expand(a.child.output)
  25. case o => o :: Nil
  26. }
  27. )
  28. }
  29. /**
  30. * Returns true if `exprs` contains a [[Star]].
  31. */
  32. protected def containsStar(exprs: Seq[Expression]): Boolean =
  33. exprs.collect { case _: Star => true }.nonEmpty
  34. }
  35. }

2.7 ResolveFunctions

这个和ResolveReferences差不多,这里主要是对udf进行resolve。
将这些UDF都在FunctionRegistry里进行查找。
  1. object ResolveFunctions extends Rule[LogicalPlan] {
  2. def apply(plan: LogicalPlan): LogicalPlan = plan transform {
  3. case q: LogicalPlan =>
  4. q transformExpressions {
  5. case u @ UnresolvedFunction(name, children) if u.childrenResolved =>
  6. registry.lookupFunction(name, children) //看是否注册了当前udf
  7. }
  8. }
  9. }

2.8 GlobalAggregates

全局的聚合,如果遇到了Project就返回一个Aggregate.

  1. object GlobalAggregates extends Rule[LogicalPlan] {
  2. def apply(plan: LogicalPlan): LogicalPlan = plan transform {
  3. case Project(projectList, child) if containsAggregates(projectList) =>
  4. Aggregate(Nil, projectList, child)
  5. }
  6. def containsAggregates(exprs: Seq[Expression]): Boolean = {
  7. exprs.foreach(_.foreach {
  8. case agg: AggregateExpression => return true
  9. case _ =>
  10. })
  11. false
  12. }
  13. }

2.9 typeCoercionRules

这个是Hive里的兼容SQL语法,比如将String和Int互相转换,不需要显示的调用cast xxx  as yyy了。如StringToIntegerCasts。
  1. val typeCoercionRules =
  2. PropagateTypes ::
  3. ConvertNaNs ::
  4. WidenTypes ::
  5. PromoteStrings ::
  6. BooleanComparisons ::
  7. BooleanCasts ::
  8. StringToIntegralCasts ::
  9. FunctionArgumentConversion ::
  10. CastNulls ::
  11. Nil

2.10 EliminateAnalysisOperators

将分析的操作符移除,这里仅支持2种,一种是Subquery需要移除,一种是LowerCaseSchema。这些节点都会从Logical Plan里移除。
 
  1. object EliminateAnalysisOperators extends Rule[LogicalPlan] {
  2. def apply(plan: LogicalPlan): LogicalPlan = plan transform {
  3. case Subquery(_, child) => child //遇到Subquery,不反悔本身,返回它的Child,即删除了该元素
  4. case LowerCaseSchema(child) => child
  5. }
  6. }

三、实践

  补充昨天DEBUG的一个例子,这个例子证实了如何将上面的规则应用到Unresolved Logical Plan:
  当传递sql语句的时候,的确调用了ResolveReferences将mobile解析成NamedExpression。
  可以对照这看执行流程,左边是Unresolved Logical Plan,右边是Resoveld Logical Plan。
  先是执行了Batch Resolution,eg: 调用ResovelRalation这个RUle来使 Unresovled Relation 转化为 SparkLogicalPlan并通过Catalog找到了其对于的字段属性。
  然后执行了Batch Analysis Operator。eg:调用EliminateAnalysisOperators来将SubQuery给remove掉了。
  可能格式显示的不太好,可以向右边拖动下滚动轴看下结果。 :) 

  1. val exec = sqlContext.sql("select mobile as mb, sid as id, mobile*2 multi2mobile, count(1) times from (select * from temp_shengli_mobile)a where pfrom_id=0.0 group by mobile, sid,  mobile*2")
  2. 14/07/21 18:23:32 DEBUG SparkILoop$SparkILoopInterpreter: Invoking: public static java.lang.String $line47.$eval.$print()
  3. 14/07/21 18:23:33 INFO Analyzer: Max iterations (2) reached for batch MultiInstanceRelations
  4. 14/07/21 18:23:33 INFO Analyzer: Max iterations (2) reached for batch CaseInsensitiveAttributeReferences
  5. 14/07/21 18:23:33 DEBUG Analyzer$ResolveReferences$: Resolving 'pfrom_id to pfrom_id#5
  6. 14/07/21 18:23:33 DEBUG Analyzer$ResolveReferences$: Resolving 'mobile to mobile#2
  7. 14/07/21 18:23:33 DEBUG Analyzer$ResolveReferences$: Resolving 'sid to sid#1
  8. 14/07/21 18:23:33 DEBUG Analyzer$ResolveReferences$: Resolving 'mobile to mobile#2
  9. 14/07/21 18:23:33 DEBUG Analyzer$ResolveReferences$: Resolving 'mobile to mobile#2
  10. 14/07/21 18:23:33 DEBUG Analyzer$ResolveReferences$: Resolving 'sid to sid#1
  11. 14/07/21 18:23:33 DEBUG Analyzer$ResolveReferences$: Resolving 'mobile to mobile#2
  12. 14/07/21 18:23:33 DEBUG Analyzer:
  13. === Result of Batch Resolution ===
  14. !Aggregate ['mobile,'sid,('mobile * 2) AS c2#27], ['mobile AS mb#23,'sid AS id#24,('mobile * 2) AS multi2mobile#25,COUNT(1) AS times#26L]   Aggregate [mobile#2,sid#1,(CAST(mobile#2, DoubleType) * CAST(2, DoubleType)) AS c2#27], [mobile#2 AS mb#23,sid#1 AS id#24,(CAST(mobile#2, DoubleType) * CAST(2, DoubleType)) AS multi2mobile#25,COUNT(1) AS times#26L]
  15. ! Filter ('pfrom_id = 0.0)                                                                                                                   Filter (CAST(pfrom_id#5, DoubleType) = 0.0)
  16. Subquery a                                                                                                                                 Subquery a
  17. !   Project [*]                                                                                                                                Project [data_date#0,sid#1,mobile#2,pverify_type#3,create_time#4,pfrom_id#5,p_status#6,pvalidate_time#7,feffect_time#8,plastupdate_ip#9,update_time#10,status#11,preserve_int#12]
  18. !    UnresolvedRelation None, temp_shengli_mobile, None                                                                                         Subquery temp_shengli_mobile
  19. !                                                                                                                                                SparkLogicalPlan (ExistingRdd [data_date#0,sid#1,mobile#2,pverify_type#3,create_time#4,pfrom_id#5,p_status#6,pvalidate_time#7,feffect_time#8,plastupdate_ip#9,update_time#10,status#11,preserve_int#12], MapPartitionsRDD[4] at mapPartitions at basicOperators.scala:174)
  20. 14/07/21 18:23:33 DEBUG Analyzer:
  21. === Result of Batch AnalysisOperators ===
  22. !Aggregate ['mobile,'sid,('mobile * 2) AS c2#27], ['mobile AS mb#23,'sid AS id#24,('mobile * 2) AS multi2mobile#25,COUNT(1) AS times#26L]   Aggregate [mobile#2,sid#1,(CAST(mobile#2, DoubleType) * CAST(2, DoubleType)) AS c2#27], [mobile#2 AS mb#23,sid#1 AS id#24,(CAST(mobile#2, DoubleType) * CAST(2, DoubleType)) AS multi2mobile#25,COUNT(1) AS times#26L]
  23. ! Filter ('pfrom_id = 0.0)                                                                                                                   Filter (CAST(pfrom_id#5, DoubleType) = 0.0)
  24. !  Subquery a                                                                                                                                 Project [data_date#0,sid#1,mobile#2,pverify_type#3,create_time#4,pfrom_id#5,p_status#6,pvalidate_time#7,feffect_time#8,plastupdate_ip#9,update_time#10,status#11,preserve_int#12]
  25. !   Project [*]                                                                                                                                SparkLogicalPlan (ExistingRdd [data_date#0,sid#1,mobile#2,pverify_type#3,create_time#4,pfrom_id#5,p_status#6,pvalidate_time#7,feffect_time#8,plastupdate_ip#9,update_time#10,status#11,preserve_int#12], MapPartitionsRDD[4] at mapPartitions at basicOperators.scala:174)
  26. !    UnresolvedRelation None, temp_shengli_mobile, None

四、总结

    本文从源代码角度分析了Analyzer在对Sql Parser解析出的UnResolve Logical Plan 进行analyze的过程中,所执行的流程。
    流程是实例化一个SimpleAnalyzer,定义一些Batch,然后遍历这些Batch在RuleExecutor的环境下,执行Batch里面的Rules,每个Rule会对Unresolved Logical Plan进行Resolve,有些可能不能一次解析出,需要多次迭代,直到达到max迭代次数或者达到fix point。这里Rule里比较常用的就是ResolveReferences、ResolveRelations、StarExpansion、GlobalAggregates、typeCoercionRules和EliminateAnalysisOperators。
 
 
——EOF——

原创文章,转载请注明:

转载自:OopsOutOfMemory盛利的Blog,作者: OopsOutOfMemory

本文链接地址:http://blog.csdn.net/oopsoom/article/details/38025185

注:本文基于署名-非商业性使用-禁止演绎 2.5 中国大陆(CC BY-NC-ND 2.5 CN)协议,欢迎转载、转发和评论,但是请保留本文作者署名和文章链接。如若需要用于商业目的或者与授权方面的协商,请联系我。

转自:http://blog.csdn.net/oopsoom/article/details/38025185

第三篇:Spark SQL Catalyst源码分析之Analyzer的更多相关文章

  1. 第五篇:Spark SQL Catalyst源码分析之Optimizer

    /** Spark SQL源码分析系列文章*/ 前几篇文章介绍了Spark SQL的Catalyst的核心运行流程.SqlParser,和Analyzer 以及核心类库TreeNode,本文将详细讲解 ...

  2. 第六篇:Spark SQL Catalyst源码分析之Physical Plan

    /** Spark SQL源码分析系列文章*/ 前面几篇文章主要介绍的是spark sql包里的的spark sql执行流程,以及Catalyst包内的SqlParser,Analyzer和Optim ...

  3. 第四篇:Spark SQL Catalyst源码分析之TreeNode Library

    /** Spark SQL源码分析系列文章*/ 前几篇文章介绍了Spark SQL的Catalyst的核心运行流程.SqlParser,和Analyzer,本来打算直接写Optimizer的,但是发现 ...

  4. 第八篇:Spark SQL Catalyst源码分析之UDF

    /** Spark SQL源码分析系列文章*/ 在SQL的世界里,除了官方提供的常用的处理函数之外,一般都会提供可扩展的对外自定义函数接口,这已经成为一种事实的标准. 在前面Spark SQL源码分析 ...

  5. 第二篇:Spark SQL Catalyst源码分析之SqlParser

    /** Spark SQL源码分析系列文章*/ Spark SQL的核心执行流程我们已经分析完毕,可以参见Spark SQL核心执行流程,下面我们来分析执行流程中各个核心组件的工作职责. 本文先从入口 ...

  6. (转)spring boot实战(第三篇)事件监听源码分析

    原文:http://blog.csdn.net/liaokailin/article/details/48194777 监听源码分析 首先是我们自定义的main方法: package com.lkl. ...

  7. Spark Scheduler模块源码分析之TaskScheduler和SchedulerBackend

    本文是Scheduler模块源码分析的第二篇,第一篇Spark Scheduler模块源码分析之DAGScheduler主要分析了DAGScheduler.本文接下来结合Spark-1.6.0的源码继 ...

  8. Spark RPC框架源码分析(三)Spark心跳机制分析

    一.Spark心跳概述 前面两节中介绍了Spark RPC的基本知识,以及深入剖析了Spark RPC中一些源码的实现流程. 具体可以看这里: Spark RPC框架源码分析(二)运行时序 Spark ...

  9. Spark Scheduler模块源码分析之DAGScheduler

    本文主要结合Spark-1.6.0的源码,对Spark中任务调度模块的执行过程进行分析.Spark Application在遇到Action操作时才会真正的提交任务并进行计算.这时Spark会根据Ac ...

随机推荐

  1. 【BZOJ3829】[Poi2014]FarmCraft 树形DP(贪心)

    [BZOJ3829][Poi2014]FarmCraft Description In a village called Byteville, there are   houses connected ...

  2. 【bzoj4872】[Shoi2017]分手是祝愿 数论+期望dp

    题目描述 Zeit und Raum trennen dich und mich. 时空将你我分开. B 君在玩一个游戏,这个游戏由 n 个灯和 n 个开关组成,给定这 n 个灯的初始状态,下标为从 ...

  3. 向spider中传递参数

    1.这里采用run.py脚本方式 # 通过CrawlerProcess同时运行几个spider import scrapy from scrapy.crawler import CrawlerProc ...

  4. Golang Frameworks

    Web frameworks help developers build applications as easily and quickly as possible. Go is still rel ...

  5. JS去遍历Table的所有单元格中的内容

    用JS去遍历Table的所有单元格中的内容,可以用如下JS代码实现 function GetInfoFromTable(tableid) { var tableInfo = ""; ...

  6. hadoop笔记 基础 归档

    核心:分布式存储和分布式计算 闲话: 底层基于socket通信 NIO——java异步io,不阻塞,不等待 bt——p2p软件(点对点传输,每个人既是上传者又是下载者.但是会占用大量网络带宽,所以很多 ...

  7. DRF(3) - 序列化组件(GET/PUT/DELETE接口设计)、视图优化组件

    一.序列化组件 基于上篇随笔的表结构,通过序列化组件的ModelSerializer设计如下三个接口: GET 127.0.0.1:8000/books/{id} # 获取一条数据,返回值:{} PU ...

  8. TI C66x DSP 系统events及其应用 - 5.7(IST)

    当CPU開始处理一个中断(INT4~15)时,它将引用中断服务表(IST).IST是一个获取包括中断服务代码的包的表. IST包括16个连续的获取包.每个中断服务获取包(ISFP)包括最多14条指令( ...

  9. centos Docker安装前升级内核3.10的方法

    首先我虚拟机系统都是Centos 6.5 .ESXI ,后安装devel .ESXI 后来 .ESXI 我所操作的都是虚拟机,但是在真实机上面如何我就不清楚了~~ 大家一定要记得安装步骤,,,不然就是 ...

  10. Mysql学习笔记—索引

    一.什么是索引 一般的应用系统,读写比例在10:1左右,而且插入操作和一般的更新操作很少出现性能问题,遇到最多的,也是最容易出问题的,还是一些复杂的查询操作,所以查询语句的优化显然是重中之重. 在数据 ...