MySQL 5.6.13基于MySQL-Proxy的读写分离

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
网络型负载均衡 NLB,每月750个小时 15LCU
简介:

一、MySQL-Proxy简介

MySQL-Proxy是处在MySQL数据库客户和服务端之间的一个中间件,支持嵌入性脚本语言lua,可以用来分析,监控和变换通信数据,支持使用的场景包括:负载均衡和故障转移处理,查询分析日志,查询重写等。

MySQL Proxy更强大的一项功能是实现“读写分离(Read/Write Splitting)”。基本的原理是让主数据库处理事务性查询,而从数据库处理SELECT查询。数据库复制被用来把事务性查询导致的变更同步到集群中的从数据库。


二、实验环境

192.168.30.115 OS:CentOS 6.4 x86_64  mysql-proxy.luojianlong.com

192.168.30.116 OS:CentOS 6.4 x86_64  master.luojianlong.com

192.168.30.117 OS:CentOS 6.4 x86_64  slave.luojianlong.com

MySQL version:mysql-5.6.13-linux-glibc2.5-x86_64

MySQL-Proxy version:mysql-proxy-0.8.2-1.el6.x86_64

拓扑图:

wKiom1NFGcqiEMilAACz0U92j20079.jpg



由于,读写分离的是基于主从复制的,上一篇博客已经介绍了mysql 5.6的主从复制,所有这里就不介绍了,直接使用做好的主从架构。

首先,在mysql-proxy.luojianlong.com上面安装mysql-proxy,使用epel的yum源来安装

1
2
3
4
5
6
7
[root@mysql-proxy ~] # cat /etc/yum.repos.d/epel.repo
[epel]
name=Extra Packages  for  Enterprise Linux 6 - $basearch
baseurl=http: //mirrors .sohu.com /fedora-epel/6/x86_64/
enabled=1
gpgcheck=0
[root@mysql-proxy ~] # yum -y install mysql-proxy


修改服务脚本配置文件/etc/sysconfig/mysql-proxy

1
2
3
4
5
6
7
# 修改为
# Options for mysql-proxy
ADMIN_USER= "admin"
ADMIN_PASSWORD= "admin"
ADMIN_LUA_SCRIPT= "/usr/lib64/mysql-proxy/lua/admin.lua"
PROXY_USER= "mysql-proxy"
PROXY_OPTIONS= "--daemon --log-level=info --log-use-syslog --plugins=proxy --plugins=admin --proxy-backend-addresses=192.168.30.116:3306 --proxy-read-only-backend-addresses=192.168.30.117:3306 --proxy-lua-script=/usr/lib64/mysql-proxy/lua/rw-splitting.lua"

其中的proxy-backend-addresses选项和proxy-read-only-backend-addresses选项均可重复使用多次,以实现指定多个读写服务器或只读服务器。

mysql-proxy的配置选项:

--proxy-address=host:port:代理服务监听的地址和端口;

--admin-address=host:port:管理模块监听的地址和端口;

--proxy-backend-addresses=host:port:后端mysql服务器的地址和端口;

--proxy-read-only-backend-addresses=host:port:后端只读mysql服务器的地址和端口;

--proxy-lua-script=file_name:完成mysql代理功能的Lua脚本;

--daemon:以守护进程模式启动mysql-proxy;

--keepalive:在mysql-proxy崩溃时尝试重启之;

--log-file=/path/to/log_file_name:日志文件名称;

--log-level=level:日志级别;

--log-use-syslog:基于syslog记录日志;

--plugins=plugin:在mysql-proxy启动时加载的插件;

--user=user_name:运行mysql-proxy进程的用户;

--defaults-file=/path/to/conf_file_name:默认使用的配置文件路径;其配置段使用[mysql-proxy]标识;

--proxy-skip-profiling:禁用profile;

--pid-file=/path/to/pid_file_name:进程文件名;


这里使用mysql-proxy-0.8.3提供的读写分离的lua脚本,将其复制保存为/usr/lib64/mysql-proxy/lua/proxy/rw-splitting.lua,就可以启动服务了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
[root@mysql-proxy ~] # vi /usr/lib64/mysql-proxy/lua/rw-splitting.lua
--[[ $%BEGINLICENSE%$
  Copyright (c) 2007, 2012, Oracle and /or  its affiliates. All rights reserved.
  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., 51 Franklin St, Fifth Floor, Boston, MA
  02110-1301  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 = 4,
         max_idle_connections = 8,
         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.src.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.src.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  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.src.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
                                                                                                                             
     -- COM_BINLOG_DUMP packet can't be balanced
     --
     -- so we must send it always to the master
     if  cmd. type  == proxy.COM_BINLOG_DUMP  then
         --  if  we don 't have a backend selected, let' s pick the master
         --
         if  proxy.connection.backend_ndx == 0  then
             proxy.connection.backend_ndx = lb.idle_failsafe_rw()
         end
         return
     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, { resultset_is_needed =  true  })
     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
         -- 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.src.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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@mysql-proxy ~] # service mysql-proxy start
Starting mysql-proxy:                                      [  OK  ]
[root@mysql-proxy ~] # ss -anptl
State       Recv-Q Send-Q                                                               Local Address:Port                                                                 Peer Address:Port
LISTEN      0      128                                                                             :::111                                                                            :::*       users :(( "rpcbind" ,1627,11))
LISTEN      0      128                                                                              *:111                                                                             *:*       users :(( "rpcbind" ,1627,8))
LISTEN      0      128                                                                             :::22                                                                             :::*       users :(( "sshd" ,2079,4))
LISTEN      0      128                                                                              *:22                                                                              *:*       users :(( "sshd" ,2079,3))
LISTEN      0      128                                                                      127.0.0.1:631                                                                             *:*       users :(( "cupsd" ,1938,7))
LISTEN      0      128                                                                            ::1:631                                                                            :::*       users :(( "cupsd" ,1938,6))
LISTEN      0      128                                                                              *:47000                                                                           *:*       users :(( "rpc.statd" ,1837,9))
LISTEN      0      100                                                                            ::1:25                                                                             :::*       users :(( "master" ,2174,13))
LISTEN      0      100                                                                      127.0.0.1:25                                                                              *:*       users :(( "master" ,2174,12))
LISTEN      0      128                                                                             :::53369                                                                          :::*       users :(( "rpc.statd" ,1837,11))
LISTEN      0      128                                                                              *:4040                                                                            *:*       users :(( "mysql-proxy" ,19973,10))
LISTEN      0      128                                                                              *:4041                                                                            *:*       users :(( "mysql-proxy" ,19973,11))


在mysql-proxy上登录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@mysql-proxy ~] # mysql -u root -pmypass -h 192.168.30.115 -P4040
mysql> create database testdb;
Query OK, 1 row affected (0.01 sec)
mysql> use testdb
     server default db:
     client default db: testdb
     syncronizing
Database changed
mysql> create table testA( id  int);
Query OK, 0 rows affected (0.43 sec)
# 分别在主从服务器上使用tcpdump命令抓包
[root@master ~] # tcpdump -i eth0 -s0 -nn -A tcp dst port 3306 and dst host 192.168.30.116
tcpdump: verbose output suppressed, use - v  or -vv  for  full protocol decode
listening on eth0, link- type  EN10MB (Ethernet), capture size 65535 bytes
[root@slave ~] # tcpdump -i eth0 -s0 -nn -A tcp dst port 3306 and dst host 192.168.30.117
tcpdump: verbose output suppressed, use - v  or -vv  for  full protocol decode
listening on eth0, link- type  EN10MB (Ethernet), capture size 65535 bytes

批量插入和查询数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@mysql-proxy ~] # for i in {1..50}; do mysql -u root -pmypass -h 192.168.30.115 -P4040 -e 'use testdb; insert into testA values(5);'; done
[root@master ~] # tcpdump -i eth0 -s0 -nn -A tcp dst port 3306 and dst host 192.168.30.116
18:47:09.733244 IP 192.168.30.115.33567 > 192.168.30.116.3306: Flags [P.],  seq  301:333, ack 155, win 131, options [nop,nop,TS val 21000991 ecr 23848994], length 32
E..Tqe@.@.
....s...t....[...Jb".....mh.....
.@s..k.".....insert into testA values(5)
18:47:09.749683 IP 192.168.30.115.33570 > 192.168.30.116.3306: Flags [.], ack 144, win 131, options [nop,nop,TS val 21001008 ecr 23848972], length 0
E..4..@.@..|...s...t."..r....u.h....>Z.....
.@s0.k..
18:47:09.775507 IP 192.168.30.117.38626 > 192.168.30.116.3306: Flags [.], ack 5185, win 499, options [nop,nop,TS val 22264873 ecr 23849027], length 0
E..4..@.@......u...t.........mm.....$......
.S.).k.C
18:47:09.778730 IP 192.168.30.115.33572 > 192.168.30.116.3306: Flags [P.],  seq  269:301, ack 144, win 115, options [nop,nop,TS val 21001037 ecr 23848927], length 32
E..TA.@.@.:....s...t.$..+L.D\..n...s+0.....
.@sM.k.......root....f
.....J.....:c.P..
18:47:09.779117 IP 192.168.30.115.33572 > 192.168.30.116.3306: Flags [.], ack 155, win 115, options [nop,nop,TS val 21001037 ecr 23849040], length 0
E..4A.@.@.:....s...t.$..+L.d\..y...ss......
.@sM.k.P
18:47:09.783718 IP 192.168.30.115.33570 > 192.168.30.116.3306: Flags [P.],  seq  290:301, ack 144, win 131, options [nop,nop,TS val 21001040 ecr 23848972], length 11
E..?..@.@..p...s...t."..r....u.h...........
[root@slave ~] # tcpdump -i eth0 -s0 -nn -A tcp dst port 3306 and dst host 192.168.30.117
tcpdump: verbose output suppressed, use - v  or -vv  for  full protocol decode
listening on eth0, link- type  EN10MB (Ethernet), capture size 65535 bytes

插入50条数据发现,只有master服务器抓到数据更新的包,slave没有

下面模拟50次查询请求,查看效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
[root@master ~] # tcpdump -i eth0 -s0 -nn -A tcp dst port 3306 and dst host 192.168.30.116
tcpdump: verbose output suppressed, use - v  or -vv  for  full protocol decode
listening on eth0, link- type  EN10MB (Ethernet), capture size 65535 bytes
18:55:32.032655 IP 192.168.30.115.33567 > 192.168.30.116.3306: Flags [P.],  seq  204:215, ack 100, win 131, options [nop,nop,TS val 21503291 ecr 24351256], length 11
E..?sf@.@.  ....s...t....[..{Jb-............
.H.;.s.......testdb
18:55:32.048136 IP 192.168.30.115.33572 > 192.168.30.116.3306: Flags [P.],  seq  183:215, ack 100, win 115, options [nop,nop,TS val 21503307 ecr 24351259], length 32
E..TC.@.@.8....s...t.$..+M..\..n...s.f.....
.H.K.s.......root....f
.....J.....:c.P..
18:55:32.048802 IP 192.168.30.115.33572 > 192.168.30.116.3306: Flags [.], ack 111, win 115, options [nop,nop,TS val 21503308 ecr 24351310], length 0
E..4C.@.@.8....s...t.$..+M..\..y...s.T.....
.H.L.s.N
18:55:32.051857 IP 192.168.30.115.33570 > 192.168.30.116.3306: Flags [P.],  seq  204:215, ack 100, win 131, options [nop,nop,TS val 21503310 ecr 24351274], length 11
E..?..@.@..l...s...t."..r..9.u.].....E.....
.H.N.s.*.....testdb
18:55:32.056674 IP 192.168.30.115.33574 > 192.168.30.116.3306: Flags [.], ack 100, win 115, options [nop,nop,TS val 21503314 ecr 24351277], length 0
E..4.n@.@......s...t.&....M2..x....s.......
.H.R.s.-
18:55:32.071103 IP 192.168.30.115.33574 > 192.168.30.116.3306: Flags [P.],  seq  183:215, ack 100, win 115, options [nop,nop,TS val 21503329 ecr 24351277], length 32
E..T.o@.@......s...t.&....M2..x....s.......
.H.a.s.-.....root...Z.5o....|........ #".
18:55:32.073535 IP 192.168.30.115.33567 > 192.168.30.116.3306: Flags [.], ack 111, win 131, options [nop,nop,TS val 21503332 ecr 24351294], length 0
E..4sg@.@.  ....s...t....[...Jb-............
.H.d.s.>
18:55:32.074669 IP 192.168.30.115.33576 > 192.168.30.116.3306: Flags [P.],  seq  204:215, ack 100, win 115, options [nop,nop,TS val 21503333 ecr 24351291], length 11
E..?..@.@.u....s...t.(...'..L=`M...s.......
.H.e.s.;.....testdb
18:55:32.075503 IP 192.168.30.115.33576 > 192.168.30.116.3306: Flags [.], ack 111, win 115, options [nop,nop,TS val 21503334 ecr 24351336], length 0
E..4..@.@.u....s...t.(...'..L=`X...sB......
.H.f.s.h
18:55:32.090373 IP 192.168.30.115.33567 > 192.168.30.116.3306: Flags [P.],  seq  215:247, ack 111, win 131, options [nop,nop,TS val 21503349 ecr 24351294], length 32
E..Tsh@.@......s...t....[...Jb-.....L......
.H.u.s.>.....root..S....9r.=*........ct.
18:55:32.091299 IP 192.168.30.115.33567 > 192.168.30.116.3306: Flags [.], ack 122, win 131, options [nop,nop,TS val 21503349 ecr 24351352], length 0
E..4si@.@.  ....s...t....[...Jb.......9.....
[root@slave ~] # tcpdump -i eth0 -s0 -nn -A tcp dst port 3306 and dst host 192.168.30.117
18:55:33.701885 IP 192.168.30.115.60055 > 192.168.30.117.3306: Flags [P.],  seq  3683:3720, ack 36062, win 501, options [nop,nop,TS val 21504886 ecr 22768728], length 37
E..Y3E@.@.I....s...u....{.....UJ.....8.....
.H #v.[lX!....select @@version_comment limit 1
18:55:33.702932 IP 192.168.30.115.60053 > 192.168.30.117.3306: Flags [P.],  seq  3693:3698, ack 36058, win 501, options [nop,nop,TS val 21504887 ecr 22768696], length 5
E..9..@.@._....s...u.....A...........{.....
.H #w.[l8.....
18:55:33.703341 IP 192.168.30.115.60053 > 192.168.30.117.3306: Flags [P.],  seq  3698:3720, ack 36091, win 501, options [nop,nop,TS val 21504888 ecr 22768729], length 22
E..J..@.@._....s...u.....A.....7....\......
.H #x.[lY.....SELECT DATABASE()
18:55:33.704708 IP 192.168.30.115.60049 > 192.168.30.117.3306: Flags [P.],  seq  3696:3720, ack 35492, win 501, options [nop,nop,TS val 21504889 ecr 22768711], length 24
E..L..@.@......s...u..........A......a.....
.H #y.[lG.....select * from testA
18:55:33.725988 IP 192.168.30.115.60057 > 192.168.30.117.3306: Flags [.], ack 36161, win 501, options [nop,nop,TS val 21504911 ecr 22768712], length 0
E..4i.@.@......s...u.....j.=........o......
.H #..[lH
18:55:33.726996 IP 192.168.30.115.60051 > 192.168.30.117.3306: Flags [.], ack 36161, win 501, options [nop,nop,TS val 21504912 ecr 22768714], length 0
E..4..@.@......s...u.....m...f.............
.H #..[lJ
18:55:33.740992 IP 192.168.30.115.60055 > 192.168.30.117.3306: Flags [.], ack 36161, win 501, options [nop,nop,TS val 21504926 ecr 22768728], length 0
E..43F@.@.I=...s...u....{.....U......_.....
.H #..[lX
18:55:33.742949 IP 192.168.30.115.60053 > 192.168.30.117.3306: Flags [.], ack 36161, win 501, options [nop,nop,TS val 21504928 ecr 22768730], length 0
E..4..@.@._....s...u.....A.....}...........
.H #..[lZ
18:55:33.744968 IP 192.168.30.115.60049 > 192.168.30.117.3306: Flags [.], ack 36161, win 501, options [nop,nop,TS val 21504930 ecr 22768731], length 0
E..4..@.@......s...u..........DE...........
.H #..[l[


测试发现读请求被平均分配到俩台服务器


到此,MySQL 5.6.13基于MySQL-Proxy的读写分离配置完成




     本文转自ljl_19880709 51CTO博客,原文链接:http://blog.51cto.com/luojianlong/1393030,如需转载请自行联系原作者



相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
打赏
0
0
0
0
348
分享
相关文章
如何实现 MySQL 的读写分离?
本文介绍了 MySQL 读写分离的实现方式及其主从复制原理,解释了如何通过主从架构提升读并发能力。重点分析了主从同步延时问题及解决方案,如半同步复制、并行复制等技术手段,并结合实际案例探讨了高并发场景下的优化策略。文章还提醒开发者在编写代码时需谨慎处理插入后立即查询的情况,避免因主从延时导致的数据不一致问题。
306 44
如何实现 MySQL 的读写分离?
MySQL 官宣:支持读写分离了!!
【10月更文挑战第8天】MySQL的读写分离功能显著提升了数据库性能、可用性和可靠性。通过将读写操作分配至不同服务器,有效减轻单个服务器负载,提高响应速度与吞吐量,并增强系统稳定性。此外,它还支持便捷的扩展方式,可通过增加只读服务器提升读操作性能。实现读写分离的方法包括软件层面(如使用数据库中间件)和硬件层面(使用独立服务器)。使用时需注意数据一致性、负载均衡及监控管理等问题。
364 0
MySQL主从复制实现读写分离
MySQL主从复制(二进制日志)、 Sharding-JDBC实现读写分离
MySQL主从复制实现读写分离
(二十五)MySQL主从实践篇:超详细版读写分离、双主热备架构搭建教学
在上篇《主从原理篇》中,基本上把主从复制原理、主从架构模式、数据同步方式、复制技术优化.....等各类细枝末节讲清楚了,本章则准备真正对聊到的几种主从模式落地实践,但实践的内容通常比较枯燥乏味,因为就是调整各种配置、设置各种参数等步骤。
822 3
mysql读写分离,主从同步
本文介绍了如何在Laravel项目中配置数据库读写分离,并实现MySQL主从同步。主要步骤包括:在`config/database.php`中设置读写分离配置;为主机授予从机访问权限;配置各MySQL服务器的`/etc/my.cnf`文件以确保唯一的`server-id`;以及通过SQL命令设置主从关系并启动从服务。文章还针对一些常见错误提供了排查方法。最后通过验证确认主从同步是否成功。[原文链接](https://juejin.cn/post/6901581801458958344)。版权所有者为作者佤邦帮主,转载请遵循相关规定。
使用MyCat实现MySQL主从读写分离(一)概述
【8月更文挑战第11天】MySQL读写分离通过主从复制分散负载,主库负责写操作,从库承担读查询,以复制技术确保数据一致性。此策略有效缓解锁竞争,提升查询效能并增强系统可用性。实现方式包括应用层处理,简便快捷但灵活性受限;或采用中间件如MyCAT、Vitess等,支持复杂场景但需专业团队维护。
238 0
【YashanDB知识库】原生mysql驱动配置连接崖山数据库
【YashanDB知识库】原生mysql驱动配置连接崖山数据库
【YashanDB知识库】原生mysql驱动配置连接崖山数据库
docker拉取MySQL后数据库连接失败解决方案
通过以上方法,可以解决Docker中拉取MySQL镜像后数据库连接失败的常见问题。关键步骤包括确保容器正确启动、配置正确的环境变量、合理设置网络和权限,以及检查主机防火墙设置等。通过逐步排查,可以快速定位并解决连接问题,确保MySQL服务的正常使用。
203 82
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等