OneCoder


  • 首页

  • 归档

  • 标签

  • 关于

  • 搜索

High Performance MySQL 翻译 第一章 MySQL架构和历史 - 逻辑架构

发表于 2013-03-25

《High Performance MySQL》是OneCoder正在阅读的书,利用茶余饭后时间进行的阅读和翻译,日积月累。

MySQL与其他数据库服务有很大的不同,它的架构特性使得它在广泛领域内成为一种实用而“廉价”的选择。MySQL并不是完美的,但是他足够灵活以适应特定的需求环境,如网络应用。同时,MySQL也可以支持嵌入式应用,数据仓库,内容索引和软件分发,高可用系统,联机事务处理等等。

为了充分的利用MySQL,你需要理解其设计,从而利用而不是抗拒它。MySQL在很多方面是灵活的。例如,它支持的硬件范围很广,你可以在各种硬件环境下配置和运行它,并且,它支持多种数据类型。然而,MySQL最不寻常和重要的特性是它的存储引擎,它在设计上将查询进程和其他服务任务与数据存储和检索分离。这种分离的特性让你可以据需选择数据的存储方式以及想要怎样的性能,特性和其他特征。

本章概要介绍MySQL的架构,存储引擎的主要区别及其重要性。最后将介绍一些历史背景和基准数据。我们视图通过简化细节和展示样例的方式介绍了MySQL。这个讨论对于数据库新手和其他数据库的专家都非常有帮助。

MySQL 逻辑架构

一个好的展现MySQL各个组件如何协同工作的图会帮助你更好的理解MySQL。图1-1展示了MySQL的逻辑架构。

图1-1

最上层是业务层,不局限于MySQL。他们大多是基于网络的客服端/服务端工具或者服务,需要进行连接管理,权限认证,安全等等。

第二层开始变得“有趣”。MySQL大部分的“大脑”都在这层,包括查询解析代码,分析,优化,缓存以及所有内嵌函数(如,dates, times, math 和 encryption)。提供的所有访问存储引擎的函数都在该层,例如:存储过程, 触发器和视图。

第三层包括存储引擎。他们负责存储和检索存储在MySQL中的所有数据。就跟GNU/Linue的各种可用的文件系统一样,不同存储引擎也有其优缺点。服务端通过存储引擎接口(API)与其通信。该API屏蔽了不同存储引擎的区别,使他们对查询层是透明的。该API包含了大量的底层函数,执行诸如"开始一个事务"或者"查询包含该主键的行"等操作。存储引擎不解析SQL(注1)或者与彼此通信;他们只负责响应来自服务端的请求。

注1:一个例外是在InnoDB引擎解析外键定义,因为MySQL服务本身并没有实现该功能。

阅读全文 »

Gradle初试

发表于 2013-03-19

Gradle是什么就不多说了,跟Maven是同类型的工具。Spring和Hibernate都早已经迁移了过来。官网地址:http://www.gradle.org。最新稳定版为1.4

Gradle Eclipse Plugin是spring的sts支持的。安装之:

Installing Gradle Tooling from update site
Alternatively you can install from update sites. The following update sites are available:

   * http://dist.springsource.com/snapshot/TOOLS/nightly/gradle (latest development snapshot)
   * http://dist.springsource.com/milestone/TOOLS/gradle (latest milestone build.)
   * http://dist.springsource.com/release/TOOLS/gradle (latest release)

Gradle的配置文件脚本是groovy语法的,Eclipse插件也安装之

http://dist.springsource.org/release/GRECLIPSE/e4.2/

将工程转换为Gradle工程。

然后编辑,配置文件即可。Gradle吸引人的地方就是文档相当的完善,对于一般使用者,最常用的就是依赖管理功能,文档地址:http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html

根据文档,先配置Gradle环境,

PATH=D:\Develop Software\gradle-1.4\bin

这里是为了能够在命令行中使用gradle。对于Eclipse 插件来说,其默认下载是使用的是1.2版本,如果强制指定我们下载的1.4版本,在某些操作的时候会报错。

可编辑gradle.bat(.sh)文件,设置JVM参数。

我们就在Eclipse中尝试gradle,通过configure->convert to gradle project,将工程变为Gradle管理的工程。

然后编写build.gradle配置文件。

 

apply plugin: 'java'
group='onecoder'
version='1.0'
repositories {
mavenCentral()
mavenRepo url: "https://oss.sonatype.org/content/repositories/opensymphony-releases"
mavenRepo url: "http://dev.anyframejava.org/maven/repo"
}
sourceSets {
main
}
dependencies {
compile "io.netty:netty:3.6.3.Final"
compile "ch.qos.logback:logback-core:1.0.7"
compile "org.slf4j:slf4j-api:1.7.3"
compile "javax.mail:mail:1.4.4"
compile "org.apache.httpcomponents:httpclient:4.2.2"
compile "org.hyperic:sigar:1.6.4"
compile "net.sourceforge.groboutils:groboutils-core:5"
}


然后fresh all一下,即可自动下载依赖了。相对pom.xml来说,配置文件看起来确实清晰简单多了。这里有很多配置,OneCoder还没有弄清楚,这里只是简单的试用,并且把个人联系的工程迁移了过来。细节问题,留待以后使用中慢慢研究吧。
 

阅读全文 »

Linux 指定MySQL服务运行的CPU核心(数)

发表于 2013-03-18

最近在利用mysqlslap对MySQL进行性能测试,但是测得的TPS、QPS的benchmark数据,从趋势上就跟网上“权威”的测试数据不同。这让OneCoder十分怀疑测试数据的准确性。

在定位问题的过程中,在独立于MySQL Server的机器上执行mysqlslap测试,测得的数据趋势正常。即初始随着并发数增大(一定范围内),TPS和QPS成上升趋势。这让我怀疑我之前在同一台服务器进行的测试,可能mysqlslap和MySQL Server争夺了CPU资源。

在一篇MySQL的测试报告中看到这样的话:

此外测试的机器具有 16 核,其中 12 核运行 mysqld ,另外 4 核运行 sysbench。

立马上网搜索指定方法。搜得Linux下的taskset命令

taskset -cp cpu序号 mysqld-pid  

即可指定mysqld服务使用的cpu核心。例如:

taskset -cp 0-47 12345

执行mysqlslap的时候,只需通过

taskset -c 48-63 mysqlslap xxxxxx

指定使用的cpu核心即可。准备给MySQL Server分配48核, mysqlslap 16核,测试看看效果。
 

阅读全文 »

数据结构 有序数组表示稀疏矩阵

发表于 2013-03-17

一般存储矩阵,自然想到二维数据。但是对于稀疏矩阵(0项很多),这无疑浪费的大量的空间。所以,这里考虑换一种表示方法。用一个三元组表示矩阵中的非零元素。

//(稀疏)矩阵数据结构, 待表示矩阵如下
// 15     0     0     22     0     -15
// 0     11     3     0     0     0
// 0     0     0     -6     0     0
// 0     0     0     0     0     0
// 91     0     0     0     0     0
// 0     0     28     0     0     0

#include <stdio.h>
#include <stdbool.h>
typedef struct {
     int col; // 列号
     int row; // 行号
     int value; // 值
} sparse_matrix;
void createMatrix(sparse_matrix *matrix);
void printMatrix(sparse_matrix *matrix);
void transposeMatrix(sparse_matrix *matrix, sparse_matrix *transposeMatrix);
#define GET_ARRAY_LEN(array,len) (len=(sizeof(array)/sizeof(array[0])));
int main() {   
     int length;
     sparse_matrix smatrix[9], transMatrix[9];
     createMatrix(smatrix);
     GET_ARRAY_LEN(smatrix,length);
     printMatrix(smatrix);
     printf("Array length is %d\n",length);
     transposeMatrix(smatrix, transMatrix);
     printf("Print transpose matrix: \n");
     printMatrix(transMatrix);
    return 0;
}
// 初始化稀疏矩阵
void createMatrix(sparse_matrix *matrix) {
     matrix[0].row = 6;// 0行元素存储行数
     matrix[0].col = 6;// 0列元素存储列数
     matrix[0].value = 8;
     matrix[1].row = 0;
     matrix[1].col = 0;
     matrix[1].value = 15;
     matrix[2].row = 0;
     matrix[2].col = 3;
     matrix[2].value = 22;
     matrix[3].row = 0;
     matrix[3].col = 5;
     matrix[3].value = -15;
     matrix[4].row = 1;
     matrix[4].col = 1;
     matrix[4].value = 11;
     matrix[5].row = 1;
     matrix[5].col = 2;
     matrix[5].value = 3;
     matrix[6].row = 2;
     matrix[6].col = 3;
     matrix[6].value = -6;
     matrix[7].row = 4;
     matrix[7].col = 0;
     matrix[7].value = 91;
     matrix[8].row = 5;
     matrix[8].col = 2;
     matrix[8].value = 28;
}

//打印稀疏矩阵
//最差时间复杂度:O(row * col * valueCount);
void printMatrix(sparse_matrix *matrix) {
     int i,j,k;
     int row = (*matrix).row;
     int col = (*matrix).col;
     int valueCount = (*matrix).value;
     int startIndex = 1;
     for (i = 0; i < row; i++) {
          for (k = 0; k < col; k++) {
               bool print = false;
               for (j = startIndex; j <= valueCount; j++) {
                    int curRow = (*(matrix + j)).row,
                    curCol = (*(matrix + j)).col;
                    if (i == curRow && k == curCol) {
                         printf("%d\t", (*(matrix+j)).value);
                         print = true;
                         startIndex++;
                    }
               }
               if (!print) {
                    printf("%d\t", 0);
               }
          }
          printf("\n");
     }
}
// 求稀疏矩阵的转置矩阵,即交换行列的位置
// 最差时间复杂度:O(col * valueCount);
void transposeMatrix(sparse_matrix *smatrix, sparse_matrix *transposeMatrix) {
     int i,n, rowNum = (*smatrix).row, colNum = (*smatrix).col, valueNum = (*smatrix).value;
     (*transposeMatrix).row = colNum;
     (*transposeMatrix).col = rowNum;
     (*transposeMatrix).value = valueNum;
     int index = 1;
     for (i = 0; i < colNum; i++) {
          for (n = 1; n <= valueNum; n++) {
               int curRow = (*(smatrix + n)).row,
                    curCol = (*(smatrix + n)).col,
                    curValue = (*(smatrix + n)).value;
               if (i == curCol) {
                    (*(transposeMatrix + index)).row = curCol;
                    (*(transposeMatrix + index)).col = curRow;
                    (*(transposeMatrix + index)).value = curValue;
                    index++;
               }
          }
     }
}

可以看到,相对于传统的使用二维数据的方式存储矩阵,该存储方式对于稀疏矩阵来说,无疑节省了大量空间。  但是对于矩阵的打印和转置来说,从数据量级看浪费了时间。所以,此种表示方式适用于矩阵中非零元素少的稀疏矩阵。尤其当矩阵中非零元素数量为cols * rows时,转置的时间复杂性为O(rows * cols * cols)。用时间换空间。

考虑再用少量空间,换一些时间。实现时间复杂度的为O(cols + elements)的快速转置:

// 快速转置算法,浪费少量空间,换取时间
// 直接计算转置后元素的存放位置
void fastTranspose(sparse_matrix *smatrix, sparse_matrix *transposeMatrix) {
     int i, rowNum = (*smatrix).row, colNum = (*smatrix).col, valueNum = (*smatrix).value;
     int row_terms[colNum + 1],
          startPos[colNum + 1];
     (*transposeMatrix).row = colNum;
     (*transposeMatrix).col = rowNum;
     (*transposeMatrix).value = valueNum;
     for (i = 0; i< colNum; i++) {
          row_terms[i] = 0; // 数组中的元素必须初始化
     }
     for (i = 1; i <= valueNum; i++) {
          int     curCol = (*(smatrix + i)).col;
          row_terms[curCol]++;//记录转置后,每行的元素个数
     }
     startPos[0] = 1;//初始化起始位置
     for (i = 1; i < colNum; i++) {
          startPos[i] = startPos[i-1] + row_terms[i-1];
     }
      for (i = 1; i <= valueNum; i++) {
           int curRow = (*(smatrix + i)).row,
                curCol = (*(smatrix + i)).col,
                curValue = (*(smatrix + i)).value;
           int j = startPos[curCol]++;// 计算出原矩阵中的元素在转置矩阵数据中的位置
           (*(transposeMatrix + j)).row = curCol;
           (*(transposeMatrix + j)).col = curRow;
           (*(transposeMatrix + j)).value = curValue;
      }
}

可见快速转置的主要思想就是直接计算出元素在转置矩阵中的顺序位置,直接存储。接下来是稀疏矩阵的乘法。

// 稀疏矩阵乘法运算, 以下面矩阵为例
// 0     0     1     1 * 0     0
//                         0     0 = 2     5
//                         0     2
//                         2     3
void matrixMulti(sparse_matrix *amatrix, sparse_matrix *bmatrix, sparse_matrix *resultMatrix) {
     int i, aRowNum = (*amatrix).row, aValueNum = (*amatrix).value;
     int j, bColNum = (*bmatrix).col, bValueNum = (*bmatrix).value;
     (*resultMatrix).row = aRowNum;//首先可知结果矩阵的行列数
     (*resultMatrix).col = bColNum;
     sparse_matrix tbMatrix[9];// 为了计算方便先求出被乘矩阵的转置矩阵,下面计算都利用转置矩阵
     fastTranspose(bmatrix, tbMatrix);
         int temp_sum = 0,
          row = (*(amatrix + 1)).row, // 矩阵A的起始行,即为结果的起始行
          col,
          rowBegin = 1,
          rIndex = 0;
     // 在循环内部控制循环的次数和起始
     for (i = 1; i <= aValueNum; ) {
          col = (*(tbMatrix + 1)).row; // 矩阵B的转置矩阵的行,即对应的列
          for (j = 1; j <= bValueNum + 1; ) {
               if (amatrix[i].row != row) {
                    storeValue(resultMatrix, row, col, temp_sum, &rIndex);
                i = rowBegin;
                    for (; col == tbMatrix[j].row; j++)
                         ;
                    col = tbMatrix[j].row;
                    temp_sum = 0;
               } else if (col != tbMatrix[j].row) {
                    storeValue(resultMatrix, row, col, temp_sum, &rIndex);
                i = rowBegin;
                col = tbMatrix[j].row;
                    temp_sum = 0;
               } else {
                    if (amatrix[i].col < tbMatrix[j].col) {
                         i++;
                    } else if (amatrix[i].col == tbMatrix[j].col) {
                         temp_sum += amatrix[i].value * tbMatrix[j].value;
                         i++;
                         j++;
                    } else {
                         j++;
                    }
               }
          }
          for (; row == (*(amatrix + i)).row; i++)
               ;
          rowBegin = i;
          row = (*(amatrix + i)).row;
     }
    (*resultMatrix).value = rIndex;
}

void storeValue(sparse_matrix *resultMatrix, int row, int col, int value, int *index) {
     if (value) {
          (*(resultMatrix + ++*index)).row = row;
          (*(resultMatrix + *index)).col = col;
          (*(resultMatrix + *index)).value = value;
     }
}

没有做太多错误边界的判断。计算结果:

初学乍练,如果有错误还望大家指出。这个乘法,OneCoder着实写了好久。算法的时间复杂度为:O(bColNum * aValueNum + aRowNum * bValueNum)。相对于传统的二维数组表示矩阵的计算来说,实现逻辑复杂好多。传统方式的时间复杂度为:O(aRowNum * aColNum * bColNum)。

阅读全文 »

MySQL 用户并发数限制问题解决

发表于 2013-03-15

对MySQL进行并发测试过程中遇到的一个小问题,记录一下。

用mysqlslap进行并发访问测试,在1024线程的时候报错:

bin/mysqlslap: Error when connecting to server: 1135 Can't create a new thread (errno 11); if you are not out of available memory, you can consult the manual for a possible OS-dependent bug

Linux系统的open files数已经修改。当然仍然报错。ulimit -a命令,可查看当前系统限制情况

max user processes = 1024


通过ulimit -u 10000命令修改当前session的限制值,然后重启MySQL,问题解决。如果你想使此值永久生效,可配置在/etc/profile 中。

阅读全文 »

MySQL5.6.10 NoSQL API访问方式体验

发表于 2013-03-12

MySQL 近期发布5.6的GA版本,其中确实有很多不错的特性值得关注和尝试。NoSQL API的支持就是其中一个比较不错的亮点,我们这就来尝试一下。详细的特性介绍可访问:http://dev.mysql.com/tech-resources/articles/mysql-5.6.html 。

从MySQL官网了解到,通过Memcache的API即可访问MySQL的NoSQL API。

Many of the latest generation of web, cloud, social and mobile applications require fast operations against simple Key/Value pairs. At the same time, they must retain the ability to run complex queries against the same data, as well as ensure the data is protected with ACID guarantees. With the new NoSQL API for InnoDB, developers have all the benefits of a transactional RDBMS, coupled with the performance capabilities of Key/Value store.
MySQL 5.6 provides simple, key-value interaction with InnoDB data via the familiar Memcached API. Implemented via a new Memcached daemon plug-in to mysqld, the new Memcached protocol is mapped directly to the native InnoDB API and enables developers to use existing Memcached clients to bypass the expense of query parsing and go directly to InnoDB data for lookups and transactional compliant updates. The API makes it possible to re-use standard Memcached libraries and clients, while extending Memcached functionality by integrating a persistent, crash-safe, transactional database back-end. The implementation is shown here:


So does this option provide a performance benefit over SQL? Internal performance benchmarks using a customized Java application and test harness show some very promising results with a 9X improvement in overall throughput for SET/INSERT operations:

首先部署Server端的Memcache plugin集成环境。目前支持的系统为Linux, Solaris, and OS X,不支持windows。文档地址:http://dev.mysql.com/doc/refman/5.6/en/innodb-memcached-setup.html

由于我采用的tar包安装的MySQL,所以在安装memcache plugin的时候需要先安装libevent包。

yum install libevent

即可。

然后,安装libmemcached所需要的表

将插件设置成随服务启动而启动的守护插件

重启MySQL服务,安装完成。默认访问端口为11211。

下面来验证一下安装,简单的可以采用telnet的方式发送memcached命令

然后通过sql,在demo_test表中查询数据:

再通过Java代码操作一下,我们采用xmemcached作为client api。官方地址:https://code.google.com/p/xmemcached。Maven依赖:

<dependency >     
      <groupId >com.googlecode.xmemcached</groupId >
      <artifactId >xmemcached</artifactId >
      <version >1.4.1</version >
</dependency >

代码如下:

 /**
      * @param args
      * @author lihzh(OneCoder)
      * @blog http://www.coderli.com
      * @throws MemcachedException
      * @throws InterruptedException
      * @throws TimeoutException
      * @throws IOException
      * @date 2013 -3 -12 下午12:07:41
      */
     public static void main(String[] args) throws TimeoutException, InterruptedException, MemcachedException, IOException {
           MemcachedClient client = new XMemcachedClient("10.4.44.208" , 11211);
            // store a value for one hour(synchronously).
           client.set( "key", 3600, "onecoder");
            // Retrieve a value.(synchronously).
           Object someObject = client.get( "key");
            // Retrieve a value.(synchronously),operation timeout two seconds.
           someObject = client.get( "key", 2000);
           System. out.println(someObject);
     }

通过mysql客户端查询记录,成功存入:

这里测试的仅仅最基本的功能,如果想使用该功能,还需要做好传统数据表与memcache表的映射关系。具体可参考:http://dev.mysql.com/doc/refman/5.6/en/innodb-memcached-developing.html。

阅读全文 »

MySQL性能测试—前期知识储备

发表于 2013-03-08

今天是妇女节,先祝所有过节的女同胞们节日快乐:)

即将对MySQL进行性能测试。所以事先对MySQL的测试工作进行一番了解。主要考察性能测试的工具,MySQL的关键指标,以及一些基础的Benchmark数据,为测试用例和场景的规划做些准备。

先说说考量的指标(转载,网址找不到了,抱歉)

(1)QPS(每秒Query量)
QPS = Questions(or Queries) / seconds
mysql > show /*50000 global */ status like 'Question';
(2)TPS(每秒事务量)
TPS = (Com_commit + Com_rollback) / seconds
mysql > show status like 'Com_commit';
mysql > show status like 'Com_rollback';
(3)key Buffer 命中率
key_buffer_read_hits = (1-key_reads / key_read_requests) * 100%
key_buffer_write_hits = (1-key_writes / key_write_requests) * 100%
mysql> show status like 'Key%';
(4)InnoDB Buffer命中率
innodb_buffer_read_hits = (1 - innodb_buffer_pool_reads / innodb_buffer_pool_read_requests) * 100%
mysql> show status like 'innodb_buffer_pool_read%';
(5)Query Cache命中率
Query_cache_hits = (Qcahce_hits / (Qcache_hits + Qcache_inserts )) * 100%;
mysql> show status like 'Qcache%';
(6)Table Cache状态量
mysql> show status like 'open%';
(7)Thread Cache 命中率
Thread_cache_hits = (1 - Threads_created / connections ) * 100%
mysql> show status like 'Thread%';
mysql> show status like 'Connections';
(8)锁定状态
mysql> show status like '%lock%';
(9)复制延时量
mysql > show slave status
(10) Tmp Table 状况(临时表状况)
mysql > show status like 'Create_tmp%';
(11) Binlog Cache 使用状况
mysql > show status like 'Binlog_cache%';
(12) Innodb_log_waits 量
mysql > show status like 'innodb_log_waits';

其中QPS和TPS自然是重点考察的性能指标。其他指标可以作为每次测试数据的参考数据列出。如果遇到瓶颈,可能还需要考量当时系统的cpu,网络,磁盘的利用率情况。这个遇到具体问题再具体分析。

工具方面,首选考察的自然是MySQL自带的测试工具mysqlslap。

./mysqlslap -a --concurrency=50,100 --number-of-queries 1000 --iterations=5 --engine=myisam,innodb --debug-info -uroot -proot

一个简单的示例即可说明工具的使用情况,

-a 自动生成sql
--concurrency=50,100分别执行50,100并发,
--iterations=5 执行5次,引擎分别选用myisam和innodb引擎测试。

--number-of-queries    Limit each client to this number of queries (this is not
                      exact).(一个客户端执行的测试SQL数量上限,通过--only-print观察自动生成的sql的执行情况来看,该条数限制的是准备数据以外的SQL语句的条数)
--engine=myisam,innodb 分别用myisam和innodb引擎进行测试。

当然你也可以通过-q指定你想要测试的sql脚本,测试结束后,mysqlslap会给出你测试的相关数据。

关于mysqlslap的更多参数可参考:http://dev.mysql.com/doc/refman/5.1/en/mysqlslap.html
从目前来看上手还是比较容易的。通过执行的query数量和时间,很容易计算出tps和qps等指标。

SysBench

也是MySQL官网提到的一个benchmark工具。只是在64位系统下安装困难,限于网络环境和考虑到其功能和我们即将进行的测试场景,暂时放弃。

性能测试基础准备其他话题

在思考和验证怎样使用mysqlslap规划测试场景的过程中,收获到一些离散的细节问题,也在此一并记录一下。也许哪天,哪一条就会有所帮助。

打开MySQL general_log 记录执行的sql情况

默认情况下该属性是关闭的,通过set global general_log=ON 打开后,可在log文件中查看数据库执行的sql记录。该需求是OneCoder想要考察JDBC的batch insert的提交方式,究竟是数据库中是执行的多条insert然后commit还是一个insert values大列表的时候产生的。

对比批量插入数据 多条insert和一条insert 大values列表(values(),(),())方式,性能差别

从目前通过mysqlslap执行测试的效果来看,后者明显优于前者。前者1000条平均时间大约是0.2ms,而后者在0.1ms左右。该测试想法是在通过mysqldump到处已有数据的时候发现其sql文件的生成方式的时候以及联想到oracle到mysql的数据迁移工具的处理方式的时候想到的。

写入性能瓶颈的一个大误解

曾经极大的错误的认为单线程下mysql可处理的批量插入数据量就是该节点的瓶颈,所以只认为多线程并发写入优化只在NDB的环境下才有效。今天在看MySQL的一些benchmark图表曲线的时候,猛然惊醒,并发数和tps的曲线是类抛物线的就说明在一定并发数的范围内,TPS是有明显提高的。于是用以前的JDBC的代码,增加了几个线程,对同一个mysql并发写入,果然TPS成倍提升。

今天总结的大概就这么多,由于是事后回忆总结,不免有所遗漏,想起来再补充把。热烈欢迎指导。

阅读全文 »

MySQL Cluster写入效率测试

发表于 2013-02-27

MySQL Cluster使用到目前为止遇到渴望得到答案的问题,也是直接影响使用的问题就是MySQL Cluster的写入效率问题和Cluster是否适合大数据存储、如何配置存储的问题。

在之前的测试中MySQL Cluster的写入效率一直不佳,这也是直接影响能否使用MySQL Cluster的关键。现在我们来仔细测试一下。使用的环境略有变化。

Data节点的内存扩展为4G。

集群配置如下:

[ndbd default]

# Options affecting ndbd processes on all data nodes:

NoOfReplicas=2    # Number of replicas

DataMemory=2000M    # How much memory to allocate for data storage

IndexMemory=300M   # How much memory to allocate for index storage

                  # For DataMemory and IndexMemory, we have used the

                  # default values. Since the "world" database takes up

                  # only about 500KB, this should be more than enough for

                  # this example Cluster setup.

MaxNoOfConcurrentOperations=1200000

MaxNoOfLocalOperations=1320000

测试代码如下:

/**
      * 向数据库中插入数据
      *
      * @param conn
      * @param totalRowCount
      * @param perRowCount
      * @param tableName
      * @author lihzh(OneCoder)
      * @throws SQLException
      * @date 2013 -1 -17 下午1:57:10
      */
     private void insertDataToTable(Connection conn, String tableName,
                 long totalRowCount, long perRowCount, long startIndex)
                 throws SQLException {
           conn.setAutoCommit( false);
           String sql = "insert into " + tableName + " VALUES(?,?,?)";
           System. out.println( "Begin to prepare statement.");
           PreparedStatement statement = conn.prepareStatement(sql);
            long sum = 0L;
            for ( int j = 0; j < TOTAL_ROW_COUNT / BATCH_ROW_COUNT; j++) {
                 long batchStart = System. currentTimeMillis();
                 for ( int i = 0; i < BATCH_ROW_COUNT; i++) {
                      long id = j * BATCH_ROW_COUNT + i + startIndex;
                     String name_pre = String. valueOf(id);
                     statement.setLong(1, id);
                     statement.setString(2, name_pre);
                     statement.setString(3, name_pre);
                     statement.addBatch();
                }
                System. out.println( "It's up to batch count: " + BATCH_ROW_COUNT);
                statement.executeBatch();
                conn.commit();
                 long batchEnd = System. currentTimeMillis();
                 long cost = batchEnd - batchStart;
                System. out.println( "Batch data commit finished. Time cost: "
                           + cost);
                sum += cost;
           }
           System. out.println( "All data insert finished. Total time cost: "
                     + sum);
           System. out.println( "Avg cost: "
                     + sum/5);
     }

分下列情景进行写入测试。

数据加载、写入在内存中时,在独立的新库、新表中一次写入100,1000,10000,50000条记录,分别记录其耗时情况。(5次平均)

100:260ms

1000:1940ms

10000:17683ms(12000-17000)

50000: 93308、94730、90162、94849、162848

与普通单点MySQL写入效率进行对比(2G内存)

100:182ms
1000:1624ms
10000:14946ms
50000:84438ms

    双线程并发写入测试

由于只有两个SQL节点,所以这里只采用双线程写入的方法进行测试。代码上采用了简单的硬编码

/**
      * 多线程并行写入测试
      *
      * @author lihzh(OneCoder)
      * @blog http://www.coderli.com
      * @date 2013 -2 -27 下午3:39:56
      */
     private void parallelInsert() {
            final long start = System. currentTimeMillis();
           Thread t1 = new Thread( new Runnable() {
                 @Override
                 public void run() {
                      try {
                           Connection conn = getConnection(DB_IPADDRESS, DB_PORT,
                                      DB_NAME, DB_USER, DB_PASSOWRD);
                           MySQLClusterDataMachine dataMachine = new MySQLClusterDataMachine();
                           dataMachine.insertDataToTable(conn, TABLE_NAME_DATAHOUSE,
                                     500, 100, 0);
                            long end1 = System.currentTimeMillis();
                           System. out.println( "Thread 1 cost: " + (end1 - start));
                     } catch (SQLException e) {
                           e.printStackTrace();
                     }
                }
           });


           Thread t2 = new Thread( new Runnable() {
                 @Override
                 public void run() {
                      try {
                           Connection conn = getConnection(DB_IPADDRESS_TWO, DB_PORT,
                                      DB_NAME, DB_USER, DB_PASSOWRD);
                           MySQLClusterDataMachine dataMachine = new MySQLClusterDataMachine();
                           dataMachine.insertDataToTable(conn, TABLE_NAME_DATAHOUSE,
                                     500, 100, 500);
                            long end2 = System.currentTimeMillis();
                           System. out.println( "Thread 2 cost: " + (end2 - start));
                     } catch (SQLException e) {
                           e.printStackTrace();
                     }
                }
           });
           t1.start();
           t2.start();
     }

测试结果:

(总条数/每次) 线程1(总/平均- 各写一半数据) 线程2 并行总耗时 单线程单点
1000/100 985/197 1005/201 1005/201 2264/226
10000/1000 9223/1836 9297/1850 9297/1850 19405/1940
100000/10000 121425/12136 122081/12201 121425/12136 148518/14851
 

从结果可以看出,在10000条以下批量写入的情况下,SQL节点的处理能力是集群的瓶颈,双线程双SQL写入相较单线程单节点效率可提升一倍。但是当批量写入数据达到一定数量级,这种效率的提升就不那么明显了,应该是集群中的其他位置也产生了瓶颈。

注:由于各自测试环境的差异,测试数据仅可做内部比较,不可外部横向对比。仅供参考。

写入测试,要做的还很多,不过暂时告一段落。大数据存储和查询测试,随后进行。

阅读全文 »

MySQL主从配置-Amoeba读写分离验证

发表于 2013-02-20

继续我们的验证工作,这次是搭建MySQL的主从配置,再利用Amoeba实现读写分离,搭建一个相对有实际意义的MySQL集群环境。

MySQL的主从配置比较简单,网上也有很多的文章,这里不多介绍。基于之前的介绍的环境考虑:

10.4.44.206 主-写
10.4.44.207 从-读1
10.4.44.208 从-读2

MySQL的主从配置确实十分简单。主要就是配置好日志的监听。这里OneCoder是参考:http://369369.blog.51cto.com/319630/790921 的文章完成的配置。配置好后,验证一下主从环境是否可用。连接206插入一条数据,在207和208上可成功查询到该数据,证明我们的主从环境配置成功。接下来我们再来配置Amoeba,只要在amoeba.xml中配置好读写库就可以了。

然后连接205Amoeba服务端写入数据,分别直接206,207,208节点查询数据,均可查到,并且与通过Amoeba节点查询出的数据是相同的。可见数据是自动同步的。


   通过208查询效果,(205,206,207节点查询效果均相同)

 

通过Amoeba查询数据,可以看到只有配置的读节点有网络传输,写节点的网络是风平浪静的。


206写

208 读节点

考虑的是,Amoeba是不支持事物的,所以我们可以考虑把写节点直接暴露出来,表引擎选择InnoDB, 其他读节点通过Amoeba进行负载均衡。不过这里就需要业务自己来控制读写的数据源选择了。

至此,对于MySQL的验证工作也基本告一段落了,对于形形色色的方案及其试用场景,OneCoder也需要好好消化总结一下了。

阅读全文 »

MySQL-Amoeba负载均衡、读写分离功能验证

发表于 2013-02-19

由于MySQL Cluster自身就实现了数据的自动同步等功能,在此之上架一层Amoeba基本只起到了分担SQL层负载的的作用,所以我们很有必要基于传统的单点MySQL服务之上的 Amoeba都能帮我们做些什么。

环境搭建的过程不再赘述,我们在两个新的虚拟机上启动两个独立的MySQL服务,然后配置在 Amoeba中即可。此时SQL节点的IP分别为:

10.4.44.206 写
10.4.44.207 读
10.4.44.208 读
10.4.44.205 Amoeba节点

这里使用的MySQL版本为,mysql-5.5.29-linux2.6-x86_64。先手动分别在三个节点上创建了相同的数据库(bigdata)和表(data_house)。修改amoeba.xml和dbServers.xml配置,重启amoeba服务。

怀着好奇,先测试通过Amoeba节点写入数据和查询数据的情况,看看数据是否是各个节点自动同步的。同样,使用的之前使用的JDBC测试代码。观察写入过程中虚拟机的网络和磁盘指标情况。

write206节点监控图表

发现果然只有206写节点有网络和磁盘传输请求,207和208节点一片平静。显然数据不是这样同步的,通过Amoeba查询,果然没有数据。在这种情况下,我们应该配置Amoeba的水平切分规则,让数据分别存储在各个节点上。不过这样的话每个节点都只有一部分数据,任何一个节点的故障都会导致数据的不完整,我们来验证看看是不是这样。

修改rule.xml中的配置,

根据ID hash(2)的值水平切分数据。这里每个规则都对应自己的读写节点。这里验证就是Amoeba的路由规则功能。执行数据插入,可以看到数据平均的分配到207和208两个数据节点上,各10000条数据。

 

  

此时执行count操作,返回值始终是10000,可见此时无法查询到完整的数据。但是有了这种切分规则,你可以灵活的进行的自己的配置,比如可以进行垂直切分等。所以可见,Amoeba的主要职责是起到了一个路由的作用,把请求分发下去,数据同步不是它关心的。所以Amoeba官网中介绍的读写分离也是基于MySQL主从配置的,这也是我们接下来要进行的工作:)

阅读全文 »
1 … 25 26 27 … 36
LiHongZhe

LiHongZhe

onecoder's blog.

354 日志
8 分类
RSS
Creative Commons
Links
  • 酷壳
  • 煮酒品茶
  • 小弟子的网络之路
  • 图表秀-在线图表制作
© 2012 - 2023 LiHongZhe
由 Jekyll 强力驱动
主题 - NexT.Muse
本站访客数 人次 本站总访问量 次