neverend的日志

          不記錄,終將被遺忘。 一萬年太久,只爭朝夕。 他們用數字構建了整個世界。

            BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
            62 Posts :: 1 Stories :: 17 Comments :: 0 Trackbacks

          #

          以root用戶登錄
          1.下載并安裝SVN服務
          $  sudo apt-get install subversion
          $  sudo apt-get install libapache2-svn

          2.設置SVN用戶組
          $ sudo addgroup subversion
          $ sudo usermod -G subversion -a root
          注銷后重新登錄

          3.創建SVN目錄
          $ sudo mkdir /home/svn
          $ cd /home/svn
          $ sudo mkdir labproject
          $ sudo chown -R root:subversion labproject

          4.創建 SVN 文件倉庫:
          $ sudo svnadmin create /home/svn/labproject
          $ sudo chmod -R g+rws labproject

          5. 通過自帶協議訪問 svnserve 服務器
           修改 /home/svn/labproject/conf/svnserve.conf 來配置其訪問控制
           取消一下配置項的注釋
           # [general]
           # password-db = passwd
           
           在password文件中編輯賬號和密碼,格式如下
           username=password
           注意,以上兩步操作行前不要留任何空白字符

           運行svnserve服務
           sudo svnserve -d -r /home/svn/labproject
           配置完成。
           如果需要將svnserve設置成開機自動啟動服務
           可在/etc/rc.loacl文件中添加:
           sudo svnserve -d -r /home/svn/labproject
           
           基本命令
           訪問SVN倉庫:
           $ svn co svn://hostname labproject --username user_name
           新增文件test.c
           $ svn add test.c
           將文件test.c提交到服務器
           $ svn commit -m "comment."
           更新文件倉庫
           $ svn up

          posted @ 2011-08-15 23:46 neverend 閱讀(528) | 評論 (0)編輯 收藏

          首先在Abode網站下載插件包。目前64位版在此處下載。我只接命令搞定:

          然后如下


          tar xvzf flashplayer10_2_p3_64bit_linux_111710.tar.gz

          mkdir -p $HOME/.mozilla/plugins

          mv libflashplayer.so $HOME/.mozilla/plugins/

          最后,重啟firefox既可。

          posted @ 2011-08-09 21:50 neverend 閱讀(652) | 評論 (0)編輯 收藏

          最近寫了些多線程的程序,用Thread.sleep()的時候有時會碰到InterruptedException。查了一些資料,下面是我自己的一些理解。

          阻塞方法
          一些多線程相關的方法是阻塞方法,比如Thread.sleep(), Thread.wait(), Thread.join()。

          這些方法的執行通常需要比較長的時間來完成,當代碼執行到阻塞方法時,一般要等待該方法返回后

          才能繼續往下執行,而InterruptedException提供了一種特殊的機制提前結束阻塞方法。

          中斷變量
          每個線程都會維護一個bool變量,表示線程處于中斷(true)或者非中斷狀態(false)。在線程初始的情況下中斷變量為false。

          這個變量的bool值可以通過Thread.isInterrupted()方法來讀取,通過Thread.interrupted()方法來清除中斷(即將中斷變量置為false)。

          線程中斷
          一個線程可以通過調用Thread.interrupt()方法來中斷另外一個線程,具體過程如下:

          1. 中斷變量被設置為true。

          2. 如果線程執行到了阻塞方法,那么該方法取消阻塞,并將中斷變量重新置為false。
          (這種機制是通過阻塞方法內部不斷輪詢中斷變量的值來實現的)

          例子:
           1 class ThreadTest implements Runnable {
           2 
           3     @Override
           4     public void run() {
           5                 System.out.println("before sleep");
           6             try {
           7                 Thread.sleep(5000);
           8             } catch (InterruptedException e) {
           9                                 System.out.println(Thread.currentThread().getName());
          10                 Thread.currentThread().interrupt();
          11                 System.out.println("after interrupt");
          12             }        
          13         System.out.println("after sleep");    
          14         
          15         try {
          16             Thread.sleep(5000);
          17         } catch (InterruptedException e) {
          18             // TODO Auto-generated catch block
          19             //e.printStackTrace();
          20             System.out.println(Thread.currentThread().getName());
          21             Thread.currentThread().interrupt();
          22             System.out.println("after interrupt");
          23         }
          24         System.out.println("after sleep");    
          25     }
          26     
          27 }
          28 
          29 public class ThreadBasic {
          30     
          31     public static void main(String[] args) {
          32         
          33         Thread t = new Thread(new ThreadTest(), "thread-1");
          34         t.start();
          35         
          36         t.interrupt();
          37         System.out.println(t.isInterrupted());
          38     }
          39 }






          posted @ 2011-06-14 20:03 neverend 閱讀(19801) | 評論 (1)編輯 收藏

          術語說明:
          QPS = req/sec = 請求數/秒

          【QPS計算PV和機器的方式】

          QPS統計方式 [一般使用 http_load 進行統計]
          QPS = 總請求數 / ( 進程總數 *   請求時間 )
          QPS: 單個進程每秒請求服務器的成功次數

          單臺服務器每天PV計算
          公式1:每天總PV = QPS * 3600 * 6
          公式2:每天總PV = QPS * 3600 * 8

          服務器計算
          服務器數量 =   ceil( 每天總PV / 單臺服務器每天總PV )

          【峰值QPS和機器計算公式】

          原理:每天80%的訪問集中在20%的時間里,這20%時間叫做峰值時間
          公式:( 總PV數 * 80% ) / ( 每天秒數 * 20% ) = 峰值時間每秒請求數(QPS)
          機器:峰值時間每秒QPS / 單臺機器的QPS   = 需要的機器

          問:每天300w PV 的在單臺機器上,這臺機器需要多少QPS?
          答:( 3000000 * 0.8 ) / (86400 * 0.2 ) = 139 (QPS)

          問:如果一臺機器的QPS是58,需要幾臺機器來支持?
          答:139 / 58 = 3



          http://blog.hummingbird-one.com/?tag=web-%E6%80%A7%E8%83%BD-qps-%E7%AD%89%E5%BE%85%E6%97%B6%E9%97%B4

          http://jjw.javaeye.com/blog/703864



          posted @ 2011-01-25 17:13 neverend 閱讀(22208) | 評論 (2)編輯 收藏

          Windows提供了WNetAddConnection()函數來建立網絡映射,NetUserAdd()函數來添加用戶。這兩個函數和在一起可以實現為遠程主機添加用戶的功能。但是,要真正實現遠程添加用戶的功能,需要在遠程主機上做正確的配置。如果遠程主機是Windows XP Pro,需要做如下配置:
          1. 開啟遠程連接
          我的電腦——>右鍵——>屬性——>遠程——>勾選“允許用戶連接到此計算機”

          2. 開啟網絡共享功能。
          新建一個文件夾,右鍵——>共享——>創建共享。

          3. 更改遠程用戶安全策略
          管理工具——>本地安全策略——>安全選項——>網絡訪問:本地賬戶的共享和安全模式,雙擊,改為“經典——本地用戶以自己的身份驗證”

          4.開啟Telnet (不確定是否必要)
          如果不做配置3,執行NetUserAdd()函數時,會報出訪問權限不夠的問題。因為默認的安全策略“僅來賓”是指不管網絡登錄的用戶/密碼參數擁有什么樣的系統權限,登陸后一律賦予Guest用戶的權限,以Guest賬戶添加一個Administrators組的賬戶時自然會出現權限不足的問題。而“經典”模式是指網絡登錄后的賬戶與輸入的用戶/密碼參數的賬戶保持一致的權限,即如果輸入管理員賬號,登錄后就擁有管理員組的權限;如果以Guest用戶登錄,登錄后就擁有Guest賬戶的權限。

          PS: Windows 2000的內置設置就是“經典”模式,所以不會出現上述問題。

          參考資料:
          http://hi.baidu.com/mofangzhe/blog/item/e5c05fedf39d62d1b21cb10d.html
          http://www.sq01.cn/viewthread.php?tid=161

          posted @ 2010-11-08 21:25 neverend 閱讀(675) | 評論 (0)編輯 收藏

               摘要: Linux/BSD在/etc/shadow文件中存放加密過的密碼,加密函數是crypt(),具體的源程序在glibc里面的crypt目錄下。 crypt()函數最初使用DES算法加密密碼明文,生成的密文格式是2個字符的salt和11個字符的DES輸出; 后來又出現了使用MD5算法生成密文的crypt()函數,這種方式更加安全。 ReadHat命令行下使用authconfig-tu...  閱讀全文
          posted @ 2010-11-06 21:14 neverend 閱讀(2011) | 評論 (0)編輯 收藏

          服務器端 192.1.0.160
          客戶機端 192.1.0.221

          一、在服務器端配置LDAP服務:
          1.下載 openldap-2.4.11.tar.gz和db-4.7.25.tar.gz

          2.安裝BerkeleyDB
          #rpm -qa|grep db
          # tar xvf db
          -4.7.25.tar.gz
          # cd db_4.
          7.25
          # cd build_unix
          /
          # ..
          /dist/configure -prefix=/usr/local/BerkeleyDB
          # make
          # make install

          安裝完成后執行
          #cp /usr/local/BerkeleyDB/include/* /usr/include/
          #cp /usr/local/BerkeleyDB/lib/* /usr/lib/
          可避免如下錯誤:
          checking   Berkeley   DB   version   for   BDB/HDB   backends...   no   
          configure:   error:   BDB/HDB:   BerkeleyDB   version   incompatible

          3.安裝OpenLDAP
          #gunzip -c openldap-2.4.15.tgz | tar xvfB -
          #cd openldap
          -2.4.15/
          #.
          /configure --prefix=/usr/local/openldap
          #make depend
          #make
          #make install

          4.啟動LDAP服務
          #cd /usr/local/openldap/libexec
          #.
          /slapd

          5.測試LDAP服務是否正常啟動
          #ldapsearch --'' -s base '(objectclass=*)' namingContexts
          返回
          dn:

            namingContexts: dc=my-domain,dc=com
          則說明正常啟動

          6.正常關閉LDAP服務
          #kill -INT `cat /usr/local/openldap/var/run/slapd.pid`

          7.日志配置
          日志級別是累加的:296 = 256 日志連接/操作/結果 + 32 搜索過濾器處理 + 8 連接管理:
          文件slapd.config中添加:
          loglevel 296

          日志信息會被記錄到 syslogd LOG_LOCAL4 機制中。還需要將下面的內容添加到 /etc/syslog.conf 中:

          local4.debug /var/log/slapd.log

          重啟syslog服務
          service syslog restart

          在/var/log/slapd.log中即可查看ldap的日志信息。
           
          二、客戶機端制作賬號遷移腳本
          IBM的《使用 OpenLDAP 集中管理用戶帳號》指出RedHat Enterprise 版本會自帶
          openldap-server、openldap-client和一些賬號移植的腳本
          但是在內網160和我昨天在虛擬機上裝的RedHat上卻沒有找到相關文件。
          今天又在虛擬機上裝了一個,發現默認安裝并不包括這些,需要定制安裝:

          在光盤安裝過程中有個界面讓選擇默認安裝還是定制安裝,選擇“定制安裝”,下一步,勾選如下軟件包:

          開發--開發庫
          openldap-devel-2.3.27
          perl-ldap
          perl-mozllla-ldap

          服務器——網絡服務器
          openldap-servers-2.3.27

          基本系統——系統工具
          openldap-client-2.3.27

          基本系統——老的軟件支持
          compat-db
          compat-openldap

          安裝完成后,就可以找到移植腳本和LDAP的一些文件了。

          遷移密碼和 shadow 信息

          Red Hat 所提供的 openldap-servers 包包含 PADL Software Pty Ltd. 公司的 MigrationTools 工具。我們將使用這些工具將數據從 Linux 系統文件(例如 /etc/group 和 /etc/password)轉換成 LDAP LDIF 格式,這是數據庫信息的一種文本格式的表示。這種格式是行界定、冒號分隔的屬性-值對。

          有一組 Perl 腳本被安裝到 /usr/share/openldap/migration/ 中執行遷移。這些 Perl 腳本的配置信息包含在 migrate_common.ph 文件的開頭。對于我們的目的來說,只需要修改命名前綴的變量來使用條目的識別名就足夠了,如下所示:

          $DEFAULT_BASE = "dc=mydomain,dc=com"

          在進行這些修改之后,請運行腳本 migrate_base.pl,它會創建根項,并為 Hosts、Networks、Group 和 People 等創建低一級的組織單元:


           運行 migrate_base.pl
          # ./migrate_base.pl > base.ldif
                      

          編輯 base.ldif,刪除除下面之外的所有條目:


           base.ldif 條目
          # cat base.ldif
                      dn: dc=mydomain,dc=com
                      dc: ibm
                      objectClass: top
                      objectClass: domain
                      dn: ou=People,dc=ibm,dc=com
                      ou: People
                      objectClass: top
                      objectClass: organizationalUnit
                      dn: ou=Group,dc=ibm,dc=com
                      ou: Group
                      objectClass: top
                      objectClass: organizationalUnit
                      

          在 LDAP 服務器上,使用 OpenLDAP 客戶機工具 ldapadd 將以下條目插入到數據庫中。簡單身份驗證必須要使用 -x 選項指定。在 slapd.conf 中定義的 rootdn 身份驗證識別名是 “cn=Manager,dc=mydomain,dc=com”。對于簡單身份驗證來說,必須使用密碼。選項 -W 強制提示輸入密碼。這個密碼就是在 slapd.conf 文件中指定的 rootpw 參數的值。包含這些條目的 LDIF 文件是使用 -f 選項指定的:


           使用 ldapadd 插入條目
          # ldapadd -x -D "cn=Manager,dc=mydomain,dc=com" -W -f base.ldif
                      

          接下來,從 /etc/group 中遷移 ldapuser 組:


           遷移 ldapuser 組
          # grep ldapuser /etc/group > group.in
                      # ./migrate_group.pl group.in > group.ldif
                      #  cat group.ldif
                      dn: cn=ldapuser,ou=Group,dc=mydomain,dc=com
                      objectClass: posixGroup
                      objectClass: top
                      cn: ldapuser
                      userPassword: {crypt}x
                      gidNumber: 500
                      # ldapadd -x -D "cn=Manager,dc=mydomain,dc=com" -W -f group.ldif
                      

          最后,從 /etc/passwd 和 /etc/shadow 中遷移 ldapuser 的信息:


           遷移 ldapuser 信息
          # grep ldapuser /etc/passwd > passwd.in
                      # ./migrate_passwd.pl passwd.in > passwd.ldif
                      # cat passwd.ldif
                      dn: uid=ldapuser,ou=People,dc=mydomain,dc=com
                      uid: ldapuser
                      cn: ldapuser
                      objectClass: account
                      objectClass: posixAccount
                      objectClass: top
                      objectClass: shadowAccount
                      userPassword: {crypt$1$TeOlOcMc$cpQaa0WpLSFRC1HIHW5bt1
                      shadowLastChange: 13048
                      shadowMax: 99999
                      shadowWarning: 7
                      loginShell: /bin/bash
                      uidNumber: 500
                      gidNumber: 500
                      homeDirectory: /home/ldapuser
                      gecos: ldapuser
                      # ldapadd -x -D "cn=Manager,dc=mydomain,dc=com" -W -f passwd.ldif
                      

          三、客戶機端配置NSS&PAM
          參考IBM《使用 OpenLDAP 集中管理用戶帳號》 配置 LDAP 客戶機部分
          方法1 圖形界面配置
          authconfig-tui
          方法2 修改配置文件
          /etc/ldap.conf、/etc/nsswitch.conf、/etc/sysconfig/authconfig 和/etc/pam.d/system-auth
          我的/etc/pam.d/system-auth的配置如下:

           

          #%PAM-1.0
          # This file is auto
          -generated.
          # User changes will be destroyed the next time authconfig is run.
          auth        required      pam_env.so
          auth        sufficient    pam_unix.so nullok try_first_pass
          auth        requisite     pam_succeed_if.so uid 
          >= 500 quiet
          auth        sufficient    pam_ldap.so use_first_pass
          auth        required      pam_deny.so

          account     required      pam_unix.so broken_shadow
          account     sufficient    pam_localuser.so
          account     sufficient    pam_succeed_if.so uid 
          < 500 quiet
          account     [
          default=bad success=ok user_unknown=ignore] pam_ldap.so
          account     required      pam_permit.so

          password    requisite     pam_cracklib.so try_first_pass retry
          =3
          password    sufficient    pam_unix.so md5 shadow nullok try_first_pass use_authtok
          password    sufficient    pam_ldap.so use_authtok
          password    required      pam_deny.so

          session     optional      pam_keyinit.so revoke
          session    required    pam_mkhomedir.so skel
          =/etc/skel/ umask=0022
          session     required      pam_limits.so
          session     [success
          =1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
          session     required      pam_unix.so
          session     optional      pam_ldap.so

          一點說明:pam_mkhomedir.so 負責用戶初次登陸沒有home目錄時為其創建home目錄。沒有這項配置的話,新用戶登陸可能會報/home目錄找不到的錯誤。

          問題:
          SSL的配置
          主從LDAP服務器的配置
          日志級別的配置

          PAM的作用究竟是什么?
          身份鑒別到底是由系統完成的還是LDAP完成的?
          資源和組織的信息如何組織?
          授權是如何進行的?
          posted @ 2010-11-02 22:49 neverend 閱讀(1441) | 評論 (0)編輯 收藏

          本文主要參考官方文檔:http://www.openldap.org/doc/admin24/quickstart.html
          和網上流傳的教程:http://www.lifv.cn/?p=462

          OpenLDAP下載地址:http://download.bergmans.us/openldap/openldap-2.2.29/openldap-2.2.29-db-4.3.29-openssl-0.9.8a-win32_Setup.exe 下載后點擊安裝即可。

          配置sldap.conf :在安裝目錄下找到sldap.conf ,修改配置如下:
          suffix "dc=example,dc=com" 
          rootdn 
          "cn=Manager,dc=example,dc=com" 
          rootpw secret 

          啟動OpenLDAP:進入cmd命令行,跳轉到OpenLDAP安裝目錄下,運行:
          slapd -1
          用可以看到控制臺下打印一片信息,openldap 默認是用的 Berkeley DB 數據庫存儲目錄數據的。

          再開一個cmd,跳轉到OpenLDAP安裝目錄下。

          測試OpenLDAP是否正常啟動:
          ldapsearch --s base (objectclass=*) namingContexts
          官方文檔里,這一條命令加了些單引號,但帶單引號的命令在Windows環境下跑不通。后面的命令也都避免
          使用引號。
          如果返回:
          dn: 
          namingContexts: dc
          =example,dc=com
          則說明OpenLDAP成功啟動

          增加一個條目:
          1.做一個LDIF文件
          2.使用ldapadd命令

          1.在安裝目錄下,新建文件example.ldif,輸入如下內容:
          dn: dc=example,dc=com 
          objectclass: dcObject 
          objectclass: organization 
          o: Example Company 
          dc: example 

          dn: cn
          =Manager,dc=example,dc=com 
          objectclass: organizationalRole 
          cn: Manager
          注意:在文檔每一行的開頭和結尾不要有空格,文檔最后最好也別回車。建議不要拷貝,用手敲這幾行。

          2.cmd在安裝目錄下,運行:
          ldapadd --D cn=Manager,dc=example,dc=com --f example.ldif

          可能會要求輸入密碼:secret (配置文件里寫的這個密碼)

          添加條目成功后,會有提示: adding new entry cn=Manager,dc=example,dc=com

          簡單查詢:
          ldapsearch --b dc=example,dc=com (objectclass=*)

          查詢成功后,會返回剛才插入的條目。

          JNDI連接OpenLDAP
          Java的JNDI接口很強大,可以連接LDAP服務。
          import java.util.Hashtable;
          import javax.naming.Context;
          import javax.naming.NamingException;
          import javax.naming.directory.DirContext;
          import javax.naming.directory.InitialDirContext; 
          public class TestOpenLDAP {

              
          /**
               * 
          @param args
               
          */
              
          public static void main(String[] args) {
                  
          // TODO Auto-generated method stub
                  TestOpenLDAP LDAPTest1 = new TestOpenLDAP();
                  String root 
          = "dc=example,dc=com"//root
                  Hashtable env = new Hashtable();
                  env.put(Context.INITIAL_CONTEXT_FACTORY, 
          "com.sun.jndi.ldap.LdapCtxFactory" );
                  env.put(Context.PROVIDER_URL, 
          "ldap://localhost/" + root);
                  env.put(Context.SECURITY_AUTHENTICATION, 
          "simple" );
                  env.put(Context.SECURITY_PRINCIPAL, 
          "cn=Manager,dc=example,dc=com" );
                  env.put(Context.SECURITY_CREDENTIALS, 
          "secret" );
                  DirContext ctx 
          = null ;
                  
          try {
                  ctx 
          = new InitialDirContext(env);
                  System.out.println( 
          "認證成功" );
                  }
                  
          catch (javax.naming.AuthenticationException e) {
                  e.printStackTrace();
                  System.out.println( 
          "認證失敗" );
                  }
                  
          catch (Exception e) {
                  System.out.println( 
          "認證出錯:" );
                  e.printStackTrace();
                  }
          if (ctx != null ) {
                  
          try {
                  ctx.close();
                  }
                  
          catch (NamingException e) {
                  
          //ignore
                  }
                  }

              }

          }

          問題:
          1. 圖形化界面LDAPBrowser的配置
          下載地址: http://files.blogjava.net/Unmi/LdapBrowser282.rar
          解壓后進入LdapBrowser282目錄,打開配置文件OpenLdap_Localhost.cfg
          修改配置:
          basedn=dc=example,dc=com
          managerdn
          =cn=Manager,dc=example,dc=com
          運行lbe.bat進入圖形界面后選擇連接OpenLdap_Localhost即可。

          2. OpenLDAP的語法,內置ObjectClass

          LDAP學習

          entry(record,directory object)  條目 一條數據 相當于數據表的一條記錄

          entry由若干個attribute組成,objectclass是必須的attribute,用于描述entry的schema

          attribute是name/value對形式,例如cn = liuxuanyu cn = mengke 一個name 可以對應多個值

          container是一種特殊的entry,為數據的組織和管理提供一個繼承體系結構,例如ou
          任何entry都可以在特定的情況下變成container

          與關系數據庫的比較:
          LDAP讀操作性能高,寫操作性能不如DB,DB 讀寫均可,讀操作性能不如LDAP
          數據結構不同
          LDAP適合于存儲繼承結構的數據


          namespace
          DN (distinguish name) DN是entry的名字,entry的唯一標識
          RDN (relative distinguish name) entry在某個容器范圍內的標識
          CN (common name) 常用名稱 習慣上被用作RDN
          DC (domain component) 域名

          LDAP只允許樹形結構

          object identifier (OID) 例如:2.5.4.3 它是屬性類型的標識符

          schema
          object class 定義了entry的類型
          有三種類型的object Class: 抽象類、輔助類和結構化類。

          構造schema的方式 :
          1. 組合現有的object class
          2. 擴展現有的object class 繼承 使用輔助類(實際上是一種聚合關系)

          The subschema publishes the schema to clients

          inetOrgPerson is a contemporary definition for a person entry RFC 2798


          3. JLDAP與JNDI的比較
           JLDAP是由novel開發的,原是針對Novel的NDS目錄設計的JAVA訪問工具。NOVEL的NDS和網景(NETSCAPE)的目錄是工具界最早的目錄產品。JLDAP并非JNDI的服務供應者,而是同一抽象層次下的訪問工具集。與JNDI-LDAP相比,JLDAP更接近于類關系數據庫的訪問方式。

             NDS是遵守LDAP協議的并進行了擴展的類MAD產品。而NOVEL也已把JLDAP捐獻給了OPENLDAP開源項目,可以世界范圍內自由使用。與 JNDI相比,JLDAP無須繼承DirContext才能實現添加,也無需預先生成添加的類,可以象普通數據訪問那樣,生成連接,然后使用::add方法添加。這樣,添加的靈活性要強于JNDI。

          但由于JLDAP目前是訪問NDS,因此,它不具備JNDI完全面向對象存儲的能力,對于高級的LDAP應用,JLDAP不是合適的選擇。


          4. OpenLDAP的深入管理
          posted @ 2010-10-08 23:05 neverend 閱讀(14385) | 評論 (5)編輯 收藏


          2.1求8位二進制數中1的個數
          解法1:直觀法,每次除以2,計算余數為1的個數 O(log2v)
          解法2:簡單位操作,每次與0x01做與運算,再右移一位。O(log2v)
          解法3:使用位操作v & (v-1) , 每次可減少二進制數字中的一個1。(若v & (v-1) == 0, 則v為2的方冪)
          解法4:空間換時間,利用題目中字長8位的破綻,建立一個窮舉數組。O(1)

          知識點:位運算的性質
          附:數組有2n+1個數,其中n個數成對出現,找出非成對出現的那個數。
          數組所有元素做異或操作。

          2.2
          1.N!的末尾有多少個零
          2.N!二進制表示中最低位1的位置

          1.解法:質因數分解可知,0只有2*5可得,所以0的個數就是質因數分解中2的個數與5的個數的最小值,實際上就是
          求5的個數Z。
          Z= [N/5] + [N/5^2] +[N/5^3]+ ……
          [N/5]表示不大于N的數中5的倍數貢獻一個5
          [N/5^2]表示不大于N的數中5^2再貢獻一個5/。

          2.解法:因為質因數分解中只有2是偶數,所以Z = [N/2] + [N/2^2] + [N/2^3] + …… +

          2.3尋找多數元素問題
          解法:減治:每次刪除兩個不同的ID,水王ID出現的次數仍舊會超過總數的一半。

          2.4從1到N的所有數中“1”出現的個數
          解法:尋找1出現的規律,比較復雜。

          2.5尋找N個整數中最大的K個數
          解法1:選擇排序,選出最大的K個數。 O(n*k)
           一點改進:部分堆排序,先建堆,再排出最大的k個數即可。O(n)+O(logn*k)
          解法2:分治,利用快速排序的劃分思路。O(n*log2k)
          解法3:二分搜索(與《編程珠璣》第二章問題A思路類似),有兩種劃分方式:
          1.設已知N個數中最小值Vmin,最大值Vmax,對區間[Vmin, Vmax]做二分即可。
          2.設N個整數是M位長的。從最高位開始,按bi位0、1二分。
          此解法適用于大數據量的處理,不過要多次讀寫若干個臨時文件。
          解法4:建一個最小堆存儲K個數,堆頂為堆中最小值。
          對第k到N個數,若A[i]大于堆頂H[0],令H[0]=A[i],再調用shift-down過程調整堆。
          此解法非常適合于N值很大的情況,復雜度為O(n * log2k)
          解法5:空間換時間,用count[Vmax]計算每個數字出現的次數。
          如果Vmax很大,將[0, Vmax]分成m個小塊,再分別討論即可。

          2.7最大公約數問題
           用位運算求解
             位運算問題:
             1.求一個整數的二進制表示中1的個數
             2.逆轉一個整數的二進制表示問題

          2.9斐波那契數列
          ·遞歸 效率最低
          ·迭代 O(n)
          ·矩陣分治法

          2.14子數組之和的最大值
          分治
          動態規劃

          2.15子矩陣之和的最大值
          固定一維,另一維轉化為子數組之和的最大值問題

          2.16求數組中最長遞增字符列的長度

          解法1:動態規劃

          假設array[]的前i個元素中,最長遞增子序列的長度為LIS[i],

          則,LIS[i + 1] = max{1, LIS[k]+1}, array[i+1] > array[k], for any k<=i

          int LIS(int[] array) {
          int[] LIS = new int[array.length];
          for (int i = 0 ; i < array.length; i++) {
              LIS[i] 
          = 1;
              
          for (int j = 0; j<i; j++) {
                  
          if (array[i] > array[j] && LIS[j] + 1 >LIS[i])
                      LIS[i] 
          = LIS[j] + ; 
              }

          }

           

          O(N^2)的時間復雜度

          解法2:

          MLIS[i]定義為前i個元素中,以array[i]為最大元素的最長遞增子序列的長度。

          可以證明,MLIS[i]的最大值也是最終的結果。

          MaxV[i]保存長度為i的遞增子序列最大元素的最小值。

          解法2的程序更新MaxV的部分應該是有問題的,由此導致時間復雜度的分析錯誤,并且解法3也是錯誤的。


          2.17數組循環移位

          void rightshift(int *arr, int N, int k) {
              K 
          %= N;
              Reverse(arr, 
          0, N-k-1);
              Reverse(arr, N
          -k, N-1);
              Reverse(arr, 
          0, N-1);
          }

           

          數組問題思路:

          排序思路

          動態規劃

          看成一個數列或向量


          2.18數組分割


          3.1字符串移位包含的問題

          給定兩個字符串s1和s2,要求判定s2能否被s1做循環移位得到的字符串包含。例如:s1 = AABCD , s2 = CDAA,返回true. 給定s1 = ABCD 和 s2 = ACBD,返回false.

          解法1:模擬字符串移位的過程,判斷是否包含子串

          解法2:判斷s2是否為s1s1的子串即可。

          解法3:不申請空間,模擬判斷s2是否為s1s1子串的過程。

          思路:字符串可以抽象成向量來考慮。


          3.2電話號碼對應英語單詞

          類似于求冪集問題

          解法1:迭代,用while循環模擬

          解法2:遞歸

           

          3.3計算字符串相似度
          遞歸求解

          int calStrDis(char[] strA, int pABegin, int pAEnd, 
                      
          char[] strB, int pBBegin, int pBEnd) {
                  
          if (pABegin > pAEnd) {
                      
          if (pBBegin > pBEnd) {
                          
          return 0;
                      } 
          else {
                          
          return pBEnd - pBBegin + 1;
                      }
                  }
                  
                  
          if (pBBegin > pBEnd) {
                      
          if (pABegin > pAEnd) {
                          
          return 0;
                      } 
          else {
                          
          return pAEnd - pABegin + 1;
                      }
                  }
                  
                  
          if (strA[pABegin] == strB[pBBegin]) {
                      
          return calStrDis(strA, pABegin + 1, pAEnd, strB,
                              pBBegin 
          + 1, pBEnd);
                  } 
          else {
                      
          int t1 = calStrDis(strA, pABegin, pAEnd, strB, pBBegin + 1
                              pBEnd);
                      
          int t2 = calStrDis(strA, pABegin + 1, pAEnd, strB, pBBegin ,
                              pBEnd);
                      
          int t3 = calStrDis(strA, pABegin + 1, pAEnd, strB, pBBegin + 1 ,
                              pBEnd);
                      
          return min(t1, t2, t3) + 1;
                  }
              }
          遞歸優化,如何存儲子問題的解?

          3.4從無頭鏈表中刪除節點
          這個問題很無恥

          3.5最短摘要生成
          有空再看

          3.6編程判斷兩個鏈表是否相交
          轉化成鏈表是否有環的問題

          3.7隊列中取最大值操作
          可分解為兩個子問題
          子問題1:設計一個堆棧,使入棧,出棧,取最大值的時間復雜度都是O(1)。
          思路:用空間換時間,加一個數組link2NextMaxItem[],link2NextMaxItem[i]存儲的是前i個元素中最大值的下標。

          子問題2:用上述特性的兩個堆棧實現一個隊列
          堆棧A負責入隊,堆棧B負責出隊。當堆棧B空的時候,將堆棧A中的數據全部彈出并壓入堆棧B

          3.8 求二叉樹結點之間的最大距離
          動態規劃實現,還是不太懂。

          3.9重建二叉樹
          遞歸求解

          3.10分層遍歷二叉樹
          隊列遍歷二叉樹+變量標記層次

          3.11程序改錯
          編寫正確的二分搜索程序
          C代碼:

          int BinSearch(SeqList * R, int n , KeyType K )//在有序表R[0..n-1]中進行二分查找,成功時返回結點的位置,失敗時返回-1
          int low=0,high=n-1,mid; //置當前查找區間上、下界的初值
            if(R[low].key==K)
            
          {
            
          return 0
           ;
            }

            
          while(low<=high)//當前查找區間R[low..high]非空
            mid=low+((high-low)/2);//使用 (low + high) / 2 會有整數溢出的問題
            if(R[mid].key==K)
            
          {
            
          return mid; //查找成功返回

            }

            
          if(R[mid].key>K)
            high
          =mid-1//繼續在R[low..mid-1]中查找

            else
            low
          =mid+1; //繼續在R[mid+1..high]中查找
            }

            
          return -1; //當low>high時表示查找區間為空,查找失敗
            }
           //BinSeareh


          Java代碼:

          public static int binarySearch(int[] srcArray, int des)
            
          {
            
          int low = 0
          ;
            
          int high = srcArray.length-1
          ;
            
          while(low <= high) 
          {
            
          int middle = (low + high)/2
          ;
            
          if(des == srcArray[middle]) 
          {
            
          return
           middle;
            }
          else if(des <srcArray[middle]) {
            high 
          = middle - 1
          ;
            }
          else {
            low 
          = middle + 1
          ;
            }

            }

            
          return -1;
            }


          4.8三角形測試用例
          測試用例的三種類型:
          正常輸入 覆蓋功能點
          非法輸入 值域錯誤 類型錯誤
          邊界值輸入 0 1 MAX MIN 

          posted @ 2010-09-29 11:10 neverend 閱讀(1254) | 評論 (0)編輯 收藏

          第1章 引言 隨著互聯網應用的廣泛普及,海量數據的存儲和訪問成為了系統設計的瓶頸問題。對于一個大型的互聯網應用,每天幾十億的PV無疑對數據庫造成了相當高的負載。對于系統的穩定性和擴展性造成了極大的問題。通過數據切分來提高網站性能,橫向擴展數據層已經成為架構研發人員首選的方式。水平切分數據庫,可以降低單臺機器的負載,同時最大限度的降低了了宕機造成的損失。通過負載均衡策略,有效的降低了單臺機器的訪問負載,降低了宕機的可能性;通過集群方案,解決了數據庫宕機帶來的單點數據庫不能訪問的問題;通過讀寫分離策略更是最大限度了提高了應用中讀取(Read)數據的速度和并發量。目前國內的大型互聯網應用中,大量的采用了這樣的數據切分方案,Taobao,Alibaba,Tencent,它們大都實現了自己的分布式數據訪問層(DDAL)。以實現方式和實現的層次來劃分,大概分為兩個層次(Java應用為例):JDBC層的封裝,ORM框架層的實現。就JDBC層的直接封裝而言,現在國內發展較好的一個項目是被稱作“變形蟲”(Amoeba)的項目,由阿里集團的研究院開發,現在仍然處于測試階段(beta版),其運行效率和生產時效性有待考究。就ORM框架層的實現而言,比如Taobao的基于ibatis和Spring的的分布式數據訪問層,已有多年的應用,運行效率和生產實效性得到了開發人員和用戶的肯定。本文就是以ORM框架層為基礎而實現的分布式數據訪問層。本課題的難點在于分庫后,路由規則的制定和選擇以及后期的擴展性,比如:如何做到用最少的數據遷移量,達到擴充數據庫容量(增加機器節點)的目的。核心問題將圍繞數據庫分庫分表的路由規則和負載均衡策略展開。 第2章 基本原理和概念 2.1基本原理: 人類認知問題的過程總是這樣的:what(什么)-?why(為什么)-?how(怎么 做),接下來,本文將就這三個問題展開討論和研究: 2.1.1什么是數據切分 "Shard" 這個詞英文的意思是"碎片",而作為數據庫相關的技術用語,似乎最早見于大型多人在線角色扮演游戲中。"Sharding" 姑且稱之為"分片"。Sharding 不是一門新技術,而是一個相對簡樸的軟件理念。眾所周知,MySQL 5 之后才有了數據表分區功能,那么在此之前,很多 MySQL 的潛在用戶都對 MySQL 的擴展性有所顧慮,而是否具備分區功能就成了衡量一個數據庫可擴展性與否的一個關鍵指標(當然不是唯一指標)。數據庫擴展性是一個永恒的話題,MySQL 的推廣者經常會被問到:如在單一數據庫上處理應用數據捉襟見肘而需要進行分區化之類的處理,是如何辦到的呢? 答案是:Sharding。 Sharding 不是一個某個特定數據庫軟件附屬的功能,而是在具體技術細節之上的抽象處理,是水平擴展(Scale Out,亦或橫向擴展、向外擴展)的解決方案,其主要目的是為突破單節點數據庫服務器的 I/O 能力限制,解決數據庫擴展性問題。 通過一系列的切分規則將數據水平分布到不同的DB或table中,在通過相應的DB路由 或者 table路由規則找到需要查詢的具體的DB或者table,以進行Query操作。這里所說的“sharding”通常是指“水平切分”, 這也是本文討論的重點。具體將有什么樣的切分方式呢和路由方式呢?行文至此,讀者難免有所疑問,接下來舉個簡單的例子:我們針對一個Blog應用中的日志來說明,比如日志文章(article)表有如下字段: 面對這樣的一個表,我們怎樣切分呢?怎樣將這樣的數據分布到不同的數據庫中的表中去呢?其實分析blog的應用,我們不難得出這樣的結論:blog的應用中,用戶分為兩種:瀏覽者和blog的主人。瀏覽者瀏覽某個blog,實際上是在一個特定的用戶的blog下進行瀏覽的,而blog的主人管理自己的blog,也同樣是在特定的用戶blog下進行操作的(在自己的空間下)。所謂的特定的用戶,用數據庫的字段表示就是“user_id”。就是這個“user_id”,它就是我們需要的分庫的依據和規則的基礎。我們可以這樣做,將user_id為 1~10000的所有的文章信息放入DB1中的article表中,將user_id為10001~20000的所有文章信息放入DB2中的 article表中,以此類推,一直到DBn。 這樣一來,文章數據就很自然的被分到了各個數據庫中,達到了數據切分的目的。接下來要解決的問題就是怎樣找到具體的數據庫呢?其實問題也是簡單明顯的,既然分庫的時候我們用到了區分字段user_id,那么很自然,數據庫路由的過程當然還是少不了 user_id的。考慮一下我們剛才呈現的blog應用,不管是訪問別人的blog還是管理自己的blog,總之我都要知道這個blog的用戶是誰吧,也就是我們知道了這個blog的user_id,就利用這個user_id,利用分庫時候的規則,反過來定位具體的數據庫,比如user_id是234,利用該才的規則,就應該定位到DB1,假如user_id是12343,利用該才的規則,就應該定位到DB2。以此類推,利用分庫的規則,反向的路由到具體的DB,這個過程我們稱之為“DB路由”。 當然考慮到數據切分的DB設計必然是非常規,不正統的DB設計。那么什么樣的DB設計是正統的DB設計呢? 我們平常規規矩矩用的基本都是。平常我們會自覺的按照范式來設計我們的數據庫,負載高點可能考慮使用相關的Replication機制來提高讀寫的吞吐和性能,這可能已經可以滿足很多需求,但這套機制自身的缺陷還是比較顯而易見的(下文會提及)。上面提到的“自覺的按照范式設計”。考慮到數據切分的DB設計,將違背這個通常的規矩和約束,為了切分,我們不得不在數據庫的表中出現冗余字段,用作區分字段或者叫做分庫的標記字段,比如上面的article的例子中的user_id這樣的字段(當然,剛才的例子并沒有很好的體現出user_id的冗余性,因為user_id這個字段即使就是不分庫,也是要出現的,算是我們撿了便宜吧)。當然冗余字段的出現并不只是在分庫的場景下才出現的,在很多大型應用中,冗余也是必須的,這個涉及到高效DB的設計,本文不再贅述。 2.1.2為什么要數據切分 上面對什么是數據切分做了個概要的描述和解釋,讀者可能會疑問,為什么需要數據切分呢?像 Oracle這樣成熟穩定的數據庫,足以支撐海量數據的存儲與查詢了?為什么還需要數據切片呢?的確,Oracle的DB確實很成熟很穩定,但是高昂的使用費用和高端的硬件支撐不是每一個公司能支付的起的。試想一下一年幾千萬的使用費用和動輒上千萬元的小型機作為硬件支撐,這是一般公司能支付的起的嗎?即使就是能支付的起,假如有更好的方案,有更廉價且水平擴展性能更好的方案,我們為什么不選擇呢? 但是,事情總是不盡人意。平常我們會自覺的按照范式來設計我們的數據庫,負載高點可能考慮使用相關的Replication機制來提高讀寫的吞吐和性能,這可能已經可以滿足很多需求,但這套機制自身的缺陷還是比較顯而易見的。首先它的有效很依賴于讀操作的比例,Master往往會成為瓶頸所在,寫操作需要順序排隊來執行,過載的話Master首先扛不住,Slaves的數據同步的延遲也可能比較大,而且會大大耗費CPU的計算能力,因為write操作在Master上執行以后還是需要在每臺slave機器上都跑一次。這時候 Sharding可能會成為雞肋了。 Replication搞不定,那么為什么Sharding可以工作呢?道理很簡單,因為它可以很好的擴展。我們知道每臺機器無論配置多么好它都有自身的物理上限,所以當我們應用已經能觸及或遠遠超出單臺機器的某個上限的時候,我們惟有尋找別的機器的幫助或者繼續升級的我們的硬件,但常見的方案還是橫向擴展, 通過添加更多的機器來共同承擔壓力。我們還得考慮當我們的業務邏輯不斷增長,我們的機器能不能通過線性增長就能滿足需求?Sharding可以輕松的將計算,存儲,I/O并行分發到多臺機器上,這樣可以充分利用多臺機器各種處理能力,同時可以避免單點失敗,提供系統的可用性,進行很好的錯誤隔離。 綜合以上因素,數據切分是很有必要的,且我們在此討論的數據切分也是將MySql作為背景的。基于成本的考慮,很多公司也選擇了Free且Open的MySql。對MySql有所了解的開發人員可能會知道,MySQL 5 之后才有了數據表分區功能,那么在此之前,很多 MySQL 的潛在用戶都對 MySQL 的擴展性有所顧慮,而是否具備分區功能就成了衡量一個數據庫可擴展性與否的一個關鍵指標(當然不是唯一指標)。數據庫擴展性是一個永恒的話題,MySQL 的推廣者經常會被問到:如在單一數據庫上處理應用數據捉襟見肘而需要進行分區化之類的處理,是如何辦到的呢? 答案也是Sharding,也就是我們所說的數據切分方案。 我們用免費的MySQL和廉價的Server甚至是PC做集群,達到小型機+大型商業DB的效果,減少大量的資金投入,降低運營成本,何樂而不為呢?所以,我們選擇Sharding,擁抱Sharding。 2.1.3怎么做到數據切分 說到數據切分,再次我們講對數據切分的方法和形式進行比較詳細的闡述和說明。 數據切分可以是物理 上的,對數據通過一系列的切分規則將數據分布到不同的DB服務器上,通過路由規則路由訪問特定的數據庫,這樣一來每次訪問面對的就不是單臺服務器了,而是N臺服務器,這樣就可以降低單臺機器的負載壓力。 數 據切分也可以是數據庫內的 ,對數據通過一系列的切分規則,將數據分布到一個數據庫的不同表中,比如將article分為article_001,article_002等子表,若干個子表水平拼合有組成了邏輯上一個完整的article表,這樣做的目的其實也是很簡單的。 舉個例子說明,比如article表中現在有5000w條數據,此時我們需要在這個表中增加(insert)一條新的數據,insert完畢后,數據庫會針對這張表重新建立索引,5000w行數據建立索引的系統開銷還是不容忽視的。但是反過來,假如我們將這個表分成100 個table呢,從article_001一直到article_100,5000w行數據平均下來,每個子表里邊就只有50萬行數據,這時候我們向一張只有50w行數據的table中insert數據后建立索引的時間就會呈數量級的下降,極大了提高了DB的運行時效率,提高了DB的并發量。當然分表的好處還不知這些,還有諸如寫操作的鎖操作等,都會帶來很多顯然的好處。 綜上,分庫降低了單點機器的負載;分表,提高了數據操作的效率,尤其是Write操作的效率。 行文至此我們依然沒有涉及到如何切分的問題。接下來,我們將對切分規則進行詳盡的闡述和說明。 上文中提到,要想做到數據的水平切分,在每一個表中都要有相冗余字符 作為切分依據和標記字段,通常的應用中我們選用user_id作為區分字段,基于此就有如下三種分庫的方式和規則: (當然還可以有其他的方式) 按號段分: (1) user_id為區分,1~1000的對應DB1,1001~2000的對應DB2,以此類推; 優點:可部分遷移 缺點:數據分布不均 (2)hash取模分: 對user_id進行hash(或者如果user_id是數值型的話直接使用user_id 的值也可),然后用一個特定的數字,比如應用中需要將一個數據庫切分成4個數據庫的話,我們就用4這個數字對user_id的hash值進行取模運算,也就是user_id%4,這樣的話每次運算就有四種可能:結果為1的時候對應DB1;結果為2的時候對應DB2;結果為3的時候對應DB3;結果為0的時候對應DB4,這樣一來就非常均勻的將數據分配到4個DB中。 優點:數據分布均勻 缺點:數據遷移的時候麻煩,不能按照機器性能分攤數據 (3)在認證庫中保存數據庫配置 就是建立一個DB,這個DB單獨保存user_id到DB的映射關系,每次訪問數據庫的時候都要先查詢一次這個數據庫,以得到具體的DB信息,然后才能進行我們需要的查詢操作。 優點:靈活性強,一對一關系 缺點:每次查詢之前都要多一次查詢,性能大打折扣 以上就是通常的開發中我們選擇的三種方式,有些復雜的項目中可能會混合使用這三種方式。 通過上面的描述,我們對分庫的規則也有了簡單的認識和了解。當然還會有更好更完善的分庫方式,還需要我們不斷的探索和發現。 第3章 本課題研究的基本輪廓 上面的文字,我們按照人類認知事物的規律,what?why?how這樣的方式闡述了數據庫切分的一些概念和意義以及對一些常規的切分規則做了概要的介紹。本課題所討論的分布數據層并不僅僅如此,它是一個完整的數據層解決方案,它到底是什么樣的呢?接下來的文字,我將詳細闡述本研究課題的完整思想和實現方式。 分布式數據方案提供功能如下: (1)提供分庫規則和路由規則(RouteRule簡稱RR),將上面的說明中提到的三中切分規則直接內嵌入本系統,具體的嵌入方式在接下來的內容中進行詳細的說明和論述; (2)引入集群(Group)的概念,保證數據的高可用性; (3)引入負載均衡策略(LoadBalancePolicy簡稱LB); (4)引入集群節點可用性探測機制,對單點機器的可用性進行定時的偵測,以保證LB策略的正確實施,以確保系統的高度穩定性; (5)引入讀/寫分離,提高數據的查詢速度; 僅僅是分庫分表的數據層設計也是不夠完善的,當某個節點上的DB服務器出現了宕機的情況的時候,會是什么樣的呢?是的,我們采用了數據庫切分方案,也就是說有N太機器組成了一個完整的DB ,如果有一臺機器宕機的話,也僅僅是一個DB的N分之一的數據不能訪問而已,這是我們能接受的,起碼比切分之前的情況好很多了,總不至于整個DB都不能訪問。一般的應用中,這樣的機器故障導致的數據無法訪問是可以接受的,假設我們的系統是一個高并發的電子商務網站呢?單節點機器宕機帶來的經濟損失是非常嚴重的。也就是說,現在我們這樣的方案還是存在問題的,容錯性能是經不起考驗的。當然了,問題總是有解決方案的。我們引入集群的概念,在此我稱之為Group,也就是每一個分庫的節點我們引入多臺機器,每臺機器保存的數據是一樣的,一般情況下這多臺機器分攤負載,當出現宕機情況,負載均衡器將分配負載給這臺宕機的機器。這樣一來, 就解決了容錯性的問題。所以我們引入了集群的概念,并將其內嵌入我們的框架中,成為框架的一部分。 如上圖所示,整個數據層有Group1,Group2,Group3三個集群組成,這三個集群就是數據水平切分的結果,當然這三個集群也就組成了一個包含完整數據的DB。每一個Group包括1個Master(當然Master也可以是多個)和 N個Slave,這些Master和Slave的數據是一致的。比如Group1中的一個slave發生了宕機現象,那么還有兩個slave是可以用的,這樣的模型總是不會造成某部分數據不能訪問的問題,除非整個 Group里的機器全部宕掉,但是考慮到這樣的事情發生的概率非常小(除非是斷電了,否則不易發生吧)。 在沒有引入集群以前,我們的一次查詢的過程大致如下:請求數據層,并傳遞必要的分庫區分字段(通常情況下是user_id)?數據層根據區分字段Route到具體的DB?在這個確定的DB內進行數據操作。 這是沒有引入集群的情況,當時引入集群會是什么樣子的呢?看圖一即可得知,我們的路由器上規則和策略其實只能路由到具體的Group,也就是只能路由到一個虛擬的Group,這個Group并不是某個特定的物理服務器。接下來需要做的工作就是找到具體的物理的DB服務器,以進行具體的數據操作。基于這個環節的需求,我們引入了負載均衡器的概念(LB)。負載均衡器的職責就是定位到一臺具體的DB服務器。具體的規則如下:負載均衡器會分析當前sql的讀寫特性,如果是寫操作或者是要求實時性很強的操作的話,直接將查詢負載分到Master,如果是讀操作則通過負載均衡策略分配一個Slave。我們的負載均衡器的主要研究放向也就是負載分發策略,通常情況下負載均衡包括隨機負載均衡和加權負載均衡 。 隨機負載均衡很好理解,就是從N個Slave中隨機選取一個Slave。這樣的隨機負載均衡是不考慮機器性能的,它默認為每臺機器的性能是一樣的。假如真實的情況是這樣的,這樣做也是無可厚非的。假如實際情況并非如此呢?每個Slave的機器物理性能和配置不一樣的情況,再使用隨機的不考慮性能的負載均衡,是非常不科學的,這樣一來會給機器性能差的機器帶來不必要的高負載,甚至帶來宕機的危險, 同時高性能的數據庫服務器也不能充分發揮其物理性能。基于此考慮從,我們引入了加權負載均衡,也就是在我們的系統內部通過一定的接口,可以給每臺DB服務器分配一個權值,然后再運行時LB根據權值在集群中的比重,分配一定比例的負載給該DB服務器。當然這樣的概念的引入,無疑增大了系統的復雜性和可維護性。有得必有失,我們也沒有辦法逃過的。 有了分庫,有了集群,有了負載均衡器,是不是就萬事大吉了呢? 事情遠沒有我們想象的那么簡單。雖然有了這些東西,基本上能保證我們的數據層可以承受很大的壓力 ,但是這樣的設計并不能完全規避數據庫宕機的危害。假如Group1中的slave2 宕機了,那么系統的LB并不能得知,這樣的話其實是很危險的,因為LB不知道,它還會以為slave2為可用狀態,所以還是會給slave2分配負載。這樣一來,問題就出來了,客戶端很自然的就會發生數據操作失敗的錯誤或者異常。這樣是非常不友好的!怎樣解決這樣的問題呢? 我們引入集群節點的可用性探測機制 ,或者是可用性的數據推送機制 。這兩種機制有什么不同呢?首先說探測機制吧,顧名思義,探測即使,就是我的數據層客戶端,不定時對集群中各個數據庫進行可用性的嘗試,實現原理就是嘗試性鏈接,或者數據庫端口的嘗試性訪問,都可以做到,當然也可以用JDBC嘗試性鏈接,利用Java的Exception機制進行可用性的判斷,具體的會在后面的文字中提到。那數據推送機制又是什么呢?其實這個就要放在現實的應用場景中來討論這個問題了,一般情況下應用的DB 數據庫宕機的話我相信DBA肯定是知道的,這個時候DBA手動的將數據庫的當前狀態通過程序的方式推送到客戶端,也就是分布式數據層的應用端,這個時候在更新一個本地的DB狀態的列表。并告知LB,這個數據庫節點不能使用,請不要給它分配負載。一個是主動的監聽機制,一個是被動的被告知的機制。兩者各有所長。但是都可以達到同樣的效果。這樣一來剛才假設的問題就不會發生了,即使就是發生了,那么發生的概率也會降到最低。 上面的文字中提到的Master和Slave ,我們并沒有做太多深入的講解。如圖一所示,一個Group由1個Master和N個Slave組成。為什么這么做呢?其中Master負責寫操作的負載,也就是說一切寫的操作都在Master上進行,而讀的操作則分攤到Slave上進行。這樣一來的可以大大提高讀取的效率。在一般的互聯網應用中,經過一些數據調查得出結論,讀/寫的比例大概在 10:1左右 ,也就是說大量的數據操作是集中在讀的操作,這也就是為什么我們會有多個Slave的原因。但是為什么要分離讀和寫呢?熟悉DB的研發人員都知道,寫操作涉及到鎖的問題,不管是行鎖還是表鎖還是塊鎖,都是比較降低系統執行效率的事情。我們這樣的分離是把寫操作集中在一個節點上,而讀操作其其他的N個節點上進行,從另一個方面有效的提高了讀的效率,保證了系統的高可用性。讀寫分離也會引入新的問題,比如我的Master上的數據怎樣和集群中其他的Slave機器保持數據的同步和一致呢?這個是我們不需要過多的關注的問題,MySql的Proxy機制可以幫助我們做到這點,由于Proxy機制與本課題相關性不是太強, 在這里不做詳細介紹。 綜上所述,本課題中所研究的分布式數據層的大體功能就是如此。以上是對基本原理的一些討論和闡述。接下來就系統設計層面,進行深入的剖析和研究。 第4章 系統設計 4.1系統實現層面的選擇 在引言部分中提到,該系統的實現層面有兩種選擇,一種是基于JDBC層面上的選擇,一種是基于現有數據持久層框架層面上的選擇,比如Hibernate,ibatis。兩種層面各有長處,也各有不足之處。基于JDBC層面上的系統實現,系統開發難度和后期的使用難度都將大大提高。大大增加了系統的開發費用和維護費用。本課題的定位是在成型的ibatis持久層框架的基礎上進行上層的封裝,而不是對ibatis源碼的直接修改,這樣一來使本系統不會對現有框架有太多的侵入性,從而也增加了使用的靈活性。之所以選擇ibatis,原因如下: (1)ibatis的學習成本非常低,熟練的Java Programmer可在非常的短時間內熟練使用ibatis; (2)ibatis是輕量級的ORM,只是簡單的完成了RO,OR的映射,其查詢語句也是通過配置文件sql-map.xml文件在原生sql的層面進行簡單的配置,也就是說我們沒有引入諸如Hibernate那樣的HQL的概念,從而增強了 sql的可控性,優秀的DBA可以很好的從sql的層面對sql進行優化,使數據層的應用有很強的可控性。Hibernate雖然很強大,但是由于 Hibernate是OR的一個重型封裝,且引入HQL的概念,不便于DBA團隊對sql語句的控制和性能的調優。 基于以上兩點理由,本課題在ORM的產品的選擇上選擇了易學易用且輕量級的持久層框架ibatis。下面的討論也都是特定于ibatis的基礎上的討論。 4.2其他開源框架的選擇 在一些大型的Java應用中,我們通常會采用Spring這樣的開源框架,尤其是 IoC(DI)這部分,有效的幫助開發人員管理對象的依賴關系和層次,降低系統各層次之間的實體耦合。Spring的優點和用處我相信這是開發人員眾所周知的,在此不再贅述。本課題的數據層也將采用Spring做為IoC(DI)的框架。 4.3系統開發技術和工具介紹 開發語言:Java JDK1.5 集成開發環境:Eclipse 3.3.4 Web環境下測試服務器:JBoss 4.2 構建工具:淘寶自行研發的構建工具Antx(類似于Maven),當然也可以用Maven 依賴的開源Jar:Spring2.0,ibaits,commons-configuration(讀取配置文件),log4j,junit等 第5章 系統分析(待續。。) 閱讀全文
          類別:默認分類 查看評論
          posted @ 2010-09-29 10:55 neverend 閱讀(172) | 評論 (0)編輯 收藏

          僅列出標題
          共3頁: 上一頁 1 2 3 下一頁 
          主站蜘蛛池模板: 休宁县| 清水县| 曲周县| 景泰县| 铜陵市| 尼勒克县| 黔西县| 怀柔区| 通许县| 柳河县| 黎平县| 科技| 临清市| 天柱县| 尖扎县| 和平区| 乃东县| 马公市| 务川| 玉田县| 宝坻区| 洮南市| 石河子市| 大厂| 日照市| 隆回县| 濮阳市| 青州市| 富川| 芦山县| 赞皇县| 盐源县| 教育| 吉水县| 盈江县| 宽甸| 晋江市| 临湘市| 宣汉县| 平武县| 承德县|