zzz's profileMagic 's Forest , ...PhotosBlogListsMore Tools Help

Windows Media Player

常用链接

站内google搜索

在线聊天室:
Hello~原来这里有个Bar阿,我才发现:)
Please wait...
Sorry, the comment you entered is too long. Please shorten it.
You didn't enter anything. Please try again.
Sorry, we can't add your comment right now. Please try again later.
To add a comment, you need permission from your parent. Ask for permission
Your parent has turned off comments.
Sorry, we can't delete your comment right now. Please try again later.
You've exceeded the maximum number of comments that can be left in one day. Please try again in 24 hours.
Your account has had the ability to leave comments disabled because our systems indicate that you may be spamming other users. If you believe that your account has been disabled in error please contact Windows Live support.
Complete the security check below to finish leaving your comment.
The characters you type in the security check must match the characters in the picture or audio.
麦子 露wrote:
在网上虾抖八豆的,看见了这个博客~~~看了文章觉得十分的温馨呢~~~我刚刚开始使用MSN吧~~可能是我属于90后的原因一直使用QQ呢~
希望加我个好友。你如果加我的话~~算是我在网上认识的第一个好友呢。斑竹是不是想问我为什么我自己不加你是吧??
其实这灰常简单,因为我不会的嘛....忽忽~~斑竹加我吧~~~
May 9
Crystalwrote:
你不能放的照片我都替大家放了,哇咔咔咔~~!!
Mar. 6
YUKO 黄wrote:
随便过来睬睬,还是什么都不要想最好~继续混日子吧!
Jan. 8
zzz zzzwrote:
     大家圣诞快乐~~喜欢我送的礼物吗?呵呵
Dec. 25
zzz zzzwrote:
~O~my God~I swear ~~
我刚才去你的blog转了转 留了些笔迹 然后打算上来放点照片,这才看到你的留言  回复完 ,再然后才看到这里的要求。。。还真是巧得来~!!  算是礼尚往来了伐 呵呵~~~
Nov. 30
Crystalwrote:
我给你留了,你也要给我留言,所谓礼尚往来!!!不然我要撒乌拉额,晓得伐?!
Nov. 29
唔,罗里八索的人:
跑来听歌的,我倒,有几首貌似是魔女幼熙的ost吧,还蛮好听。^^v
跑来看照片的,晕了,先挑日本的那些瞄吖瞄,忒多了嘎。边看边瞌睡。z..z...
没事儿,留下偶d小脚印。下回再来听歌看照片顺带留个言。
看偶多莫善良,没办法。rp perfect ahhh!! 睡了,困S~
Oct. 8
Vivien xiewrote:
哈哈哈~
在此留名
June 30
zzz zzzwrote:
你海南几张不是很经典阿,都出自我手啊,嘎嘎
 
照片评论 原来点了添加评论后再更改当前照片的话文字还是加在之前的照片这里,艾~都乱了
June 15
ssswrote:
这么多照片....看到2点半了饿(晚上)...没见到几张我的。....有也是很难看的。...郁闷..
June 14

Magic 's Forest , magic forest

无边的魔法森林 希望能用我的魔法 让他永远晴天。 ———我为自己定三戒 戒惰 戒色 戒游戏 @@ 礼仪、廉耻、忍耐、克己、百折不屈
Photo 1 of 155
More albums (59)
November 06

MySQL主从服务器+读写分离

花了三天时间,把MySQL的主从数据库+读写分离部署好了。感觉对缓解大数据流量的服务器压力还是很有效果的。

最近大把时间都花在了数据库上,幸好之前有自己研究过一段时间的数据库,貌似还是上大学的时候。。现在竟然还能回忆起来些

数据库确实是个值得花费精力的地方,很有用。

 

 

一、搭Mysql主、从服务器

                   安装Mysql不是很复杂。主从主要是备份、后续读写分布事务处理等用途

 

     useradd mysql –s /sbin/nologin                              #生成一个mysql的用户

     tar zxvf mysql-5.0.22.tar.gz

     cd mysql-5.0.22

     ./configure --prefix=/usr/local/mysql --localstatedir=/opt/data && make && make install

     cp support-files/my-large.cnf /etc/my.cnf               #配置文档
                                                                           #/etc/my.cnf 作为全局设置
                                                                            # mysql-data-dir/my.cnf 作为服务器指定设置                                                                                                                                          # ~/.my.cnf 作为用户设置

     cd /usr/local/mysql

     /usr/local/mysql/bin/mysql_install_db --user=mysql

     chown -R mysql:mysql /opt/data

     chown -R mysql:mysql /usr/local/mysql

从服务器修改/etc/my.cnf配置文档,server-id=10

     /usr/local/mysql/bin/mysqladmin –p shutdown            #停服务

     /usr/local/mysql/bin/mysqld_safe &                          #起服务,如果没有起来 多数是权限问题

     /usr/local/mysql/bin/mysqladmin –p password’111111’  #修改密码

     /usr/local/mysql/bin/mysql –p                                  #登陆,新安装应该没有密码

 

主服务器上:

     mysql>grant all privileges on *.* to back@192.168.x.x identified by ‘111111’;

                                                                               #新建一个给从服务器登陆的账号

     mysql> show master status;

+------------------+----------+--------------+------------------+
| File                    | Position    | Binlog_Do_DB | Binlog_Ignore_DB  |
+------------------+----------+--------------+------------------+
| mysql-bin.000003 |   235       |                    |                          |
+------------------+----------+--------------+------------------+

         记下file及position的值,做从服务器操作的时候需要。

 

   配置从服务器

     mysql> change master to master_host='192.168.x.x', master_user='back',
               master_password='111111', master_log_file='mysql-bin.000003', master_log_pos=235;

     mysql> start slave;                                                 #启用slave

     mysql> show slave status\G                                     #显示当前slave状态

         Slave_IO_Running: Yes
         Slave_SQL_Running: Yes                                     #都为Yes,则为正常

 

主数据库若有数据:

mysql> FLUSH TABLES WITH READ LOCK;                #数据库锁表操作,不让数据再进行写入动作。

mysql> show master status;                   

    把主服务器数据文件复制到从服务器,整个目录一起tar过去即可

 

    或者采用如下备份方法:

     mysqldump -u root -p testdb testtab>d:\testdb_testtab.sql

     恢复命令:

     create database testdb;use testdb;

     mysql>source d:\testdb_testtab.sql

 

mysql> UNLOCK TABLES;                                     #取消主数据库锁定

 

    配置从服务器,保证主服务器的position位置和备份保持一致即可

 

 

  以上主从服务器操作关键在于 备份数据position对应 以及change master命令。

 

  #关于/mysql-data/master.info文档,看my.cnf配置文档中的说明

#    Set the variables below. However, in case you choose this method, then
#    start replication for the first time (even unsuccessfully, for example
#    if you mistyped the password in master-password and the slave fails to
#    connect), the slave will create a master.info file, and any later
#    change in this file to the variables' values below will be ignored and
#    overridden by the content of the master.info file, unless you shutdown
#    the slave server, delete master.info and restart the slaver server.
#    For that reason, you may want to leave the lines below untouched
#    (commented) and instead use CHANGE MASTER TO (see above)

   也就是说还是推荐用change master to 命令来修改master.info文档。而不要用my.cnf的从服务器配置。
   在start slave前这个master.info一定要配置在一个正确的状态,除非他不存在。因为master.info为首选配置档

 

 

测试:

    主服务器上

mysql> create database testdb;
mysql> create table testtb(id int(3),name char(10));
mysql> insert into testtb values (001,’1’);
mysql> use testdb;
mysql> select * from testtb;

    从服务器上

mysql> show databases;
mysql> use testdb;
mysql> show tables;
mysql> select * from testtb;

    确认从服务器上有相同的记录即可。

 

 

二、读写分离

    这部分主要有点是用来给服务器做均衡负载的,其实还是蛮有趣的一个功能。

    搭建Mysql-proxy服务可就没那么简单了,如果能够yum一下就全都搞定那就太开心了!

    无所谓这台代理服务搭在哪里,主、从或者第三方都OK,我推荐可以在从上面做,测试比较清楚

        下面基本就用我history里的命令了,省的一句一句打。

 

     安装 lua      

628  wget http://www.lua.org/ftp/lua-5.1.4.tar.gz
638  tar zxvf lua-5.1.4.tar.gz
639  cd lua-5.1.4
640  vi Makefile                                            #这里是修改  INSTALL_TOP= /usr/local/lua
641  make linux
643  make install

     安装 libevent              

644  cd ..
645  wget http://monkey.org/~provos/libevent-1.4.12-stable.tar.gz
647  tar zxvf libevent-1.4.12-stable.tar.gz
648  cd libevent-1.4.12-stable
649  ./configure --prefix=/usr/local/libevent
650  make
651  make install

     安装check

644  cd ..
652  wget ftp://ftp.eenet.ee/pub/FreeBSD/distfiles/check-0.9.6.tar.gz
655  tar zxvf check-0.9.6.tar.gz
656  cd check-0.9.6
657  ./configure
658  make
659  make install

     安装glib2

681  cd ..
682  wget http://ftp.gnome.org/pub/gnome/sources/glib/2.22/glib-2.22.1.tar.gz
683  tar zxvf glib-2.22.1.tar.gz
684  cd glib-2.22.1
685  ./configure --prefix=/usr/local/glib2
686  make
687  make install

     安装mysql-proxy

660  cd ..
662  wget ftp://ftp.swin.edu.au/freebsd/ports/distfiles/mysql-proxy-0.7.1.tar.gz
664  tar zxvf mysql-proxy-0.7.1.tar.gz
665  cd mysql-proxy-0.7.1
./configure --prefix=/usr/local/mysql-proxy --with-mysql=/usr/local/mysql --with-lua

 

     结果提示glib2版本不对

772  cp /usr/local/glib2/lib/pkgconfig/glib-2.0.pc /usr/lib/pkgconfig/

     然后提示lua不对,于是我下了一个lua.pc

# vars from install Makefile

# grep '^V=' ../Makefile
V= 5.1
# grep '^R=' ../Makefile
R= 5.1.1

# grep '^INSTALL_.*=' ../Makefile | sed 's/INSTALL_TOP/prefix/'
prefix= /usr/local/lua
INSTALL_BIN= ${prefix}/bin
INSTALL_INC= ${prefix}/include
INSTALL_LIB= ${prefix}/lib
INSTALL_MAN= ${prefix}/man/man1
INSTALL_LMOD= ${prefix}/share/lua/${V}
INSTALL_CMOD= ${prefix}/lib/lua/${V}

# canonical vars
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include

Name: Lua
Description: An Extensible Extension Language
Version: ${R}
Requires:
Libs: -L${libdir} -llua -lm
Cflags: -I${includedir}

    接着提示libevent错误 研究了半天 直接把安装路径放到/usr/下面

648  cd ~/libevent-1.4.12-stable
853  ./configure --prefix=/usr/ && make && make install
868  cd ..
869  cd mysql-proxy-0.7.1
856  ./configure --prefix=/usr/local/mysql-proxy/ --with-mysql=/usr/local/mysql/ --with-lua --with-libevent=/usr/

    这次总算./configure 通过了

        接着make,结果又出错~~提示找不到什么.h的文件
        到make里面去看,原来是编译的时候需要几个库文件,安装路径里面没有~
        这个做的真不好。。

875  cp ../lua-5.1.4/src/lua*.h .
876  cp ../lua-5.1.4/src/lauxlib.h .

    总算make && make install通过了

892  /usr/local/mysql-proxy/sbin/mysql-proxy –V

    测试一把,又出错了。。。

894  chcon -t texrel_shlib_t /usr/local/mysql-proxy/lib/libmysql-chassis.so.0

    原来是selinux作怪,改了。

896  /usr/local/mysql-proxy/sbin/mysql-proxy –V

    总算OK了。

 

 

 

接着来配服务。

      把 rw-splitting.lua 放到/usr/local/mysql-proxy/share/mysql-proxy/下面

 

rw-splitting.lua:

--[[ $%BEGINLICENSE%$
Copyright (C) 2007-2008 MySQL AB, 2008 Sun Microsystems, Inc

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

$%ENDLICENSE%$ --]]

---
-- a flexible statement based load balancer with connection pooling
--
-- * build a connection pool of min_idle_connections for each backend and maintain
--   its size
-- *
--
--

local commands    = require("proxy.commands")
local tokenizer   = require("proxy.tokenizer")
local lb          = require("proxy.balance")
local auto_config = require("proxy.auto-config")

--- config
--
-- connection pool
if not proxy.global.config.rwsplit then
    proxy.global.config.rwsplit = {
        min_idle_connections = 1,
        max_idle_connections = 1,

        is_debug = false
    }
end

---
-- read/write splitting sends all non-transactional SELECTs to the slaves
--
-- is_in_transaction tracks the state of the transactions
local is_in_transaction       = false

-- if this was a SELECT SQL_CALC_FOUND_ROWS ... stay on the same connections
local is_in_select_calc_found_rows = false

---
-- get a connection to a backend
--
-- as long as we don't have enough connections in the pool, create new connections
--
function connect_server()
    local is_debug = proxy.global.config.rwsplit.is_debug
    -- make sure that we connect to each backend at least ones to
    -- keep the connections to the servers alive
    --
    -- on read_query we can switch the backends again to another backend

    if is_debug then
        print()
                print("[connect_server] " .. proxy.connection.client.dst.name)
    end

    local rw_ndx = 0

    -- init all backends
    for i = 1, #proxy.global.backends do
        local s        = proxy.global.backends[i]
        local pool     = s.pool -- we don't have a username yet, try to find a connections which is idling
        local cur_idle = pool.users[""].cur_idle_connections

        pool.min_idle_connections = proxy.global.config.rwsplit.min_idle_connections
        pool.max_idle_connections = proxy.global.config.rwsplit.max_idle_connections
        if is_debug then
            print("  [".. i .."].connected_clients = " .. s.connected_clients)
            print("  [".. i .."].pool.cur_idle     = " .. cur_idle)
            print("  [".. i .."].pool.max_idle     = " .. pool.max_idle_connections)
            print("  [".. i .."].pool.min_idle     = " .. pool.min_idle_connections)
            print("  [".. i .."].type = " .. s.type)
            print("  [".. i .."].state = " .. s.state)
        end

        -- prefer connections to the master
        if s.type == proxy.BACKEND_TYPE_RW and
           s.state ~= proxy.BACKEND_STATE_DOWN and
           cur_idle < pool.min_idle_connections then
            proxy.connection.backend_ndx = i
            break
        elseif s.type == proxy.BACKEND_TYPE_RO and
               s.state ~= proxy.BACKEND_STATE_DOWN and
               cur_idle < pool.min_idle_connections then
            proxy.connection.backend_ndx = i
            break
        elseif s.type == proxy.BACKEND_TYPE_RW and
               s.state ~= proxy.BACKEND_STATE_DOWN and
               rw_ndx == 0 then
            rw_ndx = i
        end
    end

    if proxy.connection.backend_ndx == 0 then
        if is_debug then
            print("  [" .. rw_ndx .. "] taking master as default")
        end
        proxy.connection.backend_ndx = rw_ndx
    end

    -- pick a random backend
    --
    -- we someone have to skip DOWN backends

    -- ok, did we got a backend ?

    if proxy.connection.server then
        if is_debug then
            print("  using pooled connection from: " .. proxy.connection.backend_ndx)
        end

        -- stay with it
        return proxy.PROXY_IGNORE_RESULT
    end

    if is_debug then
        print("  [" .. proxy.connection.backend_ndx .. "] idle-conns below min-idle")
    end

    -- open a new connection
end

---
-- put the successfully authed connection into the connection pool
--
-- @param auth the context information for the auth
--
-- auth.packet is the packet
function read_auth_result( auth )
    if is_debug then
        print("[read_auth_result] " .. proxy.connection.client.dst.name)
    end
    if auth.packet:byte() == proxy.MYSQLD_PACKET_OK then
        -- auth was fine, disconnect from the server
        proxy.connection.backend_ndx = 0
    elseif auth.packet:byte() == proxy.MYSQLD_PACKET_EOF then
        -- we received either a
        --
        -- * MYSQLD_PACKET_ERR and the auth failed or
        -- * MYSQLD_PACKET_EOF which means a OLD PASSWORD (4.0) was sent
        print("(read_auth_result) ... not ok yet");
    elseif auth.packet:byte() == proxy.MYSQLD_PACKET_ERR then
        -- auth failed
    end
end

---
-- read/write splitting
function read_query( packet )
    local is_debug = proxy.global.config.rwsplit.is_debug
    local cmd      = commands.parse(packet)
    local c        = proxy.connection.client

    local r = auto_config.handle(cmd)
    if r then return r end

    local tokens
    local norm_query

    -- looks like we have to forward this statement to a backend
    if is_debug then
        print("[read_query] " .. proxy.connection.client.dst.name)
        print("  current backend   = " .. proxy.connection.backend_ndx)
        print("  client default db = " .. c.default_db)
        print("  client username   = " .. c.username)
        if cmd.type == proxy.COM_QUERY then
            print("  query             = "        .. cmd.query)
        end
    end

    if cmd.type == proxy.COM_QUIT then
        -- don't send COM_QUIT to the backend. We manage the connection
        -- in all aspects.
        proxy.response = {
            type = proxy.MYSQLD_PACKET_OK,
        }
        if is_debug then
            print("  (QUIT) current backend   = " .. proxy.connection.backend_ndx)
        end

        return proxy.PROXY_SEND_RESULT
    end

    proxy.queries:append(1, packet, {resultset_is_needed = true})
    -- read/write splitting
    --
    -- send all non-transactional SELECTs to a slave
    if not is_in_transaction and
       cmd.type == proxy.COM_QUERY then
        tokens     = tokens or assert(tokenizer.tokenize(cmd.query))

        local stmt = tokenizer.first_stmt_token(tokens)

        if stmt.token_name == "TK_SQL_SELECT" then
            is_in_select_calc_found_rows = false
            local is_insert_id = false

            for i = 1, #tokens do
                local token = tokens[i]
                -- SQL_CALC_FOUND_ROWS + FOUND_ROWS() have to be executed
                -- on the same connection
                -- print("token: " .. token.token_name)
                -- print("  val: " .. token.text)
                if not is_in_select_calc_found_rows and token.token_name == "TK_SQL_SQL_CALC_FOUND_ROWS" then
                    is_in_select_calc_found_rows = true
                elseif not is_insert_id and token.token_name == "TK_LITERAL" then
                    local utext = token.text:upper()

                    if utext == "LAST_INSERT_ID" or
                       utext == "@@INSERT_ID" then
                        is_insert_id = true
                    end
                end

                -- we found the two special token, we can't find more
                if is_insert_id and is_in_select_calc_found_rows then
                    break
                end
            end

            -- if we ask for the last-insert-id we have to ask it on the original
            -- connection
            if not is_insert_id then
                local backend_ndx = lb.idle_ro()

                if backend_ndx > 0 then
                    proxy.connection.backend_ndx = backend_ndx
                end
            else
                print("   found a SELECT LAST_INSERT_ID(), staying on the same backend")
            end
        end
    end

    -- no backend selected yet, pick a master
    if proxy.connection.backend_ndx == 0 then
        -- we don't have a backend right now
        --
        -- let's pick a master as a good default
        --
        proxy.connection.backend_ndx = lb.idle_failsafe_rw()
    end

    -- by now we should have a backend
    --
    -- in case the master is down, we have to close the client connections
    -- otherwise we can go on
    if proxy.connection.backend_ndx == 0 then
        return proxy.PROXY_SEND_QUERY
    end

    local s = proxy.connection.server

    -- if client and server db don't match, adjust the server-side
    --
    -- skip it if we send a INIT_DB anyway
    if cmd.type ~= proxy.COM_INIT_DB and
       c.default_db and c.default_db ~= s.default_db then
        print("    server default db: " .. s.default_db)
        print("    client default db: " .. c.default_db)
        print("    syncronizing")
        proxy.queries:prepend(2, string.char(proxy.COM_INIT_DB) .. c.default_db)
    end

    -- send to master
    if is_debug then
        if proxy.connection.backend_ndx > 0 then
            local b = proxy.global.backends[proxy.connection.backend_ndx]
            print("  sending to backend : " .. b.dst.name);
            print("    is_slave         : " .. tostring(b.type == proxy.BACKEND_TYPE_RO));
            print("    server default db: " .. s.default_db)
            print("    server username  : " .. s.username)
        end
        print("    in_trans        : " .. tostring(is_in_transaction))
        print("    in_calc_found   : " .. tostring(is_in_select_calc_found_rows))
        print("    COM_QUERY       : " .. tostring(cmd.type == proxy.COM_QUERY))
    end

    return proxy.PROXY_SEND_QUERY
end

---
-- as long as we are in a transaction keep the connection
-- otherwise release it so another client can use it
function read_query_result( inj )
    local is_debug = proxy.global.config.rwsplit.is_debug
    local res      = assert(inj.resultset)
      local flags    = res.flags

    -- if inj.id ~= 1 then  -- this was causing some misleading errors
        -- ignore the result of the USE <default_db>
        -- the DB might not exist on the backend, what do do ?
        --
        if inj.id == 2 then
            -- the injected INIT_DB failed as the slave doesn't have this DB
            -- or doesn't have permissions to read from it
            if res.query_status == proxy.MYSQLD_PACKET_ERR then
                proxy.queries:reset()

                proxy.response = {
                    type = proxy.MYSQLD_PACKET_ERR,
                    errmsg = "can't change DB ".. proxy.connection.client.default_db ..
                        " to on slave " .. proxy.global.backends[proxy.connection.backend_ndx].dst.name
                }

                return proxy.PROXY_SEND_RESULT
            end
        end
    --    return proxy.PROXY_IGNORE_RESULT
    -- end

    is_in_transaction = flags.in_trans
    local have_last_insert_id = (res.insert_id and (res.insert_id > 0))

    if not is_in_transaction and
       not is_in_select_calc_found_rows and
       not have_last_insert_id then
        -- release the backend
        proxy.connection.backend_ndx = 0
    elseif is_debug then
        print("(read_query_result) staying on the same backend")
        print("    in_trans        : " .. tostring(is_in_transaction))
        print("    in_calc_found   : " .. tostring(is_in_select_calc_found_rows))
        print("    have_insert_id  : " .. tostring(have_last_insert_id))
    end
end

---
-- close the connections if we have enough connections in the pool
--
-- @return nil - close connection
--         IGNORE_RESULT - store connection in the pool
function disconnect_client()
    local is_debug = proxy.global.config.rwsplit.is_debug
    if is_debug then
        print("[disconnect_client] " .. proxy.connection.client.dst.name)
    end

    -- make sure we are disconnection from the connection
    -- to move the connection into the pool
    proxy.connection.backend_ndx = 0
end

 

    启动mysql-proxy代理

963 touch /var/log/mysql-proxy/mysql-proxy.log

965 /usr/local/mysql-proxy/sbin/mysql-proxy --proxy-read-only-backend-addresses=192.168.1.7:3306 --proxy-backend-addresses=192.168.1.9:3306 --proxy-lua-script=share/mysql-proxy/rw-splitting.lua > /var/log/mysql-proxy/mysql-proxy.log &

968 netstat –antp | grep 4040                              #看看有没有起来

969 ps –aux | grep mysql                                      #看看进程里面有没有 一些配置的路径里面都可以看到

985  /usr/local/mysql/bin/mysql -u root -p -P4040 –hlocalhost

                                                                        #登陆一下看看,

  

读写分离测试

    登陆3306端口,在从服务器testtb上增加几条记录

    客户端做多个连接到mysql-proxy的端口4040。

    insert一条记录,可以看到主服务器也有增加了记录。从服务器是同步过来的数据

    select * from testtb;在一个客户端连接的时候,显示的是主服务器的表内容

                                 而有多个客户端连接之后,就会显示从服务器的testtb表内容

 

 

 

    最后,他是同步所有database的,如果只需要备份指定的数据库,请一定要在my.cnf中加上

     replicate-do-db=dbname 

    不然,他找不到其他数据库,正常的数据库也不会更新了。

 

 

生成一个/usr/local/bin/mysql-proxy.sh

#! /bin/bash
/usr/local/mysql-proxy/sbin/mysql-proxy --proxy-read-only-backend-addresses=192.168.1.7:3306 --proxy-backend-addresses=192.168.1.9:3306 --proxy-lua-script=share/mysql-proxy/rw-splitting.lua > /var/log/mysql-proxy/mysql-proxy.log &

 

在/etc/rc.local 文档中加入一条

       /usr/local/bin/mysql-proxy.sh &

 

参考资料:

http://sery.blog.ccidnet.com/blog-htm-do-showone-uid-3587-itemid-472099-type-blog.html  

                                                                                      MySQL 主从复制读写分离实现

http://www.yayu.org/look.php?id=136                                   MySQL 主从同步关键句

http://butian.org/knowledge/develop/DB/20090520/1124.html    MySQL 主从服务器的一些技巧

http://www.3389hack.com/xueyuan/fuwuqi/MySQL/22601.html  MySQL配置文件my.cnf 例子的最详细翻译

November 05

Oracle数据库的灾难恢复(转)


     随着办公自动化和电子商务的飞速发展,企业对信息系统的依赖性越来越高,数据库作为信息系统的核心担当着重要的角色。尤其在一些对数据可靠性要求很高的行业如银行、证券、电信等,如果发生意外停机或数据丢失其损失会十分惨重。为此数据库管理员应针对具体的业务要求制定详细的数据库备份与灾难恢复策略,并通过模拟故障对每种可能的情况进行严格测试,只有这样才能保证数据的高可用性。数据库的备份是一个长期的过程,而恢复只在发生事故后进行,恢复可以看作是备份的逆过程,恢复的程度的好坏很大程度上依赖于备份的情况。此外,数据库管理员在恢复时采取的步骤正确与否也直接影响最终的恢复结果,本文主要针对 Oracle数据库可能遇到的各种故障提供了相应的恢复的方法,仅供大家参考。

 
     要对Oracle数据库备份与恢复有清晰的认识,首先有必要对数据库的几种运行状态有充分的了解。Oracle数据库的运行状态主要分为3种,他们依次为:


l Nomount(非安装)Oracle只是读取ini文件中的配置信息,并初始化SGA区。
l Mount(安装)Oracle除了需要读取ini文件还要读取控制文件,并从中获取有关数据库的物理结构等信息。
l Open(打开)数据库要检查所有文件处于同一时间点,对错误进行恢复对未完成事务回滚,并最终可以允许用户访问。


     数据库的备份主要分为三种类型:冷备份;热备份;逻辑备份;
     数据库的备份不是本文讨论的重点,在这里只作一个概要的介绍,Oracle数据库备份主要有:

 
l Cold Backup(冷备份) 主要指在关闭数据库的状态下进行的数据库完全备份,备份内容包括所有数据文件、控制文件、联机日志文件、ini文件。
l Hot Backup(热备份) 指在数据库处于运行状态下,对数据文件和控制文件进行备份,要使用热备份必须将数据库运行在(Archive Log)归档方式下。
l Export(逻辑备份)这是最简单的备份方法,可按数据库中某个表、某个用户或整个数据库来导出,并且支持全部、累计、增量三种方式。使用这种方法,数据库必须处于打开状态,而且如果数据库不是在restrict状态将不能保证导出数据的一致性。


     数据库的恢复可分为两大类:完全恢复;不完全恢复;


完全恢复指将数据库恢复到发生故障的时间点,不丢失任何数据。

不完全恢复指将数据库恢复到发生故障前的某一个时间点,此时间点以后的所有改动将会丢失。如果没有特殊需求,我们建议应尽量使用完全恢复。


     Oracle 数据库的恢复过程分两步进行,首先将把存放在重做日志文件中的所有重做运用到数据文件,之后对重做中所有未提交的事务进行回滚,这样所有数据就恢复到发生灾难那一时刻了。数据库的恢复只能在发生故障之前的数据文件上运用重做,将其恢复到故障时刻,而不能将数据文件反向回滚到之前的某一个时刻。举个例子,我们有一个2001/1/1的数据库备份,当2001/5/1使我们发现数据库中数据发生混乱,希望将数据库恢复到2001/4/30时的状态,我们只能先恢复2001/1/1的数据库备份然后在其上运用重做记录使其前滚到2001/4/30时的状态,而不能将2001/5/1的数据库向后回滚到2001 /4/30。


     为了系统的设计数据库的恢复方案,我们先对可能遇到的错误进行分类,Oracle数据库错误主要分为5大类:


l SQL语句失败
l 线程失败
l 实例失败
l 用户操作失败
l 存储设备失败


     如果发生前三种失败,不需要我们人为干涉,Oracle系统会自动进行恢复。对于用户操作型的失败(如误删除数据),我们采取的补救措施主要有导入最新的逻辑备份或进行到某一时间点的不完全恢复。从Oracle 8之后的新版本中引入了基于表空间的时间点恢复(TSPITR),可以单独将包含错误操作的表空间恢复到指定时间,而不必对整个数据库进行不完全恢复。当错误操作发现比较及时而且数据量不大的情况下也可以考虑使用logminer生成反向SQL。

 
     针对存储设备的失败的情况比较复杂也是本文讨论的重点,存储设备的失败必然会使放置在其上的文件变为不可用,我们先将Oracle数据库所涉及到的文件进行一个划分,主要可分为:


l Oracle的系统文件,指Oracle的运行文件,各种应用程序
l 数据库控制文件
l 数据库联机重做日志文件
l 数据文件
l 归档日志文件


     避免第一种文件失败主要依赖系统管理员进行操作系统级的备份,当发生事故后只能依靠操作系统备份将其恢复。
     控制文件中记录着整个数据库的结构、每个数据文件的状况、系统SCN、检查点计数器等重要信息,在创建数据库时会让用户指定三个位置来存放控制文件,他们之间互为镜像,当其中任何一个发生故障,只需将其从ini文件中注释掉故障数据文件就可重新将数据启动。当所有控制全部失效时,可以在Nomount模式下执行create controlfile来重新生成控制文件,但必须提供redo log,data file,文件名和地址以及MAXLOGFILES,MAXDATAFILES,MAXINSTANCES等信息。如果失败之前运行过alter database backup controlfile to trace或alter database backup controlfile to ‘xxx’对控制文件作备份,恢复时可使用生成的脚本来重建或用备份文件覆盖,如果使用了旧的控制文件在恢复时要使用recover xxx using backup controlfile选项来进行恢复,并使用resetlogs选项来打开数据库。

 

 


     如果丢失的是联机日志文件,分两种情况处理1、丢失的是非活动的日志文件;2、丢失的是当前激活的日志文件。


     如果是第一种情况,而发生故障的日志文件组又具有多个成员,可以先将数据库shutdown,然后用操作系统命令将损坏日志文件组中好的日志成员文件把损坏的成员文件覆盖(在同一个日志成员组中的所有日志文件的各为镜象的),如果其物理位置不可用可将其拷贝到新的驱动器上,使用alter database rename file ‘xxxx’ to ‘xxxx’改变文件位置,之后启动数据库,如果正常马上进行一个冷备份。如果损坏的日志组中只有一个日志成员,先mount上数据库,将其转换为 noarchivelog模式,执行alter database add logfile member ‘xxx’ to group ‘x’给相关组增加一个成员,再执行alter database drop logfile member ‘bad_file’将损坏的日志文件删除,由于数据库的结构发生变动需要备份控制文件,之后将数据库改回archivelog模式,做一个冷备份。

 


     如果丢失的是当前激活的日志文件,数据库又没有镜像而且当前日志组中所有成员均变为不可用。

     首先将数据库shutdown abort,从最近的一次全备份中恢复所有的数据文件,将数据库启动到mount状态。如果原来的日志文件物理位置不可用,使用alter database rename file ‘xxx’ to ‘xxx’改变文件的存放位置。然后,使用recover database until cancel命令来恢复数据库,直到提示最后一个归档日志运用完之后,输入cancel。之后用alter database open resetlogs打开数据库,如果没有问题,立即进行一个冷备份。注意!所有包含在损坏的redo log中的信息将会丢失,也就是说数据库崩溃前已经提交的数据有可能会丢失。这对于某些要求很高的应用将会损失惨重,因此应尽量使每个日志组具有多个日志成员,并且放置在不同的驱动器上一防止发生介质故障。

 


     数据文件发生故障的情况也分为多种情况,

1、丢失包含在SYSTEM表空间的数据文件;
2、丢失没有回滚段的非SYSTEM数据文件;
3、丢失有回滚段的非SYSTEM数据文件。


     如果损坏的是系统表空间的数据文件。唯一的办法是从上一次备份中恢复受损的数据文件,(如果原位置不可用使用alter database rename命令改变新文件的位置),之后在数据库mount的状态下执行recover database/datafile对数据库进行回复,才能将数据库打开。注意:当SYSTEM表空间或其中的数据文件脱机,数据库是无法被打开的,因此必须在mount状态下将所有的恢复工作完成。


     当丢失的数据文件不属于系统表空间而且也不包含回滚段时,有可选择在数据库的两种状态下进行恢复---在数据库open的状态或者在数据库mount的状态。如果用户急于访问数据库中未受损部分的数据或对损坏的数据文件进行恢复需要很长时间,可以先使受损的数据文件脱机,将数据库打开给用户访问,再恢复受损的数据文件最后将其联机。步骤如下:先在数据库mount时,将相关的数据文件或表空间进行脱机alter database datafile xxx offline,然后将数据库open,这样就能使数据库未受损的部分先供用户访问,之后再进行recover datafile/tablespace,完成后用alter database datafile/tablespace ‘xxx’ online使其恢复联机就可被访问了。 当然用户也可以选择在数据库mount状态下,用recover database/datafile将所有的恢复工作做完,将所有数据文件一起打开供用户访问。


     如果丢失的数据文件是最后一种情况,即包含有回滚段的非系统表空间数据文件。也可以选择是在数据库先open的状态还是在mount状态下恢复。不过与上一种情况不同的是当包含回滚段的数据文件损坏时,如果使其先offline将数据库打开,那么所有数据库崩溃前未提交的事务涉及到的表将无法访问,也就是说在回滚段恢复前其中涉及的对象都不允许被访问。而且当所有包含回滚段的数据文件都在offline状态时,数据库无法进行任何DML操作,因此在数据库open状态恢复包含回滚段的数据文件时,可以先创建几个临时回滚段供数据使用create rollback segment temp1 tablespace system; alter rollback segment temp1 online;,当数据文件恢复后再将他们删除alter rollback segment temp1 offline; drop rollback segment temp1;。注意:当用这种方法使恢复的数据文件online之后,所有的原有回滚段将处于offline状态,必须手工使用alter rollback segment RBSxx online;使他们恢复联机状态,这样才能被数据库正常使用。如果在数据库mount状态下完成所有恢复,则不需要上述步骤。


     如果丢失数据文件后,用户发现没有故障前的数据文件的备份,而且自从丢失的数据文件最早建立之后一直没有使用过resetlogs选项打开过数据库。也就是说用户的控制文件是在损坏的数据文件建立前创建的,归档日志中包括对损坏数据文件的所有重做记录。用户就还有一种恢复方法,用户可以先将损坏的数据文件或表空间脱机 alter database datafile / tablespace xxx offline,之后执行alter database create datafile ‘new/xxx.dbf’ as ‘old/xxx.dbf’,数据库会根据保存在控制文件中的信息重建一个空的数据文件,之后再执行recover tablespace / datafile将所有重做记录运用到数据文件,使其完全恢复到当前状态,之后便可再将其恢复联机。

 


     如果丢失的是最后一种文件---归档文件或归档文件所处的物理位置不可用,首先shutdown数据库,立即作一个冷备份。然后修改ini文件中的归档日志文件目的路径,重新启动数据库。以后再发生灾难只需从最新的备份中将相关文件恢复,数据库作recover时就不需要备份之前丢失的归档文件了。在Oracle 8之后的新版本中提供了log_archive_duplex_dest和log_archive_dest_1...5等参数允许保留多份归档文件到不同位置,甚至到远端服务器从而保证归档文件的可靠性。

 


最后再说几点数据库恢复时的注意事项:


1.本文讨论所有情况的默认前提是数据库运行在归档(ARCHIVELOG)方式下,并只涉及到一般常见的情况和最基本的恢复方法。使用Oracle提供的恢复管理器RMAN也能完成上述任务,如果运行环境比较复杂建议使用RMAN来做备份和恢复。


2.一旦数据库发生灾难,最好在进行恢复之前做一次完全的冷备份,以便在进行恢复时产生差错还可以进行补救。很大一部分数据丢失是由于不正确的恢复操作所引起的。


3. 当数据库完成恢复之后,尤其是使用resetlogs选项打开数据库之后,要马上关闭数据库进行一次完全的冷备份。因为,为防止放弃的重做日志被下次恢复时再次运用,resetlogs选项会重新创建redo log文件并将其的计数清零,这将使之前做的所有备份将变为不可用(一般情况下)。


4.要特别注意当进行数据库完全恢复,从发生故障的时间点前的备份中恢复损坏文件时,一定不要使备份中的redo log文件覆盖了当前的redo log文件,否则就只能进行不完全恢复并且要丢失一部分数据了。


原作者:吴悦

November 03

对不起。

本来想写影评的,明天再写吧

 

实在是有些受不了。

   一个28岁的人 还要寄人于篱下 被迫离开自己最亲的人,去受人照顾

   怎么也接受不了。我宁愿一个人漂泊流离 也要靠自己的能力来换取生活

    总是想不依靠别人的力量 总是想独立 而且更应该去帮助身边的人

     可是似乎很失败 28岁的自己 才刚能勉强让自己一个人生活起来 更别提拿出精力来为别人付出

 

    可是 即使如此 我也不愿意再无端受人照顾了  这是耻辱。

 

 

    谢谢你们。其实我也知道你们是好意。非常对不起。

November 02

It’s love

We shall always save a place for ourselves, only for ourselves. And then begin to love.

Have no idea of what it is, who he is, how to love or how long it will be. Just wait for one love.

Maybe no one will come out, but this kind of waiting is the love itself. 

 

刚才在朋友的blog里看到的一句话,突然觉得很有feeling。也许这样子的爱情才是真正的爱情, 态度

   可是 等待 似乎又不是一个 最好的途径

谢国忠:有效市场是个神话

 

  最好的办法,是限制货币增长与名义GDP增长率间的偏差

  货币需求是有效的吗?这个问题的答案关系到怎样的货币政策称得上最好。随着金融机构杠杆率的提高,对货币需求不断增大,这个问题就相当于在问金融体系是否有效。我认为,答案是否定的。货币当局或中央银行有责任考虑到这一点。最好的办法,是限制货币增长与名义GDP增长率间的偏差。特别是,当经济基本面可能在短期受损时,持续的偏差仍应予以纠正。

神话破灭

  这是一个严肃的学术课题。我为什么要在这里和普通读者讨论这个问题?

  首先,这与每个人都息息相关。中国的散户投资者主导着资产市场。他们做出投资或投机决定时,大多认为“政府不会让资产价格下跌”。政府支出一旦有了限制,那这种期望还有几分可信?关于对货币扩张限制的讨论,能够帮助中国投资者评估投资决策的风险。

  其次,从世界范围来看,货币供应量增速都远远高于名义GDP的增速。也就是说,货币增长被用来支撑高杠杆率,这在金融部门尤其明显。当然,原因是央行通过降低利率来应对金融危机,有时甚至是强行增加银行的流动性,以期他们能够增加贷款,刺激经济。不过,这些钱却流入了资产市场,导致资产市场繁荣兴旺。活跃的资产价格稳定了全球经济。尽管大多数分析家认为,活跃的资产市场反映了他们对全球经济蓬勃发展的正确预期。但是,我认为这不是事实。像过去的十年一样,资产市场的繁荣支持了经济增长,而不是倒过来——由于强大的经济增长导致资产市场繁荣——因此,一切只不过是个泡沫。

  尽管全球经济正温和复苏,整体经济形势仍困难重重。经合组织国家的失业率都处于历史高位。资产市场的繁荣与真实的经济困难之间的对比,在近代可谓绝无仅有。尽管工人和企业都在努力挣扎,资产玩家却重新收获了账面利润。资产繁荣的背后,是央行的货币政策。我们不禁要问,政策实现了其帮助实体经济的目标了吗?还是说,政策帮助的只是那些投机者,希望他们能给实体经济留点残羹冷炙?

  金融机构从中央银行获取大量货币,但当前的金融危机暴露了货币运行存在的严重低效。对那些引发了危机的人提供如此多的钱,理由恰恰是因为他们引发了危机,这是什么道理?好比说,当房子着火了,你必须扑灭大火并找到罪魁祸首。可问题是,纵火者被要求去救火。那么,如何可以肯定,他们不会引起另一场大火?

  大部分人认为改革金融体制,而非限制货币供应,是解决这一问题的办法。一旦如此,未来对货币的需求将是有效的。到目前为止,金融危机中发现的问题仍没有得到纠正。过去十年,全球金融体系已变得相当庞大,将中央银行、立法者和政府困在其中。行将推进的改革,不会触碰到那些引发当前危机的主要因素。即使最好的改革能够实行,核心问题仍然难以解决:金融从业人员用别人的钱来赌博,当下对注的时候,他们将得到巨大的回报;下错注,他们也不会赔偿。这个激励问题表明,现行的全球金融体系,鼓励了风险冒进的偏好,有效市场并非如此。中央银行限制货币供给,是解决这一问题的惟一办法。过去十年的资产价格通胀,以及破灭后的灾难,都证明了这一论点是可信的。

  上世纪70年代的滞胀,促使经济学家研究为何货币刺激并没有增加需求,却直接转为通胀。这一研究促进了理性预期理论的发展,解释了大众对货币政策的反应。它的结论显而易见,即决策者可以反复愚弄普通人。多人因此获得诺贝尔奖。米尔顿·弗里德曼主张将以货币供给增长为目标,作为中央银行的指导原则。这一做法将使中央银行以货币增长为目标,“自动驾驶”,并让市场决定利率。

  理性预期理论被进一步用来解释投资者的行为,并引出了有效市场理论:在一定条件下,理智的投资者将带来有效的资产价格,这种价格能够正确反映未来预期。用学术术语来说,有效资产价格,就是包含了所有有关未来的有用信息的价格。这为拆除那些源自“大萧条”时期得到的教训而建立起来的监管架构,奠定了基础。

  上世纪70年代的滞胀,使得中央银行疲于应付短期通胀。依据有效市场理论,中央银行决定完全满足金融机构的货币需求,以支持他们的杠杆率。这种组合为过去十年间出现的巨大泡沫奠定了基础。由于全球化通胀保持在较低水平,华尔街可以无限量地从中央银行获得流动性,制造泡沫。

  在西方金融市场中,机构投资者占主导地位,散户或个人投资者则主导了东方金融市场。多数机构投资者是以季度市场指数为基准,同时,他们持有的现金量还有限制。这些制约因素使他们处于不利地位,很难跑赢大盘。这就是为什么大多数机构投资者都是指数追随者。额外的管理成本,使得大多数机构投资者表现比指数差,这并不会增加市场的效率。

改革的困境

  过去十年,金融市场最显著的发展是绝对表现基金(Absolute performance funds)和对冲基金的增加。但是,他们只是放大了市场的波动性,并未提高市场效率。由于对冲基金经理的薪酬是旱涝保收,因此,他们自然乐于见到长期波动。这是一个委婉的“正面算我赢,反面算你输”的掷硬币游戏。对冲基金业让其管理人员,而不是投资人收益颇丰。

  无论怎样尝试去改善机构投资者的激励结构,“管理别人的钱”带来的激励扭曲难以改变。制度化一度被誉为提高市场效率的一大进步,已被证明使无效状况更甚。面临着变幻莫测市场的发展中国家,一直寻求以制度化来稳定市场。他们应该三思。制度化或能减少短期波动,但会变成大崩溃。

  散户或者个人投资者经常错误地将短时期波动当做趋势。“羊群行为”产生了自我实现的趋势,而这大都只是暂时的。但是,这种羊群行为有时也会持续很长一段时间,引发巨大的泡沫。这种泡沫会导致资源配置严重失当。

  为了尽量减少今后出现金融危机的可能,人们可以改革金融体系,以减少出现危机的倾向;或者在制定货币政策时,同时考虑资产价格以及消费物价。一年前,危机爆发时,世界各地的决策者发誓要改革金融体系,消除腐败和过度杠杆化。在政府花费数万亿美元救助金融机构之后,改革的动力已经减弱。美国国会改革法案已经淡化,因此,他们并无力阻止另一次重大危机。

  资本充足率要求以及透明度,是有效的金融改革的关键。例如,场外交易的衍生品的名义价值达到数万亿美元。它们在一个不透明的环境中蓬勃发展。做市商可以通过欺骗买家和监管机构获取高额利润:向他们收取超高费用,但他们并未为提供这种高风险产品投入大量资金。如果市场是透明的,而且资本要求合理,那么这项业务也不会开展得这样大。在理论上,衍生工具能够帮助买家降低风险;而在实践中,由于复杂的结构可以隐藏杠杆率,衍生工具会带来更大的风险。除非能够看到旨在针对衍生产品市场的问题的改革,否则,所谓的金融改革很难称之为“有效”。

谁又将声名扫地?

  天下没有不散的宴席,现在也是如此。有两个情形预示着另一场危机的爆发。首先,每个交易者借入美元购买东西。华尔街多数交易商是美国人、英国人,还有澳大利亚人。他们都很了解美国。美联储维持着零利率政策,美国政府也支持弱势美元,以刺激美国的出口。除非是傻瓜,人人都能看出,美国政府正在“ 帮助”你借美元去投机。但是,这些交易员对其他国家并不熟悉,特别是新兴经济体。他们一年也就去那里一两次,即使这一两次,还是和美国的投行一起去的:这些投行可是很想要卖东西给他们的。他们愿意相信任何事,除了美元会升值。当然,华尔街的银行也会这么告诉他们。由于这些人为数众多,他们的行动在短期内会自我实现。举例来说,澳元从底部算起,已经升值了35%。现在,这些家伙坐在巨大的账面利润上,感觉自己是如此聪明。当然,华尔街的交易商们在付给投资人之前,已经先喂饱了自己。

  一旦交易规模过大,就像现在这样,只需要一个小冲击就能引发飓风,而你永远不知道这个冲击会是什么。一旦有事发生,所有这些交易商就会迅速退出,这可能会导致另一场危机。

  石油价格的飙升,可能是宴会中另一个不速之客。它可能引发通胀预期回升,并引发债券市场大崩溃。由此产生的极高债券收益率可能迫使央行提高利率,以减少对通货膨胀的担心。另一轮资产价格大下滑,将再度激起对于全球主要金融机构资产负债表的恐惧,再次造成混乱。

  石油是制造泡沫的完美材料:石油供应不能及时回应油价上涨,它需要很长的时间来扩大生产能力,而且由于生活方式和生产方式的粘性,石油需求也不能迅速减少。无论是需求还是供应方面,石油的价格敏感度都较低,很适合用来制造泡沫。当流动性既便宜又易得的时候,石油投机者随处可见。

  石油投机商不再只是那些偷偷摸摸的对冲基金。普通百姓也可以通过购买交易所交易基金拥有石油或其他任何东西。而且,没有道理不去这么做。中央银行已明确表示,将保持尽可能充足的货币供给,使纸币贬值,帮助债务人。如果你赌得很大,当你下错注的时候,政府会帮你摆脱困境,并且降低利率以使你能够下更大的赌注。所以,在这个世界上,最好做个投机者,当权者永远与你同在。

  对于那些想成为投机分子的人,我有一言相劝:一旦债券市场大幅下跌,尽快逃命去吧。石油泡沫来得容易,去得也快,因为它会刺破其他泡沫。一旦这些泡沫破灭,供石油泡沫生存的氧气也就消失了。

  2010年出现二次探底的情况已经很明显。目前的经济复苏得益于企业补充库存以及财政刺激。明年,西方消费者在囊中羞涩之后迅速恢复的几率微乎其微。高失业率将使他们的收入难以支撑其消费。他们不太可能再去借钱消费。

  许多分析家都认为,只要失业率依旧居高不下,那么,就应当不断采取刺激政策。正如我刚才所说,供求不匹配——而不是需求疲软本身——是高失业率的主要原因。更多的刺激只会引发通货膨胀和金融不稳定。

  上世纪70年代的滞胀,让一批认为可以“用一点通胀换得经济大幅增长”的银行家声名狼藉。当前的这场危机,将会使那些忽略资产价格通胀,甚至创造通胀去刺激经济增长的这一代中央银行家们声名扫地。玩火者,必自焚。■