LSPE @Yahoo mysql-proxy By Farhad Saberi - Overview and architecture - Making The case for a proxy - Lua - Lua examples - The admin interface - currently known issues Overview and Architecture Same idea as any other proxy. Sits between the application and its Database: Overview and Arhitecture Read only Load Balancing with multiple –proxy-read-only-backend-addresses option Making The case for mysql-proxy Easier sites management. Hundreds of connection definitions use the same host and port. DB Host connection changes are done at one place only. No need to restart for configuration or behavioral changes. It all happens in real time. Making the case for mysql-proxy Flexible, Powerful and invisible: intercept, tokenize and modify queries Deny/allow queries based on DB or DB User. Deny/allow based on a regex Construct your own results set and return it to the application without ever actually querying the backend database. LUA embedded Making the case for a proxy It's fast and stable: Small daemon, about 35 MB in VSZ compared to a preforked apache child being 50 to 100 MB. Coded in C implements connection pooling Doesn't need to do NS lookups every time Has never “crashed” on me so far! Making the case for mysql-proxy Open source. LUA Simple scripting language Small: its whole distribution fits in a floppy disk. Extensible; it is easy to interface with C code. LUA Widely used in games: LUA Mysql-proxy is the application code and Lua is its extension language. You change a running mysql-proxy's behavior and variables in a single Lua script in real time (no restart required). Lua examples Mysql-proxy starts with –proxy-backend-addresses=db1.host.net, db2.host.net, db3.host.net Choose your backend in connect_server() Function connect_server() proxy.connection.backend_ndx=2 End And you will always connect to db2.host.net. If you change 2 to 3, you will always connect to db3. Note: Lua's array starts at 1, !0 Lua examples Deny all queries to any backend DB: function read_query(packet) if string.byte(packet) == proxy.COM_QUERY then local fields = { } local rows = { } fields = { { name = “message from DBA”, type =proxy.MYSQL_TYPE_STRING}, } rows[1] = { “maintenance”} proxy.respose = { type = proxy.MYSQLD_PACKET_OK, resultset = { fields = fields, rows = rows } } return proxy.PROXY_SEND_RESULT –means bypass backend, return to client now end end Lua examples For any query issued, this will be the result: mysql> show tables; # or anything else +--------------------------+ | message from DBA | +--------------------------+ | maintenance | +---------------------------+ Lua example denying access to just one table using a regex. Return mysql error msg to client. Function read_query(packet) if packet:byte() == proxy.COM_QUERY then local query= packet:sub(2) print (“query: “ .. query) – debugging purpose if string.match(query:lower(), “from%s+.*my_table”) then proxy.response.type = proxy.MYSQLD_PACKET_ERR proxy.response.errmsg = “access denied to table my_table” return proxy.PROXY_SEND_RESULT end end end Lua example Running any query that would match “from%s+.* my_table” will return ERROR 1105 (07000): access denied to table my_table Lua example Some other things you can do: Start/stop mysqld backend. Choose your backend DB based on which IP address, or which mysql user the client has. Modify result set by adding or removing a column from the returned table. Time your queries. Gather statistics. Your limits are that of the Lua language. The admin interface You can monitor and show internal structures of the running mysql-proxy process by programming your own admin interface in Lua. Start mysql-proxy with admin options: --admin-address=127.0.0.1:10008 --admin-username=root –admin-password=password --admin-lua-script=/mysql-proxy/share/admin/admin.lua The admin interface Function read_query(packet) If packet:byte() ~= proxy.COM_QUERY then return_error(“[admin] we only handle COM_QUERY”); --user defined end Local rows = { } local fields = { } – no need for a semicolon in Lua if packet:sub(2) == “ls” then – if query is an ls fields = { { name = “backend_ndx”, type = proxy.MYSQL_TYPE_LONG }, { name = “address” , type = proxy.MYSQL_TYPE_STRING } , { name = “connected_clients”, type = proxy.MYSQL_TYPE_LONG}, } for i = 1, #proxy.global.backends do Local b = proxy.global.backends[i] rows[#rows + 1] = { i, b.dst.name, b.connected_clients } end end … Now build proxy.response and return with proxy.PROXY_SEND_RESULT The admin interface Querying the previously Lua coded interface will return: mysql>msyql -u root -ppassword -h 127.0.0.1 -P 10008 mysql> ls; +------------------+--------------------------+-------------------------+ | backend_ndx | address | connected_clients | +------------------+--------------------------+-------------------------+ | 1 | 192.168.1.9:3309 | 23 | +------------------+--------------------------+-------------------------+ | 2 | 192.168.1.10:3309 | 142 | +------------------+--------------------------+-------------------------+ Currently known issues Read/Write splitting does not work if used by more than one client on the same proxy process. This is a known issue and I'm assuming it is being worked on. A bit complicated. Everything has to be coded by you. You need to know Lua well. (not a bad thing though)