当前位置:Gxlcms > 数据库问题 > Using MySQL as a NoSQL

Using MySQL as a NoSQL

时间:2021-07-01 10:21:17 帮助过:4人阅读

  它与MySQL + Memcached 的区别在哪呢? 对比图1 和图2 ,可从中看出其不同点。图2 展示了典型的 MySQL + Memecached 的使用. 因为 Memcached 的 get 操作比 MySQL 的内存中/磁盘上的主键查询要快很多,所以 Memcached 用于缓存数据库记录。如果 HandlerSocket 的查询速度能和 Memcached 媲美,我们就不需要使用 Memcached 来缓存记录。

  

  举一个例子,假设有一 user 表,通过 user_id 来获取用户信息:

CREATE TABLE user
(
user_id INT UNSIGNED PRIMARY KEY,
user_name VARCHAR(50),
user_email VARCHAR(255),
created DATETIME
)
ENGINE=InnoDB;

  用 SELECT 语句获取用户信息

mysql> SELECT user_name, user_email, created FROM user WHERE user_id=101;
+---------------+-----------------------+---------------------+
| user_name     | user_email            | created
|  +---------------+-----------------------+---------------------+
| Yukari Takeba | yukari.takeba@dena.jp | 2010-02-03 11:22:33
|  +---------------+-----------------------+---------------------+
1 row in set (0.00 sec)

  下面我们来看看如何使用 HandlerSocket 完成同样的事情.

安装 HandlerSocket

  HandlerSocket具体安装步骤请参考这里,基本步骤如下:

  • 1 下载HandlerSocket
  • 2 编译 HandlerSocket(客户端和服务端)
    [root@localhost handlersocket]# ./configure --with-mysql-source=mysql-source-dir --with-mysql-bindir=mysql-server-bin-dir
    [root@localhost handlersocket]# make
    [root@localhost handlersocket]# make install
    
  • 3 安装 HandlerSocket
    mysql> INSTALL PLUGIN \‘handlersocket\‘ soname \‘handlersocket.so\‘;
    

      因为 HandlerSocket是 MySQL 插件,所以可以象使用其它插件,如 InnoDB, Q4M 和 Spider 那样使用它,即不需要修改 MySQL 源代码,MySQL 最好是 5.1 或更高版本,编译 HandlerSocket 时需要 MySQL 源码和 MySQL 库。

    书写 HandlerSocket 客户端代码

      目前已提供 C++ 和 perl 调用的客户端库,下面是使用 perl 调用的实例代码:

    #!/usr/bin/perl  
    
    use strict;
    use warnings;
    use Net::HandlerSocket;  
    
    #1. establishing a connection
    my $args = { host => \‘ip_to_remote_host\‘, 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\‘, \‘PRIMARY\‘,
        \‘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, \‘=\‘, [ \‘101\‘ ], 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()
    
    #!/usr/bin/perl  
    
    use strict;
    use warnings;
    use Net::HandlerSocket;  
    
    #1. establishing a connection
    my $args = { host => \‘ip_to_remote_host\‘, 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\‘, \‘PRIMARY\‘,
        \‘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, \‘=\‘, [ \‘101\‘ ], 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();
    

      上面代码是通过 user_id=101 条件在 user 表获取用户 user_name, user_email和 created 信息,得到的结果应该和之前在 MySQL client 查询出来的结果一样。

    [matsunobu@host ~]$ perl sample.pl
    Yukari Takeba   yukari.takeba@dena.jp   2010-02-03 11:22:33
    

      对于大多数Web应用程序而言,保持轻量级的 HandlerSocket 连接是一个很好的做法(持续连接),让大量的请求可以集中于主要逻辑(上面代码中的#3部分)。

       HandlerSocket 协议是一个小尺寸的基于文本的协议,和 Memcached 文本协议类似,可以使用 telnet 通过 HandlerSocket 获取数据。

    [matsunobu@host ~]$ telnet 192.168.1.2 9998
    Trying 192.168.1.2...
    Connected to xxx.dena.jp (192.168.1.2).
    Escape character is \‘^]\‘.
    P       0       test    user    PRIMARY user_name,user_email,created
    0       1
    0       =       1       101
    0       3       Yukari Takeba   yukari.takeba@dena.jp   2010-02-03 11:22:33
    (Green lines are request packets, fields must be separated by TAB)
    

      绿色表示请求数据包,字段必须用Tab键分隔。

      现在是时候展示基准测试结果,使用上面的 user 表,从多线程远程客户端测试了执行主键查询操作的次数,所有用户数据都装入到内存中(我测试了 100 万行),也用类似的数据测试了 Memcached(我使用 libMemcached 和 Memcached_get() 获取用户数据),在 MySQL SQL 测试中,我使用了的是传统的 SELECT 语句: “SELECT user_name, user_email, created FROM user WHERE user_id=?”, Memcached 和 HandlerSocket 客户端代码均使用 C/C++ 编写,所有客户端程序都位于远程主机上,通过 TCP/IP 连接到 MySQL/Memcached。最高的吞吐量情况如下:

                                approx qps     server CPU util
    MySQL via SQL               105,000      %us 60%  %sy 28%
    Memcached                   420,000      %us  8%  %sy 88%
    MySQL via HandlerSocket     750,000      %us 45%  %sy 53%
    

      HandlerSocket的吞吐量比使用传统 SQL 时高出 7.5, 而且 %us 也只有使用传统 SQL 时的3/4, 这说明 MySQL 的 SQL 层是非常耗资源的,如果能跳过这一层,性能肯定会大大提升。有趣的是,MySQL 使用 HandlerSocket 时的速度比使用 Memcached 也要快 178%,并且 Memcached 消耗的 %sy 资源也更多。所以虽然 Memcached 是一个很好的产品,但仍然有优化的空间。

      下面是oprofile输出内容,是在 MySQL HandlerSocket 测试期间收集到的,在核心操作,如网络数据包处理,获取数据等的 CPU 资源消耗(bnx2是一个网络设备驱动程序)。

    samples  %        app name                 symbol name
    984785    5.9118  bnx2                     /bnx2
    847486    5.0876  ha_innodb_plugin.so.0.0.0 ut_delay
    545303    3.2735  ha_innodb_plugin.so.0.0.0 btr_search_guess_on_hash
    317570    1.9064  ha_innodb_plugin.so.0.0.0 row_search_for_mysql
    298271    1.7906  vmlinux                  tcp_ack
    291739    1.7513  libc-2.5.so              vfprintf
    264704    1.5891  vmlinux                  .text.super_90_sync 
    
    248546    1.4921  vmlinux                  blk_recount_segments
    244474    1.4676  libc-2.5.so              _int_malloc
    226738    1.3611  ha_innodb_plugin.so.0.0.0 _ZL14build_template  P19row_prebuilt_structP3THDP8st_tablej
    206057    1.2370  HandlerSocket.so         dena::hstcpsvr_worker::run_one_ep()
    183330    1.1006  ha_innodb_plugin.so.0.0.0 mutex_spin_wait
    175738    1.0550  HandlerSocket.so         dena::dbcontext::  cmd_find_internal(dena::dbcallback_i&, dena::prep_stmt const&,   ha_rkey_function, dena::cmd_exec_args const&)
    169967    1.0203  ha_innodb_plugin.so.0.0.0 buf_page_get_known_nowait
    165337    0.9925  libc-2.5.so              memcpy
    149611    0.8981  ha_innodb_plugin.so.0.0.0 row_sel_store_mysql_rec
    148967    0.8943  vmlinux                  generic_make_request
    

      因为 HandlerSocket 是运行于 MySQL 内部,直接与 InnoDB 交互,所以,可以使用常见的 SQL 命令,如 SHOW GLOBAL STATUS 获得统计信息,Innodb_rows_read 达到了 750000+ 是值得一看的。

    $ mysqladmin extended-status -uroot -i 1 -r | grep "InnoDB_rows_read"
    ...
    | Innodb_rows_read                      | 750192     |
    | Innodb_rows_read                      | 751510     |
    | Innodb_rows_read                      | 757558     |
    | Innodb_rows_read                      | 747060     |
    | Innodb_rows_read                      | 748474     |
    | Innodb_rows_read                      | 759344     |
    | Innodb_rows_read                      | 753081     |
    | Innodb_rows_read                      | 754375     |
    ...
    

      测试用机的详细信息如下:

    型号 戴尔PowerEdge R710
    CPU Nehalem 8核,E5540@2.53GHz
    内存 32GB(所有数据都装入缓冲池)
    MySQL 5.1.50 InnoDB
    Memcached/libMemcached 1.4.5(Memcached),0.44(libMemcached)
    Network Boradcom NetXtreme II BCM5709 1000Base-T(内建四端口,使用了其中三个)

      Memcached 和 HandlerSocket 都做了网络 I/O 限制,当我测试单个端口时,HandlerSocket 的 QPS 为 260000,而 Memcached 为 220000。 

      如下所述,HandlerSocket 有其自己的特点和优势,而其中一些对我们来说, 是真的很给力.

    支持多种查询模式

      HandlerSocket 目前支持 主键/唯一性查询,非唯一性索引查询,范围扫描,LIMIT 和 INSERT/UPDATE/DELETE,但还不支持未使用任何索引的操作。另外,multi_get()(类似于in(1,2,3), 只需一次网络往返)还可获取多行数据。到这里可查询详细信息。

    处理大量并发连接

      HandlerSocket 连接是轻量级的,因为 HandlerSocket 采用epoll()和 worker-thread/thread-pooling 架构,而 MySQL 内部线程的数量是有限的(可以由 my.cnf中的 handlersocket_threads参数控制),所以即使建立上千万的网络连接到 HandlerSocket,它的稳定性也不会受到任何影响(消耗太多的内存,会造成巨大的互斥竞争等其他问题,如bug#26590,bug#33948,bug#49169)。

    及其优秀的性能

      HandlerSocket,如上所描述, 相对于其它 NoSQL 阵容,性能表现一点也不逊色。事实上,我还未曾见过哪个 NoSQL 产品在一台普通服务器上可达到 750000+ 次查询。它不仅没有调用与 SQL 相关的函数,还优化了网络/并发相关的问题。

  • 更小的网络数据包

       和传统 MySQL 协议相比,HandlerSocket 协议更简短,因此整个网络的流量更小。

  • 运行有限的 MySQL 内部线程数

       参考上面的内容。

  • 将客户端请求分组

       当大量的并发请求抵达 HandlerSocket 时,每个工作线程尽可能多地聚集请求,然后同时执行聚集起来的请求和返回结果。这样,通过牺牲一点响应时间,而大大地提高性能。例如,你可以得到以下好处,如果有人感兴趣,我会在今后的文章中对它们加以深入的解释。

       减少fsync()调用的次数.

       减少复制延迟.

    无重复缓存

      当使用 Memcached 缓存 MySQL/InnoDB 记录时,在 Memcached 和 InnoD B缓冲池中均缓存了这些记录,因此效率非常低(内存仍然很贵). 而采用 HandlerSocket插件, 由于它访问 InnoDB 存储引擎,记录缓存在 InnoDB 缓冲池中,这样,其它 SQL 语句就可以重复使用它。

    无数据不一致的现象

      由于数据只存储在一个地方(InnoDB 内),不像使用 Memcached 时,需要在 Memcached 和 MySQL 之间检查数据一致性。

    崩溃安全

      后端存储是 InnoDB,它是事务性和崩溃安全的,即使有设置innodb-flush-log-at-trx-commit!=1,在服务器崩溃时也只会丢掉 < 1s 内的数据。

    可从 MySQL 客户端使用 SQL

      在许多情况下,人们仍然希望使用 SQL(如生产摘要报告),这就是为什么我们不能使用嵌入式 InnoDB 的原因,大多数 NoSQL 产品都不支持 SQL 接口,HandlerSocket 仅仅是一个 MySQL 插件,可以从 MySQL 客户端发送 SQL 语句,但当需要高吞吐量时,最好使用 HandlerSocket。

    从 MySQL获益

      因为 HandlerSocket 运行于 MySQL 内部,因此所有 MySQL 操作,如 SQL,在线备份,复制,通过 Nagios/EnterpriseMonitor 监控等都是支持的,HandlerSocket 获得可以通过普通的 MySQL 命令监控,如SHOW GLOBAL STAUTS,SHOW ENGINE INNODB STATUS和SHOW PROCESSLIST等.

    不需要修改/重建 MySQL

      因为 HandlerSocket 是一个插件,所以它支持 MySQL 社区版和企业服务器版,而无需对 MySQL 做出任何修改就可以使用。

    独立于存储引擎

      虽然我们只测试了5.1和5.5 InnoDB 插件,但 HandlerSocket 可以和任何存储引擎交互。

    需要学习HandlerSocket API

      尽管它很容易使用,但仍然需要学习如何与 HandlerSocket 交互,我们提供了C++ API、Perl绑定。

    没有安全功能

      和其它NoSQL数据库类似,HandlerSocket不支持安全功能,HandlerSocket的工作线程以系统用户权限运行,因此应用程序可以访问通过 HandlerSocket 协议的所有表,当然,你可以象其它 NoSQL 产品一样使用防火墙过滤数据包。

    对于 HDD 绑定工作负载没有优势

      对于 HDD I/O 绑定工作负载,数据库每秒无法执行数千次查询,通常只有 1-10% 的 CPU 利用率,在这种情况下,SQL 执行层不会成为瓶颈,因此使用HandlerSocket没有什么优势,我们只在数据完全装载到内存的服务器上使用 HandlerSocket。

      我们已经在生产环境中使用了 HandlerSocket 插件,效果很明显,因为我们减少了许多 Memcached 和 MySQL 从属服务器,而且整个网络流量也在减少。目前还没有发现任何性能问题(如响应时间慢,延迟等)。

       我认为, NoSQL/Database 社区完全低估了 MySQL, 相对于其他产品来说,它历史悠久,而且到目前为止,我优秀的前同事们也做了许多独特的、伟大的改进。从 NDBAPI 可以看出 MySQL 有成为 NoSQL 的潜力,因为存储引擎 API 和守护进程接口的完全独立,使得 Akira 和 DeNA 开发 HandlerSocket 成为可能。作为 MySQL 一名前员工和对 MySQL 长期的了解,我想看到 MySQL 变得更好,更受欢迎,而不仅仅只作为一个 RDBMS,也应该成为 NoSQL 阵营中的一员。

      觉得文章有用?立即: 和朋友一起 共学习 共进步!
    本文借鉴于http://jishu.zol.com.cn/205307.html
  • 版权声明:欢迎转载,希望在你转载的同时,添加

    人气教程排行