1.2 万变不离其宗

对于系统程序开发者来说,计算机多如牛毛的硬件设备中,有三个部件最为关键,它们分别是中央处理器CPU、内存和I/O控制芯片,这三个部件几乎就是计算机的核心了;对于普通应用程序开发者来说,他们似乎除了要关心CPU以外,其他的硬件细节基本不用关心,对于一些高级平台的开发者来说,连CPU都不需要关心,因为这些平台为特卖提供了一个通用的抽象的计算机。

早期的计算机硬件都是直接连接在同一个总线上的,由于I/O设备的速度比CPU和内存慢很多,为了协调I/O设备和总线之间的速度,一般每个设备都会有一个相应的总线控制器。

后来由于CPU核心频率的提升,导致内存跟不上CPU的速度,于是产生了与内存频率一致的系统总线,而CPU采用倍频的方式与系统总线进行通信。随着图形化的普及,特别是3D游戏和多媒体的发展,对I/O总线的速度要求更高,为了协调CPU、内存和高速的图形设备,人们专门设计了一个高速的北桥芯片,以便它们之间能够高速地交换数据。

北桥芯片运行速度高,是给高速设备使用的,为了连接低速设备,人们又设计了专门处理低速设备的南桥芯片。20世纪90年代的PC机在系统总线上采用的是PCI结构,而在低速设备上采用的ISA总线。

SMP与多核

在过去50年里,CPU的频率不断提高,从最初的几十kHz到现在的4GHz,基本上每18个月就会翻倍。但是在2004年之后就没有发生质的提高,原因是制造CPU的工艺已经达到了物理极限。

人们开始从另外一个角度提高CPU的速度,就是增加CPU的数量。其中最常见的一种形式就是对称多处理器(SMP,Symmetrical Multi-Processing)。

在个人电脑中使用多处理器是比较奢侈的行为,于是处理器厂商开始考虑将多个处理器“合并在一起打包出售”,这些处理器之间共享比较昂贵的缓存部件,只保留多个核心,这就是多核处理器(Multi-core Processor)。简单地讲,除非想把CPU的每一滴油水都榨干,否则可以把多核和SMP看成同一个概念。

不太好理解的死锁

这几天做压力测试,数据库是ORACLE的,报了死锁的错误,错误信息是ORA-00060等待资源时检测到死锁。

分析了下程序的日志,大致情况是这样的,第一个线程成功锁住了两条记录(1,2),第二个线程需要锁四条记录(1,2,3,4),因为前面两条已经被第一个线程锁住,所以第二个线程进入等待,第三个线程需要锁两条记录(1,2),也进入等待,第四个线程需要锁两条记录(3,4),并且加锁成功。这里锁数据都是在事务内用SELECT * FROM TABLE_NAME WHERE CONDITION FOR UPDAT,也就是说都是TX锁。当第一个线程提交事务后,第二个线程就报了死锁的错误。

我们都知道,发生死锁的条件是两个线程各自锁住了另外一个线程需要的资源并且等待另外一个线程释放资源。而上面第二个线程之前一直都是等待的状态,所以不是很好理解。经过对trc文件的分析,得到的结论是,对于1,2两条记录,第二个线程和第三个线程,虽然sql都没有执行成功,但是各自锁了其中一条记录,并且等待另外一个线程释放另一条记录,从而导致了死锁的发生。

咨询了一个做DBA的朋友,这个可能和表的索引有关系,一个线程使用了主键索引,而另外一个线程则使用了普通的索引,经过对索引的调整,后面暂时没有报错,但是还是感觉比较奇怪,为什么不是同一批数据同时锁住。这个错误不是必发的,但概率还是比较高的。虽然索引经过了调整,但主键索引和普通索引还是都存在的,也就是说,只是降低了死锁发生的概率。

不同的DB2版本差异

最近工作中需要写存储过程,包括ORACLE和DB2两个版本的数据库,ORACLE没什么问题,但DB2写出来的脚本在不同的数据库版本下表现有差异,脚本大概是这样的:

CREATE PROCEDURE GETNEWFLOWID(IN BkkpgOrgId VARCHAR(30), IN EntryDate VARCHAR(30), OUT FlowId VARCHAR(30))
BEGIN
SELECT NEXTVAL FOR S_TP_BOOKKEEPING_FLOW AS NEXTVAL INTO FlowId FROM SYSIBM.SYSDUMMY1;
SET FlowId = REPEAT(‘0’, 18 – LENGTH(FlowId)) || FlowId;
END;

这个脚本在DB2 9.7版本可以执行,但是在9.5上就会报错,类型转换的错误,修改脚本如下:

CREATE PROCEDURE GETNEWFLOWID(IN BkkpgOrgId VARCHAR(30), IN EntryDate VARCHAR(30), OUT FlowId VARCHAR(30))
BEGIN
SELECT CHAR(NEXTVAL FOR S_TP_BOOKKEEPING_FLOW) AS NEXTVAL INTO FlowId FROM SYSIBM.SYSDUMMY1;
SET FlowId = REPEAT(‘0’, 18 – LENGTH(FlowId)) || FlowId;
END;

修改后的脚本在9.5版本上可以执行,但是出来的字符串只有14位,在9.7版本上会报错,我把SELECT之后的FlowId用LENGTH获取长度,发现9.5版本上是11,而9.7版本上是30,所以9.7版本报错的原因就是因为REPEAT函数的第二个参数已经减为负数了。但是为什么9.5版本上字符串只有14位,现在还不知道,然后两个版本LENGTH返回的长度不同也还没搞清楚。最后我们把FlowId去空格,两个版本上的表现就一样了,出来的字符串有18位,最终代码如下:

CREATE PROCEDURE GETNEWFLOWID(IN BkkpgOrgId VARCHAR(30), IN EntryDate VARCHAR(30), OUT FlowId VARCHAR(30))
BEGIN
SELECT CHAR(NEXTVAL FOR S_TP_BOOKKEEPING_FLOW) AS NEXTVAL INTO FlowId FROM SYSIBM.SYSDUMMY1;
SET FlowId = trim(FlowId);
SET FlowId = REPEAT(‘0’, 18 – LENGTH(FlowId)) || FlowId;
END;

1.1 从Hello World说起

我们学习一种编程语言的时候,一般都是从Hello World开始的。这个程序看起来很简单,但是简单的事物背后往往又蕴涵着复杂的机制,比如C语言编写的Hello World程序:

#include <stdio.h>

int main()
{
printf(“Hello World\n”);
return 0;
}

一些问题:

程序为什么要被编译器编译了之后才可以运行?

编译器在把C语言程序转换成可执行的机器码的过程中做了什么,怎么做的?

最后编译出来的可执行文件里面是什么?除了机器码还有什么?它们怎么存放的?怎么组织的?

#<stdio.h>是什么意思?把stdio.h包含进来意味着什么?C语言库又是什么?它怎么实现的?

不同的编译器(VC、GCC)和不同的硬件平台(x86、ARM)以及不同的操作系统(Windows、Linux),最终编译出来的结果一样吗?为什么?

Hello World程序是怎么运行起来的?操作系统是怎么装载它的?它从哪开始执行,到哪儿结束?main函数之前发生了什么?main函数结束以后又发生了什么?

如果没有操作系统,Hello World程序可以运行吗?如果要在一台没有操作系统的机器上运行Hello World需要什么?应该怎么实现?

printf是怎么实现的?它为什么可以有不定数量的参数?为什么它能够在终端上输出字符串?

Hello World程序在运行时,它在内存中是什么样子的?

设置字段是否可以为空

设置字段非空

在设置之前首先要确保该字段存在数据。

Oracle:

ALTER TABLE TABLE_NAME MODIFY FIELD_NAME NOT NULL;

DB2:

ALTER TABLE TABLE_NAME ALTER COLUMN FIELD_NAME SET NOT NULL;

设置字段可以为空

Oracle:

ALTER TABLE TABLE_NAME MODIFY FIELD_NAME NULL;

DB2:

ALTER TABLE TABLE_NAME ALTER COLUMN FIELD_NAME DROP NOT NULL;

设置字段默认值

设置默认值为空,即没有默认值

Oracle:

ALTER TABLE TABLE_NAME MODIFY FILED_NAME DEFAULT NULL;

DB2:

ALTER TABLE TABLE_NAME ALTER COLUMN FILED_NAME DROP DEFAULT;

设置一个默认值

Oracle:

ALTER TABLE TABLE_NAME MODIFY FILED_NAME DEFAULT VALUE;

DB2:

ALTER TABLE TABLE_NAME ALTER COLUMN FILED_NAME SET DEFAULT 0;

删除主键约束同时删除同名的索引

删除主键约束的同时是否会删除同名的索引,取决于该同名的索引是创建主键约束时自动创建的,还是创建主键约束前手动创建的。

当我们用如下语句创建主键约束时,会自动创建同名的索引:

ALTER TABLE TABLE_NAME ADD CONSTRAINT CONSTRAINT_NAME PRIMARY KEY (FIELD1, FIELD2, FIELD3);

这个索引在主键约束被删除时会自动被删除:

ALTER TABLE TABLE_NAME DROP CONSTRAINT CONSTRAINT_NAME;

如果换一种方式:

1. 先创建主键索引

CREATE UNIQUE INDEX CONSTRAINT_NAME ON TABLE_NAME (FIELD1, FIELD2, FIELD3);

2. 再创建主键约束

ALTER TABLE TABLE_NAME ADD CONSTRAINT CONSTRAINT_NAME PRIMARY KEY (FIELD1, FIELD2, FIELD3);

这时,再通过

ALTER TABLE TABLE_NAME DROP CONSTRAINT CONSTRAINT_NAME;

无法删除索引,只能删除主键约束。经测试,这个在Oracle和DB2下面表现是一致的。

在Oracle数据库下,可以通过如下语句把主键约束和索引一起删除:

ALTER TABLE TABLE_NAME DROP CONSTRAINT CONSTRAINT_NAME CASCADE DROP INDEX;

DB2下暂时不知道用什么简单的语句,用存储过程是可以实现的。