面試某電商時,面試官問到了memcache和mysql數據庫同步的問題,當時只是粗略的想到了mysql的觸發器,現在查找了一些資料。mysql memcached UDF(用戶自定義函數)和mysql觸發器在更新mysql數據庫的時候,觸發更新memcache。
1、首先介紹mysql memcached UDF安裝
http://blog.163.com/lgh_2002/blog/static/44017526201123121912959/
1:mysql memcached UDF 其實就是通過libmemcached來使用memcache的一系列函數,通過這些函數,你能 對memcache進行get, set, cas, append, prepend, delete, increment, decrement objects操作,如果我們通過mysql trigger來使用這些函數,那么就能通過mysql更好的,更自動的管理memcache!
2:安裝方法:
1)安裝memcache
2)安裝libmemcached
shell> cd libmemcached-0.35
shell> ./configure --with-memcached=/usr/local/bin/memcached
shell> make && make install
shell> echo "/usr/local/lib" >> /etc/ld.so.conf
shell> ldconfig
3)安裝memcached_functions_mysql
shell> tar zxvf memcached_functions_mysql-0.9.tar.gz
shell> cd memcached_functions_mysql-0.9
shell> ./configure --with-mysql=/usr/local/mysql51/bin/mysql_config --with-libmemcached =/usr/local/
shell> make && make install
4)拷貝lib文件到mysql的plugin下面
shell> cp -R /usr/local/lib/libmemcached_functions_mysql.* /usr/local/mysql51/lib/mysql/plugin/
5)添加memcache UDF 函數
在mysql里執行 source install_functions.sql
這樣我們就可以使用mysql memcached UDF 了,我們可以通過下面語句查看是否已經正常安裝
1)查看mysql.func,有很多函數
mysql> select * from mysql.func;
+------------------------------+-----+---------------------------------+----------+
| name | ret | dl | type |
+------------------------------+-----+---------------------------------+----------+
| memc_add | 2 | libmemcached_functions_mysql.so | function |
| memc_add_by_key | 2 | libmemcached_functions_mysql.so | function |
| memc_servers_set | 2 | libmemcached_functions_mysql.so | function |
2)添加trigger,看是否向memcache里insert、update等
具體的語句,我們可以參照:
1)memcached_functions_mysql-0.9/sql 目錄下的trigger_fun.sql
2)使用參照文檔:http://dev.mysql.com/doc/refman/5.1/en/ha-memcached-interfaces-mysqludf.html
我們還必須注意以下幾點:
1)mysql 編譯時一定不要帶'--with-mysqld-ldflags=-all-static' 這個參數,因為這樣就限制了mysql 的動態安裝功能了
2)使用時,要觀察mysql.err日志,不知道是有意還是無意,udf更新memcache都會記錄在err日志里,注意清理該日志,否則一下就爆滿了
3)mysql 官網有這樣一句話:
The list of servers used by the memcached UDFs is not persistent over restarts of the MySQL server. If the MySQL server fails, then you must re-set the list of memcached servers.
所以,當我們重啟mysql,我們必須通過select memc_servers_set('192.168.0.1:11211,192.168.0.2:11211');語句重新注冊memcache服務器!
2、創建mysql的insert,update,delete觸發器更新memcache,參考http://blog.csdn.net/jiedushi/article/details/6176940
測試環境在Linux下進行,版本系統為CentOS5.
以下為相關軟件,包括其版本和下載地址:
mysql-5.1.30 下載
memcached-1.2.6 下載
libevent-1.4.7-stable 下載
memcached_functions_mysql-1.1 下載
libmemcached-0.26 下載
編譯安裝MySQL,安裝因個人細好而定,省略許多與測試無關的編譯細節及參數。
- <span>[</span>root@localhost ~<span>]</span><span>#tar xzf mysql-5.1.30.tar_001.gz</span>
- <span>[</span>root@localhost ~<span>]</span><span>#cd mysql-5.1.30</span>
- <span>[</span>root@localhost ~<span>]</span><span>#./configure --prefix=/usr/local/mysql51</span>
- <span>[</span>root@localhost ~<span>]</span><span>#make</span>
- <span>[</span>root@localhost ~<span>]</span><span>#make install</span>
- <span>[</span>root@localhost ~<span>]</span><span>#./scripts/mysql_install_db --user=mysql --skip-name-resolve</span>
- <span>[</span>root@localhost ~<span>]</span><span>#/usr/local/mysql51/bin/mysqld_safe</span>
省略列出安裝memcached和libevent的相關命令,具體可按照實際情況安裝,測試時我將libevent默認安裝,memcached安裝於/usr/local/memcached目錄下。
啟動memcached.
- <span>/</span>usr<span>/</span>local<span>/</span>memcached<span>/</span>bin<span>/</span>memcached <span>-</span>d <span>-</span>m <span>50</span> <span>-</span>u root <span>-</span>p <span>11211</span>
編譯安裝libmemcache.
- <span>[</span>root@localhost ~<span>]</span><span>#tar xzf libmemcached-0.26.tar.gz</span>
- <span>[</span>root@localhost ~<span>]</span><span>#cd libmemcached-0.26</span>
- <span>[</span>root@localhost ~<span>]</span><span>#./configure --with-memcached=/usr/local/memcached/bin/memcached</span>
- <span>[</span>root@localhost ~<span>]</span><span># make && make install</span>
編譯安裝Memcache UDFs for MySQL.
- <span>[</span>root@localhost ~<span>]</span><span># tar xzf memcached_functions_mysql-1.1.tar.gz</span>
- <span>[</span>root@localhost ~<span>]</span><span># cd memcached_functions_mysql-1.1</span>
- <span>[</span>root@localhost ~<span>]</span><span># ./configure --with-mysql-config=/usr/local/mysql51/bin/mysql_config</span>
- <span>[</span>root@localhost ~<span>]</span><span># make && make install</span>
編譯完成后將編譯好的庫文件復制到mysql的插件目錄下,以便於加載使用。
- cp <span>/</span>usr<span>/</span>local<span>/</span>lib<span>/</span>libmemcached_functions_mysql<span>*</span> <span>/</span>usr<span>/</span>local<span>/</span>mysql51<span>/</span>lib<span>/</span>mysql<span>/</span>plugin<span>/</span>
進入memcached_functions_mysql的源碼目錄,在目錄下有相關添加UDF的SQL文件用於初始化。
在mysql的shell中執行memcached_functions_mysql源碼目錄下的sql/install_functions.sql
或者運行memcached_functions_mysql源碼目錄下utils/install.pl這個perl腳本,把memcache function作為UDF加入mysql。
[root@localhost ~]# mysql <sql/install_functions.sql
檢查安裝是否成功
mysql> select name,dl from mysql.func;
二. 測試用例設計情況:
(1).新建兩張表:urls和results,用來更新urls表里面的內容,讓系統自動更新memcached的內容。results用來記錄更新memcached失敗的記錄。
sql語句如下:
use tests;
drop table if exists urls;
CREATE TABLE `urls` (
`id` int(10) NOT NULL,
`url` varchar(255) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
);
drop table if exists results;
CREATE TABLE `results` (
`id` int(10) NOT NULL,
`result` varchar(255) NOT NULL DEFAULT 'error',
`time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
);
(2). 建立三個trigger.
當向urls表里面插入數據時,對memcached執行set操作,trigger如下:
DELIMITER //
DROP TRIGGER IF EXISTS url_mem_insert;
CREATE TRIGGER url_mem_insert
BEFORE INSERT ON urls
FOR EACH ROW BEGIN
set @mm = memc_set(NEW.id, NEW.url);
if @mm <> 0 then
insert into results(id) values(NEW.id);
end if;
END //
DELIMITER ;
當對urls表里面的數據進行更新時,對memcached執行replace操作,trigger如下:
DELIMITER //
DROP TRIGGER IF EXISTS url_mem_update;
CREATE TRIGGER url_mem_update
BEFORE UPDATE ON urls
FOR EACH ROW BEGIN
set @mm = memc_replace(OLD.id,NEW.url);
if @mm <> 0 then
insert into results(id) values(OLD.id);
end if;
END //
DELIMITER ;
當對urls表里面的數據進行刪除操作時,對memcached執行delete操作,trigger如下:
DELIMITER //
DROP TRIGGER IF EXISTS url_mem_delete;
CREATE TRIGGER url_mem_delete
BEFORE DELETE ON urls
FOR EACH ROW BEGIN
set @mm = memc_delete(OLD.ID);
if @mm <> 0 then
insert into results(id) values(OLD.id);
end if;
END //
DELIMITER ;
(3). 設置memcached相關參數
設置同時UDF操作的memcaced的機器IP和端口:
mysql> SELECT memc_servers_set('192.168.3.184:11900');
+---------------------------------------+
| memc_servers_set('192.168.3.184:11900') |
+---------------------------------------+
| 0 |
+---------------------------------------+
1 row in set (0.00 sec)
mysql> select memc_server_count();
+---------------------+
| memc_server_count() |
+---------------------+
| 1 |
+---------------------+
1 row in set (0.00 sec)
在mysql命令行列出可以修改memcached參數的行為:
mysql> select memc_list_behaviors()/G
*************************** 1. row ***************************
memc_list_behaviors():
MEMCACHED SERVER BEHAVIORS
MEMCACHED_BEHAVIOR_SUPPORT_CAS
MEMCACHED_BEHAVIOR_NO_BLOCK
MEMCACHED_BEHAVIOR_TCP_NODELAY
MEMCACHED_BEHAVIOR_HASH
MEMCACHED_BEHAVIOR_CACHE_LOOKUPS
MEMCACHED_BEHAVIOR_SOCKET_SEND_SIZE
MEMCACHED_BEHAVIOR_SOCKET_RECV_SIZE
MEMCACHED_BEHAVIOR_BUFFER_REQUESTS
MEMCACHED_BEHAVIOR_KETAMA
MEMCACHED_BEHAVIOR_POLL_TIMEOUT
MEMCACHED_BEHAVIOR_RETRY_TIMEOUT
MEMCACHED_BEHAVIOR_DISTRIBUTION
MEMCACHED_BEHAVIOR_BUFFER_REQUESTS
MEMCACHED_BEHAVIOR_USER_DATA
MEMCACHED_BEHAVIOR_SORT_HOSTS
MEMCACHED_BEHAVIOR_VERIFY_KEY
MEMCACHED_BEHAVIOR_CONNECT_TIMEOUT
MEMCACHED_BEHAVIOR_KETAMA_WEIGHTED
MEMCACHED_BEHAVIOR_KETAMA_HASH
MEMCACHED_BEHAVIOR_BINARY_PROTOCOL
MEMCACHED_BEHAVIOR_SND_TIMEOUT
MEMCACHED_BEHAVIOR_RCV_TIMEOUT
MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT
MEMCACHED_BEHAVIOR_IO_MSG_WATERMARK
MEMCACHED_BEHAVIOR_IO_BYTES_WATERMARK
1 row in set (0.00 sec)
設置MEMCACHED_BEHAVIOR_NO_BLOCK為打開狀態,這樣在memcached出現問題時(不能連接時)
數據繼續插入到mysql中,報錯提示,如果不設置此值,如果memcached失敗,mysql需要等到timeout
才可以插入到表中。
mysql> select memc_servers_behavior_set('MEMCACHED_BEHAVIOR_NO_BLOCK','1');
+--------------------------------------------------------------+
| memc_servers_behavior_set('MEMCACHED_BEHAVIOR_NO_BLOCK','1') |
+--------------------------------------------------------------+
| 0 |
+--------------------------------------------------------------+
1 row in set (0.00 sec)
mysql> select memc_servers_behavior_set('MEMCACHED_BEHAVIOR_TCP_NODELAY','1');
+-----------------------------------------------------------------+
| memc_servers_behavior_set('MEMCACHED_BEHAVIOR_TCP_NODELAY','1') |
+-----------------------------------------------------------------+
| 0 |
+-----------------------------------------------------------------+
1 row in set (0.00 sec)
三. 簡單的功能測試:
1. 向表urls里面插入數據,然后查memcached是否也set進數據:
mysql> insert into urls (id,url) values (1, 'http://www.sina.com.cn');
Query OK, 1 row affected, 1 warning (0.00 sec)
mysql> select memc_get('1');
+------------------------+
| memc_get('1') |
+------------------------+
| http://www.sina.com.cn |
+------------------------+
1 row in set (0.00 sec)
1> telnet 192.168.3.184 11900
Trying 192.168.3.184...
Connected to 192.168.3.184 (192.168.3.184).
Escape character is '^]'.
get 1
VALUE 1 0 22
http://www.sina.com.cn
END
2.更新表urls里面的數據,然后查詢memcached里面是否也更新:
mysql> update test.urls set url='http://blog.sina.com.cn' where id=1;
Query OK, 1 row affected, 1 warning (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select memc_replace('1','http://blog.sina.com.cn');
+---------------------------------------------+
| memc_replace('1','http://blog.sina.com.cn') |
+---------------------------------------------+
| 0 |
+---------------------------------------------+
1 row in set (0.00 sec)
mysql> select memc_get('1');
+-------------------------+
| memc_get('1') |
+-------------------------+
| http://blog.sina.com.cn |
+-------------------------+
1 row in set (0.00 sec)
1> telnet 192.168.3.184 11900
Trying 192.168.3.184...
Connected to 192.168.3.184 (192.168.3.184).
Escape character is '^]'.
get 1
VALUE 1 0 23
http://blog.sina.com.cn
END
3.刪除表urls里面的數據,然后查memcached是否也刪除:
mysql> delete from test.urls where id=1;
Query OK, 1 row affected, 1 warning (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select memc_get('1');
+---------------+
| memc_get('1') |
+---------------+
| NULL |
+---------------+
1 row in set (0.00 sec)
1> telnet 192.168.3.184 11900
Trying 192.168.3.184...
Connected to 192.168.3.184 (192.168.3.184).
Escape character is '^]'.
get 1
END
四. 利用php腳本insert, update,delete表urls里面的數據,進行測試。
每條記錄的平均長度是:17K
單獨向表urls里面插入10萬條記錄需要的時間為:75秒
單獨對表urls里面更新10萬條記錄需要的時間為:70秒
單獨對表urls里面刪除10萬條記錄需要的時間為:105秒
同時進行30萬數據的insert,update, delete操作需要時間為:241秒
上面操作都沒有memcached失敗情況:
mysql> select * from results;
Empty set (0.00 sec)
測試腳本如下:
插入腳本:
0> more a.php
<?php
$conn = mysql_connect("192.168.1.61","test","test") or die(mysql_error());
mysql_select_db("test",$conn) or die(mysql_error());
//$sql = "show tables";
echo date("Y-m-d H:i:s");
//mysql_query($sql) or die(mysql_error());
for ($i=1; $i<=100000; $i++) {
$sql="insert into urls (id,url) values ($i, 'http://$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i.com');";
mysql_query($sql) or die(mysql_error());
}
echo "/n";
echo date("Y-m-d H:i:s");
?>
更新腳本:
0> more b.php
<?php
$conn = mysql_connect("192.168.1.61","test","test") or die(mysql_error());
mysql_select_db("test",$conn) or die(mysql_error());
//$sql = "show tables";
echo date("Y-m-d H:i:s");
//mysql_query($sql) or die(mysql_error());
for ($i=1; $i<=100000; $i++) {
$sql="update test.urls set url='http://xxxx.$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i$i.com' where id=$i;";
mysql_query($sql) or die(mysql_error());
}
echo "/n";
echo date("Y-m-d H:i:s");
?>
刪除腳本:
0> more c.php
<?php
$conn = mysql_connect("192.168.1.61","test","test") or die(mysql_error());
mysql_select_db("test",$conn) or die(mysql_error());
//$sql = "show tables";
echo date("Y-m-d H:i:s");
//mysql_query($sql) or die(mysql_error());
for ($i=1; $i<=100000; $i++) {
$sql="delete from test.urls where id=$i;";
mysql_query($sql) or die(mysql_error());
}
echo "/n";
echo date("Y-m-d H:i:s");
?>
五. 結論:
測試依賴的環境比較多,可能數據會不准確。整體來看速度還不錯。
適合項目應用相對比較小的場合。
優點:
觸發器中使用 UDFs 直接更新 Memcached 的內容,減輕了應用程序設計和編寫的復雜性。
缺點:
1. 如果出現mysql服務重啟,需要重新設置連接memcached關系(SELECT memc_servers_set('192.168.3.184:11900'))
2. 有可能存在bug問題,導致mysql的crash(測試時沒遇到:)).
真正的線上環境比這個復雜很多。我想到的需要考慮的問題:
1. 網絡因素,mysql和memcached是否放在同一IDC,他們之間的網絡性能是否很好。網絡性能越好,速度肯定越快,如果使用本機的memcached能適當的減少網絡開銷。
2. 插入的數據量,向mysql插入每條記錄的size,以及向memcached里面更新的數據size大小。更新mysql,memcached的數據size越大,更新的速度越慢。
所以前期規划好,在memcached里面存那兩列(key-value)是關鍵。
3. 延時問題需要考慮,mysql所在機器如果資源使用比較狠,會導致更新memcached慢(出現類似m/s的延時問題)。
4. 考慮容災問題,如果兩者中有down出現時,需要考慮怎么恢復,當前的測試是這樣考慮的:建一張錯誤表,如果在出現更新mc出現問題時,自動把更新錯誤的記錄插到
一張表里面,通過查這張表,可以知道哪些數據在什么時間更新錯誤,如果應用於生產環境,需要考慮監控和出現問題時恢復工作(寫好腳本完善這個工作)。
5. mysql自身因素,例如執行的mysql語句效率,以及連接mysql的client程序(php)的連接開銷等等。
參考
http://blog.sina.com.cn/s/blog_499740cb0100g45p.html
http://www.libing.name/2009/02/06/mysql-map-data-to-memcached.html