2011年5月13日

Innodb存储

表空间是逻辑存放所有数据的地方,默认情况下会共享一个表空间——ibdata1,但如果把innodb_file_per_table=ON后每张表可以单独放到一个表空间内,但还是有很多数据保存在共享的表ibdata1中,如undo信息等。

 

表空间由各种段(segment)组成,常见的段有数据段、索引段等。Innodb是索引组织的,数据段就是clustered index的叶结点。需要注意的是,不是每个对象都有段。

 

(extend)是由64个连续的页组成,每个页(page)固定为16KB,所以每个区总共为1M。页是innodb最小的磁盘管理单位。

 

Innodb是按行进行存放的,每个区最少可以保存2条记录,否则就成链式结构了。每行数据除了自定义列以外,还会增加事务id和回滚指针列。如果没有定义primary key也没有not nullunique,则会增加6字节的RowId列作为主键。
        
            图片来自:http://www.cnblogs.com/chjw8016/archive/2011/03/08/1976891.html

Innodb表的限制

        一个表不能包含超过1000列。

  内部最大键长度是3500字节,但MySQL自己限制这个到1024字节。

  除了VARCHAR, BLOBTEXT列,最大行长度稍微小于数据库页的一半。即,最大行长度大约8000字节。LONGBLOBLONGTEXT列必须小于4GB, 总的行长度,页包括BLOBTEXT列,必须小于4GBInnoDB在行中存储VARCHARBLOBTEXT列的前768字节,余下的存储的分散的页中。

虽然InnoDB内部地支持行尺寸大于65535,你不能定义一个包含VARCHAR列的,合并尺寸大于65535的行。

·                mysql> CREATE TABLE t (a VARCHAR(8000), b VARCHAR(10000),

·                    -> c VARCHAR(10000), d VARCHAR(10000), e VARCHAR(10000),

·                    -> f VARCHAR(10000), g VARCHAR(10000));

·                ERROR 1118 (42000): Row size too large. The maximum row size for the

·                used table type, not counting BLOBs, is 65535. You have to change some

·                columns to TEXT or BLOBs

 在一些更老的操作系统上,数据文件必须小于2GB

 InnoDB日志文件的合并尺寸必须小于4GB

最小的表空间尺寸是10MB。最大的表空间尺寸是4,000,000,000个数据库页(64TB)。这也是一个表的最大尺寸。

 InnoDB表不支持FULLTEXT索引

 

Innodb索引

默认情况下Memory使用存储hash索引,但也支持b+tree索引。Hash索引只用于=或者<=>的等式比较,不能用来加速order by操作,只能通过关键字来搜索一行。innodb只支持b+树索引,进一步分为clustered index 与 secondary index。在一次查询中,只能使用一个索引。

        

Innodb是索引组织表,clustered index的叶结点保存着整行的数据。如果,定义了primary key,则clustered index就是primary key的索引;如果没有定义primary key mysql会选中第一个仅有not null列的unique索引作为主键,并把此索引当作clustered index使用;如果没找到这样的列,innodb会创建一个6字节的RowId作为主键。所以每张表有且只有一个clustered index

 

         Secondary index的叶结点不包括行的全部数据,包含键值以外还包括一个bookmark,可以告诉innodb到什么地方可以找到相对应的完整行数据,还保存了主键的健值。Secondary index包含主键,但不包含完整的行数据,所以innodb总是会先从secondary index的叶节点判断是否能得到所需的数据。如,

         Create table t(a int, b varchar(20), primary key(a), key(b));

Explain select * from t;

         会发现mysql选择了索引b,而不是a.

复合索引

         复合索引是在多列(>=2)上建立的索引,又叫多列索引或联合索引。Innodb中的复合索引也是b+ tree结构。索引的数据包含多列(col1, col2, col3…),在索引中依次按照col1, col2, col3排序。如(1, 2), (1, 3),(2,0)…

 

         使用复合索引要充分利用最左前缀原则,顾名思义,就是最左优先。如创建索引ind_col1_col2(col1, col2),那么在查询where col1 = xxx and col2 = xx或者where col1 = xxx都可以走ind_col1_col2索引。

在创建多列索引时,要根据业务需求,where子句中使用最频繁且过滤效果好的的一列放在最左边。

索引操作

         可以通过DML语句操作innodb索引。因为innodb是索引组织的表,对索引的操作会造成锁表,先生成一张临时表,将数据从原始表中写到临时表,再将原始表删除,最后将临时表表名改为原始表表名!因增加、删除、修改字段会对主索引产生影响,所以也会锁表。对secondary indexInnodb plugin开始,支持快速索引创建的方法,在创建的过程中不需要重建表,所以速度会很快,同时引擎会在表上加S锁,在创建过程中只能进行读操作。

索引设计原则

1.       搜索的索引列,不一定是所要选择的列。也就是说,最适合索引的列是出现在where子句中的列,或者连接子句中指定的列,而不是出现在select关键字后的选择列表中的列。

2.       使用唯一索引。考虑某列的分布,索引的列的基数越大,索引的效果越好。例如,对性别M/F列做索引没多大用处。

3.       使用短索引。如果是对字符串进行索引,如果有可能应该指定前缀长度。

4.       利用最左前缀。尽量将使用频繁且过滤效果好的字段放“左边”

5.       不要过度索引。

6.       Innodb默认会按照一定的顺序保存数据,如果明确定义了主键,则按照主键顺序保存。如果没有主键,但有唯一索引,就按照唯一索引的顺序保存。如果有几个列都是唯一的,都可以作为主键的时候,为了提高查询效率,应选择最常用访问的列作为主键。另外,innodbsecondary index都会保存主键的键值,所有主键要尽可能选择较短的数据类型。可以看出,应当尽量避免对主键的修改。经过dba的测试,保证主键的递增可以提高插入性能。

 

Mysql如何使用索引

1.       对于创建的多列索引,只要查询的条件中用到了最左边的列,索引一般就会被使用。

2.       对于使用like的查询,后面如果是常量并且只有%号不在第一个字符,索引才可能被使用。

3.       如果对大文本进行搜索,应该使用全文索引,而不是使用like ‘%...%’. 但不幸的是innodb不支持全文索引。

4.       如果列名是索引,使用 index_column is null将使用索引。Oracle是不行的。

5.       如果mysql估计使用索引比全表扫描更慢,最不会使用索引。

6.       如果使用memory/head表并且where条件中不使用”=”进行索引列,那么不会用到索引。Head表只有在”=”的时候才会使用索引。

7.       or分割开的条件,如果or前的条件中的列有索引,而后面列中没有索引,那么涉及到的索引都不会被用到。

8.       不是多列索引的第一部分不会走索引。

9.       %开始的like不会走索引

10.   如果列是字符串,那么一定要在where条件中把字符串常量值用引号引起来,否则不能走索引。因为,mysql默认把输入的常量值进行转换以后才进行检索。

11.   经过普通运算或函数运算后的索引字段不能使用索引

12.   不等于操作不能使用索,<>not in

13.   Order by 优化:某些情况下,mysql可以使用一个索引满足order by,而不需要额外的排序。Where条件与order by 使用相同的索引,并且order by的顺序和索引顺序相同,并且order by的字段都是升序或者都是降序。

SELECT * FROM t1 ORDER BY key_part1,key_part2,... ;

SELECT * FROM t1 WHERE key_part1=1 ORDER BY key_part1 DESC, key_part2

DESC;

SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 DESC;

但是以下情况不使用索引:

SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC

--order by 的字段混合 ASC DESC

SELECT * FROM t1 WHERE key2=constant ORDER BY key1

-- 用于查询行的关键字与 ORDER BY 中所使用的不相同

SELECT * FROM t1 ORDER BY key1, key2

-- 对不同的关键字使用 ORDER BY     

 

可以使用explain查看sql的执行计划。

posted @ 2011-12-17 16:36 happyenjoylife 阅读(10181) | 评论 (2)编辑 收藏
原文:
http://stas-blogspot.blogspot.com/2010/03/java-bridge-methods-explained.html

Bridge methods in Java are synthetic methods, which are necessary to implement some of Java language features. The best known samples are covariant return type and a case in generics when erasure of base method's arguments differs from the actual method being invoked.

Have a look at following example:

public class SampleOne {
public static class A<T> {
public T getT() {
return null;
}
}

public static class  B extends A<String> {
public String getT() {
return null;
}
}
}

Which in reality is just an example of covariant return type and after erasure will look like following snippet:

public class SampleOne {
public static class A {
public Object getT() {
return null;
}
}

public static class  B extends A {
public String getT() {
return null;
}
}
}

And after the compilation decompiled result class "B" will be following:
public class SampleOne$B extends SampleOne$A {
public SampleOne$B();

public java.lang.String getT();
Code:
0:   aconst_null
1:   areturn
public java.lang.Object getT();
Code:
0:   aload_0
1:   invokevirtual   #2// Call to Method getT:()Ljava/lang/String;
4:   areturn
}

Above you can see there is new synthetic method "java.lang.Object getT()" which is not present in source code. That method acts as bridge method and all is does is delegating invocation to "java.lang.String getT()". Compiler has to do that, because in JVM method return type is part of method's signature, and creation of bridge method is the way to implement covariant return type.

Now have a look at following example which is generics-specific:
public class SampleTwo {
public static class A<T> {
public T getT(T args) {
return args;
}

}


public static class B extends A<String> {
public String getT(String args) {
return args;
}

}

}

after compilation class "B" will be transformed into following:
public class SampleThree$B extends SampleThree$A{
public SampleThree$B();

public java.lang.String getT(java.lang.String);
Code:
0:   aload_1
1:   areturn

public java.lang.Object getT(java.lang.Object);
Code:
0:   aload_0
1:   aload_1
2:   checkcast       #2//class java/lang/String
5:   invokevirtual   #3//Method getT:(Ljava/lang/String;)Ljava/lang/String;
8:   areturn
}

here, the bridge method, which overrides method from base class "A", not just calling one with string argument (#3), but also performs type cast to "java.lang.String" (#2). It means, that if you will execute following code, ignoring compiler's "unchecked" warning, the result will be ClassCastException thrown from the bridge method:
A a = new B();
a.getT(
new Object()));

These two examples are the best known cases where bridge methods are used, but there is, at least, one more, where bridge method is used to "change" visibility of base class's methods. Have a look at following sample and try to guess where compiler may need the bridge method to be created:
package samplefour;

public class SampleFour {
static class A {
public void foo() {
}

}

public static class C extends A {

}

public static class D extends A {
public void foo() {
}

}

}
If you will decompile class C, you will see method "foo" there, which overrides method from base class and delegates to it:
public class SampleFour$C extends SampleFour$A{

public void foo();
Code:
0:   aload_0
1:   invokespecial   #2//Method SampleFour$A.foo:()V
4:   return

}

compiler needs that method, because class A is not public and can't be accessed outside it's package, but class C is public and all inherited method have to become visible outside the package as well. Note, that class D will not have bridge method, because it overrides "foo" and there is no need to "increase" visibility.
It looks like, that type of bridge method, was introduced after bug which was fixed in Java 6. It means that before Java 6 that type of bridge method is not generated and method "C#foo" can't be called from package other than it's own via reflection, so following snippet causes IllegalAccessException, in cases when compiled on Java version < 1.6:
package samplefive;

SampleFour.C.
class.getMethod("foo").invoke(new SampleFour.C());
Normal invocation, without using reflection, will work fine.

Probably there are some other cases where bridge methods are used, but there is no source of information about it. Also, there is no definition of bridge method, although you can guess it easily, it's pretty obvious from examples above, but still would be nice to have something in spec which states it clearly. In spite of the fact that method Method#isBridge() is part of public reflection API since Java1.5 and bridge flag is part of class file format, JVM and JLS specifications do not have any information what exactly is that and do not provide any rules when and how it should be used by compiler. All I could find is just reference in "Discussion" area here.
posted @ 2011-08-20 10:19 happyenjoylife 阅读(436) | 评论 (0)编辑 收藏

异常表

每一个try语句块catch的异常都与异常表中的一项相对应,异常表中的每一项都包括:

  1. 起点
  2. 终点,始终把catch异常位置的pc指针偏移量的最大值大1
  3.  处理异常时跳转到的字节码序列中的pc指针偏移量
  4.  catch的异常类的常量池索引

 

例如:

public class Test {
    
public static void main(String[] args) {

        
try {
            Class.forName(
"java.lang.String");
        }
 catch (ClassNotFoundException e) {
            e.printStackTrace();
        }


    }

}

javap –c查看字节码如下:

 

Compiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
  Code:
   
0:    aload_0
   
1:    invokespecial    #1//Method java/lang/Object."<init>":()V
   4:    return

public static void main(java.lang.String[]);
  Code:
   
0:    ldc    #2//String java.lang.String
   2:    invokestatic    #3//Method java/lang/Class.forName:(Ljava/lang/String;)Ljava/lang/Class;
   5:    pop    
   
6:    goto    14
   
9:    astore_1
   
10:    aload_1
   
11:    invokevirtual    #5//Method java/lang/ClassNotFoundException.printStackTrace:()V
   14:    return
  Exception table:
   from   to  target type
     
0     6     9   Class java/lang/ClassNotFoundException
}

 

可见ClassNotFoundException异常可能会在0~6之间抛出,9开始处的代码处理此异常。

 

当产生异常的时候,jvm将会在整个异常表中搜索与之匹配的项,如果当前pc在异常表入口所指的范围内,并且所抛出的异常是此入口所指向的类或者其子类,则跳转到对应的处理代码继续执行。

 

方法可能会抛出哪些已检查异常

Class文件的attribute_info中保存有Exceptions属性,记录着每个方法throws的异常信息。具体的可以查看class类文件格式相关的文章。

 

athrow指令从栈顶弹出Throwable对象引用,抛出异常。

 

finally语句

jvm规范中,finally语句是通过jsr/jsr_wret指令实现的。当执行jsr/jsr_w的时候将finally执行完成后的返回地址压入栈中,进入finally后会马上将此地址保存到一个局部变量中,执行完成后,ret从此局部变量中取出返回地址。???为什么会先把返回地址保存到局部变量中呢???因为,当从finally语句返回的时候需要将返回地址成栈中弹出,当finally语句非正常结束(break,continue,return, 抛异常)的时候就不用再考虑这个问题。

 

以下是jvm规范中Compiling finally的一段:

void tryFinally() {
    
try {
        tryItOut();
    }
 finally {
        wrapItUp();
    }

}

the compiled code is
Method 
void tryFinally()
   
0     aload_0            // Beginning of try block
   1    invokevirtual #6         // Method Example.tryItOut()V
   4     jsr 14            // Call finally block
   7     return            // End of try block
   8     astore_1            // Beginning of handler for any throw
   9     jsr 14            // Call finally block
  12     aload_1            // Push thrown value
  13     athrow            // and rethrow the value to the invoker
  14     astore_2            // Beginning of finally block
  15     aload_0            // Push this
  16     invokevirtual #5         // Method Example.wrapItUp()V
  19     ret 2            // Return from finally block
Exception table:
       From     To     Target         Type
    
0        4        8           any

 

tryItOut排除任何异常后都将会被异常表中的any项捕获,执行完finally后,会执行athrow指令将异常抛出。

 

jdk的某一个版本开始就不会编译出编译出含jsr/jsr_wret的字节码了,因为有指令上的缺陷,导致jvm的检验和分析系统出现漏洞。

 

再说finally的非正常退出

finally中使用breakcontinuereturn、抛出异常等认为是finally的非正常结束。非正常结束的时候,ret指令不会被执行,很可能会出现意想不到的结果。如:

 

public class Test {
    
public static boolean test(boolean b) {
        
while (b) {
            
try {
                
return true;
            }
 finally {
                
/*
                break;                          始终返回false
                continue;                         javac编译再java执行会出现死循环
                                                在eclipse中甚至会出现报错:提示找到不main class
                return false;                     始终返回false
                throw new RuntimeException("");    抛出异常
                 
*/

            }

        }


        
return false;
    }


    
public static void main(String[] args) {
        System.out.println(test(
true));
    }

}


建议:在写finally语句的时候,尽量避免非正常结束!


 

posted @ 2011-05-27 14:39 happyenjoylife 阅读(2566) | 评论 (4)编辑 收藏

单库单表

单库单表是最常见的数据库设计,例如,有一张用户(user)表放在数据库db中,所有的用户都可以在db库中的user表中查到。

 

单库多表

随着用户数量的增加,user表的数据量会越来越大,当数据量达到一定程度的时候对user表的查询会渐渐的变慢,从而影响整个DB的性能。如果使用mysql, 还有一个更严重的问题是,当需要添加一列的时候,mysql会锁表,期间所有的读写操作只能等待。

可以通过某种方式将user进行水平的切分,产生两个表结构完全一样的user_0000,user_0001等表,user_0000 + user_0001 + …的数据刚好是一份完整的数据。

 

多库多表

         随着数据量增加也许单台DB的存储空间不够,随着查询量的增加单台数据库服务器已经没办法支撑。这个时候可以再对数据库进行水平区分。

 

分库分表规则

         设计表的时候需要确定此表按照什么样的规则进行分库分表。例如,当有新用户时,程序得确定将此用户信息添加到哪个表中;同理,当登录的时候我们得通过用户的账号找到数据库中对应的记录,所有的这些都需要按照某一规则进行。

路由

         通过分库分表规则查找到对应的表和库的过程。如分库分表的规则是user_id mod 4的方式,当用户新注册了一个账号,账号id123,我们可以通过id mod 4的方式确定此账号应该保存到User_0003表中。当用户123登录的时候,我们通过123 mod 4后确定记录在User_0003中。

分库分表产生的问题,及注意事项

1.   分库分表维度的问题

假如用户购买了商品,需要将交易记录保存取来,如果按照用户的纬度分表,则每个用户的交易记录都保存在同一表中,所以很快很方便的查找到某用户的购买情况,但是某商品被购买的情况则很有可能分布在多张表中,查找起来比较麻烦。反之,按照商品维度分表,可以很方便的查找到此商品的购买情况,但要查找到买人的交易记录比较麻烦。

 

所以常见的解决方式有:

     a.通过扫表的方式解决,此方法基本不可能,效率太低了。

     b.记录两份数据,一份按照用户纬度分表,一份按照商品维度分表。

     c.通过搜索引擎解决,但如果实时性要求很高,又得关系到实时搜索。

 

2.   联合查询的问题

联合查询基本不可能,因为关联的表有可能不在同一数据库中。

 

3.   避免跨库事务

避免在一个事务中修改db0中的表的时候同时修改db1中的表,一个是操作起来更复杂,效率也会有一定影响。

 

4.   尽量把同一组数据放到同一DB服务器上

例如将卖家a的商品和交易信息都放到db0中,当db1挂了的时候,卖家a相关的东西可以正常使用。也就是说避免数据库中的数据依赖另一数据库中的数据。

 

 

一主多备

在实际的应用中,绝大部分情况都是读远大于写。Mysql提供了读写分离的机制,所有的写操作都必须对应到Master,读操作可以在MasterSlave机器上进行,SlaveMaster的结构完全一样,一个Master可以有多个Slave,甚至Slave下还可以挂Slave,通过此方式可以有效的提高DB集群的QPS.                                                       

所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。

此外,可以看出Master是集群的瓶颈,当写操作过多,会严重影响到Master的稳定性,如果Master挂掉,整个集群都将不能正常工作。

所以,1. 当读压力很大的时候,可以考虑添加Slave机器的分式解决,但是当Slave机器达到一定的数量就得考虑分库了。 2. 当写压力很大的时候,就必须得进行分库操作。

 

         另外,可能会因为种种原因,集群中的数据库硬件配置等会不一样,某些性能高,某些性能低,这个时候可以通过程序控制每台机器读写的比重,达到负载均衡。


备份地址:http://happyenjoylife.iteye.com/admin/blogs/1042538

posted @ 2011-05-13 15:31 happyenjoylife 阅读(16139) | 评论 (4)编辑 收藏

导航

<2011年5月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

统计

常用链接

留言簿

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜