登录 |
  • 注册
  • 无密码访问

    2009年06月30日 上午 43:46 | 作者:pangyt

    == Yahoo用户无密码ssh链接配置 ==
    === 配置步骤 ===
    在雅虎内部特殊用户yahoo经常被用作批处理帐户进行一些后台远程操作,但要在ssh链接上无密码使用yahoo用户则需要配置一番。

    假设有主机hostA和hostB,现需要从hostB以yahoo用户身份连接到hostA执行一些命令cmd,即:

    hostB ---SSH登录---> hostA(运行命令cmd)
    

    则可按以下步骤进行配置:

    1. 在随意一台有yahoo用户的主机上以yahoo用户身份生成无passphrase的RSA公私钥对。这里我们以在hostB上生成为例:

    xxx@hostB:~$ sudo -u yahoo ssh-keygen -f /tmp/id_rsa -t rsa -P ''
    

    以上命令生成的公钥在/tmp/id_rsa.pub中,私钥在/tmp/id_rsa中;

    2. 将生成的RSA私钥文件复制为hostB上的/home/yahoo/.ssh/id_rsa文件(或identity文件),并将生成的RSA公钥文件分发到hostA上的/home/yahoo/.ssh/authorized_keys文件里:

    xxx@hostB:~$ sudo mv /tmp/id_rsa /home/yahoo/.ssh/id_rsa
    xxx@hostB:~$ sudo scp /tmp/id_rsa.pub xxx@hostA:
    # 在hostA上
    xxx@hostA:~$ sudo sh -c "cat /home/xxx/id_rsa.pub >> /home/yahoo/.ssh/authorized_keys"
    

    这里要确保公私钥文件以及yahoo用户目录的权限正确:

    xxx@hostB:~$ sudo chown yahoo:users /home/yahoo/.ssh/id_rsa
    xxx@hostB:~$ sudo chmod 700 /home/yahoo/.ssh/id_rsa
    xxx@hostB:~$ sudo chmod 700 /home/yahoo
    # 在hostA上
    xxx@hostA:~$ sudo chown yahoo:users /home/yahoo/.ssh/authorized_keys
    xxx@hostA:~$ sudo chmod 755 /home/yahoo/.ssh/authorized_keys
    xxx@hostA:~$ sudo chmod 700 /home/yahoo
    

    3. 确保hostA上的/usr/local/bin/push脚本存在且权限正确:

    xxx@hostA:~$ sudo chmod 755 /usr/local/bin/push
    

    4. 确保hostA上的sshd监听了2222端口:

    xxx@hostA:~$ sudo lsof -i:2222
    ...
    sshd-2222 2510 root    3u  IPv6   5394       TCP *:2222 (LISTEN)
    

    5. 现在应该就能在hostB上以yahoo用户身份从2222端口无密码ssh链接到hostA运行命令了:

    xxx@hostB:~$ sudo -u yahoo ssh -p 2222 hostA "ls /"
    

    以上的步骤是保留私钥分发公钥来实现无密码ssh链接,适用于hostB需要无密码链接到包括hostA在内的很多台其他主机进行操作的场合;当hostA需要接受来自很多台其他主机的无密码ssh链接时,在这些主机上逐一生成公私钥对并将一堆公钥放到hostA上是很麻烦的,此时就可以采取保留公钥分发私钥的策略了,即将一个公私钥对中的公钥保留在hostA上的authorized_keys文件里,而将私钥逐个分发到需要链接到hostA的其他主机上,具体的步骤大同小异,此处不再赘述。

    === 出现问题的解决方法 ===
    # 在发起链接一方(hostB)的ssh增加-v选项显示额外调试信息,在接受链接一方(hostA)查看/var/log/all日志,观察可能存在的问题;
    # 若链接时总是提示输入密码,可能的原因有:
    ## 公私钥对没有以yahoo用户身份生成;
    ## 公私钥对没有以正确的格式生成,请确保使用rsa而不是rsa1格式生成公私钥对;
    ## 公私钥对生成时没有使用空的passphrase,请确保用-P ”或者在提示输入passphrase时直接回车来保证公私钥对没有passphrase;
    ## 发起链接一方的私钥文件/home/yahoo/.ssh/id_rsa权限不对,请确保其权限为yahoo:users 700
    ## 接受链接一方的公钥文件/home/yahoo/.ssh/authorized_keys权限不对,请确保其权限为yahoo:users 755
    ## 发起或接受链接一方的yahoo用户目录权限不正确,请确保yahoo用户目录权限为yahoo:users 700
    # 若链接时提示/usr/local/bin/push: Permission denied,则表明yahoo用户所用的push shell权限不对,请确保/usr/local/bin/push文件的权限为755

    [转]pg集群配置

    2009年06月30日 上午 37:04 | 作者:pangyt

    = U.R.T PgSQL 集群 开发环境安装 =

    == 1. Machine ==

    
    PL/Proxy
    hostname:h08-vm08.corp.cnb.yahoo.com
    inet addr:10.62.245.152
    Bcast:10.62.245.255
    Mask:255.255.254.0
    OS: Linux  2.6.9-42.ELsmp
    CPU:Intel(R) Xeon(R) CPU  L5320  @ 1.86GHz
    MemTotal: 254772 kB
    Pg_Dir: /home/y/pgsql/data/
    port = 5432
    
    Node1:
    hostname:h08-vm08.corp.cnb.yahoo.com
    inet addr:10.62.245.152
    Bcast:10.62.245.255
    Mask:255.255.254.0
    OS: Linux  2.6.9-42.ELsmp
    CPU:Intel(R) Xeon(R) CPU  L5320  @ 1.86GHz
    MemTotal: 254772 kB
    Pg_Dir: /usr/local/pgsql/data/
    port = 5433
    
    Node2:
    hostname:h07-vm08.corp.cnb.yahoo.com
    inet addr:10.62.245.136
    Bcast:10.62.245.255
    Mask:255.255.254.0
    OS: Linux  2.6.9-42.ELsmp
    CPU:Intel(R) Xeon(R) CPU  L5320  @ 1.86GHz
    MemTotal: 514440 kB
    Pg_Dir: /usr/local/pgsql/data/
    port = 5433
    

    == 2. Install ==

    #在PL/Proxy、Node1、Node2节点上执行以下命令:
    
    sudo rpm -ivh postgres_4e_alone-8.3.1.0.rpm
    sudo rpm -ivh postgres_4e_contrib-8.3.0.1.rpm
    sudo rpm -ivh pgbouncer_4e-1.0.0.0.rpm
    

    == 3. Init ==

    #在PL/Proxy、Node1、Node2节点上执行以下命令:
    
    ## Add Unix User
    sudo adduser postgres
    sudo mkdir /usr/local/pgsql/data
    sudo chown postgres /usr/local/pgsql/data
    sudo visudo
    **Add "DEVEL ALL=(postgres) ALL" in the last line
    **Apend "hebing" to "User_Alias DEVEL"
    
    ## Init DB and Start service
    sudo -u postgres /usr/local/pgsql/bin/initdb -D /usr/local/pgsql/data
    sudo -u postgres /usr/local/pgsql/bin/postgres -D  /usr/local/pgsql/data >logfile 2>&1 &
    
    ## Create DB and Use Local Connection
    sudo -u postgres /usr/local/pgsql/bin/createdb URT
    
    ##检查数据库是否已经创建
    sudo -u postgres /usr/local/pgsql/bin/psql -d URT
    sudo -u postgres /usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data stop
    
    #继续在PL/Proxy节点上执行以下命令(安装plproxy节点):
    sudo mkdir /home/y/pgsql/data
    sudo chown postgres /home/y/pgsql/data
    sudo -u postgres /usr/local/pgsql/bin/initdb -D /home/y/pgsql/data
    sudo -u postgres  /usr/local/pgsql/bin/postgres -D /home/y/pgsql/data >logfile_1 2>&1 &
    sudo -u postgres /usr/local/pgsql/bin/createdb URT
    sudo -u postgres /usr/local/pgsql/bin/psql -d URT
    sudo -u postgres /usr/local/pgsql/bin/pg_ctl -D /home/y/pgsql/data stop
    

    == 4. config ==

    #在PL/Proxy、Node1、Node2节点上执行以下命令:
    
    ## 检查tcp连接端口是否已经配置,默认安装已经配置好
    sudo vim /usr/local/pgsql/data/postgresql.conf
    listen_addresses = '*'
    port = 5433
    
    ## PgSQL是基于主机的认证(HBA:"host-based authentication")
    sudo vim /usr/local/pgsql/data/pg_hba.conf
    ## 根据需要添加IP地址、数据库和角色名。IP一般是前端机IP和本地IP,角色在上一步骤中已经添加
    host    URT         postgres         10.62.0.1/16          trust
    
    ## 启动服务器
    sudo -u postgres /usr/local/pgsql/bin/postgres -D /usr/local/pgsql/data >logfile 2>&1 &
    sudo -u postgres /usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data reload
    
    #在PL/Proxy、Node1、Node2节点上执行以下命令:
    sudo vim /home/y/pgsql/data/postgresql.conf
    listen_addresses = '*'
    port = 5432
    
    sudo vim /home/y/pgsql/data/pg_hba.conf
    ## 根据需要添加IP地址、数据库和角色名。IP一般是前端机IP和本地IP,角色在上一步骤中已经添加
    host    URT         postgres         10.62.0.1/16          trust
    
    sudo -u postgres /usr/local/pgsql/bin/postgres -D /home/y/pgsql/data >logfile_1 2>&1 &
    sudo -u postgres /usr/local/pgsql/bin/pg_ctl -D /home/y/pgsql/data reload
    
    ## 检查PL/Proxy节点是否可以访问Node1和Node2节点上的数据库,检查是否可以访问PL/Proxy节点
    [hebing@h08-vm08 ~]$sudo -u postgres /usr/local/pgsql/bin/psql -d URT  -h h08-vm08.corp.cnb.yahoo.com -p 5433
    [hebing@h08-vm08 ~]$sudo -u postgres /usr/local/pgsql/bin/psql -d URT  -h h07-vm08.corp.cnb.yahoo.com -p 5433
    [hebing@h08-vm08 ~]$sudo -u postgres /usr/local/pgsql/bin/psql -d URT  -h h08-vm08.corp.cnb.yahoo.com -p 5432
    

    == 5. Install plpgsql and plproxy ==

    #在plproxy、Node1、Node2节点上安装plpgsql,包里面已经安装了。
    #如果没有安装plpgsql,可以参考以下步骤:
    sudo -u postgres /usr/local/pgsql/bin/createlang plpgsql URT -p 5433 
    
    #在plproxy、Node1、Node2节点上安装plproxy,包里面已经安装了。
    #如果没有安装plporxy,可以参考以下步骤:
    #检查是否有/usr/local/pgsql/bin目录,如果没有,修改你的.bash_profile文件,添加/usr/local/pgsql/bin到path里。
    echo $PATH
    
    gunzip plproxy-2.0.4.tar.gz
    tar xf plproxy-2.0.4.tar
    cd plproxy-2.0.4
    gmake
    sudo gmake install
    sudo -u postgres /usr/local/pgsql/bin/psql -f /usr/local/pgsql/share/contrib/plproxy.sql URT
    

    == 6. Config cluster in plproxy ==

    #创建一个plproxy schema,在plproxy里配置集群,注意要连接pgbouncer;放在一个sql脚本里;
    sudo -u postgres /usr/local/pgsql/bin/psql -d URT  -p 5432 -f cluster.init.sql
    

    == 7. Config DB Node ==

    #在Node1、Node2节点上添加操作函数(这里只写了几个简单的函数,需要添加URT的业务逻辑函数)
    sudo -u postgres /usr/local/pgsql/bin/psql -d URT -p 5433 -f node.sql
    

    == 8. Config plproxy ==

    #在plproxy节点上添加操作函数(这里只写了几个简单的函数,需要添加URT的业务逻辑函数)
    sudo -u postgres /usr/local/pgsql/bin/psql -d URT -p 5432 -f plproxy.sql
    

    == 9. Config pgbouncer ==

    #在plproxy节点上执行以下操作
    
    #在plproxy节点上安装pgbouncer,包里面已经安装了。
    #如果没有安装pgbouncer,可以参考以下步骤:
    ./configure --prefix=/usr/local --with-libevent=/prefix
    make
    sudo make install
    
    #修改pgbouncer.ini文件
    sudo vim /usr/local/pgsql/share/doc/pgbouncer/pgbouncer.ini
    #添加一下内容
    #######################
    Node1 = host=10.62.245.152 port=5433 user=postgres dbname=URT
    Node2 = host=10.62.245.136 port=5433 user=postgres dbname=URT
    listen_addr = 127.0.0.1
    listen_port = 6543
    auth_file = /usr/local/pgsql/share/doc/pgbouncer/users.txt
    logfile = /usr/local/pgsql/share/doc/pgbouncer/pgbouncer.log
    pidfile = /usr/local/pgsql/share/doc/pgbouncer/pgbouncer.pid
    admin_users = user2, someadmin, otheradmin,postgres
    stats_users = stats, root,postgres
    #########################
    
    #创建users.txt文件
    sudo vim /usr/local/pgsql/share/doc/pgbouncer/users.txt
    #添加 "postgres" ""
    
    #start pgbouncer
    sudo /usr/local/pgsql/bin/pgbouncer -d /usr/local/pgsql/share/doc/pgbouncer/pgbouncer.ini
    
    #测试
    sudo -u postgres /usr/local/pgsql/bin/psql -p 6543 -d pgbouncer -U postgres
    pgbouncer=# show databases;
    
    sudo -u postgres /usr/local/pgsql/bin/psql -h 127.0.0.1 -p 6543 -d Node1 -U postgres
    sudo -u postgres /usr/local/pgsql/bin/psql -h 127.0.0.1 -p 6543 -d Node2 -U postgres
    

    == 10. 导入ltree测试数据 ==

    #在plproxy、Node1、Node2节点上安装ltree类型,包里面已经安装了。
    #如果没有,可以参考一下步骤
    #先安装 postgresql-8.3.0, 安装目录会保存在配置文件里。然后ltree模块:
    cd postgresql-8.3.0/contrib/ltree/
    make
    sudo make install
    #安装ltree类型
    sudo -u postgres /home/y/pgsql/bin/psql -d URTCluster -f /home/y/pgsql/sha
    re/contrib/ltree.sql
    #重新载入配置
    sudo -u postgres /home/y/pgsql/bin/pg_ctl -D /home/y/pgsql/data reload
    
    #在Node1、Node2节点上执行数据导入操作
    wget http://www.sai.msu.su/~megera/postgres/gist/ltree/dmozltree-eng.sql.gz
    gunzip dmozltree-eng.sql.gz
    sudo -u postgres /usr/local/pgsql/bin/psql -d URT -f dmozltree-eng.sql -p 5433
    

    == 11. 测试 ==

    #在plproxy节点上执行
    sudo -u postgres /usr/local/pgsql/bin/psql -d URT -p 5432
    
    URT=# SELECT  * from public.xquery('select * from dmoz limit 1 offset 10;') as dmoz(id int4, name text, path ltree);
    
    URT=# SELECT  * from public.xquery('select path from dmoz where path ~ ''Top.Adult.Arts.Animation.*{1}'';') as dmoz(id int4, name text, path ltree);
    

    [转]Locale 详解

    2009年06月29日 下午 01:34 | 作者:pangyt

    locale 是国际化与本土化过程中的一个非常重要的概念,个人认为,对于中文用户来说,通常会涉及到的国际化或者本土化,大致包含三个方面:看中文,写中文,与 window中文系统的兼容和通信。从实际经验上看来,locale的设定与看中文关系不大,但是与写中文,及window分区的挂载方式有很密切的关系。本人认为就像一个纯英文的Windows能够浏览中文,日文或者意大利文网页一样,你不需要设定locale就可以看中文。那么,为什么要设定 locale呢?什么时候会用到locale呢?

    Tags: locale 设定 原因 解释

    一、为什么要设定 locale 正如前面我所讲的,设定locale与你能否浏览中文的网页没有直接的关系,即便你把locale设置成en_US.ISO-8859-1这样一个标准的英文locale你照样可以浏览中文的网页,只要你的系统里面有相应的字符集(这个都不一定需要)和合适的字体(如simsun),浏览器就可以把网页翻译成中文给你看。具体的过程是网络把网页传送到你的机器上之后,浏览器会判断相应的编码的字符集,根据网页采用的字符集,去字体库里面找合适的字体,然后由文字渲染工具把相应的文字在屏幕上显示出来。

    在下文本人会偶尔把字符集比喻成密码本,个人觉得对于一些东西比较容易理解,假如你不习惯的话,把全文copy到任何文本编辑器,用字符集替换密码本即可。

    那有时候网页显示乱码或者都是方框是怎么回事呢?个人认为,显示乱码是因为设定的字符集不对(或者没有相应的字符集),例如网页是用UTF-8编码的,你非要用GB2312去看,而系统根据GB2312去找字体,然后在屏幕上显示,当然是一堆的乱码,也就是说你用一个错误的密码本去翻译发给你的电报,当然内容那叫一个乱;至于有些时候浏览的网页能显示一部分汉字,但有很多的地方是方框,能够显示汉字说明浏览器已经正确的判断出了网页的编码,并在字体库里面找到了相应的文字,但是并不是每个字体库都包含某个字符集全部的字体的缘故,有些时候会显示不完全,找一个比较全的支持较多字符集的字体就可以了。

    既然我能够浏览中文网页,那为什么我还要设定locale呢?

    其实你有没有想过这么一个问题,为什么gentoo官方论坛上中文论坛的网页是用UTF-8编码的(虽然大家一直强烈建议用GB2312编码),但是新浪网就是用GB2312编码的呢?而Xorg的官方网页竟然是ISO-8859-15编码的,我没有设定这个locale怎么一样的能浏览呢?这个问题就像是你有所有的密码本,不论某个网站是用什么字符集编码的,你都可以用你手里的密码本把他们翻译过来,但问题是虽然你能浏览中文网页,但是在整个操作系统里面流动的还是英文字符。所以,就像你能听懂英语,也能听懂中文。 最根本的问题是:你不可以写中文。

    当你决定要写什么东西的时候,首先要决定的一件事情是用那种语言,对于计算机来说就是你要是用哪一种字符集,你就必须告诉你的linux系统,你想用那一本密码本去写你想要写的东西。知道为什么需要用GB2312字符集去浏览新浪了吧,因为新浪的网页是用GB2312写的。

    为了让你的Linux能够输入中文,就需要把系统的locale设定成中文的(严格说来是locale中的语言类别LC_CTYPE ),例如zh_CN.GB2312、zh_CN.GB18030或者zh_CN.UTF-8。很多人都不明白这些古里古怪的表达方式。这个外星表达式规定了什么东西呢?这个问题稍后详述,现在只需要知道,这是locale的表达方式就可以了。

    二、到底什么是locale? locale这个单词中文翻译成地区或者地域,其实这个单词包含的意义要宽泛很多。Locale是根据计算机用户所使用的语言,所在国家或者地区,以及当地的文化传统所定义的一个软件运行时的语言环境。

    这个用户环境可以按照所涉及到的文化传统的各个方面分成几个大类,通常包括用户所使用的语言符号及其分类(LC_CTYPE),数字 (LC_NUMERIC),比较和排序习惯(LC_COLLATE),时间显示格式(LC_TIME),货币单位(LC_MONETARY),信息主要是提示信息,错误信息, 状态信息, 标题, 标签, 按钮和菜单等(LC_MESSAGES),姓名书写方式(LC_NAME),地址书写方式(LC_ADDRESS),电话号码书写方式 (LC_TELEPHONE),度量衡表达方式(LC_MEASUREMENT),默认纸张尺寸大小(LC_PAPER)和locale对自身包含信息的概述(LC_IDENTIFICATION)。

    所以说,locale就是某一个地域内的人们的语言习惯和文化传统和生活习惯。一个地区的locale就是根据这几大类的习惯定义的,这些locale定义文件放在/usr/share/i18n/locales目录下面,例如 en_US, zh_CN and de_DE@euro都是locale的定义文件,这些文件都是用文本格式书写的,你可以用写字板打开,看看里边的内容,当然出了有限的注释以外,大部分东西可能你都看不懂,因为是用的Unicode的字符索引方式。

    对于de_DE@euro的一点说明,@后边是修正项,也就是说你可以看到两个德国的locale: /usr/share/i18n/locales/de_DE@euro /usr/share/i18n/locales/de_DE 打开这两个locale定义,你就会知道它们的差别在于de_DE@euro使用的是欧洲的排序、比较和缩进习惯,而de_DE用的是德国的标准习惯。

    上面我们说到了zh_CN.GB18030的前半部分,后半部分是什么呢?大部分Linux用户都知道是系统采用的字符集。

    三、什么是字符集?字符集就是字符,尤其是非英语字符在系统内的编码方式,也就是通常所说的内码,所有的字符集都放在/usr/share/i18n/charmaps,所有的字符集也都是用Unicode编号索引的。Unicode用统一的编号来索引目前已知的全部的符号。而字符集则是这些符号的编码方式,或者说是在网络传输,计算机内部通信的时候,对于不同字符的表达方式,Unicode是一个静态的概念,字符集是一个动态的概念,是每一个字符传递或传输的具体形式。就像 Unicode编号U59D0是代表姐姐的“姐”字,但是具体的这个字是用两个字节表示,三个字节,还是四个字节表示,是字符集的问题。例如:UTF-8 字符集就是目前流行的对字符的编码方式,UTF-8用一个字节表示常用的拉丁字母,用两个字节表示常用的符号,包括常用的中文字符,用三个表示不常用的字符,用四个字节表示其他的古灵精怪的字符。而GB2312字符集就是用两个字节表示所有的字符。需要提到一点的是Unicode除了用编号索引全部字符以外,本身是用四个字节存储全部字符,这一点在谈到挂载windows分区的时候是非常重要的一个概念。所以说你也可以把Unicode看作是一种字符集(我不知道它和UTF-32的关系,反正UTF-32就是用四个字节表示所有的字符的),但是这样表述符号是非常浪费资源的,因为在计算机世界绝大部分时候用到的是一个字节就可以搞定的 26个字母而已。所以才会有UTF-8,UTF-16等等,要不然大同世界多好,省了这许多麻烦。

    做项目的麻烦事

    2009年06月29日 下午 33:04 | 作者:pangyt

    1、相册项目,老人没有做好用户信息统计工作,现在我们无法拿到确切的用户id列表,活跃度等信息,现在想做点工作,无法操作。
    2、新相册的统计工作也没有,但是我们有数据库 哈哈 记录了时间戳,勉强通过。
    3、新相册的管理工具的开发,应该是产品提需求,我们来讨论可行性,然后再实施。

    深入Mysql字符集设置

    2009年06月28日 下午 34:26 | 作者:pangyt

    作者:laruence(http://www.laruence.com/)
    · 本文地址: http://www.laruence.com/2008/01/05/12.html
    · 转载请注明出处

    基本概念

    • 字符(Character)是指人类语言中最小的表义符号。例如’A’、’B’等;

    • 给定一系列字符,对每个字符赋予一个数值,用数值来代表对应的字符,这一数值就是字符的编码(Encoding)。例如,我们给字符’A’赋予数值0,给字符’B’赋予数值1,则0就是字符’A’的编码;

    • 给定一系列字符并赋予对应的编码后,所有这些字符和编码对组成的集合就是字符集(Character Set)。例如,给定字符列表为{’A’,’B’}时,{’A’=>0, ‘B’=>1}就是一个字符集;

    • 字符序(Collation)是指在同一字符集内字符之间的比较规则;

    • 确定字符序后,才能在一个字符集上定义什么是等价的字符,以及字符之间的大小关系;

    • 每个字符序唯一对应一种字符集,但一个字符集可以对应多种字符序,其中有一个是默认字符序(Default Collation);

    • MySQL中的字符序名称遵从命名惯例:以字符序对应的字符集名称开头;以_ci(表示大小写不敏感)、_cs(表示大小写敏感)或_bin(表示按编码值比较)结尾。例如:在字符序“utf8_general_ci”下,字符“a”和“A”是等价的;
    MySQL字符集设置

    • 系统变量:

    – character_set_server:默认的内部操作字符集

    – character_set_client:客户端来源数据使用的字符集

    – character_set_connection:连接层字符集

    – character_set_results:查询结果字符集

    – character_set_database:当前选中数据库的默认字符集

    – character_set_system:系统元数据(字段名等)字符集

    – 还有以collation_开头的同上面对应的变量,用来描述字符序。

    • 用introducer指定文本字符串的字符集:

    – 格式为:[_charset] ’string’ [COLLATE collation]

    – 例如:

    • SELECT _latin1 ’string’;

    • SELECT _utf8 ‘你好’ COLLATE utf8_general_ci;

    – 由introducer修饰的文本字符串在请求过程中不经过多余的转码,直接转换为内部字符集处理。
    MySQL中的字符集转换过程

    1. MySQL Server收到请求时将请求数据从character_set_client转换为character_set_connection;

    2. 进行内部操作前将请求数据从character_set_connection转换为内部操作字符集,其确定方法如下:

    • 使用每个数据字段的CHARACTER SET设定值;

    • 若上述值不存在,则使用对应数据表的DEFAULT CHARACTER SET设定值(MySQL扩展,非SQL标准);

    • 若上述值不存在,则使用对应数据库的DEFAULT CHARACTER SET设定值;

    • 若上述值不存在,则使用character_set_server设定值。

    3. 将操作结果从内部操作字符集转换为character_set_results。
    mysql charactor settting
    图片1
    常见问题解析

    • 向默认字符集为utf8的数据表插入utf8编码的数据前没有设置连接字符集,查询时设置连接字符集为utf8

    – 插入时根据MySQL服务器的默认设置,character_set_client、character_set_connection和character_set_results均为latin1;

    – 插入操作的数据将经过latin1=>latin1=>utf8的字符集转换过程,这一过程中每个插入的汉字都会从原始的3个字节变成6个字节保存;

    – 查询时的结果将经过utf8=>utf8的字符集转换过程,将保存的6个字节原封不动返回,产生乱码……
    mysql charactor setting 2
    图片2

    • 向默认字符集为latin1的数据表插入utf8编码的数据前设置了连接字符集为utf8

    – 插入时根据连接字符集设置,character_set_client、character_set_connection和character_set_results均为utf8;

    – 插入数据将经过utf8=>utf8=>latin1的字符集转换,若原始数据中含有\u0000~\u00ff范围以外的Unicode字符,会因为无法在latin1字符集中表示而被转换为“?”(0×3F)符号,以后查询时不管连接字符集设置如何都无法恢复其内容了。
    mysql charactor setting 3
    图片3
    检测字符集问题的一些手段

    • SHOW CHARACTER SET;

    • SHOW COLLATION;

    • SHOW VARIABLES LIKE ‘character%’;

    • SHOW VARIABLES LIKE ‘collation%’;

    • SQL函数HEX、LENGTH、CHAR_LENGTH

    • SQL函数CHARSET、COLLATION
    使用MySQL字符集时的建议

    • 建立数据库/表和进行数据库操作时尽量显式指出使用的字符集,而不是依赖于MySQL的默认设置,否则MySQL升级时可能带来很大困扰;

    • 数据库和连接字符集都使用latin1时虽然大部分情况下都可以解决乱码问题,但缺点是无法以字符为单位来进行SQL操作,一般情况下将数据库和连接字符集都置为utf8是较好的选择;

    • 使用mysql C API时,初始化数据库句柄后马上用mysql_options设定MYSQL_SET_CHARSET_NAME属性为utf8,这样就不用显式地用 SET NAMES语句指定连接字符集,且用mysql_ping重连断开的长连接时也会把连接字符集重置为utf8;

    • 对于mysql PHP API,一般页面级的PHP程序总运行时间较短,在连接到数据库以后显式用SET NAMES语句设置一次连接字符集即可;但当使用长连接时,请注意保持连接通畅并在断开重连后用SET NAMES语句显式重置连接字符集。
    其他注意事项

    • my.cnf中的default_character_set设置只影响mysql命令连接服务器时的连接字符集,不会对使用libmysqlclient库的应用程序产生任何作用!

    • 对字段进行的SQL函数操作通常都是以内部操作字符集进行的,不受连接字符集设置的影响。

    • SQL语句中的裸字符串会受到连接字符集或introducer设置的影响,对于比较之类的操作可能产生完全不同的结果,需要小心!

    [转]MySQL索引分析和优化收藏

    2009年06月28日 下午 14:39 | 作者:pangyt

    [转]MySQL索引分析和优化收藏

    索引用来快速地寻找那些具有特定值的记录,所有MySQL索引都以B-树的形式保存。如果没有索引,执行查询时MySQL必须从第一个记录开始扫描整个表的所有记录,直至找到符合要求的记录。表里面的记录数量越多,这个操作的代价就越高。如果作为搜索条件的列上已经创建了索引,MySQL无需扫描任何记录即可迅速得到目标记录所在的位置。如果表有1000个记录,通过索引查找记录至少要比顺序扫描记录快100倍。

    假设我们创建了一个名为people的表:

    CREATE TABLE people ( peopleid SMALLINT NOT NULL, name CHAR(50) NOT NULL );

    然后,我们完全随机把1000个不同name值插入到people表。下图显示了people表所在数据文件的一小部分:
    1

    可以看到,在数据文件中name列没有任何明确的次序。如果我们创建了name列的索引,MySQL将在索引中排序name列:
    2

    对于索引中的每一项,MySQL在内部为它保存一个数据文件中实际记录所在位置的“指针”。因此,如果我们要查找name等于 “Mike”记录的peopleid(SQL命令为“SELECT peopleid FROM people WHERE name=’Mike’;”),MySQL能够在name的索引中查找“Mike”值,然后直接转到数据文件中相应的行,准确地返回该行的 peopleid(999)。在这个过程中,MySQL只需处理一个行就可以返回结果。如果没有“name”列的索引,MySQL要扫描数据文件中的所有记录,即1000个记录!显然,需要MySQL处理的记录数量越少,则它完成任务的速度就越快。

    索引的类型

    MySQL提供多种索引类型供选择:

    普通索引

    这是最基本的索引类型,而且它没有唯一性之类的限制。普通索引可以通过以下几种方式创建:

    创建索引,例如CREATE INDEX <索引的名字> ON tablename (列的列表);
    修改表,例如ALTER TABLE tablename ADD INDEX [索引的名字] (列的列表);
    创建表的时候指定索引,例如CREATE TABLE tablename ( [...], INDEX [索引的名字] (列的列表) );

    唯一性索引

    这种索引和前面的“普通索引”基本相同,但有一个区别:索引列的所有值都只能出现一次,即必须唯一。唯一性索引可以用以下几种方式创建:

    创建索引,例如CREATE UNIQUE INDEX <索引的名字> ON tablename (列的列表);
    修改表,例如ALTER TABLE tablename ADD UNIQUE [索引的名字] (列的列表);
    创建表的时候指定索引,例如CREATE TABLE tablename ( [...], UNIQUE [索引的名字] (列的列表)
    );

    主键

    主键是一种唯一性索引,但它必须指定为“PRIMARY KEY”。如果你曾经用过AUTO_INCREMENT类型的列,你可能已经熟悉主键之类的概念了。主键一般在创建表的时候指定,例如“CREATE TABLE tablename ( [...], PRIMARY KEY (列的列表) ); ”。但是,我们也可以通过修改表的方式加入主键,例如“ALTER TABLE tablename ADD PRIMARY KEY (列的列表); ”。每个表只能有一个主键。

    全文索引

    MySQL从3.23.23版开始支持全文索引和全文检索。在 MySQL中,全文索引的索引类型为FULLTEXT。全文索引可以在VARCHAR或者TEXT类型的列上创建。它可以通过CREATE TABLE命令创建,也可以通过ALTER TABLE或CREATE INDEX命令创建。对于大规模的数据集,通过ALTER TABLE(或者CREATE INDEX)命令创建全文索引要比把记录插入带有全文索引的空表更快。本文下面的讨论不再涉及全文索引,要了解更多信息,请参见MySQL documentation。

    单列索引与多列索引

    索引可以是单列索引,也可以是多列索引。下面我们通过具体的例子来说明这两种索引的区别。假设有这样一个people表:

    CREATE TABLE people ( peopleid SMALLINT NOT NULL AUTO_INCREMENT, firstname CHAR(50)
    NOT NULL, lastname CHAR(50) NOT NULL, age SMALLINT NOT NULL, townid SMALLINT NOT
    NULL, PRIMARY KEY (peopleid) );

    下面是我们插入到这个people表的数据:

    3

    这个数据片段中有四个名字为“Mikes”的人(其中两个姓Sullivans,两个姓McConnells),有两个年龄为17岁的人,还有一个名字与众不同的Joe Smith。

    这个表的主要用途是根据指定的用户姓、名以及年龄返回相应的peopleid。例如,我们可能需要查找姓名为Mike Sullivan、年龄17岁用户的peopleid(SQL命令为SELECT peopleid FROM people WHERE firstname=’Mike’ AND lastname=’Sullivan’ AND age=17;)。由于我们不想让MySQL每次执行查询就去扫描整个表,这里需要考虑运用索引。

    首先,我们可以考虑在单个列上创建索引,比如firstname、lastname或者age列。如果我们创建firstname列的索引(ALTER TABLE people ADD INDEX firstname (firstname);),MySQL将通过这个索引迅速把搜索范围限制到那些firstname=’Mike’的记录,然后再在这个“中间结果集”上进行其他条件的搜索:它首先排除那些lastname不等于“Sullivan”的记录,然后排除那些age不等于17的记录。当记录满足所有搜索条件之后,MySQL就返回最终的搜索结果。

    由于建立了firstname列的索引,与执行表的完全扫描相比,MySQL的效率提高了很多,但我们要求MySQL扫描的记录数量仍旧远远超过了实际所需要的。虽然我们可以删除firstname列上的索引,再创建lastname或者age 列的索引,但总地看来,不论在哪个列上创建索引搜索效率仍旧相似。

    为了提高搜索效率,我们需要考虑运用多列索引。如果为firstname、lastname和age这三个列创建一个多列索引,MySQL只需一次检索就能够找出正确的结果!下面是创建这个多列索引的SQL命令:

    ALTER TABLE people ADD INDEX fname_lname_age (firstname,lastname,age);

    由于索引文件以B-树格式保存,MySQL能够立即转到合适的firstname,然后再转到合适的lastname,最后转到合适的age。在没有扫描数据文件任何一个记录的情况下,MySQL就正确地找出了搜索的目标记录!

    那么,如果在firstname、lastname、age这三个列上分别创建单列索引,效果是否和创建一个firstname、lastname、age 的多列索引一样呢?答案是否定的,两者完全不同。当我们执行查询的时候,MySQL只能使用一个索引。如果你有三个单列的索引,MySQL会试图选择一个限制最严格的索引。但是,即使是限制最严格的单列索引,它的限制能力也肯定远远低于firstname、lastname、age这三个列上的多列索引。

    最左前缀

    多列索引还有另外一个优点,它通过称为最左前缀(Leftmost Prefixing)的概念体现出来。继续考虑前面的例子,现在我们有一个firstname、lastname、age列上的多列索引,我们称这个索引为fname_lname_age。当搜索条件是以下各种列的组合时,MySQL将使用fname_lname_age索引:

    firstname,lastname,age
    firstname,lastname
    firstname

    从另一方面理解,它相当于我们创建了(firstname,lastname,age)、(firstname,lastname)以及(firstname)这些列组合上的索引。下面这些查询都能够使用这个fname_lname_age索引:

    SELECT peopleid FROM people WHERE firstname=’Mike’ AND lastname=’Sullivan’ AND
    age=’17′; SELECT peopleid FROM people WHERE firstname=’Mike’ AND
    lastname=’Sullivan’; SELECT peopleid FROM people WHERE firstname=’Mike’; The
    following queries cannot use the index at all: SELECT peopleid FROM people WHERE
    lastname=’Sullivan’; SELECT peopleid FROM people WHERE age=’17′; SELECT peopleid
    FROM people WHERE lastname=’Sullivan’ AND age=’17′;

    选择索引列

    在性能优化过程中,选择在哪些列上创建索引是最重要的步骤之一。可以考虑使用索引的主要有两种类型的列:在WHERE子句中出现的列,在join子句中出现的列。请看下面这个查询:

    SELECT age ## 不使用索引 FROM people WHERE firstname=’Mike’ ## 考虑使用索引 AND
    lastname=’Sullivan’ ## 考虑使用索引

    这个查询与前面的查询略有不同,但仍属于简单查询。由于age是在SELECT部分被引用,MySQL不会用它来限制列选择操作。因此,对于这个查询来说,创建age列的索引没有什么必要。下面是一个更复杂的例子:

    SELECT people.age, ##不使用索引 town.name ##不使用索引 FROM people LEFT JOIN town ON
    people.townid=town.townid ##考虑使用索引 WHERE firstname=’Mike’ ##考虑使用索引 AND
    lastname=’Sullivan’ ##考虑使用索引

    与前面的例子一样,由于firstname和lastname出现在WHERE子句中,因此这两个列仍旧有创建索引的必要。除此之外,由于town表的townid列出现在join子句中,因此我们需要考虑创建该列的索引。

    那么,我们是否可以简单地认为应该索引WHERE子句和join子句中出现的每一个列呢?差不多如此,但并不完全。我们还必须考虑到对列进行比较的操作符类型。MySQL只有对以下操作符才使用索引:<,<=,=,>,>=,BETWEEN,IN,以及某些时候的LIKE。可以在 LIKE操作中使用索引的情形是指另一个操作数不是以通配符(%或者_)开头的情形。例如,“SELECT peopleid FROM people WHERE firstname LIKE ‘Mich%’;”这个查询将使用索引,但“SELECT peopleid FROM people WHERE firstname LIKE ‘%ike’;”这个查询不会使用索引。

    分析索引效率

    现在我们已经知道了一些如何选择索引列的知识,但还无法判断哪一个最有效。MySQL提供了一个内建的SQL命令帮助我们完成这个任务,这就是EXPLAIN命令。EXPLAIN命令的一般语法是:EXPLAIN 。你可以在MySQL文档找到有关该命令的更多说明。下面是一个例子:

    EXPLAIN SELECT peopleid FROM people WHERE firstname=’Mike’ AND lastname=’Sullivan’
    AND age=’17′;

    这个命令将返回下面这种分析结果:

    table type possible_keys key key_len ref rows Extra
    people ref fname_lname_age fname_lname_age 102 const,const,const 1 Where used

    下面我们就来看看这个EXPLAIN分析结果的含义。

    table:这是表的名字。

    type:连接操作的类型。下面是MySQL文档关于ref连接类型的说明:

    “ 对于每一种与另一个表中记录的组合,MySQL将从当前的表读取所有带有匹配索引值的记录。如果连接操作只使用键的最左前缀,或者如果键不是UNIQUE 或PRIMARY KEY类型(换句话说,如果连接操作不能根据键值选择出唯一行),则MySQL使用ref连接类型。如果连接操作所用的键只匹配少量的记录,则ref是一种好的连接类型。”

    在本例中,由于索引不是UNIQUE类型,ref是我们能够得到的最好连接类型。

    如果EXPLAIN显示连接类型是“ALL”,而且你并不想从表里面选择出大多数记录,那么MySQL的操作效率将非常低,因为它要扫描整个表。你可以加入更多的索引来解决这个问题。预知更多信息,请参见MySQL的手册说明。

    possible_keys:

    可能可以利用的索引的名字。这里的索引名字是创建索引时指定的索引昵称;如果索引没有昵称,则默认显示的是索引中第一个列的名字(在本例中,它是“firstname”)。默认索引名字的含义往往不是很明显。

    Key:

    它显示了MySQL实际使用的索引的名字。如果它为空(或NULL),则MySQL不使用索引。

    key_len:

    索引中被使用部分的长度,以字节计。在本例中,key_len是102,其中firstname占50字节,lastname占50字节,age占2字节。如果MySQL只使用索引中的firstname部分,则key_len将是50。

    ref:

    它显示的是列的名字(或单词“const”),MySQL将根据这些列来选择行。在本例中,MySQL根据三个常量选择行。

    rows:

    MySQL所认为的它在找到正确的结果之前必须扫描的记录数。显然,这里最理想的数字就是1。

    Extra:

    这里可能出现许多不同的选项,其中大多数将对查询产生负面影响。在本例中,MySQL只是提醒我们它将用WHERE子句限制搜索结果集。

    索引的缺点

    到目前为止,我们讨论的都是索引的优点。事实上,索引也是有缺点的。

    首先,索引要占用磁盘空间。通常情况下,这个问题不是很突出。但是,如果你创建每一种可能列组合的索引,索引文件体积的增长速度将远远超过数据文件。如果你有一个很大的表,索引文件的大小可能达到操作系统允许的最大文件限制。

    第二,对于需要写入数据的操作,比如DELETE、UPDATE以及INSERT操作,索引会降低它们的速度。这是因为MySQL不仅要把改动数据写入数据文件,而且它还要把这些改动写入索引文件。

    【结束语】

    在大型数据库中,索引是提高速度的一个关键因素。不管表的结构是多么简单,一次500000行的表扫描操作无论如何不会快。如果你的网站上也有这种大规模的表,那么你确实应该花些时间去分析可以采用哪些索引,并考虑是否可以改写查询以优化应用。要了解更多信息,请参见MySQL manual。另外注意,本文假定你所使用的MySQL是3.23版,部分查询不能在3.22版MySQL上执行。

    [转] PHPV6的新特性

    2009年06月28日 下午 36:49 | 作者:pangyt

    初步了解 PHP V6 中的新特性

    级别: 中级

    Deepak Vohra, Web 开发人员, 独立顾问

    2009 年 6 月 04 日

    简介

    “PHP 的特性” 简单介绍了 PHP V6 中的新特性。在本文中,了解这些让 PHP V6 更加容易使用、更加安全和更加适合国际化的新特性。本文探索改进的 Unicode 支持,删除了几个函数、改进的扩展、引擎添加内容、改进的面向对象函数和 PHP V6 中的一些扩展。

    回页首

    增强对 Unicode 的支持

    PHP V6 中的主要特性是增强对 Unicode 的支持。目前,PHP 实际上是一个二进制处理器。PHP V5 没有提供原生的 Unicode 支持;它假定所有字符的长度都为 1 字节,这在处理非拉丁字符时会出现问题。您可以转换到 Unicode,但需要使用 mbstring 扩展,而默认的 PHP V5 或外部工具(比如 iconv)都不支持该扩展。

    PHP V5 有时不能正确显示文本,这取决于字符编码。Unicode 是由 Unicode Consortium 开发的行业标准,它能表示所有语言、程序和平台上的每一个字符。各个行业和标准都广泛支持 Unicode,这使得实现国际化多语言应用程序成为可能。Unicode 可以使用多种字符编码表示,最常见的是 UTF-8、UTF-16 和 UTF-32。

    PHP V6 原生地支持 Unicode (UTF-8),因此它的引擎、扩展和 API 都支持 Unicode。尽管已经添加将所有二进制字符串作为不同类型处理的支持,PHP V6 仍然能够将字符串作为 Unicode 处理。在 PHP V6 中,字符串的字面量为 Unicode,允许使用 Unicode 标识符,并且它的函数能够理解 Unicode 文本。PHP V6 能够在需要时支持 Unicode 字符串和其他编码之间的转换,并支持对 UTF-8 文件进行读写。下面给出一个从 UTF-8 文件读取内容的例子:

    $input = fopen(‘inputfile.txt’, ‘rt’);
    $str = fread($input, 100); // returns 100 Unicode characters

    向 UTF-8 文本文件写一个 Unicode 字符串 $uni:

    $output = fopen(‘outputfile.txt’, ‘wt’);
    fwrite($fp, $uni); // writes out data in UTF-8 encoding

    Unicode 模式

    在 PHP V5 中,可以根据需求将 Unicode 模式设置为打开或关闭。这表明非 Unicode 和 Unicode 变体类、方法和函数名必须存储在符号表中,这导致更多的开销。PHP V6 在 php.ini 中提供一个服务器通用的配置设置,以启用或禁用 Unicode 模式。

    Opcode 缓存用于缓存已编译的 PHP 代码。
    服务器通用的配置使引擎的某个部分的实现更加容易,引起的 opcode 缓存问题更少,并且提高运行速度,因为不需要在运行时转换符号名。

    PHP V6 保留了禁用 Unicode 模式的选项,因为一些字符串函数的 Unicode 实现变慢了 300%,这使整个应用程序的速度变慢 25%。PHP V6 在 php.ini 中提供一个运行时配置选项,用于启用或禁用 Unicode 语义。默认设置为 on。

    unicode.semantics = on

    将 unicode.semantics 设置为 off 并不意味着不使用 Unicode。当设置为 off 时,您仍然可用访问 Unicode 特性。当 Unicode 语义设置为 off,字符串的字面量是 8 位的字符串;1 个字符等于 1 个字节。

    unicode.semantics = off
    $str = “Hello, world!”;// ASCII encoding
    echo strlen($str);//result is 13

    如果设置为 unicode.semantics=on,那么字符串字面量使用 Unicode 类型。当 Unicode 语义设置为 on 时,一个字符可能 > 1 个字节。

    unicode.semantics = on
    $str = “Hello, world!”;// Unicode string
    echo strlen($str);// result is 13

    PHP V6 中的 unicode.runtime_encoding 配置选项指定在运行时执行 Unicode 和二进制字符串之间的转换时使用哪种编码。例如,将运行时编码设置为 iso-8859-1。

    unicode.runtime_encoding = iso-8859-1

    当与尚未支持 Unicode 的函数连接时,仍然需要使用运行时编码。PHP 脚本可以采用各种编码方式。PHP V6 提供 unicode.script_encoding 配置选项来指定脚本的编码。不管脚本的编码是什么,生成的字符串字面量都为 Unicode 类型。

    unicode.script_encoding = iso-8859-1
    $uni = “”; // Unicode string
    unicode.script_encoding = utf-8
    $uni = “Atildel”; // also Unicode string

    您还可以将 declare() 语句作为 PHP 脚本的第一个语句,这样也可以设置脚本的编码。declare() 构造器覆盖 php.ini 设置。declare() 设置不会传播到文件。

    unicode.script_encoding = utf-8
    declare(encoding=”iso-8859-1″);
    $uni = “Atildel”;// read as ISO-8859-1 string
    include “inputfile.php”;// file is read as UTF-8

    unicode.output_encoding 配置选项指定标准输出流所使用的编码,包括 echo、print 和 var_dump() 函数。输出流的编码被即时转换。unicode.output_encoding 配置选项不影响二进制字符串。

    unicode.output_encoding = utf-8
    unicode.script_encoding = iso-8859-1
    $unicode = “Atildel”; // Unicode string (from ISO-8859-1)
    echo $unicode; // converts $unicode to UTF-8 encoding
    echo b”Atildel”; // no conversion, raw contents

    如果启用了 Unicode 语义,HTTP 输入必须转换为 Unicode。GET 请求没有编码,并且也很少指定 POST 请求的编码。PHP V6 提供 unicode.http_input_encoding 配置选项来指定将 HTTP 输入转换为哪种 Unicode 编码。例如,将 HTTP 输入转换为 UTF-8:

    unicode.http_input_encoding = utf-8

    PHP 将尝试根据 unicode.http_input_encoding 设置进行解码。如果解码失败,PHP 将使用原始的二进制数据填充请求数组。unicode.filename_encoding 配置选项指定文件系统上文件和目录名的编码。

    unicode.filename_encoding = utf-8

    当输入和输出文件名时,与文件系统相关的函数执行所需的转换。PHP V6 提供 unicode.fallback_encoding 配置选项来指定 fallback 编码。

    unicode.fallback_encoding = iso-8859-1

    当没有给其他编码分配值时,将使用 fallback 编码。fallback 编码的默认值是 UTF-8。在 PHP V6 中,您可以使用不同的字符编码;当启用 unicode.semantics 选项时,PHP V6 将所有字符串字面量转换为 Unicode 字符串。您可以无缝地对输入和输出使用不同的字符。PHP V6 根据 php.ini 中指定的配置选项对 HTTP 输入和输出进行编码。数据库和用户代理程序能够收到所需的字符编码,而不需使用任何转换函数。

    您不一定要使用 Unicode 开发脚本来处理和输出 Unicode,但我们推荐这么做。如前所述,通过 unicode.script_encoding 配置选项指定脚本编码。为了在数据中(比如 MySQL)存储 Unicode 数据,数据库不需要配置为 UTF-8 编码,但这样做比较好。MySQL 数据库被配置为运行不同的字符集,PHP V6 将以 Unicode 编码发送查询,而 MySQL 将尝试转换它。

    两种字符串类型

    PHP V5 的字符串类型实现有很多字符串类型(二进制、字符串和 Unicode),并且有很多内部引擎函数和帮助函数的实现。PHP V6 只有两种字符串类型:二进制和 Unicode。unicode.semantics 转换(switch)决定字符串字面量的默认类型。如果启用了 unicode.semantics,默认的字符串字面量就为 Unicode。如果禁用该语义转换,默认字符串字面量的类型就为二进制。您可以显式地在 Unicode 和二进制字符串类型之间转换,也可以隐式地进行转换。使用 unicode.runtime_encoding 配置选项进行隐式转换。表 1 显示了如何进行显式转换。

    表 1. 转换字符串类型
    类型转换 描述
    (binary) 转换到二进制字符串类型
    (unicode) 转换到 Unicode 字符串类型
    (string) 如果 unicode.semantics 设置为 on,就转换到 Unicode 字符串类型,否则转换到二进制字符串类型

    PHP V6 在内部使用 IS_STRING 表示二进制字符串数据,以及使用 IS_UNICODE 表示 Unicode 字符串数据。

    在扩展中支持 Unicode

    PHP V6 包含了 Unicode/API,扩展可以借助它实现 Unicode 支持。需要向扩展模块结构添加一个标志,以表明它是否支持 Unicode。如果扩展不支持 Unicode,并且在 PHP 中启用了 Unicode 语义,那么在启动时将不加载它。Unicode 支持被添加到 PHP 数据对象中 (PDO)。

    ICU 库

    International Components for Unicode (ICU) 是一组提供 Unicode 和全球化支持的库。PHP V6 需要使用 ICU,但 ICU 是一个大型的库,将增加 PHP 的下载体积。PHP 要求使用 ICU V3.4,并且要求发布版提供 ICU 库。

    文件名编码

    不同的文件名可能使用不同的字符集进行编码。例如,Windows 文件名使用 UTF-16 编码。filename_encoding 设置指定期望的文件名编码。当 read 函数(比如 readdir() 和 readfile())遇到其编码在 filename_encoding 中不存在的文件名时,将返回一个二进制字符串。

    缓存校对器

    校对器(Collator)用于比较字符串。打开和关闭校对器需要引入开销。PHP V6 缓存了校对器,但限制存储在缓存中的对象数量,以防止使用过多的内存。PHP V6 在线程/进程通用缓存中存储最近打开的 3 个校对器。

    基于位置的函数

    对于使用系统位置的 PHP 函数,由于位置名在不同的平台中不一致甚至不可用,因此这是一个问题。PHP V6 仅在合适的地方使用基于位置的函数。基于位置的函数包括 strtoupper/strtolower 和 stristr 等。strtoupper 函数将字母表中的字符都转换为大写的。“字母表的字符顺序” 由当前的位置决定。

    字符集转换错误

    在字符编码之间执行转换可能发生转换错误,因为并不是源字符串中的所有字符都有必要存储在目标字符串中。PHP V6 为字符集转换失败提供一个额外的错误模式,它会在失败时抛出异常。PHP V6 为处理转换错误引入了两个新的 php.ini 配置选项:unicode.from_error_mode 和 unicode.from_error_subst_char。

    unicode.from_error_mode = U_INVALID_SUBSTITUTE
    unicode.from_error_subst_char = 3f

    unicode.from_error_subst_char 选项为无效的字符串指定了代替字符串的十六进制值;它的默认值是 3f。

    unicode.from_error_mode 选项指定错误模式,它在遇到无效字符串时采取行动。表 2 显示了 unicode.from_error_mode 可能使用的值。

    表 2. unicode.from_error_mode 值
    常量 值 说明
    U_INVALID_STOP 0 遇到第一个无效字符时停止
    U_INVALID_SKIP 1 跳过无效字符
    U_INVALID_SUBSTITUTE 2 替换无效字符;默认值
    U_INVALID_ESCAPE 3 对无效字符进行转义

    回页首

    删除了几个函数

    这个小节概述了在 PHP V6 中删除的几个函数。

    删除了 register_globals

    PHP V5 在 php.ini 中使用 register_globals 配置选项从环境和 HTTP 请求参数定义变量。不过,register_globals 生成不安全的代码。因此,在 PHP V6 中删除了 register_globals。

    register_globals 生成不安全代码的方式之一是 authenticate.php,它使用 $auth 变量对用户进行身份验证。因为 $auth 没有被初始化,所以它的值可能通过使用 register_globals 注入变量来定义,比如 GET authenticate.php?auth=1。

    if (authenticated_user()) {
    $auth = true;
    }
    if ($auth) {
    include "/data.php";
    }
    ?>

    register_globals 注入的变量也可能在会话中生成不安全的代码。看看以下代码,当设置了 $username 变量时,将在用户会话中显示一条欢迎消息。但是,当启用 register_globals 时,可能会使用 HTTP 请求参数注入 $username 变量。

    if (isset($_SESSION['username'])) {
    echo "Welcome {$_SESSION['username']}";
    } else {
    echo "Welcome Guest";
    }

    ?>

    所有使用 register_globals 的函数都被删除了,比如 session_register。如果需要导入请求变量,可以使用 import_request_variables。或者使用 $_POST、$_GET、$_COOKIE 和 $_REQUEST 变量。如果设置了 register_globals,PHP V6 将在启动时抛出 E_CORE_ERROR。

    删除了 magic_quotes

    在 PHP V5 中,当启用了 magic_quotes 配置选项时,它会在输入数据中自动将所有单引号(’)、双引号(”)、反斜杠(\)和 NULL 字符转义为带反斜杠的(\)PHP 脚本。Magic Quotes 减少了 SQL 注入(一种能够篡改 SQL 命令的技术)的风险。Magic Quotes 便于将数据插入到数据库中。不 使用 Magic Quotes 的理由是考虑到移植性、性能和方便性。

    您需要知道是否关闭了 Magic Quotes。如果 Magic Quotes 处于 on,而您认为它处于 off,应用程序就会出现额外的斜杠。如果 Magic Quotes 处于 off,而您认为它处于 on,应用程序就可能遭受 SQL 注入攻击。Magic Quotes 会损害性能,因为并不是所有转义数据都被插入到数据库。一个更好的代替办法是在运行时使用转义函数,比如 addslashes。Magic Quotes 可能带来不便,因为并不是所有数据都需要转义。可以使用 stripslashes 函数删除多余的斜杠。

    PHP V6 不再支持 magic_quotes、magic_quotes_sybase 和 magic_quotes_gpc 设置。如果在启动时发现这些设置,PHP 将发出一个 E_CORE_ERROR。此外,还删除了 get_magic_quotes_gpc() 函数。

    删除了 safe_mode

    在 PHP V5 中,safe_mode 配置选项可用于解决与共享服务器有关的安全问题。它确保需要打开或包含的文件的用户必须与执行这些文件的脚本的用户相同。safe_mode 并不是绝对安全的。

    考虑这样一个场景,一个应用程序使用 Web 服务器的 ID 并生成缓存文件或映像。如果该应用程序由客户机用户加载,PHP 脚本将不能打开缓存文件或映像,因为用户 ID 不匹配。并且,safe_mode 可以绕过库。

    在 PHP V6 中删除了 safe_mode 选项。如果检测到 safe_mode 设置,将抛出 E_CORE_ERROR。open_basedir 配置仍然可用,它限制 PHP 可以在特定目录树中打开的文件。

    删除不赞成使用的行为

    在 PHP V6 中删除了一些在早期的 PHP 版本中不赞成使用的行为。allow_call_time_pass_reference 配置指定在运行时通过引用传递参数时是否发出警告,它在 PHP V5 中就不推荐使用。在 PHP V6 中删除了 allow_call_time_pass_reference。

    如果要指定哪个参数是通过引用传递的,更好的方法是使用声明函数。在 PHP V6 中,call-time-pass-by-reference 将抛出一个 E_STRICT 错误。在 PHP V6 中,“var” 已经成为 “public” 的别名,并且删除了 E_STRICT 警告。受 “new lt;object-name>” 的影响,也删除了 return-by-reference。通过引用赋值 “new” 将抛出一个 E_STRICT 错误。例如,下面的语句将抛出 E_STRICT 错误:

    删除了 zend.ze1_compatibility_mode

    zend.ze1_compatibility_mode 配置启用了与 Zend Engine V1 (PHP V4) 的兼容性,它是在 PHP V5 引入的,方便了从 PHP V4 迁移到 PHP V5。

    在 PHP V6 中,已经删除了 zend.ze1_compatibility_mode 特性,如果检测到该设置,将抛出 E_CORE_ERROR。

    删除了 Freetype 1 和 GD 1 库

    FreeType 1 和 GD 1 是老版本的字体呈现和图像处理库。它们不再受到维护,并且已被更新版本的 FreeType 和 GD 代替。在 PHP V6 中,删除了对 FreeType 1 和 GD 1 库的支持。

    默认情况下不启用 dl()

    dl (string $library) 函数用于在运行时加载 PHP 扩展。在 PHP V5 中,可以在 php.ini 配置文件中启用或禁用 dl() 函数:enable_dl = On。

    dl() 函数会给从未加载过的模块带来问题。dl() 函数不能在多线程服务器中正常工作,比如 IIS 或 Zeus,因为在这些服务器中会自动禁用它。不过 dl() 函数在 PHP Command Line Interface (CLI) Server Application Programming Interface (SAPI) 中非常有用。

    在 PHP V6 中,dl() 函数在默认情况下是禁用的,但并没有删除它。SAPI 层可能会显式地注册 dl() 函数。

    FastCGI 模式

    FastCGI 提供很高的性能,如果禁用,将导致杂乱的代码。在 PHP V6 中不能禁用 FastCGI。

    删除了 register_long_arrays

    当启用 register_long_arrays 配置选项时,将注册很长的超级全局变量 — $HTTP_*_VARS 变量。$HTTP_*_VARS 变量是在 PHP V5 中为了向后兼容性而引入的,但不是必要的。使用更短的变量 $_GET、$_POST 和 $_SERVER 代替它会更好。在新的 PHP 版本中,register_long_arrays 设置和 $HTTP_*_VARS 没有得到好评。

    在 PHP V6 中,删除了 register_long_arrays 设置和 $HTTP_*_VARS 全局变量,如果检测到 register_long_arrays,将抛出 E_CORE_ERROR。

    删除 break $var

    动态的 break 操作数不起作用,如以下示例所示:

    for (..) { $var = rand(1, 2); break $var; }

    在 PHP V6 中,删除了动态的 break 操作数。取而代之的是,您可以为 $var 分配一个数字,如下所示:

    for (..) { $var = rand(1, 2); break 1; }

    回页首

    改进扩展

    扩展是 PHP 的主要组件。在 PHP V6 中,有几个组件得到了改进。

    包含在核心发布版中的 XML 扩展

    XMLReader 扩展提供一个 XML 解析器,用于阅读 XML 文件,该解析器在内部基于 SAX 解析。XMLWriter 为写 XML 文件提供一个 API。XMLReader 和 XMLWriter 使 XML 文件的读写更加容易。PHP V5 的核心发布版没有包含 XMLReader 和 XMLWriter。

    PHP V6 将在核心发布版中包含 XMLReader 和 XMLWriter 扩展,并且默认启用这两个扩展。

    正则表达式扩展

    PHP V5.x 为正则表达式提供两个库:ereg 和 Perl Compatible Regular Expression (PCRE)。ereg 库支持 Portable Operating System Interface (POSIX) 正则表达式,而 PCRE 扩展支持与 Perl 兼容的语法。打包的 ereg 库会造成问题,因此被转换成扩展并从核心发布版移动到 PHP Extension Community Library (PECL)。

    PCRE 扩展提供更多的特性,并且比 ereg 扩展快。PCRE 扩展在默认情况下是启用的,并且不可以禁用它。一些核心的 PHP 函数使用 POSIX 正则表达式。因为删除了 ereg 库,所以这些函数将使用 PCRE 表达式重新编写。PCRE 表达式提供一些与 ereg 库等效的函数。

    表 3. PCRE 扩展中的等效函数
    ereg() 函数 等效的 PCRE 函数
    ereg() preg_match()
    ereg_replace() preg_replace()

    MIME 类型检查扩展

    在 PHP V5 中,mime_magic 扩展用于媒体类型(media-type)检测,但不是很可靠。PECL 为 MIME 类型检查提供另一个扩展:Fileinfo。mime_magic 扩展将从核心发布版移动到 PECL。Fileinfo 扩展将添加到核心发布版,并默认启用。

    默认启用 SOAP 扩展

    SOAP 扩展(ext/soap)用于开发使用 Web 服务的 PHP 应用程序,包括 SOAP 服务器和 SOAP 客户机。在 PHP V5 中,SOAP 扩展不是默认启用的;开发人员必须配置 SOAP 扩展。在 PHP V6 中,默认启用 SOAP 扩展。PHP V6 还实现一些安全扩展。

    回页首

    引擎的添加内容

    在 PHP V6,将添加 64 位的整数,同时保留 32 位整数。64 位引擎的转换名为 int64。将使用一个静态标签扩展 break 关键字。将从 ?: 操作符丢弃中间参数。例如,在下面的表达式中,如果 $_GET['var'] 计算为 true,则 $var 设置为 3。如果 $_GET['var'] 计算为 false 或没有设置,则 $var 设置为 $_GET['var']。

    $var = $_GET['var'] ?: 3;

    如果 $_GET['var'] 没有设置,将抛出 E_NOTICE。PHP V6 将规定对多维数组使用 foreach 语法。

    $a = array(array(1, 2), array(3, 4));
    foreach( $a as $k => list($a, $b)) {}
    ?>

    在 PHP V5 中,{} 和 [] 都可用于访问字符串和数组元素中的字符。在 PHP V6 中,将删除 {},并且继续使用 [],而 [] 操作符将支持 substr()/array_slice() 函数。在 PHP V6 中,microtime() 在默认情况下返回一个浮点值。

    回页首

    OO 函数

    在 PHP V6 中对 OO 函数进行了一些改进。添加了关键字 static:: 用于延迟静态绑定,这意味着将执行运行时静态数字计算。

    支持名称空间

    PHP V5 不支持名称空间,而是在一个全局名称空间中定义所有函数和变量。PHP V6 通过关键字 namespace 添加对名称空间的支持。为了定义一个已经定义的类名或函数名,必须使用名称空间将新的类或函数与已经存在的类或函数区分开来。XMLReader 是一个已经存在的类,但是假设您仍然想定义另一个 XMLReader。您应该这样定义 XMLReader 类:

    namespace XmlNamespace { class XMLReader { }}
    $reader = new XmlNamespace\XMLReader();
    $reader2= new XMLReader();
    ?>

    为了在 XmlNamespace 中调用 XMLReader 类,需要使用前缀 XmlNamespace\。名称空间中可以包含类和函数,但不能包含变量。

    支持类型提示返回值

    PHP V5 仅支持类型提示(其中定义了类型)参数。PHP V6 支持类型提示返回值。

    静态和动态方法调用

    在 PHP V5 中,可以对方法进行静态或动态调用,而不管它是否标记为 static。例如,在下面的清单中,进行了静态和动态方法的调用:

    class classA { static function aStatic() { echo "static function\n"; }
    function bDynamic() { echo "dynamic function\n"; }}
    classA::aStatic("static call");
    classA::bDynamic("static call");
    $classA = new classA;
    $classA->aStatic(“dynamic call”);
    $classA->bDynamic(“dynamic call”);
    ?>

    在 PHP V6 中,使用静态调用语法调用动态函数将抛出一个 E_FATAL 错误,反之亦然。

    回页首

    PHP 的添加内容

    opcode 缓存改善了性能。另一个 PHP cache(APC)扩展曾经使用过一段时间,它也将添加到核心 PHP 发布版。默认情况下将不启用 APC 扩展。提供其他安全性的 Hardened PHP 补丁的一些特性已经合并到 PHP。

    现在,许多 PHP 扩展甚至对可修复错误使用 E_ERROR。E_ERROR 终止脚本的运行。因此,在 PHP V6 中,扩展中仅对不可修复的错误使用 E_ERROR。E_STRICT 错误表明这是语言级别的警告或错误,目前还没有包含在 E_ALL 中。在 PHP V6 中,已经将 E_STRICT 添加到 E_ALL。在 PHP V6 中,将为 PHP 添加 MySQL 原生驱动器。并且添加对大于 2 GB 的文件的支持。

    在 PHP V6 中,将删除 ASP 样式的标记 (<% %>)。但会保留 PHP 短标记 ()。

    回页首

    结束语

    本文介绍了 PHP V6 中的新特性和已更改的特性。其中最主要的特性就是对 Unicode 的支持。删除了一些配置选项,比如 register_globals、magic_quotes、safe_mode 和 register_long_arrays。改进了扩展支持和 OO 函数。PHP V5.3 支持 PHP V6 的 50% 的特性。

    来源:http://www.ibm.com/developerworks/cn/opensource/os-php-v6/index.html

    正则表达式元字符

    2009年06月28日 下午 43:10 | 作者:pangyt

    http://www.phpweblog.net/fuyongjie/archive/2009/03/11/6375.html

    [^xyz] 字符的补集,除xyz之外的字符
    ^-?[0-9]*.?[0-9]*$ 所有小树
    通用字符簇:
    [[:alpha:]] 任何字母
    [[:digit:]] 任何数字
    ============
    常用的正则表达式
    1、非负整数:”^\d+$”
    2、正整数:”^[0-9]*[1-9][0-9]*$”
    3、非正整数:”^((-\d+)|(0+))$”
    4、负整数:”^-[0-9]*[1-9][0-9]*$”
    5、整数:”^-?\d+$”
    6、非负浮点数:”^\d+(\.\d+)?$”
    7、正浮点数:”^((0-9)+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$”
    8、非正浮点数:”^((-\d+\.\d+)?)|(0+(\.0+)?))$”
    9、负浮点数:”^(-((正浮点数正则式)))$”
    10、英文字符串:”^[A-Za-z]+$”
    11、英文大写串:”^[A-Z]+$”
    12、英文小写串:”^[a-z]+$”
    13、英文字符数字串:”^[A-Za-z0-9]+$”
    14、英数字加下划线串:”^\w+$”
    15、E-mail地址:”^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$”
    16、URL:”^[a-zA-Z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\s*)?$”
    =============
    Perl和POSIX表达式的写法区别
    preg ereg
    \d+ [0-9]*
    [abc\d+] abc[0-9]* //perl中字符都要加上分隔符[]或()或||
    \w [A-Za-z0-9_]
    [log]$i ereg中模式修正符i,m,s等无效,所以最接近的表达式是: log$
    perl中字符串必须包含在[],()之间或是紧邻^、$
    ====================================
    基本元字符
    元字符 说明
    . 匹配任意单个字符
    | 逻辑或操作符
    [] 定义一个字符集合,匹配该集合中的一个字符
    [^] 对字符集合求非(是对整个集合求非,而不是紧挨着^符号的字符)
    - 在字符集合中定义一个区间。如[A-Za-z]
    \ 对下一个字符转义。比如\n表示换行。

    数量元字符
    元字符 说明
    * 匹配前一个字符(子表达式)零次或多次
    *? *的懒惰型版本(防止正则表达式的“贪婪性”)
    + 匹配前一个字符或子表达式一次或多次
    +? +的懒惰型版本
    ? 匹配前一个字符或子表达式零次或一次
    {n} 匹配前一个字符或子表达式的n次重复,比如[A-Z]{6}表示匹配由六个大写字母组成的字符串。
    {m,n} 匹配至少m次至多n次
    {m,} 匹配至少m次
    {m,}? {m,}的懒惰型版本

    位置元字符
    元字符
    说明
    ^ 行首
    $ 行尾
    \< 单词开头
    \> 单词结尾
    \b 单词边界(单词的开头和结束)
    \B \b的反义

    特殊字符元字符
    元字符 说明
    [\b] 匹配一个退格字符
    \c 匹配一个控制字符
    \d 匹配任意一个数字字符,等价于[0-9]
    \D \d的反义
    \f 换页符
    \n 换行符
    \r 回车符
    \s 匹配一个空白字符
    \S \s的反义
    \t 制表符
    \v 垂直制表符
    \w 匹配任意字母、数字、下划线。等价于[A-Za-z0-9_]
    \W \w的反义
    \x 匹配一个十六进制数字
    \0 匹配一个八进制数字

    回溯引用和前后查找
    元字符 说明
    () 定义一个子表达式
    \1 第一个子表达式,同理\2表示第2个子表达式。\0通常表示整个正则表达式。
    ?= 向前查找
    ?<= 向后查找
    ?! 负向前查找
    ?!= 负向后查找
    ?() 条件(if then)
    ?()| 条件(if then else)

    ssh 和 rsync 的使用笔记

    2009年06月28日 下午 42:45 | 作者:pangyt

    参考资料:
    ssh http://suso.org/docs/shell/ssh.sdf
    rsync
    摘录:
    ssh-keygen -t dsa
    scp ~/.ssh/id_dsa.pub username@arvo.suso.org:.ssh/authorized_keys
    chmod 700 ~/.ssh
    chmod 600 ~/.ssh/authorized_keys
    ps auxw | grep ssh-agent
    ssh-add
    —– tcp端口转发—–
    ssh -L 3306:mysql.suso.org:3306 username@arvo.suso.org
    The -L (which means Local port) takes one argument of
    ::
    ———-
    ssh -R 8022:localhost:22 username@my.home.ip.address
    [转]:ssh的三个强大的端口转发命令:

    QUOTE:
    ssh -C -f -N -g -L listen_port:DST_Host:DST_port user@Tunnel_Host
    ssh -C -f -N -g -R listen_port:DST_Host:DST_port user@Tunnel_Host
    ssh -C -f -N -g -D listen_port user@Tunnel_Host
    -f Fork into background after authentication.
    后台认证用户/密码,通常和-N连用,不用登录到远程主机。

    -p port Connect to this port. Server must be on the same port.
    被登录的ssd服务器的sshd服务端口。

    -L port:host:hostport
    将本地机(客户机)的某个端口转发到远端指定机器的指定端口. 工作原理是这样的, 本地机器上分配了一个 socket 侦听 port 端口, 一旦这个端口上有了连接, 该连接就经过安全通道转发出去, 同时远程主机和 host 的 hostport 端口建立连接. 可以在配置文件中指定端口的转发. 只有 root 才能转发特权端口. IPv6 地址用另一种格式说明: port/host/hostport

    -R port:host:hostport
    将远程主机(服务器)的某个端口转发到本地端指定机器的指定端口. 工作原理是这样的, 远程主机上分配了一个 socket 侦听 port 端口, 一旦这个端口上有了连接, 该连接就经过安全通道转向出去, 同时本地主机和 host 的 hostport 端口建立连接. 可以在配置文件中指定端口的转发. 只有用 root 登录远程主机才能转发特权端口. IPv6 地址用另一种格式说明: port/host/hostport

    -D port
    指定一个本地机器 “动态的’’ 应用程序端口转发. 工作原理是这样的, 本地机器上分配了一个 socket 侦听 port 端口, 一旦这个端口上有了连接, 该连接就经过安全通道转发出去, 根据应用程序的协议可以判断出远程主机将和哪里连接. 目前支持 SOCKS4 协议, 将充当 SOCKS4 服务器. 只有 root 才能转发特权端口. 可以在配置文件中指定动态端口的转发.

    -C Enable compression.
    压缩数据传输。

    -N Do not execute a shell or command.
    不执行脚本或命令,通常与-f连用。

    -g Allow remote hosts to connect to forwarded ports.
    在-L/-R/-D参数中,允许远程主机连接到建立的转发的端口,如果不加这个参数,只允许本地主机建立连接。注:这个参数我在实践中似乎始终不起作用,参见III)

    实例说明:
    一台服务器提供ftp服务,因为ftp传输是明文密码,如果不做ssh端口之前,我们可以通过tcpdump命令很容易的捕捉到明文信息。所以我们要对21端口进行转发:
    (ftp-server)# ssh -CNfg -R 2121:localhost:21 root@10.4.2.50
    然后登录到10.4.2.50机器,我们可以通过netstat -an|grep :2121查看端口已经侦听
    (10.4.2.50)# ftp localhost 21就可以登录到ftp-server了,而且tcpdump无法捕获到有效的信息。
    2121端口任意选择,只要是机器上没有占用的端口就行

    来一个稍微复杂一点的,做网关的例子:
    假如内网有一台提供ftp(linux,port is 2121,称为A机器)的机器,通过网关服务器(linux,port is 8888,称为B机器)进去,现在外网有一台C机器需要访问网关服务器的某个端口(port is 21)来访问内网的ftp服务器。大家可以看到,其实这就像是一个基于ssh的防火墙程序,好,下面我们来具体操作:
    1。login A 机器
    # ssh -CNfg -R 8888:localhost:2121 root@B机器IP
    这样我们就在B机器上开了一个8888->2121的端口转换,但是由于8888端口只能侦听在localhost主机上,因此,虽然我们已经可以在B机器上使用
    # ftp localhost 8888 来访问真正的ftp服务器,但仍然无法提供给外网的机器访问

    2。login B机器
    # ssh -CNfg -L 21:localhost:8888 root@localhost
    这样做,是做本地机器上的21->8888端口转换,可以侦听在任何地址上的请求。
    2(1)。
    如果C机器也是一台linux机器,那也可以这样设置:
    # ssh -CNfg -R 21:localhost:8888 root@C机器IP

    3。使用C机器,可以是linux下的ftp命令,也可以是windows下的客户端软件都可以访问B机器的21端口来连接后台真正的ftp服务器(真正的端口是2121)
    如果是按照2(1)中的设置,则访问的地址为本机IP。

    简单介绍一个在linux下使用的设置ssh端口转换的程序

    http://gstm.sourceforge.net/?page_id=5

    [p_w_upload=1197]

    在windows机器下使用putty也可以建立端口转发
    假设从windows机器上将本地的8888端口转发到B的21端口,可以做如下设置
    Connection->SSH-Tunnels中可以设置putty的端口转发,Source port为listen_port,填8888,Destionation为DST_Host:DST_port,填写B机器IP:21,设置完了点Add.
    注意是local还是remote?
    [p_w_upload=1198]
    设置好后,我们可以在dos下用netstat命令看一下是否开启了本地的转发端口
    [p_w_upload=1199]
    接下来我们访问localhost的8888端口来访问ftp服务器了

    [转]PHP沉思录之五

    2009年06月28日 上午 38:34 | 作者:pangyt

    Session处理是所有的Web应用都必须面对的问题。PHP中对session有效期的处理,和其他的解决方案有着很大的不同,这是和PHP的工作机制相关的。

      在传统的client/server应用中,对于session失效的情况,可以交给网络协议自己来处理。无论是client端主动关闭连接,还是因为网络异常而导致的连接中断,server端都能够得到通知,触发连接中断的事件。只要编程响应这一事件,执行指定的操作即可。但对于web应用来说,情况却完全不一样。HTTP协议本身是无状态的,也就是说,每当client/server完成一次请求/响应的过程后,连接就会被断开。在断开连接以后,server并不知道client是否继续“在线”,还会继续发送下一次请求。换句话说,无论client端的用户已经关闭了浏览器窗口,还是用户仅仅在阅读当前网页并准备在下一秒钟继续浏览,或者用户因为Windows崩溃/停电/硬盘坏掉/网线被拔/地球爆炸而彻底无法再发送下一个请求,server都一无所知。(在HTTP 1.1中,浏览器可以通过keep-alive参数,来通知server不要在响应请求后主动断开连接,从而实现物理上的长连接。但是,这只是为了提高网络传输的性能而采取的措施,HTTP在逻辑上仍然是无状态的。)因此,只能通过某种模拟的方式来判断当前session是否有效。如果某个session 在超过一段时间后没有对server端发出请求,server都会判断用户已经“离线”,当前session失效,并触发连接中断的事件。要做到这一点,server需要运行一个后台线程,定时扫描所有的session信息,判断session是否已经超时。

      PHP处理session的原理也不例外,但是在具体的实现方式上,却与众不同。这是因为,由于PHP的工作机制,它并没有一个后台线程,来定时地扫描session信息并判断其是否失效。它的解决之道是,当一个有效请求发生时,PHP会根据某个概率,来决定是否调用一个GC(Garbage Collector)。GC的工作,就是扫描所有的session信息,用当前时间减去session的最后修改时间(modified date),同配置参数(configuration option)session.gc_maxlifetime的值进行比较,如果生存时间已经超过gc_maxlifetime,就把该session删除。这是很容易理解的,因为如果每次请求都要调用GC代码,那么PHP的效率就会低得令人吃不消了。这个概率取决于配置参数 session.gc_probability/session.gc_divisor的值(可以通过php.ini或者ini_set()函数来修改)。默认情况下,session.gc_probability = 1,session.gc_divisor=100,也就是说有1%的可能性会启动GC。

      这三个参数,session.gc_maxlifetime/session.gc_probability /session.gc_divisor都可以通过php.ini或者ini_set()函数来修改。但要记得,如果使用ini_set()函数的话,必须在每一个页面的开始处都调用ini_set()。

      这又导致了另外一个问题,gc_maxlifetime只能保证session生存的最短时间,并不能够保存在超过这一时间之后session 信息立即会得到删除。因为GC是按概率启动的,可能在某一个长时间内都没有被启动,那么大量的session在超过gc_maxlifetime以后仍然会有效。当然,发生这种情况的概率很小,但是如果你的应用对session的失效期要求很精确的话,这会导致很严重的问题。解决这个问题的一个方法是,把 session.gc_probability/session.gc_divisor的机率提高,如果提到100%,就会彻底解决这个问题,但显然会对性能造成严重的影响。另一个方法是放弃PHP的GC,自己在代码中判断当前session的生存时间,如果超出了 gc_maxlifetime,就清空当前session。

      PHP中的session有效期默认是1440秒(24分钟),也就是说,客户端超过24分钟没有刷新,当前session就会失效。要修改这个默认值,正确的解决办法是修改配置参数session.gc_maxlifetime。

      我曾经在网上搜索过这个问题的解决方式,找到的结果千奇百怪。有的说要设置“session_life_time”,据我知所,PHP中没有这个参数。有的说要调用session_set_cookie_params,或者设置session.cookie_lifetime,这仅仅用于设置 client端cookie的生存时间,换言之,只当client端cookie的生存时间小于server端的session生存期时,修改这个值才有效,并且最长不能超过server端的session生存期,原因很简单,当server端的session已经失效时,client端cookie的生存时间再长也是没有意义的。还有的说要调用 session_cache_expire,这个参数用于通知浏览器和proxy,当前页面的内容应该被缓存多长时间,和session的生存期并没有直接关系。

      听起来,这种解决方案很完美。但是,当你在实际中尝试修改session.gc_maxlifetime的值的时候,你很可能会发现,这个参数基本不起作用,session有效期仍然保持24分钟的默认值。甚至可能出现,在开发环境下工作正常,在服务器上却无效!

      为了彻底解决这个问题,需要对PHP的工作细节进行进一步的分析。

      在默认情况下,PHP 中的session信息会以文本文件的形式,被保存在系统的临时文件目录中。这个路径由配置参数session.save_path指定。在Linux 下,这一路径通常为\tmp,在 Windows下通常为C:\Windows\Temp。当服务器上有多个PHP应用时,它们会把自己的session文件都保存在同一个目录中(因为它们使用同一个session.save_path参数)。同样地,这些PHP应用也会按一定机率启动GC,扫描所有的session文件。

      问题在于,GC在工作时,并不会区分不同站点的session。举例言之,站点A的gc_maxlifetime设置为2小时,站点B的 gc_maxlifetime设置为默认的24分钟。当站点B的GC启动时,它会扫描公用的临时文件目录,把所有超过24分钟的session文件全部删除掉,而不管它们来自于站点A或B。这样,站点A的gc_maxlifetime设置就形同虚设了。

      找到问题所在,解决起来就很简单了。在页面的开始处调用session_save_path()函数,它能够修改 session.save_path参数,把保存session的目录指向一个专用的目录,例如\tmp\myapp\。这样,gc_maxlifetime参数就工作正常了。

      使用公用的session.save_path还会导致安全性问题,因为这意味着,同一台服务器上的其它PHP程序也可以读取你的站点的 session文件,这可能被用于黑客攻击。另一个问题是效率:在一个繁忙的站点中,可能存在成千上万个session文件,而把许多不同网站的 session文件都放在同一个目录下,无论是对单个文件的读写,还是遍历所有文件进行GC,都无疑会导致性能的降低。因此,如果你的PHP应用和别的 PHP应用运行在同一台服务器上的话,强烈建议你使用自己的session.save_path。

      严格地来说,这算是PHP的一个bug。当PHP在进行GC时,它应该区别来自不同站点的session文件,并应用不同的gc_maxlifetime值。目前,最新的PHP 5.2.X仍然存在这个问题。

      上文说到,在一个繁忙的站点中,可能存在成千上万个session文件,即使区分了不同站点的session.save_path目录,单个站点的session文件数目仍然可能导致效率问题。为了解决这一问题,可行的几种方法有:

    * 如果PHP运行在Linux系统下,使用ReiserFS文件系统取代默认的ext2/ext3文件系统。ReiserFS对于大量小文件的存取性能,比ext2/ext3有极大的提高。
    * 将session.save_path指向一个内存路径。这意味着,session文件的读写只在内存中进行,而不执行磁盘操作。
    * session.save_path 接受一个额外的N参数,用于指定目录的级数。例如,“5;/tmp” 将导致创建类似这样的session文件:/tmp/4/b/1/e/3 /sess_4b1e384ad74619bd212e236e52a5a174If。具体的说明,请参见:http://cn.php.net/manual/en/session.configuration.php#ini.session.save-path
    * 终极的解决方案,是放弃PHP的session处理机制,自己编码接管所有的session处理操作,通过 session_set_save_handler()函数来实现。通过自己接管session处理,可以将所有的session保存在专门的数据库(往往使用内存表)中,从而彻底解决session文件带来的问题,并且可以方便地实现session的共享和复制。这也是大型的PHP应用一般会使用的方式。关于session_set_save_handler()函数的使用,网上和相关图书都有详细的说明,这里不再赘述。值得一提的是,即使在这种方式下,启动GC的概率仍然取决于session.gc_probability/session.gc_divisor。