3.1 关于Backup Server

 什么是Backup Server?
Backup Server是一个基于Open Server的工具。它可以处理SQLServer10。0以及更高版本的数据Dump/Load。因为它是独立于ASE的处理过程,完成它的工作不依赖于ASE。因此,在联机数据库Dump/Load时,不会降低ASE的性能。
 
网络环境下的Backup Server
Backup Server必须与ASE放在同一台机器上(如果在Open VMS上,可以在同一簇内)。你也可以如下图所示的环境,使用分布在网络上的两个Backup Server来完成Dump/Load。这两个Backup Server一个与ASE放在同一台机器上另一个可在远程某一节点上。
 
上图的配置,可以将本地(Local)的数据库Dump到远程(Remote)节点的外部设备上,或者从Remote节点读取Dump,将数据库Load到本地的ASE。

当将本地数据库Dump到远程机器的设备时,由Local Backup Server读取数据库数据,再送到网络另一端的Remote Backup Server,经它将数据写到外部设备上。同样,从远程设备Load时,load Backup Server发出命令给Remote Backup Server,Remote Backup Server读取Dump设备内容并传送到Local Backup Server,由Load Backup Server将读到的数据写到数据库设备。网络上的Dump性能仅受网络传输能力的影响。

Backup Server新的特性,提高了系统Dump/Load的能力
? 能自动测出设备类型和密度;
? 支持多个SYBASE Dump到同一设备卷;
? 支持一个SYBASE Dump到多个设备卷。

Backup Server支持并行Dump设备
Backup Server支持最大32个Backup设备并行地Dump/Load。亦即可以将一个数据库分成碎片Dump到多个设备上。ASE并行Dump/Load的设备数受操作系统能打开的最大文件数和共享内存资源的限制。因此,各种不同硬件平台,不同的操作系统下,并行Dump/Load设备数不同。
多文件(Multi-file),多卷(Multi-volumn)Dump
? 操作系统文件和Raw Partifion是Single_volumn,Single-file的介质,这就是说这两种设备只能含有一个SYBASE Dump。
? QIC磁带和可移动硬盘是Single-file,,Multi-Volumn介质,意味着一个Dump可以跨越多个卷。这些设备不允许叠加,如果你想在同一个设备卷上追加存放多个Dump,系统将提示是否覆盖已有的内容。
? DAT,8mm,9-Track磁带是Multi-file,multi-volumn设备。以上三种设备可以包含一个或多个数据库Dump。设备卷上的最后一个Dump可以跨过磁带,继续存放在另一设备带上。
Backup Server在网络上的Server名称
  网络环境中的所有Server必须在Interfaces文件中有一个注册信息项,包括Server的名字,在网络中的网络节点地址及SYBASE Server用的网络端口号Backup Server也必须将它的信息放在Interfaces文件中。可以用Sybinit来完成这个工作。并且Backup Server的名字也必须加在master数据库的Sysservers表中。如果你还有一个Remote Backup Server,也需将它的信息放在本地的Interfaces文件中。
  Backup Server项在Interfaces文件和Sysservers表中必须有准确的名字。如果在启动Backup Server时用-s选项指定了Backup Server的名字,那么在整个网络中都要用这个名字来标识这个Backup Server。换言之,在Interfaces文件中,不能使用别名来标识某一个Backup Server。
Backup Server使用的设备
  Backup Server必须使用不回卷(No Rewind)设备,以便Backup Server能够控制磁带设备的位置。
  下表列出了某些硬件平台上的Dump设备名称。其中N代表了设备号。

Platform Type Device Name
 HP9000 4mm(SCSI and /HPIB) /dev/rmt/Num
  9 track (/SCSI and /HPIB) /dev/rmt/Nmn
 NCR 8mm /SCSI (5 GB and 2.2 GB) dev/nrmtN
  4mm/SCSI dev/nrmtN
 RS6000 9 track /SCSI /dev/rmtN.1
  8mm DAT /SCSI (5 GB and 2.2 GB) /dev/rmtN.1
  QIC /SCSI (1/4?cartridge /dev/rmtN.1
 Sun4 9 track /SCSI / dev /nrmtN
  QIC /SCSI (1/4?cartridge) / dev / nrarN
  8mm /SCSI (5 GB and 2.2 GB) / dev / nrstN
 Sun4 SVR4
 (Solaris) 9 track See un4 SVR4?on page
 4-10
  1/4 cartridge 
  8mm /SCSI (5 GB and 2..2 GB 
 VAX VMS 9 track (HSC and /DSSI) See pen VMS?on page
 4-10
  8mm (/HSC 5 GB and 2 GB and /DSSI) 
  TK50 (HSC and /DSSI) 
 AXP VMS 8mm / SCSI See pen VMS?on page
 4-10
  9 track 
  4mm DAT /SCSI 
 AXP OSF/1 4mm DAT /SCSI See XP OSF/1?on page
 4-10
  TZ851 
   Table 4-1:Device names for database dumps to tape


3.2 Backup Server的名字
当使用Backup Server做Dump/Load时,系统会自动地寻找SYB_BACKUP这样的Backup Server名字。这就要求在master库的sysservers表中必须有一条记录来描述这个Server,其srvname为SYB_BACKUP。在安装Backup Server时,如果使用缺省值,那么安装过程将自动生成一个名字为SYB_BACKUP的Backup Server,同时完成这些操作。
1.master库的sysservers表中增加一条记录如下
srvid srvstatus srvname srvnetname
n 0 SYB_BACKUP SYB_BACKUP
2.$SYBASE/interfaces文件中增加一项,servername为SYB_BACKUP,有它自己的网络地址和网络端口号。
3.在$SYBASE/install目录下,创建启动Backup Server名为RUN_SYB_BACKUP的文件。
文件中启动Backup Server命令的-S选项指明BackupServer在网上的名字为SYB_BACKUP。

在做Dump/Load时,系统自动地找SYB_BACKUP作为BackupServer。再根据(1)中指出的srvnetname到interfaces文件中找到server。但是,如果在安装时未使用SYB_BACKUP作为Backup Server名字。那么安装仅完成以上(2),(3)的操作。例如,你使用MY_BACKUP作为Backup Server名,则$SYBASE/interfaces中Server名为MY_BACKUP,启动Backup Server命令的-S选项参数为MY_BACKUP。完成(1)的操作,则必须依靠手工完成,在ISQL中,用sa帐号登录。
1>sp_addserver SYB_BACKUP, null, MY_BACKUP
2>go 
  其中SYB_BACKUP为固定参数,MY_BACKUP是你指定的网络上Backup Server的名字。
3.3 Sybase的数据库设备
数据库设备是指用于存取数据库的存储介质。它既可以是磁盘的一个分区,也可以是文件系统中的一个文件。但无论用哪种作数据库设备,它们在能被用来存取数据库对象之前必须初始化。一旦数据库设备被初始化,该设备就能:
? 为某个用户数据库分配一块可用空间
? 在为某用户数据库分配的空间内指定并存储某个特定的数据库对象
? 用于存储某数据库的事务日志
选择磁盘分区还是unix文件来作数据库设备应视具体情况而定。一般来说,如果是最终用户的数据库,建议您用磁盘化区而非文件。因为对磁盘文件,ASE不能确切地知道数据是否真正被写到文件中去。如果是开发人员的数据库,那么用磁盘文件要稍好些,因为它使得建在其上的数据库恢复起来要容易一些。但对于master device来讲,无论是最终用户还是开发人员的数据库,磁盘文件无疑更为合适。因为相对而言,对master device的修改更少些而且镜像更为方便简单些。

如果在磁盘分区作数据库设备,应避免使用柱面0,C,swap分区以及其它已被OS占用的分区。如果选择磁盘文件作数据库设备,要保证SYBASE用户在相关的目录下有写权限。

命令diskinit命令对数据库设备进行初始化,使得物理上的硬盘分区或文件可以被ASE使用,同时把逻辑设备映射到物理设备上,而每一次disk init执行后,都会在sysdevices系统表中增加一行。其映射关系如图:
 
disk init的语法:

disk init
 name = "device-name",
 phyname = "physical-name",
 vdevno = virtual - device - number ,
 size = number - of - pages


其中:
name: 逻辑设备名
physname: 硬盘分区或磁盘文件名
vdevno: 虚拟设备号。它们取值可以是从1到max configured -1未被使用的任何值。可以用sp_helpdevice或从errorlog文件中查看device。用sp_configure来看max configured的值。最大取值为255。一般来说,the master device的vdevno 总为0,如果系统缺省设置最大设备数为10,而您希望使用更多的逻辑设备,可以重新设置备数限制。
在OS下打入:
这是sql 11.0.X 以下版本的配置
isql -Uxx -Pxx

1) sp_configure " device", 20 
 2) reconfigure with override 
 3) go

这个新的配置值必须在Server重新启动后才生效。若一个数据库设备被删除掉后,其vdevno只有在重新启动Server后才能被新的设备所用。
3.4 数据库选项(dboptions)的使用与管理
一、概述
ASE能管理多个数据库,每个数据库有自己的选项。ASE允许用户通过设置数据库选项,来更好地管理和使用数据库。当大批数据需要加载到数据库的表时,如果把该库的Select into/bulkcopy选项设置为true,同时暂时删掉表上的索引和触发器,再使用系统提供的实用程序bcp,数据将以惊人的高速度被装载到用户表里。也曾有用户抱怨,调用dbwritetext函数不能成功地把一幅bmp图写入数据库的image字段中。原因是用户数据库的select into/bulkcopy选项没有设置成true。还有正在开发数据库应用的用户希望日常工作不受数据库日志满的不断打扰。这也可以通过设置数据库选项办到。因此,我们这里将数据库选项的设置和用法详细介绍给读者,希望它有助于你有效地使用和管理用户数据库。

二、数据库选项的设置
使用系统存贮过程sp_dboption来设置数据库选项,它的语法规则如下:
sp_dboption [dbname,optname,{true┃false}] 
其中:
? dbname为用户所需设置选项的数据库名称。如果执行带参数的sp_dboption,则当前使用的库必须是master。不带参数便显示数据库选项清单。但是,用户不能设置master库的数据库选项。
? optname为用户所要设置或关闭的选项名称。ASE能识别选项名的任何唯一的字符串。当其名称为关键字或含嵌入空格或标点符号时,用引号括起来。
? {true┃false}——设置该选项时为true,关闭该选项时为false。

使用sp_dboption,能设置以下数据库选项:
? abort tran on log is full 
? allow null by default 
? dbo use only
? ddl in tran
? disable free space acctg 
? no chkpt on recovery 
? read only 
? select into/bulkcopy 
? single user 
? trunc log on chkpt
查看数据库的选项设置情况,使用sp_helpdb存贮过程。
三、数据库选项的用途

(1)Selectinto/bulkcopy
使用bcp或批拷贝程序接口将数据高速拷贝到一张没有索引和触发器的表,Writetext或者Select into生成永久表,则此选项必须设置为true。因此,当你使用bcp实用程序往用户数据库加载数据时,或者调用dbwretetext函数把一幅图片插入到某张表的image字段,或者通过Select into命令形成结果表时,别忘了把该选项置为true。ASE为了快速执行上述操作,减少记录修改操作的日志。但是,这种不记日志的操作会使事务日志与数据库中数据不同步。这种事务日志对于系统失败后的恢复就没有用处。系统禁止在这种情况下,dump transation到转储设备。因此,这些不记日志记录的操作完成后,将Select into/bulkcopy选项关闭后,执行dump database。

由Select into或批拷贝对数据库产生未被日志记录的修改后,执行dump transaction命令时,显示错误信息,并指导用户改用dump database(而Wrrietext命令没有这种保护)。

由于临时库tempdb从不恢复,所以使用Select into产生临时表时不必将Selectinto/bulkcopy选项打开。对于有索引的表进行bcp时,一般进行慢速批拷贝并且记入日志,所以此时也不必将Select into/bulkcopy设置为true。

(2)trunc log on chkpt
该选项表示每当checkpoint检查进程执行时(通常每分钟12次以上),事务日志被载截,即将已提交的事务日志删除。但是,该选项设置成true以后,人工执行checkpoint操作时都不载截事务日志。在开发数据库应用时,防止日志增长太快.可以将该选项设置成true,所以它很有用。

(3)abort tran on log full 
该选项决定当指定数据库日志段最后机会阀值被超越时,正在运行的事务的处理办法。缺省值为false,也就是说事务被暂停挂起,直到空间空余出来再恢复。本选项设置为true时,在日志存储空间空余出来之前,所有用户的需要写事务日志的查询将被去掉。

(4)allow nulls by default
该选项置为true,将列的缺省状态从not null改为null,这与ANSI标准一致。T-SQL中列的缺省状态为not null,即不允许列为空值。但用户可在列定义时指定该列为null状态。allow nulls by default true与缺省状态相反。

(5)dbo use only 
该选项设置为true时,只有数据库拥有者才能使用该数据库。

(6)ddl in tran
该选项设置为true时,用户在事务中可以使用某些数据库定义语言的命令,例如:Create table,grant和alter table命令。?只有ddl in tran选项设置为true时,下述命令才可在用户自定义事务内部使用:
alter table create table drop rule create default create trgger drop table create index create view drop trigger create procedure ceate default drop view create rule drop index grant create Schera drop procedure revoke 
下述命令在任何情形下都不能在用户自定义事务内部使用。
alter database load database truncate table create database load transation update statistics disk init recorfigure drop database select into
另外,系统过程sp_helpdb,sp_helpdevice,sp_helpindex,sp_helpjoins,sp_helpserver和sp_spaceused,由于在执行时产生临时表,也不能在用户自定义事务中使用。再者,用户自定义事务中不能使用修改master数据库的系统过程。
 
(7)no free space acctg
该选项抑制剩余空间计帐及非日志段阀值活动的执行,由于不再计算这些字段的剩余空间,所以将加速恢复时间。关于该选项,在阀值管理部分,已有详细介绍。

(8)no chkpt on recovery
当保持最新的数据库备份时,此项设置为true。此时,存在一个基本库和一个辅助库。首先,基本库转储到辅助库中。然后,基本库的事务日志间断地转储并装载到辅助库中

若此选项设置为缺省值false,重新启动ASE时,恢复数据库后将向数据库增加一条检查点记录。检查点记录确保机制不会不必要地重新运行,改变顺序号,并导致随后从基本库装载事务日志的失败。

将辅助库的此项设置打开,恢复过程将不产生检查点,这样从基本库转储的事务日志可以被装载至辅助库。
 
(9)read only
该选项使得用户可以从数据库中检索数据,但不能修改数据。

(10)single user
该选项设置为true时,每次只能有一个用户访问该数据库。
3.5 Index & Performance
在应用系统中,尤其在联机事务处理系统中,对数据检索及处理速度已成为衡量应用系统成败标准。而采用索引来加快数据处理速度也成为广大数据库用户所接受的优化方法。
索引的使用效果不仅仅依赖于ASE的优化策略,在相当程度上也依赖于应用程序的设。怎样正确地使用索引,不能一概而论,究竟是让索引满足程序设计的需要,或是程序设计遵循已建立的索引,这两者是相符相承的。只有正确地使索引与程序结合起来,才能产生最佳的优化方案。
建立索引的目地是为了优化检索速度,如果检索所需要的时间过长,便有理由怀疑是否索引不存在或者优化器没有使用索引。尚若是索引不存在,那么就要取决于用户是否愿意用空间来换取时间,使用索引来解决检索速度慢的问题。如果优化器未使用表上已有的索引,那么要分析为什么,关于这一点将在后一点篇幅来说明如果update的效率很低,很可能是由于表上有太多的索引需要维护,从而浪费了时间。

优化器怎样使用索引
 
Table scan
如果表上没有任何索引,那么检索将采用Table Scan方式进行,其所用时间主要依赖于表的大小。
例如:
- dbcc checktable 测出表占76923页
-系统每秒读取50页
-76923页/50页/秒=1538秒 (大约25分钟)
如果系统有比较大的cache,某些数据可能由于以前已被读到内存,那么读取数据时间可能会低于估计的时间。一般情况下,Tablescan检索是由于表上没有ClusteredIndex或者优化器认为,表中将有20%的数据做为结果追回。

   使用索引(条件为指定值)
索引中包含指定记录的值及地址,ASE不必做全表扫描。
例: select * from title where title_id="mc3021" 
 

当优化器认为读取索引页I/O加读取数据页I/O比做Table Scan效果更好时,检索将使用索引。

   使用索引(条件为某范围内值) 例:
select * from titles
where title_id >"BU1032"
and title-id <"mc3032" 
 
如果数据是排序的(有Clustered索引),索引将被用来限制数据的扫描范围。
 使用索引避免检索排序所需要的时间。
例:
select * from titles
order by title_id 
 
对Clustered索引来说,如果索引顺序与Server顺序一致,那么上面的查询不需要重新排列返回结果。但是,若数据存储本身是升序排列,而查询要求降序排列,那么索引对加快查询没有任何作用。

对于Non-Clustered索引,优化器将判断查询Non_Clustered索引页,找到满足条件的数据进行排序是否比Table Scan更快,优化器将找出最佳结果。从以上几例可以看出,并非在表上建立了Clustered或on-Clustered索引之后,就一定会被使用,优化器是否使用索引取决于数据的查询命令,ASE将从几个检索方案中选择最佳的一个。

在什么样的条件下才选择Clustered索引呢?
选择什么样的索引基于用户对数据的检索条件,这些条件体现于where从句和join表达式。如果你的应用与以下情况相符,你可以考虑选择Clustered索引。
? 主键时常作为where子句的条件
? 某一列经常以这样的格式出现在where表达式中(x<=column <="y)" 
? 某一列非常频繁地被访问
? 某列被用作order by或group by
? 某列很少被改写
? 某列常出现在join中。

Non-Clustered常被用在以下情况:
? 某列常用于Aggregate函数(如Sum,....)
? 某列常用于join,order by,group by。
? 查询检索出的数据不超过表中数据量的20%

索引覆盖
索引覆盖是指Non_Clustered索引项中包含查询所需要的全部信息。这种索引之所以比较快也正是因为索引页中包含了查询所必须的数据,不需去访问数据页。如果Non-Clustered索引中包含结果数据,那么它的检索速度将快于Clustered索引。覆盖索引的缺点:由于索引项比较多,要占用比较大的空间。而且update操作会引起索引值改变。

ASE对索引的限制
? 每个表上最多仅能有一个Clustered索引。
? 如果表上有一个Clustered索引,最多还能有249 Non-Clustered索引。
? 当没有Clustered索引时,则可有250个Non-Clustered索引。
? 索引最多建立在256个列上。
? 当索引被创建时,ASE需要120%的附加空间。

索引维护
随着应用系统的运行,数据不断地发生变化,当数据变化达到某一个程度时将会影响到索引的使用。上面讲到,某些不合适的索引影响到ASE的性能,这时需要用户自己来维护索引。一种方法是删除老的索引,重新建新的索引。另外一种方法是保持索引统计有效(使用命令update statistics),在以下情况下需要重新索引。
? 使用数据模式发生了较大变化。
? 某段时间内有极大量的数据插入。
? ASE排序改变。
? dbcc发现索引错误。

当重建Clustered索引时,这张表的所有Non-Clustered索引将被重建。

维护索引统计表:
数据库拥有者必须用命令维护统计表。 update statistics table_name [index_name] 

索引优化调整
用这条命令可以改善创建索引的性能,减少建索引所用的时间。 在ASE中可以调整如下参数: sp_configure "extent i/o buffers",nnnn带来的影响是增加了extent i/o buffers大小,在ASE使用内存不变情况下,减少了procedure和data cache,而且同一时刻仅有一个用户能用到extent buffer。
3.6 SYBASE ASE 事务日志
SYBASE ASE的每一个数据库,无论是系统数据库(master,model, sybsystemprocs, tempdb),还是用户数据库,都有自己的transaction log,每个库都有syslogs表。Log记录用户对数据库修改的操作,所以如果不用命令清除, log会一直增长直至占满空间。清除log可用dump transaction 命令;或者开放数据库选项trunc log on chkpt,数据库会每隔一段间隔自动清除log。管理好数据库log是用户操作数据库必须考虑的一面。

下面就几个方面谈谈log及其管理:

一、ASE 如何记录及读取日志信息
我们知道,ASE是先记log的机制。Server Cache Memory中日志页总是先写于数据页:
 
 
Log pages 在commit ,checkpoint,space needed 时写入硬盘。
  Data pages 在checkpoint,space needed 时写入硬盘。
系统在recovery 时读每个database 的syslogs 表的信息,回退未完成的事务(transaction)(数据改变到事务前状态);完成已提交的事务(transaction)(数据改变为事务提交后的状态)。在Log中记下checkpoint点。这样保证整个数据库系统的一致性和完整性。

二、Transaction logs 和checkpoint 进程
checkpoint 命令的功能是强制所有“脏”页(自上次写入数据库设备后被更新过的页)写入数据库设备。自动的checkpoint 间隔是由ASE 根据系统活动和系统表sysconfigures中的恢复间隔(recovery interval)值计算出的。通过指定系统恢复所需的时间总量,恢复间隔决定了checkpoint 的频率。

如果数据库开放trunc log on chkpt选项,则ASE在数据库系统执行checkpoint时自动清除log。但用户自己写入执行的checkpoint命令并不清除log,即使trunc log on chkpt选项开放。只有在trunc log on chkpt选项开放时,ASE自动执行checkpoint动作,才能自动清除log 。这个自动的checkpoint动作在ASE中的进程叫做checkpoint进程。当trunc log on chkpt选项开放时,checkpoint进程每隔0秒左右清除log,而不考虑recovery interval设置时间的间隔。

三、Transaction log 的大小
没有一个十分严格的和确切的方法来确定一个数据库的log应该给多大空间。对一个新建的数据库来说,log大小为整个数据库大小的20%左右。因为log记录对数据库的修改,如果修改的动作频繁,则log的增长十分迅速。所以说log空间大小依赖于用户是如何使用数据库的。 例如:
? update,insert和delete 的频率
? 每个transaction 中数据的修改量
? ASE系统参数recovery interval 值
? log是否存到介质上用于数据库恢复
还有其它因素影响log大小,我们应该根据操作估计log大小,并间隔一个周期就对log进行备份和清除。

四、检测log 的大小
若log 在自己的设备上,dbcc checktable (syslogs) 有如下信息:
例:***NOTICE:space used on the log segment is 12.87Mbytes,64.35%
   ***NOTICE:space free on the log segment is 7.13Mbytes,35.65% 
根据log剩余空间比例来决定是否使用dump transaction 命令来备份和清除log。

用快速方法来判断transaction log 满的程度。
1>use database_name
2>go
1>select data_pgs (8,doa mpg)
2>from sysindexes where id=8
3>go
Note:this query may be off by as many as 16 pages.
在syslogs 表用sp_spaceused 命令。

五、log 设备
一般来说,应该将一个数据库的data和log存放在不同的数据库设备上。这样做的好处:
? 可以单独地备份(back up)transaction log 
? 防止数据库溢满
? 可以看到log空间的使用情况。[dbcc checktable (syslogs)] 
? 可以镜像log设备

六、log 的清除
数据库的log是不断增长的,必须在它占满空间之前清除。前面已经讨论过,清除log可以开放数据库选项trunc log on chkpt,使数据库系统每隔一段时间间隔自动清除log,还可以执行命令dump transaction 来清除log.trunc log on chkpt 选项同dump transaction with truncate_only 命令一样,只是清除log而不保留log到备份设备上。所以如果只想清除log而不做备份,可以使用trunc log on chkpt 选项及dump transaction with truncate_only,dump transaction with no_log 命令。若想备份,应做dump transaction database_name to dumpdevice。

七、管理大的transactions
有些操作是大批量地修改数据,log增长速度十分快,如:
? 大量数据修改
? 删除一个表的所有记录
? 基于子查询的数据插入
? 批量数据拷贝
?
下面讲述怎样使用这些transaction 使log 不至溢满:
  大量数据修改    例 : 
1>update large_tab set col_1=0
2>go 

若这个表很大,则此update动作在未完成之前就可能使log满,引起1105错误(log full)而且执行这种大的transaction所产生的exclusive table lock,阻止其他用户在update期间修改这个表,这可能引起死锁。为避免这些情况,我们可以把这个大的transaction分成几个小的transactions,并执行dump transaction 动作。

上述例子可以分成两个或多个小transactions. 
例如:
1> update large_tab set col1=0
2> where col2<x
3>go
1>dump transaction database_name with truncate_only
2>go

1>update large_tab set col1=0
2>where col2>=x
3>go
1>dump transaction database_name with truncate_only
2>go 

若这个transaction 需要备份到介质上,则不用with truncate_only 选项。若执 行dump transaction with truncate_only,应该先做dump database 命令。

删除一个表的所有记录:
  例:
1>delete table large_tab
2>go 

同样,把整个table的记录都删除,要记很多log,我们可以用truncate table命令代替上述语句完成相同功能。
1>truncate table large_tab
2>go 

这样,表中记录都删除了,而使用truncate table 命令,log只记录空间回收情况,而不是记录删除表中每一行的操作。

基于子查询的数据插入
  例:
1>insert new_tab select col1,col2 from large_tab
2>go 

同样的方法,对这个大的transaction,我们应该处理为几个小的transactions。
1>Insert new_tab
2>select col1,col2 from large_tab where col1<=y
3>go
1>dump transaction database_name with truncate_only
2>go
1>insert new_tab
2>select col1,col2 from large_tab where col1>y
3>go
1>dump database database_name with truncate_only
2>go 

同样,若想保存log到介质上,则dump transaction 后不加with truncate_only 选项。若执行dump transaction with truncate_only,应该先做dump database 动作。

批量数据拷贝
在使用bcp把数据拷入数据库时,我们可以把这个大的transaction变成几个小的transactions处理,避免log剧增。

开放trunc log on chkpt 选项
1>use master
2>go
1>sp_dboption database_name,trunc,true
2>go
1>use database_name
2>go
1>checkpoint
2>go 
bcp... -b 100 (on unix)
bcp... /batch_size=100(on vms) 
关闭trunc log on chkpt选项,并dump database。

在这个例子中,一个批执行100行拷贝。也可以将bcp输入文件分成两或多个分开的文件,在每个文件执行后做dump transaction 来避免log 满。

若bcp使用快速方式(无索引,无triggers),这样操作不记log,换句话说,log 只记载空间分配情况。在这种情况下,要先做dump database(为恢复数据库用)。若log太小,可置trunc log on chkpt 选项,这样在每次checkpoint后清除log。

八、Threshold 和transaction log 管理
ASE提供阈值管理功能,它能帮助用户自动监视数据库log设备段的自由空间。这方面的详细讨论见NO.5技术支持杂志。log的管理是灵活而复杂的,我们应该在实践中摸索经验,针对每个数据库的不同情况,不同操作,做不同处理。
3.7 阀值管理(Threshold Management)
在使用Sybase数据库管理系统(ASE)开发企业应用系统时,或者开发好的数据库应用系统投入实际运行后,由于用户不断地增加或者修改数据库中的数据,用户数据库的自由存储空间会日益减少。特别是数据库日志,增长速度很快。一旦自由空间用尽,ASE在缺省情况下挂起所有数据操纵事务,客户端应用程序停止执行。这样有可能会影响企业日常业务处理流程。Sybase ASE提供自动监视数据库自由存贮空间的机制——阈值管理,当数据库使用剩余空间低于一定值时,通过执行一个自定义的存储过程,来控制自由空间。在空间用完之前,采取相应措施,这样有利于业务处理顺利进行。如果能充分利用ASE的阈值管理功能,用户能使一些数据库管理工作自动化,规程化。所以,在此我们将ASE这一重要功能介绍给读者。

ASE的阈值管理允许用户为数据库的某个段上的自由空间设置阈值和定义相应的存储过程。当该段上的自由空间低于所置阈值时,ASE自动运行相应存储过程。与阈值相对应的存储过程由用户定义,ASE不提供。一般来说,数据库管理员可通过这些存储过程来完成一些日常管理事务,例如:
备份数据库,清理日志和删除旧数据
备份数据库日志
扩展数据库空间
拷贝出表中的内容,清理日志,等等。

(一)段(Segment)
ASE的阈值管理是基于段(Segment)的,因此,让我们先回顾一下段的概念。每个数据库创建时,它有三个缺值段:(1)System段;(2)default段;(3) logsegment段。以后,还可以为该数据库增加用户自定义段。将来所有的数据库对象都建立在这些段上,要么是系统定义的段,要么是用户定义的段。数据库的系统表存放在System段上。在没有指明段时,建立的对象存放在default段上。数据库的事务日志放在logsegment段上,该段是通过使用建立数据库( Create database )命令的log on 选项来定义的。

(二)最后机会阈值(Last_chance Threshold)
缺省情况下,SQLServer监测日志段的自由空间,当自由空间量低于事务日志能成功转储的需要时,ASE运行sp_ thresholdaction过程。此自由空间量称为最后机会阈值( Last_chance threshold ),它由ASE计算得来,并且用户不能改变。
 
sp_thresholdaction必须由用户编写,ASE不提供。另外,如果最后机会阈值越出,那么在日志空间释放前,ASE一直挂起所有事务。但可以使用sp_dboption对某一数据库来改变这一行为。设置abort tran on log full选项为true,可使得最后机会越出时,ASE撤回所有还未被注册的事务。

(三)阈值管理
系统缺省建立最后机会阈值,由用户编写缺省阈值处理存储过程( sp_thresholdaction ),来控制自由空间。除此之外,还可以使用以下存储过程管理阈值:
? sp_addthreshold 建立一个阈值
? sp_dropthreshold 删除一个阈值
? sp_helpthreshold 显示阈值有关的信息
? sp_dboption 改变阈值的“挂起或取消”行为和取消阈值管理
? sp_helpsegment 显示某个段上空间大小和自由空间大小的信息
(四)增加阈值(sp_addthreshold)
它用于创建阈值( threshold )来监测数据库段中空间的使用。如果段中自由空间低于指定值,ASE运行有关的存储过程。增加阈值的命令语法为:
sp_addthreshold database, segment, free_pages, procedure 
其中:
? database——要添加阈值的数据库名。必须是当前数据库名称。
? segment——其自由空间被监测的段。当指定“default“ 段时要用引号。
? free_pages——阈值所指的自由空间页数。当段中自由空间低于该标准时,ASE运行有关存储过程。
? procedure——当segment中的自由空间低于free_pages时,ASE 执行该存储过程。该过程可以放置在当前ASE或Open server的任意数据库中。但是,超出阈值时,不能执行远程ASE上的存储过程。
例如:sp_addthreshold pubs2, logsegment, 200, dump_transaction
其中,存储过程定义为:
create procedure dump_transaction
@ dbname varchar (30),
@ segmentname varchar (30),
@ space_left int,
@ status int
as
dump transaction @dbname to "/dev/rmtx"

那么,当日志段上可用空间小于200页时,ASE运行存储过程dump_transaction,将pubs2 数据库的日志转储到另一台设置上。

sp_addthreshold不检查存储过程存在已否。但当阈值越出时,如果存储过程不存在,ASE把错误信息送到错误日志( errorlog )中。系统允许每个数据库有256个阈值,而同一段上二个阈值之间的最小空间为128页。其存储过程可以是系统存储过程,也可是在其它数据库里的存储过程,或者Open Server远过程调用。

(五)删除阈值(sp_dropthreshold)
它删除某个段的自由空间阈值,但是不能删除日志段的最后机会阈植。删除阈值的命令语法为:
sp_dropthreshold database_name,segment_name,free_pages 
其中三个参数分别为:阈值所属数据库名,阈值所监测的自由空间的段名,和自由空间页。例如:
sp_dropthreshold pubs2, logsegment, 200
删除pubs2库中logsegment段的阈值200。
(六)显示阈值(sp_helpthreshold)
它报告当前数据库上与所有阈值有关的段,自由空间值,状态以及存储过程或报告某一特定段的所有阈值。显示阈值的语法为:
sp_helpthreshold [segment_name] 
其中segment_name是当前数据库上一个段的名字。
  例如:
? sp_helpthreshold logsegment 显示在日志段上的所有阈值
? sp_helpthreshold 显示当前数据库上所有段上的全部阈值
(七)sp_dboption的新选择

abort tran when log is full 
当日志段的最后机会阈值被超越时,试图往该日志段上记日志的用户进程将被挂起还是被撤回?缺省情况下系统挂起所有进程。但是可以使用sp_aboption改变它。执行sp_dboption salesdb,"abort tran when log is full",true 命令后,一旦日志满了,则数据库修改事务将会被回滚。

disable free space acctg
这个选择取消数据库中的阈值管理,但不影响最后机会阈值。执行:sp_dboption saledb, "disable free space acctg",true 它取消对非日志段上自由空间的统计。取消后,对系统有以下影响:
? ASE仅计算日志段上的自由空间
? 日志段上的阈值继续处于活动状态
? 在数据段上,系统表不改变,并且sp_spaceused得到的值是该选择被取消时刻的值
? 数据库段上的阈值失效
? 恢复加快

(八)阈值的触发过程
频繁的插入和删除可能会使数据库段中的空间波动,阈值可能被多次超越,ASE使用系统变量@@thresh_hysteresis,避免连续触发阈值存储过程。它的值由ASE设定。在system 10.0中,@@thresh_hysteresis是64页。

如图所示:
 
因此,增加一个阈值,它必须与下一个最近的阈值相距至少2*@@thresh_hysteresis页。

一个阈值被触发,需要以下几个条件成立:
? 必须到达阈值
? 阈值处于活动状态(即它被建立后或者自由空间达到阈值减于@@thresh_hysteresis)
? 只有自由空间减少阈值才触发,如果自由空间增加,它永远不触发
3.8 日常后备数据库
SYBASE 数据库的备份主要是通过 dump 命令来实现的, 分为数据库备份和日志备份。 简单语法格式如下:
dump database database_name to device_name
dump transaction database_name to device_name
如果使用磁带设备做备份, 还要使用capacity、init选项。

应该多长时间备份一次数据库呢? 这决定于发生系统故障时( 例如磁盘故障 ) 应用系统允许丢失多长时间的数据。 如果允许丢失一个小时的数据, 那么可以考虑每天至少备份一次数据库, 并每个小时至少备份一次日志。

备份数据库之前, 应对数据库做 dbcc 检查, 确保备份的数据库是完好的数据库。如果数据库有损坏, 备份时可能不会报错, 但将来可能无法装载(load)。

通常情况下, 只有发生严重故障需要恢复时才进行装载数据库, 但是 SYBASE 建议偶尔对备份的数据库装载到一个测试环境上, 以保证备份工作过程正常并熟悉备份和装载工作过程。

Master 数据库的备份也不能忽略。每次master 数据库的内容有变动时, 应及时备份 master 数据库, 例如增加 login, 增加设备, 增加用户数据库等。

用户可根据自己的应用制定可行的备份方案。 详细的命令语法及备份方案的详细建议均可在 SYBASE 手册中查到。

举例:Unix平台后备pubs2数据库
? 启动ASE和BACKUP SERVER
? isql -Usa -Pxxxxxx -SSYBASE
dump database pubs2 to "/sybase/dump/pubs2_db.990705"
go
dump transaction pubs2 to "/dev/nrct0" capacity=1500000,file="pubs2_log.990705" with init(capacity取值为磁带容量的70%左右,以K为单位)
go
3.9 使用dump transaction with no_log的危险性
在命令参考手册中的dump transaction with no_log条目下,有一条警告信息告诉你,你应该把这条命令作为没有其它办法时的最后一招才使用它。但是“最后一招”究竟是什么意思呢?当你使用这条命令时会怎样呢?那你应使用哪条命令来代替它呢?最后,若这条命令如此有问题,为什么Sybase却要提供它呢?

Sybase技术支持建议你定期的dump你的transaction log。你必须根据你的数据库中记入日志的活动量的大小以及你的数据库的大小来决定dump的方式。有些地方按月dump transaction;有些地方每夜dump transaction。

若你从未做过dump transaction,transaction log将最终会满。 SQLServer使用log(日志)是出于恢复目的的。 当log满时,服务器将停止事物的继续进行,因为服务器将不能将这些事物写进日志,而服务器不能运行大多数的dump tran命令,因为ASE也需在日志中记录这些命令。

这就是为什么当其它dump tran命令不能执行时no_log可执行的原因。但是想一下dump transaction with no_log被设计执行的环境,将不做并发性检查。

若你在对数据库的修改发生时使用dump transaction with no_log,你就会冒整个数据库崩溃的风险。在多数情况下,它们被反映成813或605错误。为了在数据库被修改时,删除transaction log中的不活跃部分可使用dump transaction with truncate_only。这条命令写进transaction log时,并且它还做必要的并发性检查。这两条命令都有与其相关的警告,在命令参考手册中会看到这些警告。请确保在使用其中任一条命令以前,你已理解这些警告和指示。 Syase提供dump transaction with no_log来处理某些非常紧迫的情况。为了尽量确保你的数据库的一致性,你应将其作为“最后一招”。
3.10 在ASE 11.9.2版中采用了行级加锁机制以提高性能
Sybase公司关系型数据库管理系统产品的最新版本----Adaptive Server? Enterprise 11.9.2通过对行级加锁机制的创新以满足日益增长的可伸缩性的需求,并使得产品的性能和可伸缩性得到了明显的提高;特别是在商业应用中,使得这个产品能够满足尽量少占用硬件和操作系统资源的用户需求。本白皮书对这种新加锁机制的实现方法进行了详细的介绍,并且针对特定的采用页面级加锁机制的应用系统和以前所推荐的提高性能的典型优化方法,就新的行加锁机制所带来的效益进行了量化的分析。

随着Adaptive Server Enterprise (ASE) 11.9.2的发布,Sybase公司已经使得其数据库管理系统产品ASE在11.9版本中所申明增加的功能都得到了实现。这次发布ASE11.9.2表明了对于那些采用较早地使用了Sybase ASE产品的应用系统,它们所遇到的一系列与并发处理有关的课题得到了解决。

从高层的观点看来,有人可能会说新功能不过只是实现了行加锁功能。然而实际上却不止如此,Sybase公司已经抓住了这次机遇,在新产品中实现了包括新型存贮和索引等在内的范围广泛的新技术,以扩大其产品的功能并改善产品的性能。这种方法不仅是提供了更小尺度的加锁机制,而且还比其它一些方法诸如传统地对索引、分配、OAM,和GAM页面加锁进行限制明显地提高了性能。

在本技术报告文件中,通过一些特定的实例对新的加锁机制进行了阐述,而这些实例都是使其并发性及其性能得到了很大的提高。应该注意到,为了使本文件内容更加集中,所提供的材料都是在很高层次上的。有关更详细的信息,请参看如下的文件:
? New Functionality in Sybase Adaptive Server Enterprise 11.9.2[1],本文件是ASE 11.9.2 文档系列之一,该资料详尽介绍了本版本中所包含的全部新功能。
? When (and When not to) Use Data-Only Locking [2]( by Ian Smart),在这本由笔者撰写的资料中包含了本文献中所引用实例的详细分析、测试数据和硬件/软件配置。
     3.10.1 所支持的加锁机制

1) 全页加锁
全页加锁既是一个新术语,它又是由ASE(Adaptive Server Enterprise)在过去所支持的一种加锁类型。这种类型有下列特性:
? 对所有可被访问的页面在页面级加锁
? 当各种类型的页面以任何方式发生改变时,对这些排它性的页面进行加锁;而且这种加锁机制一直保持到该事务终止;
? 当下一个所需的页面已经成功地获得,对那些已经释放的的当前访问页进行共享页面加锁(如果采用了第三层ANSI隔离,则把这种加锁机制保持到该事务终止为止)
? 采用页级时间印记(timestamp)以确定是否发生改变,详细信息记录在事务日志中,以便在系统恢复时以向前或向后方式使用。
这种加锁方式常常提供性能最高的解决方案,特别是当应用设计时已经考虑了这些特性时更是如此。但是,有一些应用系统,当发生某些活动时,这种对整个页面进行加锁的方式就可能会对系统性能产生有重大意义的影响。对于那些面对诸如文件系统或其它已经支持更细小尺度加锁机制的数据库厂家产品的一般环境而设计的应用系统而言,这种情况尤其如此。

此外,还存在一系列问题,它们要围绕着更加困难的条件进行工作。它们通常要采用更加具有Sybase特性的解决方案。对于商用的应用软件制造厂商而言,对他们是一个挑战,因为这将要求他们必须跨越他们所支持的数据库平台,去完成维护其原代码的工作,而这个工作有相当工作量。在这个领域的基本问题如下:
? 对已经按照升序值创建的非群聚性索引的最末端叶型页面存在着争议
? 对非群聚性索引的表进行插入和查询时可能发生死锁;
? 在按照群聚性的索引值进行更新和对非群聚性索引的表进行查询访问之间可能发生死锁;
? 在没有作索引的表的最后一行可能发生冲突(尽管对最后的特定地址可以使用分区) ;
? 有可能使行数很少的表之间发生潜在的冲突(尽管对特定的地址可以使用填充因子[ fillfactors]和每页最大行数[ max_rows_per_page]这两个参数)
? 对每个页面两边进行加锁的需要常常被分割开来;
? 如果一个表特别小,以致在一个单一页面中进行驻留,那么对单一行的访问实际上将破坏对整个表的加锁机制。
2) 仅对数据加锁

仅对数据加锁机制试图去解决本文前一节所关注的主要问题(其他的议题将在其它功能领域中加以解决)。这种加锁方式支持两类不同的工作方式: 数据行加锁和数据页加锁。在这两种情况中,对于它们所支持的加锁方式,都与以前的加锁机制有所不同。仅对数据加锁具有下列特性:
? 在索引页面中不会破坏事务加锁。相反,而是采用了一种称之为锁存的机制。锁存是一种类似于旋转锁(spinlocks)的同步方法,它们与事务无关并且只保留很短的周期(一般而言,当一个任务在数据库中物理上改变一小片数据时,这个周期相当于在共享存贮区中在一个2K的页面改变某些字节数据的时间)一旦完成之后,这个任务将直接打开这个锁存。当这种情况还可能临时同其它组块时 ,因为这种锁存不能对服务器任务进行有上下文的切换,也不能涉及死锁,并且只能保持主要的一小段时间,所以它们不能产生有显著意义的争用。
? 采用一个RID对单一行进行数据行加锁(行标识[RID----Row ID]是逻辑页号与所在页面上该行号的组合);
? 支持固定的行标识 RIDs, 它可以是向前的,允许不进行其RID的改变,就完成数据行的移动。当一行变大超过了它的可用空间时,采用上述结果对非群聚索引不需要进行任何改变。
? 不需要进行任何争用就可以在表的尾部进行插入操作,这一功能已经增加进来。.
? 支持采用范围加锁、下一个关键字加锁和无限大加锁等方式对逻辑范围值进行加锁
? 支持由最顶层操作所导致的页面分割。这些情况直接加以提交,"系统"事务可以导致在更短一点的时间周期里保持分裂的页面处于锁定状态。
为了支持这些变化,需要对采用的存贮表结构进行一系列改善。这些改进的主要效果如下:
? 群聚索引现在被存贮为象许多人所熟悉的IBM DB2产品所采用的“放置索引”("placement indexes.”)方式。这种结构类似于非群聚性的索引,需要类似的空间总量。这种修正的结构导致了在数据初始存贮时可以按照顺序跨数据页进行存储,但是当发生插入时,它们就要尽可能紧密存放以便在正确的逻辑页面中不存在页面分割。此外,在数据页中的数据顺序在新行增加时是不进行维护整理的。这种索引的应用使每个群聚化的索引周游增加了一次I/O操作。
? 行位移表已经增加到索引页和数据页中。这种增加和新的行索引行存贮格式具有使每个索引页面所存贮的索引条目个数减少的潜在能力。
? 固定行标识(RIDS)。当一行移动时,对于分配新行位置的向前地址被放在用于驻留该行的位置上。当这种移动需要改变非群聚性索引时,对该行的访问需要增加一次I/O操作以得到‘向前’的位置。
? 一般而言,索引将更小和更短,这是因为如下原因:
? 从每个叶级页面中采用双重键限制机制来限制双重键(Duplicate key)例如,如果值“绿”("GREEN”)在下列行标识(RIDs)值等于123-1,234-2,和345-3的行中, 就分别存贮值“绿”("GREEN”),123-1,234-2,345-3,而不是存贮值“绿”("GREEN,”)三次。在每个索引页中每个值只存贮一次。
? 在非群聚性索引树的非叶型结点中将后缀实行压缩(例如,如果键值是"GREEN”和"HAMILTON”,而在这两个值之间发生分裂,那么就在非页级索引页面中存储"G”和"H”)。
3) 数据页和数据行加锁

只对数据加锁机制支持两种方式:数据页加锁和数据行加锁。这些与它们的工作方式和所提供的功能相类似。这两种方式仅在对数据访问产生阻碍作用时,在加锁的尺度上有所区别。在数据页加锁方式下再采用数据行加锁方式具有两种作用(一种起正向作用,另一种起反向作用)。首先,较小尺度加锁机制的使用可能导致减少争用与冲突,然而当大量数据发生变化时,就有可能对加锁产生大量阻碍的情况发生。

4) 特定使用的加锁类型
除非对配置参数加以特定,对所有的表都予置了隐含的全页面加锁机制。
sp_configure ‘lock scheme’,0, [allpages | datapages | datarows]

当数据库从原先版本的服务器中转储出来重新加载时,所有的表都被定义为全页面加锁的表。当建立一个新表时,可以不使用这个缺省值,可采用如下的句法格式:
create table <tablename>… lock [allpages | datapages | datarows]

为了在使用的一个表中改变加锁类型,可以采用如下的句法格式:
alter table <tablename> lock [allpages | datapages | datarows]

在一个现存的表中改变加锁方式,将引起下列三种行动后果发生:
? 首先,如果一张表从全页加锁转变为仅对数据加锁,或者从仅对数据加锁转变为全页加锁,在这两种类型之间就要对表进行选择以允许进行存贮格式改变。如果这是一个分区表,就要同时假定必要的并行级别和工作线程已经配置好的情况下,才能执行。
? 其次, 对表中的群聚性索引必须重新创建。因为我们能保证数据,所以如果从全页加锁方式转换为只对数据加锁时,这种重新创建可以通过"with sorted_data"来完成。然而,当从仅对数据加锁机制转换为全页加锁方式时,就要进行并行的索引创建操作。(请注意:如果这是一个分区表时,那么并行等级和工作线程的数目必须加以配置才能允许进行这种改变,否则这种迁移将会失败)
? 最后,非群聚性的索引将被重建,如果服务器已经为并行处理所加以配置,当进行本步骤时将加以采用。
由于这些活动同潜在的工作量有关,从全页加锁机制改变为仅对数据加锁或从仅对数据加锁改变为全页加锁机制都可能是耗费时间的活动。为了标注这一点,有以下一些选择:
? 如果可能的话,应该配置使用并行方式。这至少对执行非群聚性索引的哈斯(杂凑,即hashed)创建方法是必须的,但是如果可能的话,采用分区表和分区扫描将使系统得到更大的改进。
? 在选择进入和创建群聚性的索引之后,该任务将被设置检查点(checkpointed )所以,如果有充分的硬件资源,通过允许在任何一个时间点上,检查点任务可以具有多于10个(系统缺省值)的异步I/O请求,利用dbcc进行调谐将能够带来有益的效果。(‘maxwritedes’, number)
? 进一步作为降低使用检查点成本的一种方法,在相关的高速缓冲池(cache pool)、大数据量的I/O操作中,采用对高淘汰程度进行标记的方法,并允许清洁程序(好象家庭主妇一样)保持特别活跃的状态,将为那些检查点需要从高速缓冲池中刷新较“脏”的页面的而增加的I/O操作次数,并因此花费了在检查点上的时间,都能够大大减少作出贡献。
? 如果预先进行了配置,则可以对并行的选择进入可以使用预先分配的盘区。所以,通过将sp_configure number of pre-allocated extents设置为16也将对系统性能有明显的积极的效果。
备注:在仅对数据加锁类型之间进行改变不需要对数据进行备份, 而且执行起来只需很短的一段时间。

     3.10.2 对小量数据的多次并发访问

1) 在小型表中采用不同加锁方式之间的比较
对于那些被访问的数据同时(或者)仅有一张或很少数量的页面发生改变的情况,在页面级进行加锁极有可能导致表中很大比例的页面都实施了加锁机制。使用按数据行进行加锁将因为降低了加锁的尺度而明显增加了系统的吞吐量。

为了对使用按数据行进行加锁改变了原先发生的组块移动,已经设计了一系列基于TPC/B基准的测试实验。在这些实验中,使用了各种TPC/B的变量以创建略有不同的条件和可能发生的情况。在下面这些结果中,前三个执行的是直接更新。在实验4与5中,采用了选择、删除以及插入的组合(请注意,这些实验比其它的实验作了更多的工作)在实验6与7中, 由于补偿索引直至补偿列引起了数据更新的拖延。

利用已经执行的全部工作,在更新三张表的实验中,最小的表总是最后一个被更新。然后事务在历史记录表中插入一个条目。

备注:下列测试是在具有12个处理器的HP9000/891 T500的设备谁进行的。Adaptive Server Enterprise 配置为具有10个驱动核心( engines)和 1GB的内存。
  测试结果如下图所示:
 
从这些数据可以看出,利用数据页加锁方式与全页加锁方式相比较而言,对于吞吐量来说几乎没有什么差别。这是由于两者之间在加锁尺度方面没有什么差别。然而,一旦使用了数据按行加锁方式,100行的数据可以单独访问时,此时在性能上的明显改进就能够达到了。

2) 在小型表中采用填充因子(Fillfactor)与仅对数据加锁方式之间的比较

在上文中所提及的小型表中,对表传统的工作方式是采用了诸如在每页的某一行,在群聚性索引上有效地放置了数目为1 的填充因子。我们采用100行的表重复进行了同样的一组实验,此处表都已经具有采用这类填充因子创建的群聚性索引。

下图显示了实验结果,它们分别是较早时间进行的按数据页和数据行加锁的结果,以及与之形成对照的采用1%的填充因子的全页加锁实验结果:
 
正如所看到的,在直接更新的情况,对吞吐量来说有一点微小的提高;而在删除/插入的测试情况中,对吞吐量却有明显的提高;而对延迟的更新实验,吞吐量则要有一些降低。

这些实验说明对这些课题来说,Sybase公司传统的响应在大多数情况下还是有效的,而且对那些已经用这种方法实现的用户来说并没有明显的缺陷。然而,对于填充因子方法来说还是有一些缺点,因而就限制了它们的用途。

3) 何时采用只对数据加锁或填充因子

随着引入仅对数据加锁方式,现在数据库设计者对使用更新的表中,如何解决争用冲突课题方面,有了选择。在表比较小或保持较小时,采用填充因子对于不使用延迟更新的情况将对吞吐量的提高有轻微的改善作用。

如果表比“少数”页大,那么填充因子的使用,特别是该因子为1时,就成为应该禁止和代价昂贵的一种方式了。这是由精确度所决定的,填充因子在每个页面里都留下了一些没有使用的空间。这样一来明显增加了磁盘空间需求,对大表尤其如此,这将导致更大的数据库和对更多硬件的需要。

其次,因为填充因子不是永久的,客户必须安排重构索引的程序段以对性能进行调整和维护。这样一来使争用和冲突发生在特定的区域,使之变得更加令人烦恼。事实上在过去,当用户激活逻辑分区的数据,静态数据放在另外的表里时,就把静态数据紧紧地包装在一起,在利用填充因子使之激活,这种情况对于用户并不是罕见的情况。使用这种技术已经引用了把数据放回到一个单一的结果集合中来的组合操作,这使得编写SQL程序变得格外复杂。

采用仅对数据加锁的方法可以使用户不一定需要使用填充因子来解决更好地利用可用空间的问题。这种选择使得用户可以在“两个世界之间的最佳者”选项中作出决定,以为其环境选择最优的方案。

     3.10.3 堆栈表和热点

1) 在堆栈表中增加行

应用系统更新行时,如果同时使用了填充因子(fillfactor)或每页最大行数(max_rows_per_page),在这一话题是插入一行时,特别是在特定的位置(即热点--"hot spot”)插入时,这些方法就变得几乎没有价值。无论何时在一个没有群聚性索引的表(即所谓“堆”表--"heap”)中进行插入时,这种插入总是插到表的最后一页中去。在Sybase ASE 11.0之中,Sybase公司提供了允许用户在分区表中把这种插入蔓延分散到跨分区里去。然而,在采用这种方式解决问题的同时,这种方法不能处理由于空间开销而不能使用堆的情况,也不能解决那些因为查询原因要把数据按着添加的顺序存放的表的问题。

随着在Adaptive Server Enterprise 11.9.2版本中增加了“追加行”的逻辑,仅对数据加锁和锁存技术的采用,使得索引访问与空间分配得以协调,从而明显提高了性能。现在通过执行这样的任务,就能够不需要再采用依靠数据库加锁的一系列复杂的传统事务就能进行表的追加。

在下列的实验中,基于TPC/B的历史记录表被用于确定对堆表追加行时可能提高性能的级别。在每个事务的末尾插入历史记录表,作为工作性能的一条审计记录。运行TPC/B 的正常过程要采用多张已经分区化的历史记录表。(例如,在第三节中所显示的小表实验中,采用了具有100张分区化的60张历史记录表)。
  这种实验的结果如下图所示:
 
备注:此处运行号分别对应于在所有直接更新和放置适当位置的小表测试中的运行号。
如果分析加锁争用与冲突,可以观察到排他性的组块加锁大大减少,这是由于对于“下一行加锁”的技术已经被增加到按数据行加锁机制的一部分。但是这种功能还没有充分扩展为包括仅对数据加锁的方式,这就是在这些案例中为什么还会发生某些组块现象的原因。

从这些结果,可以看到需要群聚或分区的堆表不止是一个选择,当采用仅对数据加锁方式时将给用户在明显提高其吞吐量的方面一个很实际的替换方式,特别是在数据行加锁方式的情况下,更是如此。

2) 追加到非群聚性索引的叶级末端
正如在4.1节中所描述的,对于堆表页链末端的热点(hot spots)可以通过使用群聚性的索引或分区来加以解决。但是当非群聚性索引最末级(即叶级)的最后一个页面发生争用冲突时,或者在到达一个特定的点的中途,利用上述技术中的任一种方法都不能使上述问题按传统的方式加以解决。既然按照升序值建立索引是非常普通的(例如序列号、日期/时间列等),这就成为迄今为止难于克服的一个问题.

在采用仅对数据加锁的情况下,索引页不再按照传统方式进行加锁,而宁可使用锁存技术。因为锁存仅仅是一个短时期的同步技术,所以使用传统的页加锁方式时,不会加大开销或可能发生当主要的组块现象。

采用4.1节中的历史记录表,设计了一个实验,其中按照日期时间列建立了非群聚性的索引,这个日期时间列的值是在该列插入的时刻使用getdate() 所得到的。这个按常数升序所建立的索引引起了索引叶级的最后页面的争用冲突。这个表自身被划分为1000个分区,以试着避免在任何一个数据页发生冲突。

实验结果如下图所示:
 
因为在这种情况下前面所提及的议题还是有争论的,也没有考虑加锁的尺度,所以选择仅按数据行加锁或数据页加锁是没有什么实质意义的。正如所看到的,一旦完全通过锁存技术避免对索引加锁的话,那么明显可以使性能得到提高和改善。

     3.10.4 死锁

1) 发生死锁的原因
以前,由于采用通过服务器访问或变更数据的技术,就存在可能发生死锁的情况。对于常常是更多地由ASE的处理特性所引起的死锁, 而不是由不恰当的事务设计所引起的死锁,就给数据库管理员(DBA)带来了特别的麻烦。通过几种增强方式,ASE 11.9.2将这些系统所引起的死锁问题得到了解决。

随着锁存技术的引入,对于其它用户而言,索引页只是在很短的周期内不能被使用,永远不会延续到数据库事务中。所以锁存技术的特性将那些在索引页中发生死锁的情形都排除掉了。这一点对在使用仅仅是数据表时,可以使用并发技术的情况具有巨大的效果。

此外,为了进一步处理针对数据可能发生的死锁,已经认识到可以对释放共享锁的时间点加以改变,这样一来,就能够使对以前发生死锁的数据行的并发访问保持一致。在处理一个连接(join)时,以前的处理方式是将在外部表中加锁的行一直保持到检索到内部行时为止。然而在大多数情况下,一旦检索到数据就不再需要对进行访问的行继续加锁。随着引入固定的行标识(RIDs),这就意味着,外部表的扫描从这一行扫描到下一行,一旦所需要的信息已经被检索到之后,在所连接的外部表中,对行的加锁就可以释放了。这个功能被称之为“带有加锁方式的读提交”("read committed with lock”),并可用如下的命令加以配置:
sp_configure ‘read committed with lock’,0|1
2) 客户情形
为了证实上述情形,建立了一个实际的客户环境,以便按照一系列它们所提供的测试进行,用于验证他们所认为的最坏的并发情况下以确保不发生死锁。

在这类“最坏情况”测试中的应用负载如下:
? 在一台应用服务器上执行项分开的流式任务,它们包括如下任务:
? 大量的插入
? 在一个单一逻辑事务中进行5000条SQL语句的更新操
? SQR报告
? 用户执行OLTP工作(插入、更新和联机查询)
? 使用复杂查询的报告。向下钻取的报告:
? 访问一定数量的表(典型为6或7)
? 引用至少包含两个子查询的SQL语句;
? 在光标定义中采用截然不同的光标(cursors),以引起工作表的使用。
3) 可能发生的死锁
采用较早版本的服务器产品,进行上述的应用实验,可能发生死锁,其主要原因如下:

插入与非群聚性的索引访问
如果插入一行,服务器就会采用群聚性的索引以访问存贮该行所在的页面,并在一种排它性的页面加锁方式下执行插入操作。接着服务器就对表的每个非群聚性索引进行访问,在一种排它性的页面级加锁方式下,在其末端(叶级)结构中插入相应的条目。如果第二个用户也通过非群聚性的索引访问该表,他们可以按照非群聚性索引保持对该叶级页面实施共享加锁,与此同时,这些非群聚性的索引也由这个排它性的页面加锁所阻碍并用在插入操作中。这种对由第二个用户对非群聚性索引的叶级结构所进行的加锁方式,可能阻碍插入尝试并为第一个用户增加一个条目,从而可能引起死锁。

更新的范围
在这个商业应用的设计中,开始首先在一张表中采用零值与一个标识符两部分组成的键值插入所有的新行。在一个单一的逻辑事务中对所有的行使用相同的标识符来进行并发更新。这些更新采用唯一键值去代替零值,就引发了索引行移动到索引的叶级结构的最后。当多个用户并发执行这个任务时,第一个用户就锁定进行处理的页面和索引所在的叶级结构的末端。这将阻碍用户试图对第二个按行范围所进行的工作。当第一个用户到达所确定的页面边缘,即该范围即将终止,下一批所组块并妨碍的页面范围即将开始之时,就可能发生死锁。

按照不同顺序访问表
即使访问行是不同的,对表按照任何不一致的顺序进行访问,也会意味着可能发生死锁。这是因为一个用户所访问的行可能在另一个用户访问的同一个以不同顺序排列的页面中。对于应用系统而言,当然这种设计明显是很差的,但是却还令人惊奇地普遍存在。

连接与更新
正如在 5.1节所叙述的有关“采用加锁机制的读提交”段的内容,当处理连接,同时对外部表的加锁方式会一直保持到内部行被检索到为止,这种情况就会发生。如果第二个用户在访问所连接的外部表中的行之前,先访问了所连接的内部表的行,这种操作可能受阻,因为加锁的顺序被连接所破坏,从而也会引起死锁。

4) 问题的解决方案
采用Adaptive Server Enterprise 11.9.2的产品, 应用对索引页的锁存技术以代替传统的加锁方式,以及更小尺度的按行加锁方式,导致了前三种死锁类型不再发生。采用“按照加锁进行读提交”可以处理第四种死锁情况。

作为进一步的测试,在前面所述及的最坏情况实验中,同时还进行了增加额外负载的运行实验。这类实验包括一个拥有7000条SQL语句的单一事务,和从Microsoft Excel电子数据表中卸载数据(加入ASE服务器)。这两个版本的测试已经进行了多次,从未发生过死锁。

5) 一类新的死锁以及如何解决
就在Adaptive Server Enterprise产品的设计中进行修正以防止死锁和增加并发性的同时,也发生了一种情形,可能发生死锁,而这种情况在过去是不可能发生的。

当一次更新操作必须访问整个表时(例如此时不支持索引),可能会发生一种改变。在它们已经被确认为符合要求之后,将只对个别行加锁(以前是对一张表加锁)。当访问小表的时候,对于因为性能的原因而建立的任何索引,一般而言都不会很多。如果一个用户扫描这些小表之一,并对一行加以限定,这将随之带来一个排他性的行加锁。如果第二个用户也访问同一张表,这种扫描就会因为第一个用户的排他性行加锁所阻碍。如果第一个用户试图更新同一张表中的另一行,这种扫描也会被第二个用户所阻碍,所以引起死锁。

为了解决这一问题,已经采用了一种称之为“并发优化”的优化程序。这个方法指明了如果在一张表中需要进行存取行的页面数小于某个固定的数字(其缺省值为16),那么将不再考虑使用任何一个索引,而宁可对表进行直接扫描。这就意味着对于很小的表,利用索引而不是用全表扫描方式直接访问某些可能会产生直接影响的行,存在引起死锁的可能性。在考虑扫描一张表之前,需要确定在一张表中能有多少页,可采用如下的命令在页级结构中加以配置:
sp_chgattribute <tablename>, “concurrency_opt_threshold”, <value>

为了提高性能,也需要仅仅对表中的少数行添加索引的建立。与以前一般都忽略了索引不同,现在利用索引是为了提供最大的并发性并避免死锁的发生。

      3.10.5 何时使用不同类型的加锁方法

为了在尽量降低资源消耗的前提下实现性能和并发性的平衡, Sybase 已经建立了集中加锁方式,它们分别具有略有不同的特性。在什么时候采用那一种加锁方式,对这一问题。可以利用下列的“大拇指原则”,基于环境和应用程序的基本情况就应该采用的适当的加锁框架,提出原则性的指导意见。

1) 全页加锁方式
? 除非有特别的理由,应尽量使用全页加锁方式,因为它消耗诸如内存和CPU之类的资源最少。
? 如果发生争用冲突,并不是空间的问题,而且也可以排除正在执行的工作负载是由于在特定点(例如热点(hot spot))进行插入或延迟更新所引起的,在全页加锁方式下采用填充因子(Fillfactor)或max_rows_per_page 将能够在不增加维护问题(重组)的条件下对行级加锁提供类似的性能改善。
? 如果一张堆(栈)表中发生了大量的插入操作,而且空间不成为问题的话,采用较大数目分区的全页加锁方式将使性能提高到一定的水平。
? 如果工作负载已经是同CPU紧密相关的,那么附加的仅对数据加锁方式所需要的CPU实用程序,可能也是使用与CPU较少关联的全页加锁方式的一个主要原因。这一点对于已经使用了填充因子来解决争用冲突问题的情况尤其如此。
2) 数据行加锁方式
? 对于那些发生争用冲突而且工作负载基本上是由于延迟更新所引起的情况,应该使用数据行加锁方式,而不要再考虑空间问题。
? 一张表占据数据库空间的总量是一个问题,如果表较小而且有争用和冲突的话,应该采用按行加锁方式。
? 在一列需要非群聚性索引而且该列上所有的插入操作都在这个索引的一个确定点上发生时,按行加锁方式提供了既可以不发生索引加锁争用冲突的情况,又具有所需要的索引的能力。
? 对于那些正在使用堆(栈)表,但空间成为问题的情况,仅对数据加锁方法提供了较高的性能。
? 对于死锁的发生,利用仅对数据行加锁是一个可选的解决方案,其原因是或者由于较低的加锁争用冲突,或者由于采用了加锁方式下的读提交。
3) 数据页加锁方式
? 对于那些需要仅对数据加锁但加锁机制的数目是一个问题时(例如由于内存),那么采用仅对数据页加锁可能就是一种最优的选择。
? 当由于某个特定的或罕见的原因需要对一张表实现仅对数据加锁时,那么采用数据页加锁比数据行加锁方式占用的资源较少,而且当加锁争用冲突只是限制在索引时,会有相同或更好的性能。
      3.10.6 结论

在推出Adaptive Server Enterprise 11.9.2产品之前,在Sybase公司提供了最高性能的关系性数据库服务器产品的同时,也存在着一组特定的加锁问题限制了用户可能获得的性能。这些问题主要对那些不是针对该产品所专门设计的应用系统有所影响,可能需要在工作中逐步靠近Sybase产品,或者进行代价昂贵的性能改善以使应用系统更符合Sybase的技术特性。

随着Adaptive Server Enterprise 11.9.2新版产品的普遍上市,Sybase公司正在向广大用户提供已被证明能够解决加锁争用冲突问题的解决方案。他们既能使用户在现存的应用系统中同样达到以前所达到的非常高超的性能水平,同时又向用户提供了一系列可以解决所可能遇到的加锁争用冲突问题的解决方案。

在本技术白皮书中所包含的测试结果,和通过论述这些结果的文献已经论证了新型的加锁和锁存技术,可以使用户在不需要任何应用系统作任何改变的条件下,大幅度地提高性能与可伸缩性。

Sybase已经作出了改变,对于先前或者是在有限的方式,或者是具有相当重要影响作用的方式下,使用Sybase产品的问题,都提供了采用范围广泛的组合方法以利用Sybase Adaptive Server Enterprise 11.9.2版本产品,清除以前所遇到的各种障碍。这样一来,就可以使所有服务器的用户,都能达到以前那些专门为Sybase环境所特意设计的系统中所能体验到的性能水平。
3.11 DBCC

我们知道,在数据库系统的开发和应用中,必须保证数据库的完整性和一致性。当数据库出现了严重错误;当我们怀疑数据库受到破坏(如无法用drop命令删除数据库或对象,使用某个表时出现“不可靠数据”的信息等);当用户改变了Server的缺省排序的顺序或改变了字符集而需要检查;当SA对系统做定期检查;这些时候,我们都需要使用数据库一致性检查工具(Database Consistenecy Checker,简称DBCC)。DBCC是一个实用命令集,用来检查一个数据库的逻辑一致性及物理一致性。在开发和应用中,DBCC是我们经常要使用的命令。

DBCC命令的格式如下 
dbcc
(checktable ((表名|表标识( [, skip_ncindex] ) |
checkdb [(数据库名[, skip_ncindex] )] |
checkalloc [ (数据库名[, fix | nofix] )] |
tablealloc( {表名|表标识}
[,{full |optimized |fast |null}
[, fix |nofix] ]]) |
indexalloc ( {表名|表标识},索引标识
[,{full |optimezed | fast | null}
[, fix |nofix ]] ) |
checkcatalog [ (数据库名)] |
dbrepair(数据库名,dropdb ) |
reindex({表名|表标识} ) |
fix_text({表名|表标识} ) 
dbcc的权限,对于checktable,fix_text和reindex是缺省赋给表的属主,对于checkdb,checkalloc,checkcatalog,dbrepair,indexalloc和tablealloc,是缺省赋给数据库属主的。DBO自动获得DBCC命令和全部选项的权限。该权限不可转授。此外,dbcc在数据库是活动时运行,除了dbrepair选项和带有fix选项的dbcc checkalloc以外。

checktable选项
checktable是用来对一个指定的表做检查,确保索引和数据页正确地连接,索引按正确的顺序存储,所有指针的一致性,每页上数据信息的合理性,页偏移的合理性。如果日志段在它自己的(日志)设备上,对syslogs表使用dbcc checktable命令可以报告已使用的和剩余的日志空间,使用skip_ncindex选项使得dbcc checktable跳过对用户表上非聚簇索引(nonclustered index)的检查。缺省是检查所有的索引。

例1.检查日志使用的空间量和未用的空间量:
dbcc checktable (syslogs) 

若日志段在日志设备上,则会返回如下信息:
checking syslogs
The total number of data page in the table is 1.
NOTICE:Space used on the log segment is 0.20 Mbytes, 0.13%.
NOTICE:Space free on the log segment is 153.4Mbytes,99.87%.
DBCC execution Completed.If dbcc printed error messages,
Contact a user with SA role. 
若日志不在它自己的设备上,则会显示下列信息:
NOTICE:Notification of log space used/free.
Can not be reported because the log segment is not on its own device.
例2. dbcc checktable (titles)
The total number of data page in this table is 3.
Table has 18 data rows.
DBCC execution Completed. If DBCC printed error messages. contact a user with SA role. 
checkdb选项
运行checkdb选项同checktable检查的内容一样,但它是对一指定数据库中的每张表都做这样的检查。若未指定数据库名,checkdb检查当前的数据库。checkdb返回的信息,也同于checktable。

checkalloc选项
checkalloc是检查指定数据库,看其所有正确分配的页和尚未分配的页的情况。若未指定数据库名,则checkalloc检查当前数据库。checkalloc会返回已分配的和使用的空间数量。checkalloc的缺省模式为nofix,要使用fix选项,必须把数据库置于单用户模式。  例:
dbcc checkalloc (pubs2)
.
.
.

alloc page 0 (#of extent=32 used pages=68 ref pages=68) 
 alloc page 256 (# of extent=32 used pages=154 ref pages=154) 
 alloc page 512 (# of extent=28 used pages=184 ref pages=184) 
 alloc page 768 (# of extent=1 used pages=1 ref pages=1) 
 total (# of extent=93 used pages=407 ref pages=407) in this database. 
 DBCC execution completed.If dbcc printed error message, 
 Contact a user with System Adminstrator (SA) role.


tablealloc选项
tablealloc检查指定的表以确保所有页都被正确地分配。它是checkalloc的缩小版本。对单张表进行相同的完整性检查。使用tablealloc可以生成三种类型的报表:full,optimized和fast。full选项相当于表一级的checkalloc;它报告各种类型的分配错误。optimized选项基于表的对象分配映像(OAM)页里列出的分配页生成报告。它并不报告,也不能整理OAM页里没有列出的在分配页上没有引用的扩展(extent)。如果没有指明类型,或使用了null,则optimized选项是缺省的设置。fast选项,并不生成分配报告,但生成一个被引用但并没有在扩展里分配的页的额外的报告。fix|nofix选项决定tablealloc 是否整理表中发现的分配错误。对于所有的表,缺省为fix,但系统表除外,它们的缺省为nofix。要对系统表使用fix选项,必须首先将数据库置成单用户模式。  例:
dbcc tablealloc(titles)
显示信息如下:
The default report option of OPTIMIZED is used for this run. The default fix option of FIX.is used for this run.
.
.
.
Total #of extent=3
Alloc page 256 (# of extent=1 used pages=2 ref pages=2).
Alloc page 256(# of extent=1 used pages=2 ref pages=2)
Alloc page 256 (# of extent=1 used pages=2 ref pages=2)
Total (# of extent=3 used pages=8 ref pages=8) in this database. 
indexalloc 选项
indexalloc检查指定的索引,确保所有的页都被正确地分配,它是checkalloc的缩小版本,对单独一条索引指定同样的完整性检查。其中各选项与tablealloc相同。
 
checkcatalog选项
checkcatalog选项用于检查系统表内,系统表之间的一致性。例如:它确保在syscolumns表中的每一(数据)类型在systypes表中都有一个相匹配的记录;对于sysobjects中的每个表和视图在syscolumns表中应有关于它们每一列的描述记录;确保在syslogs中的最后一个检查点是有效的。checkcatalog也报告任何已定义的段。若不指定数据库名,则检查当前数据库。

dbrepair选项
dbrepair(数据库名,dropdb)选项是删除一个受破坏的数据库。受破坏的数据库是不能用drop database命令删除的,drop database只能删除正常的数据库,当执行dbrepair命令时,任何用户(包括执行此命令的用户)都不得使用正被删除的数据库。该选项要在master库中运行。
 
reindex选项
reindex选项通过运行dbcc checktable的“fast”执行方式检查用户表上索引的完整性。如果它检测出索引有问题则会删除并重建索引。在ASE的排列顺序改变之后,SA或表属主应该执行这一选项。此选项不能在用户定义的事务中运行。 例:
dbcc reindex (titles)
返回信息:One or more indexes corrupt.They will be rebuilt. 
fix_text选项
ASE的字符集由单字节转变为多字节后,fix_text选项用于升级文本值。ASE的字符集由单字节转变为多字节字符集会使文本数据的管理更加复杂。由于文本值可能较大足以覆盖若干页,ASE必须能处理(通过页约束)可能横跨页的字符。为做到这点,服务器需要在每一文本页上添加一些信息。SA或表属主必须在文本数据的每一个表上运行dbcc fix_text,以计算所需要的新页数。

总之,DBCC命令所返回的信息能准确地反映数据库及它的各个对象的状态,是我们检测数据库的好帮手。
3.12 Sybase ASE的字符集

     3.12.1 概念

一、什么是字符集?

字符集是字符(包含字母,数字,符号和非打印字符等)以及所指定的内码所组成的特定的集合。通常一个字符集包含一个字母表中的字符,例如拉丁字母表被使用在英语语言中,那么如果要使用拉丁字符,就要配置使用英语语言集中的特定的字符集合――拉丁语言字符集。这里为什么特指了是英语语言集呢?因为字符集是基于某种操作系统平台和某种语言集支持的。语言集的集合被称为语言组,它可能包含一种或多种语言。本地字符集是基于特定语言组中所包含的一种或多种语言支持的,在特定操作系统平台上编码的集合。
在Client/Server系统中,支持多语言的数据处理,但是所有的语言必须属于同一个语言组。例如,从下表可以看出,如果服务器中的数据用组1中的字符集,则同一数据库中可以有法语,德语,英语以及该组中的其它语言。而在这个数据库中就不能同时存储日语,法语了。
这里请注意一个非常特别的字符集—Unicode—它支持世界上超过650种语言的国际字符集。Unicode允许在同一服务器上混合使用不同语言组的不同语言。

                  表1-1  Adaptive Server支持的语言和字符集
 
 
注意:表中所显示的所有字符集,因为任何字符集的前128(十进制)个字符都包含拉丁字母表,所以所有字符集都支持英语。各字符集中前128个字符之外的字符各不相同,用于支持不同的本地语言字符。

二、什么是排序顺序?

每种字符集都有一种或多种排序顺序,Adaptive Server使用它们存储数据。排序顺序与特定的语言或语言组及特定的字符集联系密切,不同的语言对同样字符的排序是不同的,因此,需要特定语言的排序顺序,以便正确地对字符进行排序。另外,排序顺序与特定地字符集也密切相关,对于特定字符集可使用的排序顺序位于字符集目录的排序顺序定义的文件中(.srt文件)。
有关字符集及其可用排序顺序的列表,如下所示:
                 
                          表1-2可用的排序顺序
 
 

排序顺序用于
――创建索引
――将数据存入按索引排序的表
――指定order by子句

对于不同类型排序顺序的解释
二进制排序顺序:
对于所有字符集都至少提供一个二进制排序顺序,这一排序顺序基于字符集中分配给代表每个字符的代码(“二进制”代码)的算数值,适用于每个字符集的前128个字符和亚洲语言。当字符集支持一种以上的语言时,二进制排序顺序将会得出不正确的结果,这时就应该选择其它排序顺序了。

字典排序,区分大小写,区分重音:
分别对大写和小写字母进行排序。字典排序顺序识别字母的各种重音形式,并将它们排在相关联的非重音字母之后。

字典排序,不区分大小写,区分重音:
按字典顺序排序,大写字母与小写字母等同,在排序结果中大小写字母混合使用。对于避免表中名称的重复条目很有用。

字典排序,不区分大小写,区分重音,具有优先级:
在排序时不区分大小写,在所有其它条件相同时,大写字母具有高的优先级(即大写字母先出现)。
当order by子句中指定的列与表的聚簇索引键值相匹配时,使用这种排序顺序可能导致大表性能降低,因此,不建议使用这种排序顺序,除非特意要求大写字母排在小写字母之前。

字典排序,不区分大小写,不区分重音:
将加重音格的字母与未加重音格的相关联字母同等对待,它在排序中混合了重音字符。

三、什么是字符集转换?

为保持客户端与服务器之间的数据完整性,数据必须在字符集之间进行转换,目的是跨机器和字符集使用时,确保“a”还是“a”,此过程就是字符集转换。

字符集转换的方式
本地字符集的转换:
Adaptive Server支持属于同一语言组的本地字符集之间的转换。如果服务器把一种本地字符集作为它的缺省值,则客户端字符集必须属于同一个语言组,此时可以在服务器上浏览所有客户端提交的数据。如下图:

               图1-3 服务器端和客户端字符集属于同一语言组
 

本图中,服务器端与客户端使用的语言集与字符集都同属于组1(见表1-1),那么他们之间实现的就是本地字符集转换方式。

Unicode系统中的转换方式:
在Unicode系统中,由于服务器的缺省字符集为UTF-8,所以客户端字符集可以是任何语言组中的一种本地字符集。如下图:

图1-4Unicode系统中的字符集转换
 
上图中来自每一个客户端的数据经过服务器和每个客户端时,都会被正确转换,而无论每一个客户端选择的时哪个语言组的字符集,原因是ASE服务器端选择了缺省字符集UTF-8.

字符集转换类型
直接转换:
支持同一语言组内两种本地字符集之间的转换。例如,Adaptive Server支持CP437与CP850之间的转换,因为他们同属于第1语言组。

Unicode转换:
Unicode转换可应用于所有本地字符集,在两种本地字符集之间进行转换时,Unicode转换方式把Unicode作为中间字符集。例如,在服务器缺省字符集CP437和客户端字符集CP860之间进行转换时,CP437先被转换成Unicode,Unicode再转换成CP860。
Unicode转换方式既可以用于服务器缺省字符集UTF-8,还可用于本地字符集。除非使用服务器缺省字符集UTF-8,否则您必须专门配置您的服务器才能使用Unicode转换方式(配置方法请参看配置篇之如何配置字符集的转换类型)。

如何选择字符集的转换方式
这将取决于系统的类型。
在非Unicode系统中,服务器和客户端的字符集为本地字符集,因此可以使用Adaptive Server直接转换,但是有些字符集没有直接转换,这种情况就必须使用Unicode转换了。
如下表所示:
 
             表1-5 字符集转换方式
 
 
――如果系统中使用的所有字符集都在表1-5的列1中,则使用直接转换。前提是所有字符集同属于一个语言组。
――如果系统中使用的所有字符集都在表1-5的列2中,或者有些在列1中,有些在列2中,那么必须配置服务器使用Unicode转换方式。前提是所有字符集同属于一个语言组。

在Unicode系统中,如果服务器缺省字符集为Unicode UTF-8,那么所有的转换将在UTF-8与客户端使用的本地字符集之间进行,因此,在Unicode系统中,只能使用Unicode转换。

     3.12.2 配置

一、 如何配置字符集的转换类型

禁用字符集转换的配置方法
在isql环境中执行:
1> sp_configure “disable character set conversion”,1
2> go
 “disable character set conversion”参数默认配置值为0,即启用字符集转换。

如何配置字符集的转换类型
把”enable unicode conversions”参数设置为1或者2。当配置为1时,此设置使用直接转换或Unicode转换;当配置为2时,此设置使用Unicode转换;默认配置值为0,使用直接转换。
在isql环境中执行:
1> sp_configure ”enable unicode conversions”,1
2> go

二、如何配置服务器端缺省字符集

直接转换法
直接转换法是指直接使用Sybase提供的实用程序,如UNIX平台上使用sqlloc命令或者编辑sqlloc.rs脚本文件;Windows平台使用“服务器配置”图形化管理工具直接配置服务器端字符集。

使用直接转换法的条件是:
――服务器中没有用户数据
――对服务器中用户数据的损坏是可以接受的
――绝对确定服务器中的数据只使用ASCII-7字符集

间接转换法
间接转换法相对于直接转换法而言,需要通过一下三步来完成配置工作:
1. 先将服务器端的数据使用bcp命令导出
2. 再选择直接转换法之中的一种方式配置服务器端字符集
3. 再使用带有-J 参数的bcp命令把数据导回服务器端

配置服务器端字符集的方法
sqlloc—适用于UNIX平台的命令
在$SYBASE_OCS/bin目录下执行:sqlloc,将出现一个图形化界面,在这个界面中您可以直接选择语言集,字符集,排序顺序,便很容易地就完成了配置工作。

编辑sqlloc.rs脚本文件

将$SYBASE-ASE/init/sample_resource_files/sqlloc.rs拷贝到$SYBASE_OCS/bin目录下,按以下黑体字提示编辑该文件:
 sybinit.release_directory: /home/sybase―――输入Sybase产品的安装路径
 sqlsrv.server_name: SYB125―――输入数据库服务器的名称
 sqlsrv.sa_login: sa
 sqlsrv.sa_password: ―――输入sa的口令,若为空,则什么也不填
 sqlsrv.default_language: us_english―――输入想要配置的语言集
 sqlsrv.language_install_list: USE_DEFAULT
 sqlsrv.language_remove_list: USE_DEFAULT
 sqlsrv.default_characterset: cp850―――输入想要配置的字符集
 sqlsrv.characterset_install_list: USE_DEFAULT
 sqlsrv.characterset_remove_list: USE_DEFAULT
 sqlsrv.sort_order: binary―――输入想要配置的排序顺序# An example sqlloc resource file...
 # sybinit.release_directory: USE_DEFAULT
 # sqlsrv.server_name: PUT_YOUR_SERVER_NAME_HERE
 # sqlsrv.sa_login: sa
 # sqlsrv.sa_password: 
 # sqlsrv.default_language: french
 # sqlsrv.language_install_list: spanish,german
 # sqlsrv.language_remove_list: USE_DEFAULT
 # sqlsrv.default_characterset: cp437
 # sqlsrv.characterset_install_list: mac,cp850
 # sqlsrv.characterset_remove_list: USE_DEFAULT
 # sqlsrv.sort_order: dictionary

保存已经修改好的sqlloc.rs脚本文件,执行以下命令:

sqllocres -r sqlloc.rs

注意屏幕上出现的提示信息,如无异常,则完成配置工作。

“服务器配置”图形化管理工具――适用于Windows平台
“服务器配置”管理工具提供了一个易于操作的图形化管理平台,根据工具中提示的信息,很容易就完成了字符集的配置工作,这里就不多讲了,请参看相关文档说明。

三、如何配置客户端缺省字符集

配置客户端缺省字符集实际上就是对“$SYBASE\locales”目录下locales.dat文件的修改。
在Windows平台用写字板方式打开该文件,在UNIX平台可以直接使用“vi”命令打开该文件,我们会看到,该文将中所有字符集的配置都是以服务器端操作系统平台名称分组的:
.
.

[aix]
  locale = C, us_english, iso_1
  locale = En_US, us_english, iso_1
  locale = en_US, us_english, iso_1
  locale = default, us_english, iso_1
  locale = En_US.IBM-850, us_english, cp850
  locale = en_JP, us_english, eucjis
  locale = Fr_FR, french, cp850
 .
  .
 [axposf]
  locale = C, us_english, iso_1
  ; Use Posix Locales, straight from the Posix Guidelines
  locale = en_US.88591, us_english, iso_1
  locale = fr_FR, french, iso_1
  locale = zh_CN, chinese, eucgb
  locale = zh_TW, tchinese, euccns
  locale = ko_KR, korean, eucksc
  locale = us_english.utf8, us_english, utf8
  locale = default, us_english, iso_1


.
.
其中,操作系统名称放在每一组最开始的“[]”中,而且请注意上面黑体字,每一组中都会存在一行“locale = default,…”。我们要修改客户端的默认字符集,就是对这一行进行修改。
例如,某系统服务器端是SUN平台,服务器端语言集为english,字符集为cp850。我们要修改客户端字符集与服务器端一致,怎么做?
首先找到[SUN]操作系统分组,然后修改“locale = default,…”为“locale = default,us_English,cp850”。

修改前:
 [sun]
  ; from JLE, KLE, CLE, OS/4.1.1, man setlocale()
  ; and Sun Software Internationalization Guide (p/n 800-5972-08)
  ; use setenv LC_CTYPE, LC_MESSAGES, LANG
  locale = C, us_english, iso_1
  locale = fr, french, iso_1
  locale = de, german, iso_1
  locale = tr, us_english, iso88599
  locale = zh, chinese, eucgb
  locale = zh_CN, chinese, eucgb
  locale = zh_TW, tchinese, euccns
  locale = ko, korean, eucksc
  locale = us_english.utf8, us_english, utf8
  locale = default, us_english, iso_1修改后:
 [sun]
  ; from JLE, KLE, CLE, OS/4.1.1, man setlocale()
  ; and Sun Software Internationalization Guide (p/n 800-5972-08)
  ; use setenv LC_CTYPE, LC_MESSAGES, LANG
  locale = C, us_english, iso_1
  locale = fr, french, iso_1
  locale = de, german, iso_1
  locale = tr, us_english, iso88599
  locale = zh, chinese, eucgb
  locale = zh_CN, chinese, eucgb
  locale = zh_TW, tchinese, euccns
  locale = ko, korean, eucksc
  locale = us_english.utf8, us_english, utf8
  locale = default, us_english, cp850


保存该文件,就完成对客户端字符集的修改了。

这里,还要说明一种特殊情况:
为了满足服务器端某些应用的特殊需求,在服务器端设置了一个环境变量:LANG,此时客户端字符集该如何设置呢?
例如,某系统服务器端是Windows平台,使用语言集english,字符集iso_1,并设置环境变量LANG=C。我们要修改客户端字符集与服务器端一致,怎么做?
首先找到[NT]操作系统分组,然后在该组中加入一行
“locale = C,us_English,iso_1”

修改前:
 [NT]
  locale = enu, us_english, iso_1
  locale = fra, french, iso_1
  locale = deu, german, iso_1
  locale = japanese, japanese, sjis
  locale = chs, chinese, eucgb
  locale = cht, tchinese, big5
  ; locale = kor, korean, eucksc
  locale = us_english.utf8, us_english, utf8
  locale = default, us_english, iso_1
 修改后:
 [NT]
  locale = enu, us_english, iso_1
  locale = fra, french, iso_1
  locale = deu, german, iso_1
  locale = japanese, japanese, sjis
  locale = chs, chinese, eucgb
  locale = cht, tchinese, big5
  ; locale = kor, korean, eucksc
  locale = us_english.utf8, us_english, utf8
  locale = default, us_english, iso_1
 locale = C,us_english,iso_1

因此在修改客户端字符集之前,请先查看服务器端是否设置了环境变量“LANG“,再决定如何修改。

四、如何选择ASE字符集使之支持中文字符

目前在ASE 12.5中支持中文字符的字符集有四种:CP936,EUCGB,UTF-8和GB18030。
其中EUCGB字符集是基于GB2312-80编码规范的,它的EUC (Extended Unix Code)编码范围是第一字节0xA1~0xFE(实际只用到0xF7),第二字节0xA1~0xFE。
CP936字符集是基于GBK编码规范(实际上的国家标准是GB13000-90),是对GB2312进行的扩展,第一字节为0x81~0xFE,第二字节分两部分,一是0x40~0x7E,二是0x80~0xFE。其中和GB2312相 同的区域,字完全相同。
GB18030字符集(国家标准号是GB18030-2000)是2000年3月17日发布的新的中文编码标准。它是GB2312的扩充,采用单/双/四字节编码体系结构,收录了27000多个汉字以及臧文、蒙文、维吾尔文等主要的少数民族文字。Sybase 从ASE 12.5.0.3之后开始支持GB18030字符集。
UTF-8字符集是现有ASCII系统向Unicode转换的一个过渡方案。它使用1-3字节表示一个字符。简体中文的每个字符在utf8中的长度基本上都是3个字节。它的最主要的优点是可以同时支持超过650种语言的字符。缺点是针对中文字符来说,需要增加50%的空间用来存储。
一般来说,由于EUCGB不支持国标一、二级字库以外的汉字,所以我们推荐用户在服务器端和客户端都使用CP936字符集,或者在ASE 12.5.0.3之后还可以使用GB18030字符集,它可以支持一些比较生僻的汉字。它的不足是只有一种排序方式,即区分大小写的Binary方式。所以,如果需要使用支持中文字符集且不区分大小写的数据库时,就只能使用UTF-8作为服务器端字符集,而客户端使用CP936或GB18030字符集。
另外,还有一种选择是,服务器端和客户端都使用iso_1字符集,虽然iso_1字符集并不直接支持中文字符,但我们将服务器端和客户端都设置成iso_1字符集后,系统也不会在客户端和服务器端进行字符转换,它只不过将一个汉字的两个字节当做两个单独字符来处理,一般情况下没有问题。但当执行like匹配查询的时候,它有可能返回不正确的结果,原因是服务器端是根据单字节去匹配查询条件的,很可能会有前一个汉字的第二字节与后一个汉字的第一字节的内码组合符合查询条件,被服务器端作为查询结果返回来。
如果客户端是通过JDBC访问数据库的,那么,为了支持中文字符,服务器端最好采用CP936/GB18030或UTF-8作为字符集。如果应用是多层结构的,那么应用服务器的字符集也最好采用与数据库服务器端相同的字符集。

五、如何查看服务器端、客户端字符集

查看服务器端字符集:
在isql环境中执行:
1> sp_helpsort
2> go

查看客户端字符集:
在isql环境中执行:
1> select @@client_csname
2> go

3.12.3 错误处理

为什么会出现字符集转换失败?

1. 当字符存在于客户端字符集中但在服务器字符集中不存在时,Adaptive Server的字符集转换将报告转换错误,反之亦然。
用户会碰到下面的错误消息:
Msg 2402,Severity 16 (EX_USER):
Error converting client characters into server's character set. Some character(s) could not be converted.
转换错误会阻止插入与更新语句的执行。如果发生此情况,请检查数据中有问题的字符并替换它们。

2. 当客户端发送数据时Adaptive Server遇到转换错误,它用ASCII码的问号(?)代替可疑字符所占字节,但查询批处理继续进行直到完成为止。
语句完成后,Adaptive Server将发送一下消息:
Msg 2403,Severity 16 (EX_USER):
WARNING!  Some character(s) could not be converted into client's character set.  Unconverted bytes were changed to question marks (`?')。

3. 当在客户端查询服务器端存储的数据时,当碰到中文汉字,在客户端显示乱码的现象,且没有任何提示信息。
这是我们在应用中经常会碰到的现象,产生的原因是:客户端与服务器端字符集不符。怎么解释呢?假设我们先期设置服务器端字符集为iso_1,客户端字符集也为iso_1,然后我们从客户端向服务器端录入了所有的数据;之后当我们需要查询时,使用的客户端,它的字符集为cp850,那么势必在该客户端上显示的字符集为乱码。
当出现这种情况时,最好配置客户端字符集为先期客户端使用的字符集或者配置客户端字符集与服务器端字符集一致,使得客户端字符集与服务器端字符集匹配。这里我们不建议用户修改服务器端字符集,因为服务器中此时已经存在大量的数据,在使用直接转换方式时,由于源字符集与目的字符集中可能存在无法转换的字符而导致Adaptive Server无法启动;如果使用间接的转换方式,会增加工作量。

      3.12.4 附:如何安装cp936字符集

以在Windows平台安装cp936字符集为例,说明如何安装使用服务器中没有被默认安装的字符集。(在ASE 12.5.0.3版本中安装GB18030字符集的方法类似)

(这里SYBASE的安装路径为c:\sybase)

1.c:\>cd \sybase\charsets\cp936
 2.c:\sybase\charsets\cp936> charset -Usa -Psa_pass -Sserver_name binary.srt cp936
 3.在SQL环境中
 1>select name,id from syscharsets
 2>go
 找到name为cp936对应的id(假设为171)
 4.1>sp_configure "default character set id",171
 2>go


5.重启server两次
(注:第一次启动后,server会自动宕掉,需要第二次重启后才能使用)