CONAN ZONE

          你越掙扎我就越興奮

          BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
            0 Posts :: 282 Stories :: 0 Comments :: 0 Trackbacks
          轉(zhuǎn)自:http://whitesock.javaeye.com/blog/811339

          1 Overview

              最近一篇關(guān)于MySQL HandlerSocket的blog吸引了不少人的注意,甚至MySQL Performance Blog上也有關(guān)于HandlerSocket的性能評測。該blog中聲稱對于一個(gè)CPU bound而非IO bound的MySQL Server(即絕大部分的數(shù)據(jù)可以從緩存中取得,例如InnoDB Buffer Pool有接近100%的命中率),使用HandlerSocket可以將查詢性能提升7.5倍。目前HandlerSocket已經(jīng)被其作者應(yīng)用到生產(chǎn) 環(huán)境,效果良好。筆者在昨天抽空安裝并試用了一下HandlerSocket Plugin。

          2 Installation

              安裝的過程基本順利,不過可能是由于筆者是在MySQL 5.1.42+InnoDB Plugin 1.0.6上編譯安裝HandlerSocket的原因,編譯HandlerSocket的過程中報(bào)如下錯(cuò)誤:

              configure: error: MySQL source version does not match MySQL binary version

              調(diào)查一下后發(fā)現(xiàn)是configure腳本沒有能夠找到MySQL source的版本。筆者調(diào)整了configure腳本,跳過了該檢查。編譯安裝后會在MySQL的plugin目錄中生成一個(gè)so文件。然后在MySQL中安裝該plugin即可。

          mysql> INSTALL PLUGIN handlersocket soname 'handlersocket.so';
          mysql
          > show plugins;
          +---------------------+----------+--------------------+---------------------+---------+
          | Name                | Status   | Type               | Library             | License |
          +---------------------+----------+--------------------+---------------------+---------+
          | handlersocket       | ACTIVE   | DAEMON             | handlersocket.so    | BSD     |
          18 rows in set (0.00 sec)

          3 Getting Started

              首先創(chuàng)建一個(gè)測試用的表。

          mysql> CREATE TABLE `user` (
            `
          user_idint(10) unsigned NOT NULL,
            `
          user_namevarchar(50DEFAULT NULL,
            `user_email` 
          varchar(255DEFAULT NULL,
            `created` 
          datetime DEFAULT NULL,
            
          PRIMARY KEY (`user_id`),
            
          KEY `INDEX_01` (`user_name`)
          ) ENGINE
          =InnoDB

          mysql
          > insert into user values(1, "John", "john@test.com", CURRENT_TIMESTAMP);
          mysql
          > insert into user values(2, "Kevin", "Kevin@test.com", CURRENT_TIMESTAMP);
          mysql
          > insert into user values(3, "Dino", "Dino@test.com", CURRENT_TIMESTAMP);

          接下來按照blog中的例子,編寫如下perl腳本handlersocket.pl。

          #!/usr/bin/perl

          use strict;
          use warnings;
          use Net::HandlerSocket;

          #1. establishing a connection
          my $args = { host => 'localhost', port => 9998 };
          my $hs = new Net::HandlerSocket($args);

          #2. initializing an index so that we can use in main logics.
          # MySQL tables will be opened here (if not opened)

          my $res = $hs->open_index(0, 'test', 'user', 'INDEX_01', 'user_name,user_email,created');
          die $hs->get_error() if $res != 0;

          #3. main logic
          #fetching rows by id
          #execute_single (index id, cond, cond value, max rows, offset)

          $res = $hs->execute_single(0, '=', [ 'kevin' ], 1, 0);
          die $hs->get_error() if $res->[0!= 0;
          shift(@$res);
          for (my $row = 0$row < 1++$row) {
            
          my $user_name= $res->[$row + 0];
            
          my $user_email= $res->[$row + 1];
            
          my $created= $res->[$row + 2];
            
          print "$user_name\t$user_email\t$created\n";
          }

          #4. closing the connection
          $hs->close();

          最后執(zhí)行handlersocket.pl,結(jié)果如下:

          perl handlersocket.pl
          Kevin   Kevin
          @test.com  2010-11-14 22:35:22

          4 Philosophy of the HandlerSocket

              通常,MySQL Server可以被看成兩層架構(gòu):即SQL Layer和Storage Engine Layer,它們之間通過Handler API進(jìn)行交互。MySQL Server在接收到客戶端的Query請求后,通常需要在SQL layer中進(jìn)行詞法分析,語法分析,優(yōu)化等過程,最終生成一個(gè)樹型的查詢計(jì)劃,交由執(zhí)行引擎執(zhí)行。執(zhí)行引擎根據(jù)查詢計(jì)劃,跟相應(yīng)的存儲引擎通信,得到查 詢結(jié)果。

              HandlerSocket的作者認(rèn)為,對于CPU bound的MySQL server來說,SQL layer消耗了過多的資源,以致總體性能不佳。HandlerSocket則繞過了MySQL Server的SQL layer,直接跟存儲引擎交互,從而帶來了大幅的性能提升。默認(rèn)情況下HandlerSocket Plugin監(jiān)聽9998和9999兩個(gè)端口,其中9998只支持讀操作,9999支持讀寫操作,但是性能跟9998端口相比稍慢。

              HandlerSocket的作者在其blog中列出了一系列HandlerSocket的優(yōu)點(diǎn),以下是筆者認(rèn)為其中比較重要的:

          • Can handle lots of concurrent connections
          • Extremely high performance
          • No duplicate cache
          • No data inconsistency
          • Independent from storage engines
          • Supporting lots of query patterns
          • No need to modify/rebuild MySQL
          • All operational benefits from MySQL

                其中No duplicate cache和No data inconsistency這兩條,筆者感觸頗深。關(guān)于MySQL的NoSQL擴(kuò)展,可能很多項(xiàng)目都在使用memcached,或者自己實(shí)現(xiàn)的cache 等。這種獨(dú)立緩存的實(shí)現(xiàn)方式有個(gè)重要的局限,即如何保證MySQL和cache之間的數(shù)據(jù)一致性,盡管Memcached Functions for MySQL(基于Trigger和MySQL UDF)從某種程度上提供了一種解決方案。

               此外,關(guān)于Independent from storage engines和Supporting lots of query patterns。理論上通過Handler API可以和任何存儲引擎交互,但是目前HandlerSocket只是在InnoDB Plugin上進(jìn)行了測試。此外,HandlerSocket的作者在其blog上指出,通過HandlerSocket,不僅可以通過主鍵查詢,也可以 通過普通索引,甚至不使用索引進(jìn)行查詢(包括范圍查詢),甚至還可以進(jìn)行insert/update/delete等寫操作。

              關(guān)于All operational benefits from MySQL,正如在其blog中提到的,可以比較方便地通過MySQL的既存功能對HandlerSocket進(jìn)行監(jiān)控,例如以下幾個(gè)監(jiān)控指標(biāo):

          mysql> show global status like 'handler_read%';
          mysql
          > show global status like 'Com_select%';
          mysql
          > show global status like 'InnoDB_rows_read';

          5 Client API

              遺憾的是到目前為止,HandlerSocket只有C和Perl的客戶端API,Java和Python等版本的客戶端API貌似在開發(fā)中。在這里簡單分析一下Perl版本的客戶端API。

          5.1 Connection

              與HandlerSocket Plugin創(chuàng)建連接的方式如下:

          use Net::HandlerSocket;
          my $args = { host => 'localhost', port => 9998 };
          my $hs = new Net::HandlerSocket($args);

            其中Net::HandlerSocket模塊存放于HandlerSocket的分發(fā)tar包的perl-Net-HandlerSocket目錄中,編譯安裝方式如下:

          cd perl-Net-HandlerSocket/
          perl Makefile
          .PL
          make
          make install

          5.2 Opening index

              在進(jìn)行操作之前,首先需要打開一個(gè)索引,如下:

          my $err = $hs->open_index(3, 'database1', 'table1', 'PRIMARY', 'f1,f2');
          die $hs->get_error() if $res->[0!= 0;

          其中'database1'為schema名,'table1'為表名,'PRIMARY'為索引名,'f1,f2'為查詢的列名。關(guān)于方法的open_index的第一個(gè)參數(shù)3,用來在每個(gè)Net::HandlerSocket對象中唯一標(biāo)識一個(gè)表名。

          5.3 Query

              通過execute_single方法進(jìn)行查詢,例如:

          my $res = $hs->execute_single(3, '=', [ 'foo' ], 1, 0);
          die $hs->get_error() if $res->[0!= 0;
          shift(@$res);

          execute_single方法的第一個(gè)參數(shù)需要跟之前open_index方法的第一個(gè)參數(shù)一致。第二個(gè)參數(shù)'='指定了檢索條件,目前支持'=', '>=', '<=', '>'和'<'。第三個(gè)參數(shù)[ 'foo' ]為一個(gè)arrayref,指定了檢索的key,其長度必須小于或者等于對應(yīng)索引的列數(shù)。第四個(gè)和第五個(gè)參數(shù)指定了查詢的limit和offset。

              execute_single方法的返回值類型為arrayref,其第一個(gè)元素為error code:

          • 0:正常。
          • 負(fù)數(shù):I/O 錯(cuò)誤,對應(yīng)的Net::HandlerSocket對象需要被丟棄。
          • 正數(shù):其它錯(cuò)誤,但是與HandlerSocket Plugin的連接仍然正常可用,因此對應(yīng)的Net::HandlerSocket對象可以繼續(xù)使用。

              第一個(gè)元素之后的其它元素即查詢結(jié)果,如果返回的row數(shù)大于1,那么也是存放在這個(gè)一維數(shù)組中。假設(shè)查詢結(jié)果共5行,每行三列,那么對應(yīng)的代碼如下:

          die $hs->get_error() if $res->[0!= 0;
          shift(@$res);
          for (my $row = 0$row < 5++$row) {
            
          for (my $col = 0$col < 3++$col) {
              
          my $value = $res->[$row * 5 + $col];
              
          # 
            }
          }

          5.4 Insert records

              execute_single方法也可以用來插入記錄,例如:

          my $args = { host => 'localhost', port => 9999 };
          my $hs = new Net::HandlerSocket($args);

          my $res = $hs->execute_single(3, '+', [ 'foo', 'bar', 'baz' ]);
          die $hs->get_error() if $res->[0!= 0;
          my $num_inserted_rows = $res->[1];

          需要注意的是,此時(shí)連接的是9999端口,因?yàn)?998端口只支持讀操作。

          5.5 Update or delete records

              對于更新和刪除操作,同樣使用execute_single方法,例如:

          my $args = { host => 'localhost', port => 9999 };
          my $hs = new Net::HandlerSocket($args);

          my $res = $hs->execute_single(3, '=', [ 'bar' ], 1, 0, 'U', [ 'fubar', 'hoge' ]);
          die $hs->get_error() if $res->[0!= 0;
          my $num_updated_rows = $res->[1];

          my $res = $hs->execute_single(3, '=', [ 'baz' ], 1, 0, 'D');
          die $hs->get_error() if $res->[0!= 0;
          my $num_deleted_rows = $res->[1];

          execute_single方法的第六個(gè)參數(shù)指定的操作類型,目前支持'U'和'D'。對于'U'操作,execute_single方法的第七個(gè)參數(shù)指定了更新后的值;對于'D'操作,第七個(gè)參數(shù)被忽略。

          5.6 Multiple operations

              可在一次調(diào)用中執(zhí)行多個(gè)操作,這樣速度更快,例如:

          my $rarr = $hs->execute_multi([
            [ 
          0, '>=', [ 'foo' ], 5, 0 ],
            [ 
          2, '=', [ 'bar' ], 1, 0 ],
            [ 
          4, '<', [ 'baz' ], 10, 5 ],
          ]);
          for my $res (@$rarr) {
            
          die $hs->get_error() if $res->[0!= 0;
            
          shift(@$res);
            
          # 
          }

          6 Reference

              http://yoshinorimatsunobu.blogspot.com/2010/10/using-mysql-as-nosql-story-for.html (翻)

              http://www.mysqlperformanceblog.com/2010/11/02/handlersocket-on-ssd/





          posted on 2010-11-19 18:02 CONAN 閱讀(380) 評論(0)  編輯  收藏 所屬分類: Database
          主站蜘蛛池模板: 唐山市| 姜堰市| 尉犁县| 乌恰县| 万载县| 黄龙县| 九江县| 汝南县| 唐河县| 六枝特区| 疏附县| 刚察县| 宜君县| 河池市| 南川市| 浦江县| 浮山县| 丰原市| 渝中区| 恩施市| 龙海市| 新闻| 宜宾市| 克什克腾旗| 余干县| 皋兰县| 奉贤区| 八宿县| 繁峙县| 兖州市| 榆林市| 克山县| 池州市| 柘城县| 兴仁县| 张家口市| 高淳县| 建始县| 双峰县| 大冶市| 渝北区|