0%

索引结构——B+树

回表查询

InnoDB 引擎中使用的是聚簇索引,其主索引的实现树中的叶子结点存储的是完整的数据记录,而辅助索引中存储的则只是辅助键和主键的值。

索引覆盖

如果索引已经包含了所有满足查询需要的数据,这时我们称之为覆盖索引(Covering Index),这时就不再需要回表操作。

最左匹配

一个查询可以只使用索引中的一部分,更准确地说是最左侧部分(最左优先),这就是传说中的最左匹配原则。

即最左优先,如:

  • 如果有一个 2 列的索引 (col1,col2),则相当于已经对 (col1)、(col1,col2) 上建立了索引;
  • 如果有一个 3 列索引 (col1,col2,col3),则相当于已经对 (col1)、(col1,col2)、(col1,col2,col3) 上建立了索引;但是 (col2,col3) 上并没有。

索引下推

对索引中包含的字段先做判断,过滤掉不符合条件的记录,减少回表字数。

建立索引原则

  • 原文链接
  • 最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a = 1 and b = 2 and c > 3 and d = 4如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。
  • =in可以乱序,比如a = 1 and b = 2 and c = 3 建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式。
  • 尽量选择区分度高的列作为索引,区分度的公式是count(distinct col)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0;
  • 索引列不能参与计算,保持列干净,比如from_unixtime(create_time) = ’2014-05-29’就不能使用到索引,原因很简单,b+树中存的都是数据表中的字段值,但进行检索时,需要把所有元素都应用函数才能比较,显然成本太大。所以语句应该写成create_time = unix_timestamp(’2014-05-29’)
  • 尽量的扩展索引,不要新建索引。比如表中已经有a的索引,现在要加(a,b)的索引,那么只需要修改原来的索引即可。

查询优化神器 - explain命令

参考链接

public class Dom4jTest {

    public static void main(String[] args) throws DocumentException {
        String svgURI = System.getProperty("user.dir") + "/test.svg";
        SAXReader reader = new SAXReader();
        File file = new File(svgURI);
        Document document = reader.read(file);
        Element root = document.getRootElement();
        List<Element> childElements = root.elements();

        for (Element child : childElements) {
            if ("g".equals(child.getQName().getName())) {
                List<Element> gElements = child.elements();
                for (Element gEle : gElements) {
                    if ("g".equals(gEle.getQName().getName())) {
                        List<Element> elements = gEle.elements();
                        for (Element e : elements) {
                            if ("dfg:desc".equals(e.getQName().getName())) {
                                System.out.println(e.getText());
                            }
                        }
                    }
                }
            }
        }
    }
}

<!-- SVG解析包 -->
<dependency>
    <groupId>org.apache.xmlgraphics</groupId>
    <artifactId>batik-all</artifactId>
    <version>1.12</version>
    <type>pom</type>
</dependency>
<svg>
  <rect x="1" y="1" width="1198" height="598"
        fill="none" stroke="blue" stroke-width="1" />

  <path d="M200,300 Q400,50 600,300 T1000,300"
        fill="none" stroke="red" stroke-width="5"  />
  <g fill="black" >
    <circle cx="200" cy="300" r="10"/>
    <circle cx="600" cy="300" r="10"/>
    <circle cx="1000" cy="300" r="10"/>
  </g>
  <g fill="#888888" >
    <circle cx="400" cy="50" r="10"/>
    <circle cx="800" cy="550" r="10"/>
  </g>
  <path d="M200,300 L400,50 L600,300 L800,550 L1000,300"
        fill="none" stroke="#888888" stroke-width="2" />
</svg>
//You first load the XML as a Document:
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse("image.svg");
//Then you use XPath to select the desired nodes. The expression below selects the contents of the d attributes of all the path elements inside the file:
String xpathExpression = "//path/@d";
//Now we can instantiate the XPath processor and compile the expression:
XPathFactory xpf = XPathFactory.newInstance();
XPath xpath = xpf.newXPath();
XPathExpression expression = xpath.compile(xpathExpression);
//Since the expected result is a node-set (two strings), we evaluate the expression on the SVG document using XPathConstants.NODESET as the second parameter:
NodeList svgPaths = (NodeList)expression.evaluate(document, XPathConstants.NODESET);
//From there you can extract the first set of path data using:
svgPaths.item(0).getNodeValue();

Xpath简介

XPathXML Path的简称,它是一种用来确定XML(可扩展标记语言)文档中某部分位置的语言。Xpath也是一种表达式语言,它基于XML的树状结构,可以用来在整个树中来寻找指定的节点,因此它的返回值可能是节点,节点集合,原子值,以及节点和原子值的混合等。

Xpath定位方法

Xpath选取节点

/ 表示从根节点开始选取
// 表示选择任意位置的某个节点,而不考虑它们的位置
nodename 选取此节点的所有子节点。
. 选取当前节点。
.. 选取当前节点的父节点。
@ 选取属性。
<?xml version="1.0" encoding="ISO-8859-1"?>

<bookstore>

<book>
  <title lang="eng">Harry Potter</title>
  <price>29.99</price>
</book>

<book>
  <title lang="eng">Learning XML</title>
  <price>39.95</price>
</book>

</bookstore>
路径表达式 结果
bookstore 选取 bookstore 元素的所有子节点。
/bookstore 选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始终代表到某元素的绝对路径!
bookstore/book 选取属于 bookstore 的子元素的所有 book 元素。
//book 选取所有 book 子元素,而不管它们在文档中的位置。
bookstore//book 选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于 bookstore 之下的什么位置。
//@lang 选取名为 lang 的所有属性。

以html选择节点为例

由于HTML文档本身就是一个标准的XML页面,因此我们可以使用XPath的语法来定位页面元素。

定位节点

/html/body/form/input 查找form下所有input节点元素
//input 查找html中所有input节点元素

使用通配符(*)选择未知的元素

XPath 通配符可用来选取未知的 XML 元素。

通配符 描述
* 匹配任何元素节点。
@* 匹配任何属性节点。
node() 匹配任何类型的节点。


//form/* 查找form下所有节点元素
//* 查找html中所有节点元素
//*/input 查找所有input节点元素
//title[@*] 选取所有带有属性的 title 元素。

选择分支

对于存在多个元素时想唯一定位,可以使用[]中括号来选择分支,下标从1开始

//*/td[7]/a[1] 定位到第7td元素中第1a元素
//*/td[7]/span[2] 定位到第7td元素中第2span元素
//*/td[last()]/a[last()] 定位到最后一个td元素中最后一个a元素
//*/td[a] 定位包含a元素的所有td元素

选择最后一个元素可以用last()函数,但是选择第一个元素没有first()函数。

选择属性

还可以利用标签内的属性来加以区分定位,在<>开始标记内除标签外,其他都可以看做是属性.

//input[@name] 定位所有含name属性的input元素
//input[@*] 定位所有含属性的input元素
//input[@value='2'] 定位出value属性值为2input元素
//input[@type='vedio'][@value='1']//input[@type='vedio' and @value='1'] 多个属性定位
//input/@id 返回所有input元素的id属性

选取若干路径

过在路径表达式中使用|运算符,您可以选取若干个路径。

路径表达式 结果
`//book/title //book/price`
`//title //price`
`/bookstore/book/title //price`

常用函数

  • 字符串查找函数: contains()

    contains(string1,string2),表示如果 string1 包含 string2,则返回 true,否则返回 false

  • 获取元素的文本内容: text()
  • 从起始位置匹配字符串:starts-with()

更多函数可参考:http://www.w3school.com.cn/xpath/xpath_functions.asp

//a[contains(@href,'baidu')] 定位href属性中包含baidu的所有a元素
//a[text()='推广'] 链接文本信息是推广的所有a元素
//a[starts-with(@href,'/ads')] 链接href属性以/ads开始的所有a元素
//a[contains(text(),'推广')] 链接文本信息包含推广的所有a元素

Xpath轴

上面这些方法都不能定位时,这时候就得考虑依据元素的父辈、兄弟或者子辈节点来定位了,这就需要用到Xpath轴,利用轴可定位某个相对于当前节点的节点集。

语法:

轴名称::标签名
轴名称 描述
ancestor 选取当前节点的所有先辈(父、祖父等)。
ancestor-or-self 选取当前节点的所有先辈(父、祖父等)以及当前节点本身。
attribute 选取当前节点的所有属性。
child 选取当前节点的所有子元素。
descendant 选取当前节点的所有后代元素(子、孙等)。
descendant-or-self 选取当前节点的所有后代元素(子、孙等)以及当前节点本身。
following 选取文档中当前节点的结束标签之后的所有节点。
namespace 选取当前节点的所有命名空间节点。
parent 选取当前节点的父节点。
preceding 选取文档中当前节点的开始标签之前的所有节点。
preceding-sibling 选取当前节点之前的所有同级节点。
self 选取当前节点。


例子 结果
child::book 选取所有属于当前节点的子元素的 book 节点。
attribute::lang 选取当前节点的 lang 属性。
child::* 选取当前节点的所有子元素。
attribute::* 选取当前节点的所有属性。
child::text() 选取当前节点的所有文本子节点。
child::node() 选取当前节点的所有子节点。
descendant::book 选取当前节点的所有 book 后代。
ancestor::book 选择当前节点的所有 book 先辈。
ancestor-or-self::book 选取当前节点的所有 book 先辈以及当前节点(如果此节点是 book 节点)
child::*/child::price 选取当前节点的所有 price 孙节点。

示例

XPath教程

概述

HBase是一个K/V存储型NoSQL数据库,与一般k/v数据库不同的是,Hbase的valuerowkeycolumn family:qualifierTimeStamp这个三个维度快速定位。

HBase中rowkey可以唯一标识一行记录,在HBase查询的时候,有两种方式:

  • 通过get方式,指定rowkey获取唯一一条记录
  • 通过scan方式,设置startRow和stopRow参数进行范围匹配
  • 全表扫描,即直接扫描整张表中所有行记录

rowkey长度原则

rowkey是一个二进制码流,可以是任意字符串,最大长度64kb,实际应用中一般为10-100bytes,以byte[]形式保存,一般设计成定长。

建议越短越好,不要超过16个字节

rowkey散列原则

  • 尽量不要将rowkey设计成连续的值,如时间戳;
  • 为rowkey增加随机散列字段,使数据均衡分布在每个RegionServer;
  • 如果没有散列字段,所有的数据都会集中在一个RegionServer上,这样在数据检索的时候负载会集中在个别的RegionServer上,造成热点问题,会降低查询效率。

什么是热点?
热点发生在大量的client直接访问集群的一个或极少数个节点(访问可能是读,写或者其他操作)。大量访问会使热点region所在的单个机器超出自身承受能力,引起性能下降甚至region不可用.

rowkey唯一原则

必须在设计上保证其唯一性,rowkey是按照字典顺序排序存储的.

设计方法

  • Salting: 指将随机数据添加到行键的开头。
  • Hashing: 哈希会使同一行永远用一个前缀加盐。
  • 反转: 以手机号为rowkey,可以将手机号反转后的字符串作为rowkey
  • 时间戳反转: Long.Max_Value - timestamp
  • 尽量保持 ColumnFamily 名称尽可能小,最好是一个字符
  • 虽然详细的属性名称更易于阅读,但更喜欢使用较短的属性名称来存储在HBase中。

参考链接

Hbase版本数量

Hbase版本数量分为最大版本数量最小版本数量

  • HBase最大版本数量:HBase 通过 HColumnDescriptor 为每个列族配置要存储的最大行数版本。最大版本的默认值为1。最大版本的数量可能需要根据应用程序需求增加或减少。不建议将最高版本数设置为极高的级别(例如,数百个或更多)。
  • HBase最小版本数量:与最大行版本数一样,HBase 通过 HColumnDescriptor 为每个列族配置要保留的最小行数版本。最小版本的默认值为0,这意味着该功能被禁用。行版本参数的最小数目与生存时间参数一起使用,并且可以与行版本参数的数目组合在一起,以允许诸如“保留最多T分钟值的数据,最多N个版本,但是至少保留 M 个版本 “(其中M 是最小行版本数的值,M < N )。仅当对列族启用了生存时间并且必须小于行版本的数量时,才应设置此参数。

HBase 生存时间(TTL)

  • ColumnFamilies 可以以为单位来设置 TTL(Time To Live)长度,一旦达到到期时间,HBase 将自动删除行。
  • 也支持设置时间以每个单元为基础生存。
  • 单元 TTL 以毫秒为单位而不是秒。
  • 单元 TTL 不能将一个单元的有效生命周期延长超过 ColumnFamily 级 TTL 设置。

测试

默认Hbase列族最多保存一个版本的数据,可以通过下面命令修改,也可以使用HColumnDescriptor

alter 'test', NAME => 'cf', VERSIONS => 3

创建表的时候指定版本数量:

hbase(main):018:0> create 'test',{NAME=>'base_info',VERSIONS=>3 },{NAME=>'extra_info',VERSIONS=>1 } 
0 row(s) in 4.2670 seconds

更多详细的用法可以参考help 'create':

hbase(main):016:0> help 'create'
Creates a table. Pass a table name, and a set of column family
specifications (at least one), and, optionally, table configuration.
Column specification can be a simple string (name), or a dictionary
(dictionaries are described below in main help output), necessarily
including NAME attribute.
Examples:

Create a table with namespace=ns1 and table qualifier=t1
  hbase> create 'ns1:t1', {NAME => 'f1', VERSIONS => 5}

Create a table with namespace=default and table qualifier=t1
  hbase> create 't1', {NAME => 'f1'}, {NAME => 'f2'}, {NAME => 'f3'}
  hbase> # The above in shorthand would be the following:
  hbase> create 't1', 'f1', 'f2', 'f3'
  hbase> create 't1', {NAME => 'f1', VERSIONS => 1, TTL => 2592000, BLOCKCACHE => true}
  hbase> create 't1', {NAME => 'f1', CONFIGURATION => {'hbase.hstore.blockingStoreFiles' => '10'}}
  hbase> create 't1', {NAME => 'f1', IS_MOB => true, MOB_THRESHOLD => 1000000, MOB_COMPACT_PARTITION_POLICY => 'weekly'}

Table configuration options can be put at the end.
Examples:

  hbase> create 'ns1:t1', 'f1', SPLITS => ['10', '20', '30', '40']
  hbase> create 't1', 'f1', SPLITS => ['10', '20', '30', '40']
  hbase> create 't1', 'f1', SPLITS_FILE => 'splits.txt', OWNER => 'johndoe'
  hbase> create 't1', {NAME => 'f1', VERSIONS => 5}, METADATA => { 'mykey' => 'myvalue' }
  hbase> # Optionally pre-split the table into NUMREGIONS, using
  hbase> # SPLITALGO ("HexStringSplit", "UniformSplit" or classname)
  hbase> create 't1', 'f1', {NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
  hbase> create 't1', 'f1', {NUMREGIONS => 15, SPLITALGO => 'HexStringSplit', REGION_REPLICATION => 2, CONFIGURATION => {'hbase.hregion.scan.loadColumnFamiliesOnDemand' => 'true'}}
  hbase> create 't1', 'f1', {SPLIT_ENABLED => false, MERGE_ENABLED => false}
  hbase> create 't1', {NAME => 'f1', DFS_REPLICATION => 1}

You can also keep around a reference to the created table:

  hbase> t1 = create 't1', 'f1'

Which gives you a reference to the table named 't1', on which you can then
call methods.