Innodb 作为 MySQL 中使用最为广泛的 事务型存储引擎,不仅在事务实现数据版本控制方面和其他存储引擎有一定的区别,其数据结构也是以非常有特点的方式存储的。

每个Innodb表的数据其实可以说就是以一个树型(B-Tree)结构存储的,表的数据和主键(Primary Key)共同组成了一个索引结构,也就是我们常说的Innodb的Clustered Primary Key。在这个Clustered Primary Key中,Leaf Nodes其实就是实际的表记录,我们常规理解上的索引信息全部在Branch Nodes上面。

除了Clustered Primary Key之外的其他所有索引在Innodb中被称为Secondary Index。Secondary Index就和普通的B-Tree索引差不多了,只不过在Secondary Index的所有Leaf Nodes上面同时包含了所指向数据记录的主键信息,而不是直接指向数据记录的位置信息。

所以,在 Innodb 中,如果主键值占用存储空间较大的话,会直接影响整个存储 Innodb 表所需要的物理空间,同时也会直接影响到 Innodb 的查询性能。

下面是画的一张 Innodb 索引基本结构图,包括 Primary Key 和 Secondary Index 两种索引的比较。

innodb_index_structure

, ,

随着 BI (DW) 在各个企业中重要性的不断提升,各个数据库厂家都希望能搭上这辆班车。这不,MySQL 也联合 Infobright 一起推出了开源的 数据仓库解决方案,而且是开源的。

其实现的各种DW该有的功能就不多说了,但是 Infobright 有一点非常吸引人的技术特点不能不提,那就是以列为导向的架构设计。

以列为导向的架构设计是非常适合于DW应用场景的,对于大多数DW的分析场景中,实际关注的数据很多时候都只有那么一列或者少数几列的数据。所以在以列为导向的设计中,大部分的分析查询都只需要读取某一个(或者几个)表的几列,而不需要像传统以行为导向的数据库(或者存储引擎)那样需要扫描整个表的数据,这两者IO量的差距是非常大的。除了以列为导向的架构设计之外,Infobright 和很多其他的DW解决方案一样,也会进行数据压缩,而且由于其以列为导向的存储方式,压缩比率在很多情况下都会比以行为导向的存储方式更高,效果更理想。有人通过测试比较,常规的以行为导向的存储数据压缩比率较高的时候也就 3:1 左右,但是 Infobright 的却很容易就做到 10:1 的压缩比率。

此外,从MySQL 以及 Infobright 的官方报道中除了上述技术特点(或者说优势)之外,还有很多其他的被描绘的非常神奇的功能,如被称为 “知识网格” (Knowledge Grid) 的自我管理功能,完全不需要索引或者分区,神奇的自我查询优化器等等。

这里是官方给出的一张 Infobright 的架构图:

infobright architecture

infobright architecture

感兴趣的朋友可以通过自行阅读其 技术白皮书 获取更多的细节

, , ,

自Sun收购了MySQL AB 以来,虽然对于我们来说并没有看到Sun给MySQL带来太多实质性的改进和优化,但是从Sun官方目前在各种场合的表态可以看出他们还是比较重视MySQL这个数据库产品的。而且,Sun也保证了会一直保持MySQL的开源之路。
收购了MySQL之后,Sun在整合MySQL和自己以前的软硬件产品方面也在不断的努力和尝试,看看下面这个视频中Sun为整合优化MySQL到自己的软硬件平台所建立的实验室吧,还是耗费了不少 $ 的,呵呵。

, ,

这几天看了不少Google针对于MySQL开发的google-mysql-tools,找到一个很有意思的Patch:MirroredBinlogs

这个Patch通过修改MySQL Replication中Slave IO线程的实现,让该线程在写入relay log的同时,再Mirror了一份与Master端完全一模一样binlog。这里所说的一模一样不仅仅是binlog的内容完全一样,同时还包括binlog的文件名。也就是说,该线程在Slave端完全copy了一份Master的binlog日志。

在该 Patch 的描述中,该 Patch 产生的初衷是为了解决Slave与Master之前的顺利切换,并保证切换之后其他Slave仍然能够正常从新的Master继续进行复制。

作者设想了如下一个场景:
在 Hierarchical Replication(级联复制)环境中,第一层是有一台 Master ,第二层是两台 Slaves ,这两台Slave主要作用是作为第三层更多 Slave 的 Master 。也就是,第二层的两台 Slave 的角色在整个集群环境中是一个复制代理。如果我们使用的是普通的MySQL,那么中间代理层的两台Slave之间的binlog日志可能会有较大差异,因为两台Slave自身也会有产生binlog的event。而通过使用该Patch之后,通过 Slave IO 线程将第一层中 Master 的binlog完全一模一样的copy到第二层的 Slave 上面,而使这一层的binlog完全一致。这样,当第二层的两台复制代理机器中的一台Crash之后,可以很容易的将第三层中以 前面 Crash 的 Slave 作为 Master 的所有 Slave 可以很容易的切换 Master 到另外一台 代理 Slave 上面。

只不过,开发者已经停止了该Patch的更新,并将该Patch整合到了一个新的叫 GlobalTransactionIds(MySQL Hierarchical Replication & Global Group IDs)的Patch中,只不过该Patch还正在开发中。从 Google 在 GlobalTransactionIds 的介绍中可以看到比其他 Patch 更为详细的一些说明,不知道是否算是对该 Patch 比较重视的一个表现呢? 希望不是我的一厢情愿吧。

自己目前还没有详细测试过 MirroredBinlogs 这个 Patch,也不知道是否奏效,不过我想 Google 应该不会在技术这种事情上拿自己的名声和我们开这种玩笑吧,哈哈。如果有哪位朋友已经做过相关的测试的话,就 Share 一下效果吧…

, , ,

同事 Struct Chen 的 Amoeba For MySQL 项目已经 出来不短时间了,可一直没有时间好好体验,最近一直在做一些软件和硬件的功能以及性能相关的测试工作,Amoeba For MySQL自然也将被列入计划之内。

对 Amoeba For MySQL 的测试到目前位置主要还是功能方面的测试,配置了一套四台机器的环境:

Server A: Amoeba Server

Server B: MySQL Master

Server C: MySQL Slave1

Server D: MySQL Slave2

软件环境:RHEL5.2 (X86_64),JAVA 1.5.0_16, Amoeba For MySQL 0.31

测试功能:

1、读写分离
2、数据垂直切分
3、数据水平切分

amoeba.xml配置如下:

< ?xml version="1.0" encoding="gbk"?>
< !DOCTYPE amoeba:configuration SYSTEM "amoeba.dtd">
<amoeba :configuration xmlns:amoeba="http://amoeba.meidusa.com/">
    
<server>
        
<property name="port">8066</property>
        
<property name="ipAddress">10.0.38.21</property>
        
<!-- proxy server net IO Read thread size -->
        
<property name="readThreadPoolSize">500</property>
        
<!-- proxy server client process thread size -->
        
<property name="clientSideThreadPoolSize">500</property>
        
<!-- mysql server data packet process thread size -->
        
<property name="serverSideThreadPoolSize">500</property>
        
<!-- socket Send and receive BufferSize(unit:K)  -->
        
<property name="netBufferSize">200</property>
        
<!-- Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm). -->
        
<property name="tcpNoDelay">true</property>
        
<property name="user">user</property>
        
<property name="password">password</property>
    
</server>
    
    
<connectionmanagerlist>
        
<connectionmanager name="defaultManager">
            
<classname>com.meidusa.amoeba.net.AuthingableConnectionManager</classname>
        
</connectionmanager>
    
</connectionmanagerlist>
    
    
<dbserverlist>
 
        
<dbserver name="master">
            
<factoryconfig>
                
<classname>com.meidusa.amoeba.mysql.net.MysqlServerConnectionFactory</classname>
                
<property name="manager">defaultManager</property>
                
<property name="port">3306</property>
                
<property name="ipAddress">10.0.38.37</property>
                
<property name="schema">test</property>
                
<property name="user">root</property>
                
<property name="password">password</property>               
            
</factoryconfig>
 
            
<poolconfig>
                
<classname>com.meidusa.amoeba.net.poolable.PoolableObjectPool</classname>
                
<property name="maxActive">500</property>
                
<property name="maxIdle">200</property>
                
<property name="minIdle">10</property>
                
<property name="minEvictableIdleTimeMillis">600000</property>
                
<property name="timeBetweenEvictionRunsMillis">600000</property>
                
<property name="testOnBorrow">true</property>
                
<property name="testWhileIdle">true</property>
            
</poolconfig>
        
</dbserver>
 
        
<dbserver name="slave1">
            
<factoryconfig>
                
<classname>com.meidusa.amoeba.mysql.net.MysqlServerConnectionFactory</classname>
                
<property name="manager">defaultManager</property>
                
<property name="port">3306</property>
                
<property name="ipAddress">10.0.38.22</property>
                
<property name="schema">test</property>
                
<property name="user">root</property>
                
<property name="password">password</property>               
            
</factoryconfig>
 
            
<poolconfig>
                
<classname>com.meidusa.amoeba.net.poolable.PoolableObjectPool</classname>
                
<property name="maxActive">500</property>
                
<property name="maxIdle">200</property>
                
<property name="minIdle">10</property>
                
<property name="minEvictableIdleTimeMillis">600000</property>
                
<property name="timeBetweenEvictionRunsMillis">600000</property>
                
<property name="testOnBorrow">true</property>
                
<property name="testWhileIdle">true</property>
            
</poolconfig>
        
</dbserver>
 
        
<dbserver name="slave2">
            
<factoryconfig>
                ... ...           
            
</factoryconfig>
 
            
<poolconfig>
                ... ...
            
</poolconfig>
        
</dbserver>
        
 
        
<dbserver name="virtualSlave" virtual="true">
            
<poolconfig>
                
<classname>com.meidusa.amoeba.server.MultipleServerPool</classname>
                
<!-- 1=ROUNDROBIN , 2=WEIGHTBASED -->
                
<property name="loadbalance">1</property>
                
<property name="poolNames">slave1,slave2</property>
            
</poolconfig>
        
</dbserver>
        
    
</dbserverlist>
    
    
<queryrouter>
        
<classname>com.meidusa.amoeba.mysql.parser.MysqlQueryRouter</classname>
        
<property name="ruleConfig">${amoeba.home}/conf/rule.xml</property>
        
<property name="functionConfig">${amoeba.home}/conf/functionMap.xml</property>
        
<property name="ruleFunctionConfig">${amoeba.home}/conf/ruleFunctionMap.xml</property>
        
<property name="LRUMapSize">1500</property>
        
<property name="defaultPool">master</property>
        
        
<property name="writePool">master</property>
        
<property name="readPool">virtualSlave</property>
        
<property name="needParse">true</property>
    
</queryrouter>
</amoeba>

rule.xml配置:

< ?xml version="1.0" encoding="gbk"?>
< !DOCTYPE amoeba:rule SYSTEM "rule.dtd">
<amoeba :rule xmlns:amoeba="http://amoeba.meidusa.com/">
    
<tablerule name="test_horiz" schema="test" defaultPools="master">
        
<rule name="rule1">
            
<parameters>ID</parameters>
            
<expression>< ![CDATA[ ID <= 100000000]]></expression>
            
<defaultpools>master</defaultpools>
            
<readpools>slave1</readpools>
            
<writepools>master</writepools>
        
</rule>
        
        
<rule name="rule2">
            
<parameters>ID</parameters>
            
<expression>< ![CDATA[ ID between 100000001 and 200000000 ]]></expression>
            
<defaultpools>master</defaultpools>
            
<writepools>master2</writepools>
            
<readpools>slave2</readpools>
        
</rule>
        
        
<rule name="rule3">
            
<parameters>ID</parameters>
            
<expression>< ![CDATA[ ID > 200000000 ]]></expression>
            
<defaultpools>master</defaultpools>
        
</rule>
 
    
</tablerule>
    
<tablerule name="master" schema="test" defaultPools="master"/>
    
<tablerule name="slave1" schema="test" defaultPools="slave1"/>
    
<tablerule name="slave2" schema="test" defaultPools="slave2"/>
    
<tablerule name="master" schema="t" defaultPools="master"/>
</amoeba>

由于第一步主要还只是测试读写分离与数据的垂直切分,所以水平切分的规则设置的很简单,仅仅是按照ID的范围设置简单的规则。而设置一些更为复杂的规则。

总体测试效果还是比较满意的,预期的功能都全部正常,当然所用于测试的SQL语句也大都比较简单,主要还是根据当前工作中遇到的一些SQL。

这次测试没有进行性能测试,等后面再找时间测试一下,不知道有没有哪位朋友已经做过了相应的性能测试没?性能测试之后就要开始规划应用到某些应用上面去了

,