首页 文章 精选 留言 我的

精选列表

搜索[架构师],共5862篇文章
优秀的个人博客,低调大师

架构师杂谈JVM之JIT

JIT技术是JVM中最重要的核心模块之一。我的课程里本来没有计划这一篇,但因为不断有朋友问起,Java到底是怎么运行的?既然Hotspot是C++写的,那Java是不是可以说运行在C++之上呢?为了澄清这些概念,我才想起来了加了这样一篇文章,算做番外篇吧。 Just In Time Just in time编译,也叫做运行时编译,不同于 C / C++ 语言直接被翻译成机器指令,javac把java的源文件翻译成了class文件,而class文件中全都是Java字节码。那么,JVM在加载了这些class文件以后,针对这些字节码,逐条取出,逐条执行,这种方法就是解释执行。 还有一种,就是把这些Java字节码重新编译优化,生成机器码,让CPU直接执行。这样编出来的代码效率会更高。通常,我们不必把所有的Java方法都编译成机器码,只需要把调用最频繁,占据CPU时间最长的方法找出来将其编译成机器码。这种调用最频繁的Java方法就是我们常说的热点方法(Hotspot,说不定这个虚拟机的名字就是从这里来的)。 这种在运行时按需编译的方式就是Just In Time。 主要技术点 其实JIT的主要技术点,从大的框架上来说,非常简单,就是申请一块既有写权限又有执行权限的内存,然后把你要编译的Java方法,翻译成机器码,写入到这块内存里。当再需要调用原来的Java方法时,就转向调用这块内存。 我们看一个例子: #include<stdio.h> int inc(int a) { return a + 1; } int main() { printf("%d\n", inc(3)); return 0; } 上面这个例子很简单,就是把3加1,然后打印出来,我们通过以下命令,查看一下它的机器码: # gcc -o inc inc.c # objdump -d inc 然后在这一堆输出中,可以找到 inc 方法最终被翻译成了这样的机器码: 40052d: 55 push %rbp 40052e: 48 89 e5 mov %rsp,%rbp 400531: 89 7d fc mov %edi,-0x4(%rbp) 400534: 8b 45 fc mov -0x4(%rbp),%eax 400537: 83 c0 01 add $0x1,%eax 40053a: 5d pop %rbp 40053b: c3 retq 我来解释一下(读者需要一定的x86汇编语言的知识)。 第一句,保存上一个栈帧的基址,并把当前的栈指针赋给栈基址寄存器,这是进入一个函数的常规操作。我们不去管它。 第三句,把edi存到栈上。在x64处理器上,前6个参数都是使用寄存器传参的。第一个参数会使用rdi,第二个参数使用 rsi,等等。所以 edi 里存的其实就是第一个参数,也就是整数 3,为什么使用rdi的低32位,也就是 edi 呢?因为我们的入参 a 是 int 型啊。大家可以换成 long 型看看效果。 第四句,把上一步存到栈上的那个整数再存进 eax 中。 第五句往后,把 eax 加上 1, 然后就退栈,返回。按照x64的规定(ABI),返回值通过eax传递。 我们看到了,其实第三句,第四句好像根本没有存在的必要,gcc 默认情况下,生成的机器码有点傻,它总要把入参放到栈上,但其实,我们是可以直接把参数从 rdi 中放入到 rax 中的。不满意。那我们可以自己改一下,让它更精简一点。怎么做呢?答案就是运行时修改 inc 的逻辑。 #include<stdio.h> #include<memory.h> #include<sys/mman.h> typedef int (* inc_func)(int a); int main() { char code[] = { 0x55, // push rbp 0x48, 0x89, 0xe5, // mov rsp, rbp 0x89, 0xf8, // mov edi, eax 0x83, 0xc0, 0x01, // add $1, eax 0x5d, // pop rbp 0xc3 // ret }; void * temp = mmap(NULL, sizeof(code), PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); memcpy(temp, code, sizeof(code)); inc_func p_inc = (inc_func)temp; printf("%d\n", p_inc(7)); return 0; } 在这个例子中,我们使用了 mmap 来申请了一块有写权限和执行权限的内存,然后把我们手写的机器码拷进去,然后使用一个函数指针指向这块内存,并且调用它。通过这种方式我们就可以执行这一段手写的机器码了。 运行一下看看: # gcc -o inc inc.c # ./inc 8 再回想一下这个过程。我们通过手写机器码把原来的 inc 函数代替掉了。在新的例子中,我们是使用程序中定义的数据来重新造了一个 inc 函数。这种在运行的过程创建新的函数的方式,就是JIT的核心操作。 解释器,C1和C2 在Hotspot中,解释器是为每一个字节码生成一小段机器码,在执行Java方法的过程中,每次取一条指令,然后就去执行这一个指令所对应的那一段机器码。256条指令,就组成了一个表,在这个表里,每一条指令都对应一段机器码,当执行到某一条指令时,就从这个表里去查这段机器码,并且通过 jmp 指令去执行这段机器码就行了。 这种方式被称为模板解释器。 模板解释器生成的代码有很多冗余,就像我们上面的第一个例子那样。为了生成更精简的机器码,我们可以引入编译器优化手段,例如全局值编码,死代码消除,标量展开,公共子表达式消除,常量传播等等。这样生成出来的机器码会更加优化。 但是,生成机器码的质量越高,所需要的时间也就越长。JIT线程也是要挤占Java 应用线程的资源的。所以C1是一个折衷,编译时间既不会太长,生成的机器码的指令也不是最优化的,但肯定比解释器的效率要高很多。 如果一个Java方法调用得足够频繁,那就更值得花大力气去为它生成更优质的机器码,这时就会触发C2编译,c2是一个运行得更慢,但却能生成更高效代码的编译器。 由此,我们看到,其实Java的运行,几乎全部都依赖运行时生成的机器码上。所以,对于文章开头的那个问题“Java是运行在C++上的吗?”,大家应该都有自己的答案了。这个问题无法简单地回答是或者不是,正确答案就是Java的运行依赖模板解释器和JIT编译器。 多说一点优化 我们这节课所举的例子中,可以做更多的优化,例如,既然我进到inc函数以后,完全没有使用栈,那其实,我就不要再为它开辟栈帧了。所以可以把push rbp, pop rbp的逻辑都去掉。 进一步优化成这样: char code[] = { 0x89, 0xf8, // mov edi, eax 0x83, 0xc0, 0x01, // add $1, eax 0xc3 // ret }; 可以看到,指令更加精简了。我们重新编译运行,还是能成功打印出8。 根据这个问题:为什么 lea 会被用来计算? 我们还可以写出更优化的代码来: char code[] = { 0x8d, 0x47, 0x01, // lea 0x1(rdi), rax 0xc3 // ret }; 如果开启 gcc 的优化编译,我们也可以得到这样的代码,例如,还是针对这个方法: int inc(int a) { return a + 1; } 使用 -O2 优化: # gcc -o inc inc.c -O2 # objdump -d inc 就可以看到,inc 的机器码变成这样了: 00000000004005f0 <inc>: 4005f0: 8d 47 01 lea 0x1(%rdi),%eax 4005f3: c3 retq 这和我们手写的优化的机器码是完全一样的了。 实际上,C1和C2所要做的和gcc的优化编译是一样的,就是使用特定的方法生成更高效的机器码。但是从原理上来说,运行时生成机器码这个技术,大家都是相通的。 最后,补充一句,iOS禁掉了JIT编译,所用的手段就是无法申请一块同时具有写权限和执行权限的内存。那么,JIT的核心基石,运行时生成可执行的机器码就无法存在了。

优秀的个人博客,低调大师

架构师带你深入解读HashMap

HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,其中 HashMap 是 Map 接口的常用实现类,HashSet 是 Set 接口的常用实现类。虽然 HashMap 和 HashSet 实现的接口规范不同,但它们底层的 Hash 存储机制完全一样,甚至 HashSet 本身就采用 HashMap 来实现的。 通过 HashMap、HashSet 的源代码分析其 Hash 存储机制 实际上,HashSet 和 HashMap 之间有很多相似之处,对于 HashSet 而言,系统采用 Hash 算法决定集合元素的存储位置,这样可以保证能快速存、取集合元素;对于 HashMap 而言,系统 key-value 当成一个整体进行处理,系统总是根据 Hash 算法来计算 key-value 的存储位置,这样可以保证能快速存、取 Map 的 key-value 对。 在介绍集合存储之前需要指出一点:虽然集合号称存储的是 Java 对象,但实际上并不会真正将 Java 对象放入 Set 集合中,只是在 Set 集合中保留这些对象的引用而言。也就是说:Java 集合实际上是多个引用变量所组成的集合,这些引用变量指向实际的 Java 对象。集合和引用 就像引用类型的数组一样,当我们把 Java 对象放入数组之时,并不是真正的把 Java 对象放入数组中,只是把对象的引用放入数组中,每个数组元素都是一个引用变量。 HashMap 的存储实现 当程序试图将多个 key-value 放入 HashMap 中时,以如下代码片段为例: [java]view plaincopy HashMap<String,Double>map=newHashMap<String,Double>(); map.put("语文",80.0); map.put("数学",89.0); map.put("英语",78.2); HashMap 采用一种所谓的“Hash 算法”来决定每个元素的存储位置。 当程序执行 map.put("语文" , 80.0); 时,系统将调用"语文"的 hashCode() 方法得到其 hashCode 值——每个 Java 对象都有 hashCode() 方法,都可通过该方法获得它的 hashCode 值。得到这个对象的 hashCode 值之后,系统会根据该 hashCode 值来决定该元素的存储位置。 我们可以看 HashMap 类的 put(K key , V value) 方法的源代码: [java]view plaincopy publicVput(Kkey,Vvalue) { //如果key为null,调用putForNullKey方法进行处理 if(key==) returnputForNullKey(value); //根据key的keyCode计算Hash值 inthash=hash(key.hashCode()); //搜索指定hash值在对应table中的索引 inti=indexFor(hash,table.length); //如果i索引处的Entry不为null,通过循环不断遍历e元素的下一个元素 for(Entry<K,V>e=table[i];e!=;e=e.next) { Objectk; //找到指定key与需要放入的key相等(hash值相同 //通过equals比较放回true) if(e.hash==hash&&((k=e.key)==key ||key.equals(k))) { VoldValue=e.value; e.value=value; e.recordAccess(this); returnoldValue; } } //如果i索引处的Entry为null,表明此处还没有Entry modCount++; //将key、value添加到i索引处 addEntry(hash,key,value,i); return; } 上面程序中用到了一个重要的内部接口:Map.Entry,每个 Map.Entry 其实就是一个 key-value 对。从上面程序中可以看出:当系统决定存储 HashMap 中的 key-value 对时,完全没有考虑 Entry 中的 value,仅仅只是根据 key 来计算并决定每个 Entry 的存储位置。这也说明了前面的结论:我们完全可以把 Map 集合中的 value 当成 key 的附属,当系统决定了 key 的存储位置之后,value 随之保存在那里即可。 上面方法提供了一个根据 hashCode() 返回值来计算 Hash 码的方法:hash(),这个方法是一个纯粹的数学计算,其方法如下: [java]view plaincopy staticinthash(inth) { h^=(h>>>20)^(h>>>12); returnh^(h>>>7)^(h>>>4); } 对于任意给定的对象,只要它的 hashCode() 返回值相同,那么程序调用 hash(int h) 方法所计算得到的 Hash 码值总是相同的。接下来程序会调用 indexFor(int h, int length) 方法来计算该对象应该保存在 table 数组的哪个索引处。indexFor(int h, int length) 方法的代码如下: [java]view plaincopy staticintindexFor(inth,intlength) { returnh&(length-1); } 这个方法非常巧妙,它总是通过 h &(table.length -1) 来得到该对象的保存位置——而 HashMap 底层数组的长度总是 2 的 n 次方,这一点可参看后面关于 HashMap 构造器的介绍。 当 length 总是 2 的倍数时,h & (length-1) 将是一个非常巧妙的设计:假设 h=5,length=16, 那么 h & length - 1 将得到 5;如果 h=6,length=16, 那么 h & length - 1 将得到 6 ……如果 h=15,length=16, 那么 h & length - 1 将得到 15;但是当 h=16 时 , length=16 时,那么 h & length - 1 将得到 0 了;当 h=17 时 , length=16 时,那么 h & length - 1 将得到 1 了……这样保证计算得到的索引值总是位于 table 数组的索引之内。 根据上面 put 方法的源代码可以看出,当程序试图将一个 key-value 对放入 HashMap 中时,程序首先根据该 key 的 hashCode() 返回值决定该 Entry 的存储位置:如果两个 Entry 的 key 的 hashCode() 返回值相同,那它们的存储位置相同。如果这两个 Entry 的 key 通过 equals 比较返回 true,新添加 Entry 的 value 将覆盖集合中原有 Entry 的 value,但 key 不会覆盖。如果这两个 Entry 的 key 通过 equals 比较返回 false,新添加的 Entry 将与集合中原有 Entry 形成 Entry 链,而且新添加的 Entry 位于 Entry 链的头部——具体说明继续看 addEntry() 方法的说明。 当向 HashMap 中添加 key-value 对,由其 key 的 hashCode() 返回值决定该 key-value 对(就是 Entry 对象)的存储位置。当两个 Entry 对象的 key 的 hashCode() 返回值相同时,将由 key 通过 eqauls() 比较值决定是采用覆盖行为(返回 true),还是产生 Entry 链(返回 false)。 上面程序中还调用了 addEntry(hash, key, value, i); 代码,其中 addEntry 是 HashMap 提供的一个包访问权限的方法,该方法仅用于添加一个 key-value 对。下面是该方法的代码: [java]view plaincopy voidaddEntry(inthash,Kkey,Vvalue,intbucketIndex) { //获取指定bucketIndex索引处的Entry Entry<K,V>e=table[bucketIndex];//① //将新创建的Entry放入bucketIndex索引处,并让新的Entry指向原来的Entry table[bucketIndex]=newEntry<K,V>(hash,key,value,e); //如果Map中的key-value对的数量超过了极限 if(size++>=threshold) //把table对象的长度扩充到2倍。 resize(2*table.length);//② } 上面方法的代码很简单,但其中包含了一个非常优雅的设计:系统总是将新添加的 Entry 对象放入 table 数组的 bucketIndex 索引处——如果 bucketIndex 索引处已经有了一个 Entry 对象,那新添加的 Entry 对象指向原有的 Entry 对象(产生一个 Entry 链),如果 bucketIndex 索引处没有 Entry 对象,也就是上面程序①号代码的 e 变量是 null,也就是新放入的 Entry 对象指向 null,也就是没有产生 Entry 链。JDK 源码 在 JDK 安装目录下可以找到一个 src.zip 压缩文件,该文件里包含了 Java 基础类库的所有源文件。只要读者有学习兴趣,随时可以打开这份压缩文件来阅读 Java 类库的源代码,这对提高读者的编程能力是非常有帮助的。需要指出的是:src.zip 中包含的源代码并没有包含像上文中的中文注释,这些注释是笔者自己添加进去的。 Hash 算法的性能选项 根据上面代码可以看出,在同一个 bucket 存储 Entry 链的情况下,新放入的 Entry 总是位于 bucket 中,而最早放入该 bucket 中的 Entry 则位于这个 Entry 链的最末端。 上面程序中还有这样两个变量: * size:该变量保存了该 HashMap 中所包含的 key-value 对的数量。 * threshold:该变量包含了 HashMap 能容纳的 key-value 对的极限,它的值等于 HashMap 的容量乘以负载因子(load factor)。 从上面程序中②号代码可以看出,当 size++ >= threshold 时,HashMap 会自动调用 resize 方法扩充 HashMap 的容量。每扩充一次,HashMap 的容量就增大一倍。 上面程序中使用的 table 其实就是一个普通数组,每个数组都有一个固定的长度,这个数组的长度就是 HashMap 的容量。HashMap 包含如下几个构造器: * HashMap():构建一个初始容量为 16,负载因子为 0.75 的 HashMap。 * HashMap(int initialCapacity):构建一个初始容量为 initialCapacity,负载因子为 0.75 的 HashMap。 * HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的负载因子创建一个 HashMap。 当创建一个 HashMap 时,系统会自动创建一个 table 数组来保存 HashMap 中的 Entry,下面是 HashMap 中一个构造器的代码: [java]view plaincopy //以指定初始化容量、负载因子创建HashMap publicHashMap(intinitialCapacity,floatloadFactor) { //初始容量不能为负数 if(initialCapacity<0) thrownewIllegalArgumentException( "Illegalinitialcapacity:"+ initialCapacity); //如果初始容量大于最大容量,让出示容量 if(initialCapacity>MAXIMUM_CAPACITY) initialCapacity=MAXIMUM_CAPACITY; //负载因子必须大于0的数值 if(loadFactor<=0||Float.isNaN(loadFactor)) thrownewIllegalArgumentException( loadFactor); //计算出大于initialCapacity的最小的2的n次方值。 intcapacity=1; while(capacity<initialCapacity) capacity<<=1; this.loadFactor=loadFactor; //设置容量极限等于容量*负载因子 threshold=(int)(capacity*loadFactor); //初始化table数组 table=newEntry[capacity];//① init(); } 上面代码中粗体字代码包含了一个简洁的代码实现:找出大于 initialCapacity 的、最小的 2 的 n 次方值,并将其作为 HashMap 的实际容量(由 capacity 变量保存)。例如给定 initialCapacity 为 10,那么该 HashMap 的实际容量就是 16。 程序①号代码处可以看到:table 的实质就是一个数组,一个长度为 capacity 的数组。 对于 HashMap 及其子类而言,它们采用 Hash 算法来决定集合中元素的存储位置。当系统开始初始化 HashMap 时,系统会创建一个长度为 capacity 的 Entry 数组,这个数组里可以存储元素的位置被称为“桶(bucket)”,每个 bucket 都有其指定索引,系统可以根据其索引快速访问该 bucket 里存储的元素。 无论何时,HashMap 的每个“桶”只存储一个元素(也就是一个 Entry),由于 Entry 对象可以包含一个引用变量(就是 Entry 构造器的的最后一个参数)用于指向下一个 Entry,因此可能出现的情况是:HashMap 的 bucket 中只有一个 Entry,但这个 Entry 指向另一个 Entry ——这就形成了一个 Entry 链。如图 1 所示: 图 1. HashMap 的存储示意 HashMap 的读取实现 当 HashMap 的每个 bucket 里存储的 Entry 只是单个 Entry ——也就是没有通过指针产生 Entry 链时,此时的 HashMap 具有最好的性能:当程序通过 key 取出对应 value 时,系统只要先计算出该 key 的 hashCode() 返回值,在根据该 hashCode 返回值找出该 key 在 table 数组中的索引,然后取出该索引处的 Entry,最后返回该 key 对应的 value 即可。看 HashMap 类的 get(K key) 方法代码: [java]view plaincopy publicVget(Objectkey) { //如果key是null,调用getForNullKey取出对应的value if(key==) returngetForNullKey(); //根据该key的hashCode值计算它的hash码 inthash=hash(key.hashCode()); //直接取出table数组中指定索引处的值, for(Entry<K,V>e=table[indexFor(hash,table.length)]; e!=; //搜索该Entry链的下一个Entr e=e.next)//① { Objectk; //如果该Entry的key与被搜索key相同 if(e.hash==hash&&((k=e.key)==key ||key.equals(k))) returne.value; } return; } 从上面代码中可以看出,如果 HashMap 的每个 bucket 里只有一个 Entry 时,HashMap 可以根据索引、快速地取出该 bucket 里的 Entry;在发生“Hash 冲突”的情况下,单个 bucket 里存储的不是一个 Entry,而是一个 Entry 链,系统只能必须按顺序遍历每个 Entry,直到找到想搜索的 Entry 为止——如果恰好要搜索的 Entry 位于该 Entry 链的最末端(该 Entry 是最早放入该 bucket 中),那系统必须循环到最后才能找到该元素。 归纳起来简单地说,HashMap 在底层将 key-value 当成一个整体进行处理,这个整体就是一个 Entry 对象。HashMap 底层采用一个 Entry[] 数组来保存所有的 key-value 对,当需要存储一个 Entry 对象时,会根据 Hash 算法来决定其存储位置;当需要取出一个 Entry 时,也会根据 Hash 算法找到其存储位置,直接取出该 Entry。由此可见:HashMap 之所以能快速存、取它所包含的 Entry,完全类似于现实生活中母亲从小教我们的:不同的东西要放在不同的位置,需要时才能快速找到它。 当创建 HashMap 时,有一个默认的负载因子(load factor),其默认值为 0.75,这是时间和空间成本上一种折衷:增大负载因子可以减少 Hash 表(就是那个 Entry 数组)所占用的内存空间,但会增加查询数据的时间开销,而查询是最频繁的的操作(HashMap 的 get() 与 put() 方法都要用到查询);减小负载因子会提高数据查询的性能,但会增加 Hash 表所占用的内存空间。 掌握了上面知识之后,我们可以在创建 HashMap 时根据实际需要适当地调整 load factor 的值;如果程序比较关心空间开销、内存比较紧张,可以适当地增加负载因子;如果程序比较关心时间开销,内存比较宽裕则可以适当的减少负载因子。通常情况下,程序员无需改变负载因子的值。 如果开始就知道 HashMap 会保存多个 key-value 对,可以在创建时就使用较大的初始化容量,如果 HashMap 中 Entry 的数量一直不会超过极限容量(capacity * load factor),HashMap 就无需调用 resize() 方法重新分配 table 数组,从而保证较好的性能。当然,开始就将初始容量设置太高可能会浪费空间(系统需要创建一个长度为 capacity 的 Entry 数组),因此创建 HashMap 时初始化容量设置也需要小心对待。 欢迎工作一到五年的Java工程师朋友们加入Java架构开发:468947140 群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代! 本文转自:https://blog.csdn.net/caihaijiang/article/details/6280251

优秀的个人博客,低调大师

系统架构师考试需求大纲

1. 考试目标 考试合格人员应能够根据系统需求规格说明书,结合应用领域和技术发展的实际情况,考虑有关约束条件,设计正确、合理的软件架构,确保系统架构具有良好的特性;能够对项目系统架构进行描述、分析、设计与评估;能够按照相关标准编写相应的设计文档;能够与系统分析师、项目管理师相互协作、配合工作;具有高级工程师的实际工作能力和业务水平。 2. 考试要求 掌握计算机硬软件与网络的基础知识; 熟悉信息系统开发过程; 理解信息系统开发标准、常用信息技术标准; 熟悉主流的中间件和应用服务器平台; 掌握软件系统建模、系统架构设计基本技术; 熟练掌握信息安全技术、安全策略、安全管理知识; 了解信息化、信息技术有关法律、法规的基础知识; 了解用户的行业特点,并根据行业特点架构合适的系统设计; 掌握应用的数学基础知识 熟练阅读和正确理解相关领域的英文文献; 3. 考试范围 3.1 考试科目一:系统信息综合知识 3.1.1 计算机软件与网络基础知识 操作系统 操作系统的类型和结构 操作系统基本原理 网络操作系统及网络管理 嵌入式操作系统与实时操作系统 数据库系统 数据库管理系统的类型、结构和性能评价 常用的关系型数据库管理系统 数据库模式 数据库规范化 分布式数据库系统,并行数据库系统 数据仓库与数据挖掘技术 数据库工程 备份恢复 嵌入式系统 嵌入式系统的特点 嵌入式系统的硬件组成与设计 嵌入式系统应用软件及开发平台 嵌入式系统网络 嵌入式系统数据库 数据通信与计算机网络 数据通信的基本知识 开放系统互连参考模型 常用的协议标准 网络互连与常用网络设备 计算机网络的分类与应用 多媒体 多媒体的类型、特点及数据格式 多媒体数据的压缩编码 系统配置与性能评价 多层结构、分布式系统 系统配置方法(双份、双重、热备份、容错、集群) 性能计算(响应时间、吞吐量、TAT) 性能设计(系统调整、Amdahl解决方案、响应特性、负载均衡) 性能指标(SPEC-Int、SPEC-Fp、TPC、Gibsonmix、响应时间) 性能评估 3.1.2 信息化基础知识 信息系统工程总体规划 总体规划目标、范围 总体规划的方法论 信息系统的组成 信息系统的实现 政府信息化与电子政务 电子政务的概念、内容和技术形式 中国政府信息化的策略和历程 电子政务建设的过程模式和技术模式 企业信息化与电子商务 企业信息化的概念、目的、规划、方法 ERP的主要模块和主要算法 企业业务流程重组(BPR) CRM、PDM在企业的应用 知识管理 企业应用集成 全程供应链管理的思想 商业智能 电子商务的类型、标准 信息资源管理 国际和国内有关信息化的标准、法律和规定 3.1.3 系统开发基础知识 开发管理 项目的范围、时间、成本 文档管理工作、配置管理 软件开发的质量与风险 软件的运行与评价 需求管理 需求变更 需求跟踪 需求变更风险管理 软件开发方法 软件开发生命周期 软件开发模型(瀑布模型、演化模型、增量模型、螺旋模型、原型,构件组装模型、RUP,敏捷方法) 构件与软件重用 逆向工程 形式化方法 软件开发环境与工具 集成开发环境 开发工具(建模工具、分析设计工具、编程工具、测试工具、项目管理工具等) 设计方法 分析设计图示(DFD、ERD、UML、流程图、NS图、PAD) 结构化分析与设计 模块设计 面向对象的分析与设计 I/O设计、人机界面设计 设计模式 基于构件的开发 构件的概念与分类 中间件技术 典型应用架构(J2EE、.NET) 应用系统构建 应用系统设计与开发(分析与设计方法的使用、外部设计、内部设计、程序设计、测试) 软件包的使用(开发工具、运行管理工具、业务处理工具、ERP、群件、OA工具) 测试与评审 测试评审方法 验证与确认(V&V) 测试自动化 测试设计和管理方法 3.1.4 软件架构基础知识 软件架构的概念 软件架构的风格 特定领域软件架构 基于架构的软件开发方法 软件架构评估 软件产品线 设计模式 3.1.5 安全性与可靠性技术 信息安全与保密 加密和解密 身份认证(数字签名、密钥、口令) 访问控制 安全保密管理(防泄漏、数字水印) 安全协议(SSL、PGP、IPSec) 系统备份与恢复 防治病毒 系统可靠性 可靠性设计(容错技术、避错技术) 可靠性指标与评估 安全性规章与保护私有信息规则 信息系统安全法规与制度 计算机防病毒制度 保护私有信息规则 3.1.6 标准化与知识产权 标准化意识,标准化的发展,标准的的生命周期 国际标准、美国标准、国家标准、行业标准、地方标准、企业标准 代码标准、文件格式标准、安全标准、软件开发规范和文档标准 标准化机构 知识产权 3.1.7 应用数据 概率统计应用 图论应用 组合分析 算法(数值算法与非数值算法)的选择与应用 运筹方法(网络计划技术、线性规划、预测、决策、库存管理、模拟) 数学建模 3.1.8 专业英语 具有高级工程师所要求的英文阅读水平 掌握本领域的英语术语 3.2 考试科目二:系统架构设计案例分析 3.2.1 系统规划 系统项目的提出与可行性分析 系统方案的制定、评价和改进 新旧系统的分析和比较 现有软件、硬件和数据资源的有效利用 3.2.2 软件架构设计 软件架构设计 XML技术 基于架构的软件开发过程 软件质量属性 架构模型(风格) 特定领域软件架构 基于架构的软件开发方法 架构评估 软件产品线 系统演化 3.2.3 设计模式 设计模式的概念 设计模式的组成 模式和软件架构 设计模式分类 设计模式的实现 3.2.4 系统设计 处理流程设计 人机界面设计 文件设计、存储设计 数据库设计 网络应用系统的设计 系统运行环境的集成与设计 中间件、应用服务器 性能设计与性能评估 系统转换计划 3.2.5 软件系统建模 系统需求 建模的作用和意义 定义问题(目标、功能、性能等)与归结模型(静态结构模型、动态行为模型、物理模型) 结构化系统建模、数据流图 面向对象系统建模 统一建模语言(UML) 数据库建模、E-R图 逆向工程 3.2.6 分布式系统设计 分布式通信协议的设计 基于对象的分布式系统设计 基于Web的分布式系统设计 基于消息和协同的分布式系统设计 异构分布式系统的互操作性设计 3.2.7 嵌入式系统设计 实时系统和嵌入式系统特征 实时任务调度和多任务设计 中断处理和异常处理 嵌入式系统开发设计 3.2.8 系统的可靠性分析与设计 系统的故障模型和可靠性模型 系统的可靠性分析和可靠度计算 提高系统可靠性的措施 系统的故障对策和系统的备份与恢复 3.2.9 系统的安全性和保密性设计 系统的访问控制技术 数据的完整性 数据与文件的加密 通信的安全性 系统的安全性设计 3.3 考试科目三:系统架构设计论文 3.3.1 系统建模 定义问题与归结模型 结构化系统建模 面向对象系统建模 数据库建模 3.3.2 软件架构设计 软件架构设计 特定领域软件架构 基于架构的软件开发方法 软件演化 3.3.3 系统设计 处理流程设计 系统人机界面设计 文件设计、存储设计 数据库设计 网络应用系统的设计 系统运行环境的集成与设计 系统性能设计 中间件、应用服务器 3.3.4 分布式系统设计 分布式通信协议的设计 基于对象的分布式系统设计 基于Web的分布式系统设计 基于消息和协同的分布式系统设计 异构分布式系统的互操作性设计 3.3.5 系统的可靠性分析与设计 系统的故障模型和可靠性模型 提高系统可靠性的措施 系统的故障对策和系统的备份与恢复 3.3.6 系统的安全性和保密性设计 系统的访问控制技术 数据的完整性 数据与文件的加密 通信的安全性 系统的安全性设计

优秀的个人博客,低调大师

数据架构师: 您要治理什么?

Robert Catterall 是一位数据库专家,他想确定实现数据治理的必要性。在 IBM Information Governance Solutions 的主管 Steven Adler 的帮助下,他探索了数据治理的原则以及它如何帮助数据库团队。 我是一位数据库工作者(更准确地说,是 DB2 工作者)。我的工作处于 IBM Information Management 软件组合的低层。如果说那些从事分析、主数据管理、数据集成和其他高层软件技术的人是数据巨轮的高级船员,他们站在舰桥上,穿着带金色肩章的白制服,用双筒望远镜了望远方,那么我是在轮机舱工作。当舰桥上传来指令,命令我加快数据流动的速度时,我回答说,“我们已经到极限了,船长!” … 但是我仍然要想办法完成任务。你们成天谈论战略和模式,而我谈论的是比特和字节、速度和供应。欢迎来到我的世界。好了,不打比方了,我们来讨论实际问题吧! 我曾经遇到过许多似是而非的概念,最近遇到的一个是 “数据治理”。对不起,长官,但是这听起来是针对某个问题的解决方案。让我告诉您一个小秘密:数据是没有自主行动力的,这意味着它不需要 “治理”。它需要操控,我和我的朋友们能够应付,谢谢您。请找别的什么东西去 “治理” 吧! 您说什么?您在谈到治理数据时,实际上是打算治理别的什么东西?OK,现在我感兴趣了。请多说点儿,我洗耳恭听。 重点在于人 关于数据治理,我应该听取谁的意见?没人比 Steve Adler 更合适了,他是 IBM Information Governance Solutions 的主管和 IBM Information Governance Council 的主席。他在电话上告诉我 “数据治理” 这个词在修辞方面的矛盾性实际上是有意义的:它提醒人们提出问题。“‘数据治理’ 究竟是什么意思?” 谈话就此展开了。Adler 指出,真正的目标是对行为进行治理。 人与数据库交互:他们引导数据流的走向;他们解释并处理数据请求。人也会犯错 — 常常是由于他们采用的过程和依赖的应用程序系统有缺陷 — 这会在信息管道中引入错误,给下游造成数据质量问题。Adler 指出,数据治理的意图主要是系统化地找到并解决可能引入错误的点。目标是形成决策者能够信任的数据 — 还要向他们提供数据可信的证明。 这是一个很不错的说词。但在加入您的事业之前,我想看看 “蓝色巨人” 是如何吃 “垃圾食品” 的。Adler 问我是否了解 IBM 的产品目录。我确实了解 — 毕竟我为 IBM 工作了 17 年。IBM 有大量信息,有多达 1.2 亿条记录。显然,这些记录中很大一部分包含错误的信息:错误、缺失和未及时发布的数据。在 255 个 IBM 产品公告中,只有 5 个完全没有错误。这真是让人震撼的统计数字。 为了找出数据质量问题的来源,团队在通向生产数据库的数据流中不同的位置上设置 “捕捉器”。捕捉器帮助团队发现在特定条件下可能发生的错误。找出问题的原因之后,团队就可以设计和实现基于过程和基于技术的解决方案,从而消除不准确的信息的来源。 不是我的问题(也许是?) 我想,只要消除可能导致数据质量问题的条件就行了,所以由您负责。我希望您成功。不需要围着我和 DBA 照管的数据库打转 — 它们很可靠。有多可靠?精确度至少有 99.9%。我谈论的数据库有多少数据记录?生产数据库可能有超过 10 亿条记录。是的,由于数据库规模如此之大,尽管错误率非常低,错误数量仍然相当大。好吧,您说的有道理。在数据库内部和周围放上一些捕捉器可能是好主意。 Adler 说,但是不要就此止步,因为数据质量问题不仅仅是数据记录不准确。有时候,急迫的问题是必须处理数据分类。例如,一个组织通过并购形成了更大的规模,最终通过多个业务线为更大型的企业客户提供服务。客户公司之一抱怨说,当他们通过不同业务线的代表向服务提供组织提出同一个问题时,却得到了不同的回答。这是因为对于不同的业务线,相同术语的含义不同。这是数据定义问题 — 数据治理要通过有效的主数据管理 (MDM) 消除此类问题。 如何从 A 到 B? 我承认,我开始看出数据治理的一些价值了。这个概念不像我最初认为的那么不知所谓。但是,看到潜在价值和获得实际价值是两回事儿。如何通过数据治理工作获取价值?从哪里做起?如何推动不断进步? Adler 告诉我他喜欢的方式分为六个步骤: 确定目标。一些目标可以有变动,其他目标是固定的(例如处理数据质量问题)。是的 — 在出发之前,必须明确要去哪里。 确定要度量什么。如果想要提高数据质量,那么如何能够知道是否已经取得了进步?可能要检查存储库中一定比例的文档,记录信息不正确或缺失的情况。决定度量的过程和条件对于评估基线情况和跟踪过程很重要。我明白了。说说容易:如果想要说服人,就要提供数字。 了解组织的决策模型。是专制模型?代表模型?还是民主模型?无论是哪种,它适合您的公司吗?您正在开发的数据治理策略支持这种决策模型吗?决策 “更好” 的含义是什么?需要做出大量决策吗?还是要更快地做出决策?我想,在改进数据质量时,总是要花时间评估数据在自己的环境中如何驱动决策。可能会发现需要改进的东西。 有效地传播数据治理策略。如何把策略的相关信息告诉相关人员和感兴趣的其他各方?通过电子邮件?通过时事通讯?不能只依靠同事之间的口头交流。 度量结果。数据治理策略取得的实际效果如何?如果已经制定了度量进展的计划(这个列表中的第二项),那么得到具体的数字应该不太困难。解释这些数字会很有意思。 审查整个数据治理工作。是否采用了适当的过程?是否应用了适当的技术?是否实现了有效的控制?“审查” 这个词有点儿让我不舒服,但是我知道对于策略来说这是不可缺少的,无论是数据治理策略还是其他策略。我不喜欢评判别人的工作,但是我理解有时候需要这么做。 这些是非常高级的检查项,具体的项目计划应该远远不只六个步骤。但是,在面对复杂的任务时,以适当方式对难题进行拆分确实有助于明确工作重点。对于我来说,Adler 的方法看起来是不错的问题拆分方式。可以以此为基础开展工作。 好吧,我接受了 一个城市的居民嘲笑另一个城市时会说,“那儿没有 ‘那儿’ 这个概念”。我过去也是这么看待数据治理的:很有意思的概念,但是拜托 — 它有什么实际意义吗?Steve Adler 在关于数据治理的业务价值的争论中说服了我。我现在认为数据治理是有价值的。 因此,如果信息管理领域的大人物再到数据库 “轮机舱” 与您谈论数据治理,那么好好听他们说。最好考虑一下自己能够在哪些方面发挥作用。宏大的计划如果能够参考实干家的意见,往往会取得更好的结果。 就到这里吧。也许我会在咖啡机旁遇到您。谁知道呢?也许我们会轻松地谈论一下数据治理。 本文作者:佚名 来源:51CTO

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册