Ubuntu 升级到 10.04 lucid

本来能够直接从 9.04 升级上来的,但由于我启用了 sshfs,而 mount 在网络启动之前执行,所以系统升级完启动后就挂在 mount sshfs 那里不动了,一开始不知道,以为不行了,就直接重新安装了 10.04,也好,4、5年了,该重装一会了。

启用 Ctrl+Alt+Backspace 快捷键重启 X

System->Preferences->Keyboard->Layouts->Options,打开 Key sequence to kill the X server 并选中 Control+Alt+Backspace,立即生效,或者执行命令:

$ setxkbmap -option terminate:ctrl_alt_bksp

这个是每次启动 X 之后执行才生效。

设置系统默认输入法

System->Administration->Language Support 中,Install/Remove Languages 中装上中文语言支持,安装 fcitx,然后在 Keyboard input method system 里选上 fcitx,重启 X。

Mysql 服务启动的问题

无法启动或者重启,修改 /etc/init/mysqld.conf,在 start on (net-device-up 后面添加内容如下:

start on (net-device-up IFACE=eth0

如果提示 Can't create test file 类似的信息导致无法启用,是由于用户变更了 datadir,而系统没有赋予 mysql 读写那个目录的权限所致。修改 /etc/apparmor.d/usr.sbin.mysqld 添加下面的内容:

/your new datadir/ r,
/your new datadir/** rwk,

/etc/init.d/apparmor restart 后 mysql 就能启动了。注意上面的 datadir 如果是 ln 指向,需要填写真实路径。

mysql 还有个烦人的问题,就是 bind-address 默认只是本机,如果想改成 ip 或者 0.0.0.0 代表所有地址,还不能着急,先 bind 127.0.0.1 启动一会,然后停止服务修改 bind-address,不然 sudo service mysql 脚本又会出现问题。

好像 mysql 的自动启动取消了?在 /etc/rc.local 中加一句 service mysql start

启动时检查完磁盘就没有反应的问题

多半是由于 fstab 中有挂载 cifs/smbfs/sshfs 的行为,其实这个时候磁盘还没有检查完,而且永远也检查不完,因为网络还没有启用,那些需要网络挂载驱动器的行为就陷入了永远的等待状态,昏死的设定。

显卡驱动安装参考

Openoffice.org 字体列表中不显示中文字体名称

只显示为英文,这个需要修改 /etc/environment

LANG="en_US.UTF-8"
LANGUAGE="en_US:en_GB:en"
LC_CTYPE="zh_CN.UTF-8"

如果输入法又变成 ibus,sudo im-switch -s fcitx 后重启 X 即可。openoffice 是根据当前环境来决定是显示字体的中文名称还是英文名称的。

Samba 共享中的 symbolic link 无法被访问

需要在 /etc/samba/smb.conf 中添加下面几句内容:

[global]
    follow symlinks = yes
    wide links = yes
    unix extensions = no

一点使用感觉

  • 很多操作都是在窗口模式下实现,反而用 cli 不知该怎么作了。
  • 易用性倒是好多了,却同时搞得一些复杂操作麻烦或者出问题了,比如 mysql 服务启动的问题,比如 fatab 里面加上个 sshfs 就无法启动的问题等等。

参考

Mysql升级到5.1后库升级失败的问题

一台 mysql 5.0 服务器,升级到 5.1 后,发现原来有个 database 名字变成了 #mysql50#t-2008-zbb ,刚开始没在意想直接 RENAME DATABASE ,结果这个语法由于过渡危险已经取消了,改用ALTER DATABASE db_name UPGRADE DATA DIRECTORY NAME,结果执行错误:

mysql> ALTER DATABASE `#mysql50#db_name` UPGRADE DATA DIRECTORY NAME;
ERROR 1450 (HY000): Changing schema from '#mysql50#db_name' to 'db_name' is not allowed.

原来这里面还有个 BUG ,刚刚修正过来,发行版中肯定还没有呢。幸好,从中得到了提示,因为 View 的存在导致库无法升级的,删掉所有视图后 UPGRADE 成功:

mysql> ALTER DATABASE `#mysql50#db_name` UPGRADE DATA DIRECTORY NAME;
Query OK, 0 rows affected (0.08 sec)

这台服务器还作了双向同步,我还得手工重置同步状态,又是麻烦一连串儿的事情,幸亏这次操作的是测试服务器,下次升级正式服务器之前,记得先把所有 View 删掉,升级完成后再重新创建。

另外 RENAME DATABASE 实在是危险,我执行过程中出错终止了,结果一部分表在新库里、一部分表在旧库中,不小心把未转完的目标库删掉了(不然后面的正常 ALTER DATABASE 无法继续),结果就丢失了这些表的数据。

mysql的复制

这篇算是我的学习笔记,主要内容都来自mysql官方文档,然后参考一些网上的文章自己实践了个例子,所以内容上前后也有些重复。

mysql支持从一个主服务器到多个从服务器的复制,并且复制是异步的,也就是说从服务器不用一直保持与主服务器的连接,这使得一些远程、临时性的节点也能进行同步。同时,还可以指定参与同步的数据库或者数据表。

同步目前主要用于以下几个方面:

  • 架构扩容,一主写多从读的方式已经用得非常普遍了
  • 数据安全,同步也是一种备份,而且还可以是实时的,多好啊
  • 数据分析,大量的分析运算在从服务器上运行,不影响主系统的负载
  • 远程数据发布,或者说远程备份

同步的基本设置

主服务器的my.cnf设置

同步是通过binlog来实现的,虽然主服务器不能决定哪些操作在binlog中记录(默认记录全部),但从服务器可以根据配置来有选择的执行。每个主、从服务器都要有一个唯一ID,从服务器还要记录主服务器的主机名、log文件名以及位置等信息(在master.info中),并保存一份收到的binlog,这样多台从服务器就可以有不同的同步状态或进度,并且下次连线的时候再以“续传”的方式继续同步进程。binlog的开启需要修改主服务器的my.cnf

# 必须写在[mysqld]节里面
[mysqld]
# mysql-bin是log文件的前缀,也可以使用其它的名字,比如服务器名
# 如果不带路径,会把log文件写到`/var/lib/mysql`下
log-bin=mysql-bin
# serverid在一个同步体系中必须是唯一的,大于等于1且小于2^32-1的整数
server-id=1

如果使用InnoDb,为了保证稳定,还应设置如下两行:

innodb_flush_log_at_trx_commit=1
sync_binlog=1

并且确保没有设置skip-networking,禁用网络自然无法同步。但是,innodb_flush_log_at_trx_commit=1在某些服务器上会导致写数据速度急剧下降,可尝试调整为2。

用户

每个从服务器都要使用标准的mysql用户来连接主服务器,对应的权限名称为REPLICATION SLAVE(这是一个和库无关的权限,属于服务器管理权限的一种)。虽然使用独立用户并不是必需的,但由于用户名和密码都将以明文方式存储在记录replication相关信息的master.info文件中,还是采用独立用户更安全一些。语法如下:

mysql> GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%.domain.tld' IDENTIFIED BY 'passwd';

从服务器的my.cnf设置

server-id是必须设置的,binlog不用开。

获取主服务器的同步信息

从服务器必须知道要从主服务器binlog的哪个位置开始进行同步,相当于有一份数据、一份要执行的指令(binlog)以及一个当前执行哪一条指令,要获得这三个信息,必须临时暂停主服务器上的处理:

mysql> FLUSH TABLES WITH READ LOCK;

(这个连InnoDB的commit都能停住),暂停状态下数据、指令就都不会变了,查询出当前记录的binlog的位置:

mysql > SHOW MASTER STATUS;

得到的File列是当前的binlog记录文件名,Position就是日志记录,如果还没东西可记,File为空字符串而Position为4。后面设置从服务器的时候,CHANGE MASTER TO命令就要用到这些信息,比如:

mysql> CHANGE MASTER TO
    ->     MASTER_HOST='master_hostname',
    ->     MASTER_USER='repl_username',
    ->     MASTER_PASSWORD='repl_password',
    ->     MASTER_LOG_FILE='binlog_filename',
    ->     MASTER_LOG_POS=binlog_position;

复制主服务器的数据到从服务器

保持主服务器的暂停处理状态,先把数据都弄到从服务器上去。导出可以用mysqldump:

mysqldump -h db_host --add-drop-table --default-character-set=utf8 --user=root -pPASSWD --extended-insert=false db_name > db_name.sql

带上--all-databases参数可以导出所有库,不过不想参与同步的库是没必要导的。如果带上--master-data参数,还能够把从服务器设置的一些sql一并导出来。

当然,直接拷贝数据库文件也是可以的,速度也更快,但如果在mysql服务启动的状态下拷贝,可能会存在缓存、日志不同步的情况,所以要停掉服务以后再拷贝,并且不要拷贝和目标库无关的日志等文件。

为了进一步保证数据的一致性,可以在导出数据前给所有数据加锁:

mysql> FLUSH TABLES WITH READ LOCK;

导出完成后再解开:

mysql> UNLOCK TABLES;

同步的进阶说明

binlog的几种形式

一种是记录所操作的“命令”(mysql 5.1.4前版本只能用这个),简称为命令复制SBR;另外一种是记录所有受影响的行,简称为行复制RBR。从mysql 5.1.8开始支持包含上述两种方式的混合型日志。命令复制的优点是:

  • 产生的日志小
  • 不要求每个表都要有主键

缺点是:

  • 某些函数的返回值是无法复制的(比如LOAD_FILE(), UUID(), UUID_SHORT(), USER(), FOUND_ROWS(), SYSDATE(),但RAND(), NOW()能正常复制)
  • 写数据时用到的锁较多
  • 主从服务器的表必须一样

行复制的优点为:

  • 最安全,所有的操作都能记录和同步
  • 写数据的时候使用到的锁较少

缺点为:

  • 写大量数据时(比如全表UPDATE)日志里要写很多东西,日志锁定时间也相对更长,可能会带来一些并发问题
  • 所有的表必须要有显式主键(一般情况下都会有主键的,除非超大数据量或特殊设计)
  • 带有大文本的数据复制可能要慢一些,这个好理解,一个update操作,如果是行复制的话,肯定是更新了n行,复制传递的数据量就是n倍大小
  • 查看更新传递和执行的进度不方便
  • 无法用WHERE @server_id <> server-id这个小技巧来临时指定只更新某台服务器

在具体处理的时候,依然会有一些操作比如GRANT REVOKE之类是用命令方式同步的。总体来讲,经常要进行大量数据变更的情况下,命令复制似乎更合适一些。

常用配置参数说明

控制主服务器的:

  • auto_increment_increment, auto_increment_offset 自增主键的跳号间隔和初始值,有什么用呢?有人作双向复制的时候,设置A服务器主键自增从1开始每次跳10,B服务器则从2开始每次跳10,这样双向复制的过程中就不会产生自增主键重号的问题了。这两个是全局而非针对某表的设置。不过我更倾向于用UUID实现。
  • slave_exec_mode 从服务器容错方式,5.1.24后版本才有。

控制从服务器的:

  • master.info中配置的优先级比my.cnf中的高。
  • log-slave-updates 从服务器的变更也记入日志,作链式复制时必须。
  • replicate-do-db=db_name 只复制指定数据库,可以写多行来指定复制多个库。注意在命令复制模式下mysql只认用use db_name选定的,不会从跨表操作的sql中自动判断;而行复制则是依照实际变更的数据属于哪个表来判断。
  • replicate-rewrite-db=”from_name->to_name” 主、从数据库名称不一样的时候,用此命令进行转换。

控制日志的:

  • binlog_format 日志类型,ROW或STATEMENT,新版中还可选MIXED。
  • log-bin[=base_name] 开启二进制日志(复制必须),可以指定日志文件名。
  • sync_binlog 什么时候写日志到磁盘,这个机制比较复杂,一般设为1相对较安全。

同步的管理

有一些命令:

  • SHOW SLAVE STATUS 在从服务器上查看状态。
  • SHOW SLAVE HOSTS 在主服务器上,启用了report-host以后,显式已连接的从服务器。
  • STOP/START SLAVE [IO_THREAD/SQL_THREAD] 在从服务器上,停止复制(的日志下载/SQL执行操作)。

同步实现实例

复制还是在局域网内方便,尤其是双向复制,因为跨公网的话不光要连进去,还要从里面连出来。 综合考虑,M-M-Slaves的模式的一般适用性可能更强一些。下面以Master-Master简要举例:

首先分别修改M1/2的my.cnf:

[mysqld]
# Replication
server-id=1/2
log-bin=/var/log/mysql/svr1/2
log-error=/var/log/mysql.err
relay-log=/var/log/mysql/svr1/2-relay
binlog_format='STATEMENT'
sync_binlog=1
log-slave-updates
innodb_flush_log_at_trx_commit=2
binlog-do-db=db_to_replicate

master-host=ip_of_M1/2
master-port=port_of_M1/2
master-user=rep
master-password=passwd
master-connect-retry = 60
replicate-do-db=db_to_replicate

然后在M1/2上创建同步用户:

mysql> grant replication slave on *.* to 'rep'@'%' identified by 'passwd';

重启M1/2的mysql服务,并停止slave:

mysql> stop slave;

下面,把数据从M1上复制到M2上,先在M1上:

mysql> use db_to_replicate;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> flush tables with read lock;
Query OK, 0 rows affected (0.00 sec)

mysql> show master status;
+-------------+----------+--------------+------------------+
| File        | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+-------------+----------+--------------+------------------+
| svr5.000002 |       98 | db_to_replicate   |                  | 
+-------------+----------+--------------+------------------+
1 row in set (0.00 sec)

目的是锁上M1上所有表(为只读状态),并记下当前binlog的记录位置。然后不要退出mysql,另外开一个session,导出数据:

$ mysqldump -h 127.0.0.1 --add-drop-table --default-character-set=utf8 --user=root -p --extended-insert=false --skip-lock-tables db_to_replicate > db_to_replicate.sql

mysqldump时用参数指定不再次加锁,也可以采用直接拷贝数据库文件的方式(导入时更快,也得更小心一点)。导出完成后可以解锁,让M1正常运行了:

mysql> unlock tables;

现在到M2上去,用刚才导出的数据把库建起来(如果采用直接执行sql的方式,记得先停掉binlog),然后进行比较重要的一步,让M2从M1刚才导出数据的时候开始恢复同步:

mysql> stop slave;
Query OK, 0 rows affected (0.00 sec)

mysql> CHANGE MASTER TO MASTER_HOST='ip_of_M1', MASTER_PORT=port_of_M1, MASTER_USER='rep', MASTER_PASSWORD='passwd', MASTER_LOG_FILE='svr5.000002', MASTER_LOG_POS=98;
Query OK, 0 rows affected (0.01 sec)

mysql> start slave;
Query OK, 0 rows affected (0.01 sec)

最后两个参数就是刚才在M1上用show master status;得到的数值。

同理,在M1上也作一次CHANGE MASTER TO操作,指向M2,两边的双向主主复制基本上就算是完成。

另外注意以后切换MASTER的时候,直接修改my.cnf是不行的,因为都存到优先级更高的master.info文件中去了,还是用CHANGE MASTER TO来操作更靠谱。

通过ssh进行远程同步的连接方法

以M2远程登录M1的方式为例,M2 ssh到M1,以便M2作为slave能连接master M1,使用的是ssh隧道:

M2 $  ssh -p 8022 -L 127.0.0.1:3307:ip_of_M1:3306 -CfNg user@domain.tld -o ControlPath=/tmp/ssh-mysql-3307-M1-3306

双向同步时,M1还要连接到M2才行,依然是从M2上来打洞:

M2 $ ssh -p 8022 -R 0.0.0.0:3307:localhost:3306 user@domain.tld -fN -o ControlPath=/tmp/ssh-mysql-3307-local-3306

注意在建立这些ssh隧道的时候,一般不能使用ssh的连接共享

Update @ 2009-03-16

马上就遇到了innodb启用了innodb_file_per_table之后,直接拷贝.ibd文件过来不生效的问题,并且是在建立好M-M复制之后,如此这般(M1是旧服务器,M2上把文件拷贝过来导入的服务器)(补注:依然失败):

  • M1: 确认所有同步内容已经处理完毕。
  • M1: stop slave;
  • M2: flush tables with read lock;
  • M2: 备份mysql data目录下相应库的所有文件(就是拷贝过来的那些),并且删除掉(db.opt不要删)。
  • M2: unlock tables; 不然下面的操作无法进行。
  • M2: 使用其它手段重新创建所有表,数据不需要。
  • M2: show master status; 得到位置后面要用到。
  • M2: flush tables with read lock; 准备用拷贝过来的ibd覆盖刚才用sql重新创建的表,这样数据就有了。
  • M2: 复制/拷贝/恢复刚才备份的所有.frm, .ibd文件。
  • M2: unlock tables; M2恢复正常。
  • M1: CHANGE MASTER TO MASTER_HOST=’ip_of_M2′, MASTER_PORT=port_of_M2, MASTER_USER=’rep’, MASTER_PASSWORD=’passwd’, MASTER_LOG_FILE=’fwolf-office.000101′, MASTER_LOG_POS=57943; 目的是跳过刚才那些重新建表的处理。
  • M1: start slave; M1恢复正常。

至于原因嘛,因为拷贝过来的都是ibd文件,而innodb的schema信息都存在系统表INFORMATION_SCHEMA里,偏偏这个系统表又是只读的,所以只能重新建一遍库了。

还遇到了特定名称的库无法创建的情况,会提示.frm无法创建之类的,可以这样处理:创建失败后,从其它库中建一个名称相同的,然后把.frm, .ibd文件拷贝过来,然后执行drop table XXX,然后就能正常创建、删除了。

……慢,表都能看到了,可哪个里面都没有数据,还是不行,看来innodb迁移到其它服务器,尤其是配置不同、使用状态不同的情况下,还真不是一般的麻烦,我还是老老实实改用mysqldump得了,删库重建再导入,慢也就慢这一次而已。大体过程:

  • M1: stop slave;
  • M1: dump all data: mysqldump -h ip_of_M1 –add-drop-table –default-character-set=utf8 –user=root -p –extended-insert=true –net_buffer_length=600k–skip-lock-tables db_to_replicate > db_to_replicate.sql
  • M2: delete database & recreate it
  • M2: 执行刚才的sql,导入数据,最好先关闭binlog
  • M2: stop slave;
  • M1/2上分别change master to, start slave;

如果遇到这样的错误:

ERROR 1201 (HY000): Could not initialize master info structure; more error messages can be found in the MySQL error log

可以在CHANGE MASTER TO之前先RESET SLAVE;

参考

升级到Ubuntu Intrepid后感觉到的一些变化

第一,一台服务器,双网卡,配置两个ip地址,这两个ip地址同属一个网段,因此网关设置为相同。升级前一切正常,升级后只能让同网段的其它机器ping通,其它网段机器即使防火墙规则允许也ping不通,去掉其中一个ip地址的网关设置后就可以了。

第二是和mysql相关的怪怪的问题,以前mysql设置中都有一项:

innodb_flush_log_at_trx_commit=1

升级后,一台P4 1.7 256M内存的机器insert数据正常,另外一台IBM x3650 双5160 3.0GHz cpu 4G内存机器的insert却极慢(10条数据要5秒),不光是比以前Feisty慢,比刚才那台P4机器都要慢很多倍。将此项值调整为2以后恢复正常,insert 1000条数据耗时0.25秒左右。P4机器也将此值调整为2后有改进,但不如x3650明显,insert 1000条数据耗时5秒多。

第三,登录后欢迎屏幕里的那个系统信息我很喜欢:

  System information as of Tue Jan  6 23:50:02 CST 2009

  System load:    0.0                Memory usage: 77%   Processes:       118
  Usage of /home: 19.4% of 24.03GB   Swap usage:   34%   Users logged in: 3

如果没有出现,把landscape-common这个包装上,并且平时可以用landscape-sysinfo命令调出类似信息。

Update @ 2009-01-13

字体问题又出现了,网上有很多修改文泉驿字体的方法,我则是/etc/fonts/conf.avail44-wqy-zenhei.conf69-language-selector-zh-cn.confWenQuanYi Zen HeiWenQuanYi Bitmap Song前面加上Simsun,字体倒是还是原样(默认宋体),但firefox和其它X程序有时候字体乱成一片,也有说像墨点或者重叠的,反正鼠标选中后就恢复了,有人说是驱动的问题,把nvidia-glx-96换成nvidia-glx-71干脆X就起不来了,其它的173 177 180根本就不支持我的GForce4 MX440。

如果不换Simsun,用网上的方法调整一下文泉驿字体的处理方式,倒是不乱,但字体略显模糊,还能接受。但其它X程序依然有时文字是乱的。查到可能是显卡驱动的问题,只能等待了?

另外注意一点,nvidia-glx-??安装后必须重启系统,只重启X是不起作用的。

Update @ 2009-01-16

旧IBM本本,raedon9000的显卡,fglrx不支持,所以卸掉xorg-driver-fglrx后X能起来了,但登录界面无法输入,并且小红点失效,reinstall xserver-xorg-vesa后dpkg-reconfigure xserver-xorg后好了。总体来说反而比N卡好配。

Update @ 2009-02-08

如果安装nvidia显卡驱动时出现这样的错误:

  dpkg-divert: `diversion of /usr/lib/xorg/modules/extensions/libGLcore.so to /usr/lib/nvidia/libGLcore.so.xlibmesa by nvidia-glx-96' clashes with `diversion of /usr/lib/xorg/modules/extensions/libGLcore.so to /usr/lib/nvidia/libGLcore.so.xlibmesa by nvidia-glx-71'
  dpkg: error processing /var/cache/apt/archives/nvidia-glx-96_96.43.09-0ubuntu1.1_i386.deb (--unpack):
   subprocess pre-installation script returned error exit status 2
  Errors were encountered while processing:
   /var/cache/apt/archives/nvidia-glx-96_96.43.09-0ubuntu1.1_i386.deb
  E: Sub-process /usr/bin/dpkg returned an error code (1)

可以试着用dpkg-divert --list|grep GL找到libGLcore.so相关的内容,可能会定义到另外一个包里了:

$ dpkg-divert --list|grep GL
diversion of /usr/lib/xorg/modules/extensions/libGLcore.so to /usr/lib/nvidia/libGLcore.so.xlibmesa by nvidia-glx-71
diversion of /usr/lib/libGL.so.1 to /usr/lib/nvidia/libGL.so.1.xlibmesa by nvidia-glx-96
diversion of /usr/lib/libGL.so.1.2 to /usr/lib/nvidia/libGL.so.1.2.xlibmesa by nvidia-glx-96

可以看出是nvidia-glx-71没删干净,在这里手工清除就可以了:

$ sudo dpkg-divert --remove /usr/lib/xorg/modules/extensions/libGLcore.so
Removing `diversion of /usr/lib/xorg/modules/extensions/libGLcore.so to /usr/lib/nvidia/libGLcore.so.xlibmesa by nvidia-glx-71'

Update @ 2009-02-09

ati的驱动也出问题了,rv3??核心的显卡全不支持,比如我的Radeon X600,暂时只能删掉fglrx,用其它的驱动替代。删除xorg-driver-fglrx相关的包,装上xserver-xorg-video-ati,然后sudo dpkg-reconfigure xserver-xorg,生成了一个极小的xorg.conf,图形就有了,但据说3D性能几乎没有,办公用没啥大事。

Section "Device"
    Identifier  "Configured Video Device"
    Option      "UseFBDev"      "true"
EndSection

Section "Monitor"
    Identifier  "Configured Monitor"
EndSection

Section "Screen"
    Identifier  "Default Screen"
    Monitor     "Configured Monitor"
    Device      "Configured Video Device"
EndSection

Firefox工具栏的自定义设置总是丢失的问题原因也找到了,可以暂时禁用TabMixPlus扩展,或者是禁用Ubuntu Firefox Modifications,我选择后者。

字体也有一点小问题,比如方正小标宋的“黑体”效果就没有了,重新刷新一下fc-cache -fv就好了。

忙碌的5月

2008年的5月是难以忘怀的,有人戏称是上帝的手机不小心开了震动模式了,权当是我们勇敢面对明天的一点自我安慰吧,逝者已矣。这么大的事件,我没帮上什么忙,也没帮上什么倒忙,除了捐了自己微薄的一个月工资以外,几乎什么都没有作,有些惭愧,但我也没闲着,原先考虑的.NET还是PHP?问题基本被消灭掉了,灾要救,其余的工作也要继续,不是么?

这个项目我最终选择了用PHP来作,原因不再说了,正确性让时间去证明吧。工作内容大体分两部分,一个比较简单的子系统和另外一个大一点,逻辑关系和计算规则比较复杂,专业性也较强的子系统。之所以说是子系统,这次开发的内容仍然要和原来.NET的系统一起使用,换个说法就是只升级了一部分,而原来的系统就是.NET和Java混合着用的,现在.NET、Java、PHP全到齐了:-)。

因为只是子系统开发、升级,所以用户管理、验证部分仍然使用原系统中的,省去了这部分的工作量,但需要作一些焊接的工作。

数据库方面,原系统仍然使用Sybase,新系统换到mysql库,为此专门搞了一个单向同步数据的模块,放到cron中每隔5分钟执行一次,效果还可以接受,同步过来的数据也算是对原Sybase的一个备份。不过库结构的命名是全新的,并且除字典表以外,主键全面从identity转向uuid。自定义的uuid按时间排序,还分配了可自定义部分,目前感觉使用效果应该比identity要好,还是需要时间证明了。uuid的调试是麻烦些,需要一点小技巧和烂笔头,还有phpMyAdmin这个好工具。

时间上,大概是从4月初开始的,刚开始连我4个人,其中1个PHP还没学会,另外2个是今年的应届毕业生(项目过程中,5月底左右,才完成论文答辩,领了毕业证)。我除了项目还有其它工作,刚开始帮他们起了个头,中间一直到5月中下旬才全面投入项目。PHP还没学会的这个后来主攻文档和测试、项目协调。

一开始花了大概10天的时间构建系统框架、连入adodb,smarty等类库,以及非常重要的开发规范制定。然后开始作那个比较小的子系统,一来可以不断完善系统框架,二来也算是锻炼队伍。原计划4月底完成,拖到了5月10号左右。

然后开始较复杂的子系统,由于时间太紧迫,又请了两位外援,一位是几乎全能的老手,除了专业业务不熟悉,别的前、后台都没问题,另外一位是一年经验的PHP开发者,速度虽然略慢但代码质量还可接受。

从5月15号开始,几乎是封闭式开发了,每天除了吃饭睡觉和大概一个小时的休息(乒乓球)时间,每天都工作到24点以后。原计划5月底开始测试(并非完成),跳票到了6月10号,还算是在可接受范围之内吧,只是一些非重点非必要的辅助功能都被我们留在以后作了。

项目基本上就是这些情况吧,谈几点感受:

  • 石家庄这种鬼地方,一半用户是微软的盲目崇拜者,一半用户是Java的盲目信仰者,PHP及其它开源技术的土壤简直就是盐碱地。
  • 在较成熟的PHP应用环境下,或者开发团队中有PHP熟练者的情况下,即使是.NET和Java的高手也是可以快速熟悉起来产生生产力的。
  • 不要信仰工具,不管是用什么开发工具,没有我们后来请的两位外援,再有2月也拿不出东西来。
  • 工作经验的确很重要,不仅仅是工作质量的差别,毕业生和有工作经验的人相比,工作精神、压力承受程度、解决问题的思维方式都有很大差别的。好在我们的团队成员在工作态度上还都是一流的,这一点我得感谢他们。
  • 欠缺的知识:在开发工作量测量、开发时间测量上还没有太好的方式,代码质量也没有很好的检查方法。需求表达、结构设计基本上靠文字描述和口头讲解(当然也有时间的原因),没有趁手的case工具。数据库结构设计和维护从原来的PowerDesigner又回归到了原始的sql文件+维护版sql文件,感觉用起来虽然不太方便,效率也不低,多服务器的环境下尤其好用。
  • 花一些时间搭建高仿真的测试环境很重要,我们的测试环境已经运行了3年多,系统和数据都是和生产环境一样的,对开发起到了很好的作用。
  • 拍脑门定工期的方式真的是后患无穷,但也没有更有说服力和科学依据的更好方式,头疼,系统分析这块当年没学好,就是学好了这么多年的发展也用不得了。

小结:很辛苦,但有所得,也很快乐,按照葛优的话说就是即完成了任务,又锻炼了队伍,呵呵。

与所有默默开垦盐碱地的同志们同勉,并对给予我默默支持的家人和同事致以无限感激。

WordPress升级后分类乱码的原因及解决

另外一个网站WordPress很久没有升级了,主要是嫌网站太慢,ssh上去操作麻烦,加上主人也很少更新。不过当WordPress Automatic upgrade出现之后这一切都简单多了我只要在页面上点点鼠标就能升级了,顺便还能把WP的文件和数据库打包下载到本地。同时,WP的新版2.5还支持插件的自动升级,以后无论是升级WP还是升级插件,基本上就不用ssh了。

WordPress Automatic upgrade的安装和WP的升级基本顺利,不过中间执行完upgrade.php升级数据库后,返回wpau有一个错误,没什么提示信息,直接retry后说升级不成功,清除结果再次运行就没问题了。

升级成功之后,blog标题成了??问号,我知道这是字符集的问题,看了看wp-include/wp-db.php中已经可以自动识别字符集(原来都是自己hack这个文件),就在wp-config.php中添加了两句配置:

define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');

乱码问题解决。以为这就没什么事,又分类的名称怎么都成了“??”了,到分类管理里面一看还是问号,编辑修改成中文保存后依然是问号,如果改成英文的就没事了,其它的内容比如文章什么的中文都正常,怎么回事呢?

左思右想,觉得还是和升级时候的那个错误有关,我是从比较旧版本的WP升级过来的,以前是通过hack wp-include/wp-db.php文件设置数据库用utf8编码连接,升级过程中这个文件被新版文件覆盖了,而wp-config.php中添加相应设置之前有一个空档,而升级数据库恰恰是在这个空档时进行的。WP 2.3添加了tag功能,后来查看分类的数据表发现分类和tag是保存在一起的,所以可以确认这些被修改了的数据表在创建时,程序并不是使用utf8编码连接的数据库服务器,而是默认的latin1_swedish_ci字符集。在这样字符集的数据表中,自然是无法保存中文的,所以原来的中文、新输入的中文就都变成了问号。

解决方法有些麻烦,因为没有用phpMyAdmin,都是直接编辑好sql命令在mysql中执行的,好在涉及到的表不多:

  • wp_term_relationships
  • wp_term_taxonomy
  • wp_terms
  • wpau_active_plugins_info
  • wpau_upgrade_log

修改表的字符集语法如下:

ALTER TABLE [table_name]  DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

这还没完,还得把每个表中的字符型字段的编码转换过来,这个就略微多一点了:

# 查看表中各列的详细情况,包括字符集:
show full columns from wp_term_relationships;

# 更改列的字符集或类型、长度
alter table  wp_term_taxonomy change taxonomy taxonomy varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
alter table  wp_term_taxonomy change description description longtext CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;

alter table  wp_terms change name name varchar(55) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
alter table  wp_terms change slug slug varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;

alter table  wpau_active_plugins_info change plugin_name plugin_name varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
alter table  wpau_active_plugins_info change plugin_status plugin_status varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;

alter table wpau_upgrade_log change task_name task_name varchar(150) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
alter table wpau_upgrade_log change task_status task_status varchar(150) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
alter table wpau_upgrade_log change task_description task_description varchar(150) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL;
alter table wpau_upgrade_log change task_log task_log text CHARACTER SET utf8 COLLATE utf8_general_ci NULL;

其实wpau的两个表不用改的,老外设计的程序一般不会写中文进去,改过来只是好看一些。

另外WP 2.5把slug弄没了很不舒服,虽然现在我只用id作permanent link,但还是原意有个整齐一点的slug更好看,所以用上了slugshow插件,希望WP啥时候把那个貌似智能其实很傻的功能改过来。

Update @ 2008-04-05

差点忘记了还有一个css裸奔节,顺便贴一下我blog首页穿衣服和不穿衣服的样子,首先是穿衣服的:

Free Image Hosting at www.ImageShack.us

然后是不穿衣服的:

Free Image Hosting at www.ImageShack.us

我的脱衣脚本还是很好用的嘛。