lealone / Lealone

比 MySQL 和 MongoDB 快10倍的 OLTP 关系数据库和文档数据库
Other
2.44k stars 513 forks source link

主键查询性能不高是怎么回事? #48

Closed xiang82 closed 9 years ago

xiang82 commented 11 years ago

我最近使用lealone做了一个简单实验:

  1. 通过hbase client写入了5千万条记录到表test50m;
  2. 使用select * from test50m limit 100会很快返回结果,并且结果正确;
  3. 但是查询加了where条件后 select * from test50m where date_time='1970-06-23 14:40:01';会耗时在500多秒才出结果。为什么性能会这么差呢? date_time是表test50m的主键,也是hbase表的rowkey。 另外通过监控整个查询过程,我发现表test50m所在的regionserver会轮流响应查询,对于这种简单查询,不是应该通过主键定位到某个region,只在该region响应查询就行了码?
codefollower commented 11 years ago

我看下你的建表语句是怎样的,按你的描述如果date_time真的是主键会很快的,只会出来一个region有这一行。

xiang82 commented 11 years ago

CREATE HBASE TABLE IF NOT EXISTS test50m( SPLIT KEYS('1970-04-26 17:46:46','1970-08-20 11:33:20','1970-12-14 05:20:00','1971-04-08 23:06:40'), COLUMN FAMILY CF ( date_time TIMESTAMP primary key, intcol INT, tincola TINYINT, tintcolb TINYINT, fcola FLOAT, fcolb FLOAT , tintcolc TINYINT, tscol TIMESTAMP) )

codefollower commented 11 years ago

我明白了,用CREATE HBASE TABLE的方式建表,不需要在COLUMN FAMILY CF指定primary key(或rowKey)的, 只需要在insert时,用insert into test50m(_rowkey_,intcol, ...) values('1970-04-26 17:46:46',...)就可以了。 _rowkey_是个伪字段。

然后你查询时用select * from test50m where _rowkey_='1970-06-23 14:40:01';

CREATE HBASE TABLE是用来支持多列族的,rowKey本身并不属于某个列族,所以就没有采用你上面的设计。

如果还是想使用primary key的方式,也就是传统RDBMS的方式,直接用CREATE TABLE语法就可以了: 比如: CREATE TABLE IF NOT EXISTS test50m( date_time TIMESTAMP primary key, intcol INT, tincola TINYINT, tintcolb TINYINT, fcola FLOAT, fcolb FLOAT , tintcolc TINYINT, tscol TIMESTAMP )

目前CREATE TABLE比CREATE HBASE TABLE的功能弱一些,比如还不支持SPLIT KEYS和其他表参选项, 这些功能后续会加上。

freemanhjr commented 11 years ago

不管怎样为何region server 为何轮流响应,并行处理不应如此!

codefollower commented 11 years ago

@freemanhjr Issue #46 不是已经说了么? select * from test50m 为什么需要并行?我把5000万记录在server端并行处理后都返回给你client,client端你要一下显示5000万记录吗?OOM怎么办?没有人会设计出这样的client的。

这里的问题是因为date_time并不真的是rowKey,当select * from test50m where date_time='1970-06-23 14:40:01'时,实际上是全表扫描,并且没有索引,然后一条条比较date_time是否是'1970-06-23 14:40:01'。

freemanhjr commented 11 years ago

我的意思是全表扫描不能所有region一起扫吗?为何要顺序扫呢?

codefollower commented 11 years ago

1). 你还是没有明白为什么连HBase都这么设计?为什么选择串行(也就是顺序扫)?

假如执行 ResultScanner rs = hTable.getScanner(new Scan()) 涉及1万个region,你认为HBase会并行打开并行扫这么多region吗? 应用会一直调用rs.next()取出所有记录吗? 如果你来设计,你认为一边执行rs.next()一边打开相关region好, 还是执行rs.next()前就把1万个region全打开全加载到内存好?

如果让你实现分页,你是一次性把所有记录取回来client,然后不管用户怎么点下一页都从client缓存取? 还是只多取一两页,用户点下一页不在client缓存时才从server端取?

2). 现在需求变了,要实现select count(*) from table的功能, 这时还需要像1那样打开一个region统计完再打开另一个吗?

你把1和2的问题都想清楚了,然后你再评判一下HBase和Lealone现在的设计对不对? 如果你觉得不合理,你可以具体描述你的方案。

codefollower commented 11 years ago

另外,xiang82发的这问题并不是想说到底是串行还是并行的问题,这只是一个没有正确使用rowkey的问题, 当然,这并不能怪xiang82,只能怪CREATE TABLE和CREATE HBASE TABLE的语法确实容易错误引导用户把primary key 定义在某个列族里。

xiang82 commented 11 years ago

@codefollower 按照您的说法,我使用create table又做了一次实验: CREATE TABLE IF NOT EXISTS test1m( date_time TIMESTAMP primary key, intcol INT, tincola TINYINT, tintcolb TINYINT, fcola FLOAT, fcolb FLOAT , tintcolc TINYINT, tscol TIMESTAMP )

查询: select * from test1m where date_time='1970-01-01 00:00:01.0' 似乎还是不快,会是什么原因呢?

另外,我在hbase里查看了一下,date_time即作为了rowkey,又做为了CF里的一个列,不知道这个是否正确,是否这个原因导致了全部操作? hbase(main):002:0> scan 'TEST1M',{LIMIT=>1} ROW COLUMN+CELL
1970-01-01 00:00:01 column=CF:DATE_TIME, timestamp=1377742346912, value=\xFF\xFF\xFF\xFF\xFEH\x8F\xE8
1970-01-01 00:00:01 column=CF:FCOLA, timestamp=1377742346912, value=@q\x93\x87\x01\x10\xA18
1970-01-01 00:00:01 column=CF:FCOLB, timestamp=1377742346912, value=\xC0C\x19\xDAa\xE0\xC5\xA8
1970-01-01 00:00:01 column=CF:INTCOL, timestamp=1377742346912, value=\x00pa\x99
1970-01-01 00:00:01 column=CF:TINTCOLA, timestamp=1377742346912, value=32
1970-01-01 00:00:01 column=CF:TINTCOLB, timestamp=1377742346912, value=N
1970-01-01 00:00:01 column=CF:TINTCOLC, timestamp=1377742346912, value=\x01
1970-01-01 00:00:01 column=CF:TSCOL, timestamp=1377742346912, value=\x00\x00\x00\x00+\x00;v
1 row(s) in 1.3360 seconds

codefollower commented 11 years ago

嗯,我debug了一下,确认这个是个bug: 把date_time='1970-01-01 00:00:01.0'条件遗忘了。