DEV3200 Slide Guide
DEV3200 Slide Guide
This Guide is protected under U.S. and international copyright laws, and is the exclusive property of
MapR Technologies, Inc.
© 2017, MapR Technologies, Inc. All rights reserved. All other trademarks cited here are the property of
their respective owners.
Welcome to DEV 320 – Apache HBase Model and Architecture, Lesson 1: Introduction to
Apache HBase.
L1-1
®
Lesson 1: Introduction to Apache HBase
L1-2
®
Lesson 1: Introduction to Apache HBase
L1-3
®
Lesson 1: Introduction to Apache HBase
In this section, we will look at the differences between relational databases and HBase.
L1-4
®
Lesson 1: Introduction to Apache HBase
What is Apache HBase? It is a NoSQL database that runs on top of Hadoop as a distributed
scalable big data store.
HBase is a column family oriented datastore. Column-oriented data bases save their data
grouped by columns. The motivation for NoSQL databases came because of limitations of
relational databases.
Before we deep dive into HBase, let us take a look at a scenario and see if a relational
database can be used in this use case. We will then discuss how HBase compares to
relational databases.
L1-5
®
Lesson 1: Introduction to Apache HBase
An office supply company, the Big Office Supply Company, sells their products through
different outlets. They have their own stores around the country, and an online shop. They
want to expand their online presence.
L1-6
®
Lesson 1: Introduction to Apache HBase
They are interested in tracking point of sales from all the different channels to help manage
their inventory. They want to track their shopper’s purchase history data.
They also want to track social media to find out what is trending, if there are any likes, any
negative comments in Facebook, Twitter, etc. and to increase their online presence. They want
to add more targeted ad campaigns.
L1-7
®
Lesson 1: Introduction to Apache HBase
They are interested in increasing their web presence to create more targeted web marketing
campaigns. They are also creating mobile applications and hope to gather some data from
these apps to use for targeted marketing campaigns.
L1-8
®
Lesson 1: Introduction to Apache HBase
When deciding what database best suits our data, here are some questions we may want to
ask.
L1-9
®
Lesson 1: Introduction to Apache HBase
Consider the point of sales data. Is this structured or unstructured? Is this data going to vary
across the different points of sale?
What can you say about data from social media? Is this structured? Do you think the data that
is collected from mobile apps will change? Do you think the data that is collected from mobile
apps will change? Do you think the number of users will increase?
L1-10
®
Lesson 1: Introduction to Apache HBase
L1-11
®
Lesson 1: Introduction to Apache HBase
L1-12
®
Lesson 1: Introduction to Apache HBase
Point-of-sales data and purchasers’ history data is most probably going to be structured and a
reasonably static schema. Volume of data can grow, but maybe not exponentially.
A relational database can be used for this. However, we also need to take into account data
from social media.
This will be unstructured data which will require a dynamic data model and can also produce
exponentially large volumes. Since the mobile apps are just being developed, the data that is
collected from mobile apps can change easily. This means that you will need a flexible data
model. In this case, you would want to consider HBase.
L1-13
®
Lesson 1: Introduction to Apache HBase
Here are some things to consider in making your decision to pick a NoSQL database over a
relational database.
1. What does your data look like? Are there multiple levels of hierarchies? If your data does
not have a simple tabular structure, then you may want to consider NoSQL.
2. Is your data model likely to change? Do you have all the information you need at the
time of design? If not, then you need the flexibility to change the schema - NoSQL
3. Is your data growing exponentially? Are the number of users going to multiply? This has
to do with being able to scale easily and cheaply – then NoSQL.
4. Will you be doing real-time analytics on operational data? If you are looking at data that
is brought together from many upstream systems to build an application, use NoSQL.
Now let us take a step back and take a look at the pioneers and history of HBase. The next few
slides will go over some of the limitations of relational databases and how HBase compares.
L1-14
®
Lesson 1: Introduction to Apache HBase
Let us take a look at some of the challenges that people face nowadays with relational
databases.
Relational databases were the standard for years, so what changed? With more and more data
came the need to scale. One way to scale is vertically with a bigger server. This can get
expensive, however, and there are limits as your size increases.
L1-15
®
Lesson 1: Introduction to Apache HBase
Here is an example of how a relational database was scaled vertically. In 1999, eBay scaled
vertically by buying bigger servers, but this did not scale for very long.
L1-16
®
Lesson 1: Introduction to Apache HBase
An alternative to vertical scaling is to scale horizontally with a cluster of machines, which can
use commodity hardware. This can be cheaper and more reliable.
To horizontally partition, or shard, an RDBMS, data is distributed on the basis of rows. Some
rows reside on a single machine and the other rows reside on other machines. It is
complicated to partition or shard a relational database, as it was not designed to do this
automatically. You also lose the querying, transactions, and consistency controls across
shards. Relational databases are not designed to run efficiently on clusters.
L1-17
®
Lesson 1: Introduction to Apache HBase
Facebook split its MySQL database into approximately 4,000 shards, and 9,000 instances of
memcached, in-memory key-value store for small chunks of data, in order to handle the site’s
massive data volume. This became very difficult to maintain and scale, and now Facebook has
moved their messaging to HBase. There are several videos on the internet about this.
[Link]
L1-18
®
Lesson 1: Introduction to Apache HBase
In late 1999, EBay scaled out across a cluster by logically partitioning their databases for user
data, item data, and purchase data. However this SQL option did not scale enough for EBay,
and they have now moved their items catalog to HBase.
L1-19
®
Lesson 1: Introduction to Apache HBase
In 2006 Google came up with BigTable, which was designed to run on a cluster. Google
published a paper titled “Bigtable: A Distributed Storage System for Structured Data”.
Bigtable is a distributed storage system for managing structured data that is designed to scale
to a very large size: petabytes of data across thousands of commodity servers. It is a sparse,
distributed, persistent, multi-dimensional sorted map.
HBase is the open source implementation of the the Bigtable storage architecture.
L1-20
®
Lesson 1: Introduction to Apache HBase
The vital factor for HBase was the ability to support large volumes of data by running on
clusters.
HBase was designed to scale due to the fact that data that is accessed together is stored
together. Grouping the data by key is central to running on a cluster. In HBase, the data is
automatically distributed across a cluster. Sharding distributes different data across multiple
servers, and each server is the source for a subset of data. Distributed data is accessed
together which makes it faster for scaling.
L1-21
®
Lesson 1: Introduction to Apache HBase
Database normalization eliminates redundant data, which makes storage efficient. However a
normalized schema causes joins for queries, to bring the data back together again. Complex
JOIN queries can be difficult to implement and maintain, and also use up a lot of database
resources. As shown in the figure, you can end up with a bottleneck.
HBase does not support relationships and joins, so it can avoid the limitations associated with
a relational model.
L1-22
®
Lesson 1: Introduction to Apache HBase
Relational DBs do not handle variable data. An RDBMS uses defined data types, and any
changes to the schema means altering the database.
In HBase however, two records do not have to look alike. All data is stored as bytes, and new
data types or structures can be added dynamically, as can new fields.
L1-23
®
Lesson 1: Introduction to Apache HBase
L1-24
®
Lesson 1: Introduction to Apache HBase
L1-25
®
Lesson 1: Introduction to Apache HBase
Match the features listed on the left with the appropriate database, an RDBMS or HBase.
L1-26
®
Lesson 1: Introduction to Apache HBase
L1-27
®
Lesson 1: Introduction to Apache HBase
L1-28
®
Lesson 1: Introduction to Apache HBase
Now that you have seen the difference between RDBMS and HBase, let's take a look at some
typical HBase use cases.
L1-29
®
Lesson 1: Introduction to Apache HBase
Information Exchange
These cases represent high volume and velocity writes and reads. An example is Facebook
messages.
Content Serving
These cases use high volume and velocity reads.
For example, consider a web store that refers to web applications that serve data for millions of
Internet users. Web applications require a data store that can manage a huge amount of data.
L1-30
®
Lesson 1: Introduction to Apache HBase
Many real-world applications today can fall under the category of time series applications that
manage and store data generated over time, such as server logs, performance metrics, sensor
data, a stock ticker, and transport tracking.
Data often trickles in and is added to an existing data store for further usage, such as analytics,
processing, and serving. Many HBase use cases fall in this category, using HBase as the data
store that captures incremental data coming in from various data sources.
L1-31
®
Lesson 1: Introduction to Apache HBase
Information Exchange use cases are characterized by high volume reads and writes.
An example is the enormous volume of reads and writes used storing Facebook message
histories.
L1-32
®
Lesson 1: Introduction to Apache HBase
A content serving web application is read heavy, and is characterized by high volume, high
velocity reads.
An example is a web store for web applications, that requires a data store that can manage a
huge amount of data for millions of Internet users.
Writes to the database are random with a few updates. Writes have less throughput, and low
latency.
Reads to the data base are low latency. EBay uses HBase to perform low latency reads for
their items catalog.
L1-33
®
Lesson 1: Introduction to Apache HBase
L1-34
®
Lesson 1: Introduction to Apache HBase
L1-35
®
Lesson 1: Introduction to Apache HBase
L1-36
®
Lesson 1: Introduction to Apache HBase
L1-37
®
Lesson 1: Introduction to Apache HBase
In the next lesson, we will take an in-depth look at the HBase data model.
L1-38
®
Lesson 2: Apache HBase Data Model
Welcome to DEV 320 – Apache HBase Data Model and Architecture, Lesson 2: Apache HBase Data
Model.
L2-1
®
Lesson 2: Apache HBase Data Model
L2-2
®
Lesson 2: Apache HBase Data Model
This lesson describes the HBase data model. You will look at how data is stored in HBase table cells and
how an HBase table is physically stored on disk. You will also use basic operations to create an HBase
table using the HBase shell.
L2-3
®
Lesson 2: Apache HBase Data Model
In this section we will take a look at the HBase data model components.
L2-4
®
Lesson 2: Apache HBase Data Model
Data is organized into HBase tables. Tables are made up of a number of rows. Data is stored in rows.
Each row is composed of columns that are grouped into column families.
Every column value or cell is versioned by a timestamp. The entire cell, with the added structural
information, is called KeyValue in HBase terms. The entire cell, the row key, column family name,
column name, timestamp, and value are stored for every cell for which you have set a value. The key
consists of the row key, column family name, column name, and the timestamp.
The value in a cell is versioned by version number, which is the timestamp of when the cell was
written.
We will go into how to create an HBase table later. Now let us take a look at some of the other
components in a little more detail.
L2-5
®
Lesson 2: Apache HBase Data Model
Let us look at a row key in more detail. Data stored in HBase is located by a unique identifier, its row
key. This is like a primary key from a relational database. Once you have set up and added data to the
HBase table, you cannot change the row key to be another column. In the above example, once data
has been put in the table, you cannot pick the column, street in Address, to be the row key.
L2-6
®
Lesson 2: Apache HBase Data Model
Records in HBase are stored in sorted order according to the row key. This is a fundamental tenet of
HBase, and is also a critical semantic used in HBase schema design. Row keys are stored in
lexicographical order, which is why Row 100 comes before Row 2 in the example shown here.
L2-7
®
Lesson 2: Apache HBase Data Model
Tables are divided into sequences of rows, by row key range, called regions. These regions are then
assigned to the data nodes in the cluster called RegionServers. This scales read and write capacity by
spreading it across the cluster. This is done automatically and this is how HBase was designed for
horizontal sharding.
L2-8
®
Lesson 2: Apache HBase Data Model
Each row is comprised of columns grouped into column families. Columns are added
dynamically. Column families can contain an arbitrary number of columns.
Every row in a table has the same number of column families.
L2-9
®
Lesson 2: Apache HBase Data Model
Column family grouping should be such that it facilitates common access patterns of data.
Columns that read or write together make good column families. In the above example,
customer address data is grouped together and customer order data is grouped together.
Also note that in-memory column families provide fast access to small data. We will talk later
about access patterns and also designing schemas for better performance.
L2-10
®
Lesson 2: Apache HBase Data Model
HBase data is stored in Hfiles on HDFS, the Hadoop Distributed File System. Column families are stored
in separate files and can also be accessed separately.
L2-11
®
Lesson 2: Apache HBase Data Model
Column families are made of columns. Column names or column qualifiers identify the column.
Here we are looking at a table that contains customer information. The Address column family
contains the following columns; street, city, and state.
L2-12
®
Lesson 2: Apache HBase Data Model
The data is stored in HBase table cells. The entire cell, with the added structural information, is
called KeyValue in HBase terms. The entire cell, the row key, column family name, column name,
timestamp, and value are stored for every cell for which you have set a value.
The Key consists of the row key, column family name, column name, and timestamp.
In the next slide, we will see how the KeyValue works when retrieving data.
L2-13
®
Lesson 2: Apache HBase Data Model
[Link] you were to retrieve the item that a row key maps to, for example smithj, you get data from all the
columns.
[Link] you retrieved the item from the row key, and column family, you would get the data for the
columns in that column family.
[Link] addition, if you specify the column qualifier, you would get all the timestamps and associated
values.
4. You can then specify which timestamp and associated value you would like to return. By default,
only the latest version is returned.
L2-14
®
Lesson 2: Apache HBase Data Model
L2-15
®
Lesson 2: Apache HBase Data Model
L2-16
®
Lesson 2: Apache HBase Data Model
L2-17
®
Lesson 2: Apache HBase Data Model
L2-18
®
Lesson 2: Apache HBase Data Model
L2-19
®
Lesson 2: Apache HBase Data Model
L2-20
®
Lesson 2: Apache HBase Data Model
In this section, you will see how the logical HBase table model maps to physical storage on
disk.
L2-21
®
Lesson 2: Apache HBase Data Model
Logically cells are stored in a table format, but physically rows are stored as linear sets of cells
containing all the key value information inside them.
The lower tables shows the physical storage in files. Column families are stored in separate files.
The entire cell, the row key, column family name, column name, timestamp, and value are stored for
every cell for which you have set a value.
L2-22
®
Lesson 2: Apache HBase Data Model
Table cells are versioned, uninterpreted arrays of bytes. The version is by default a timestamp that is a
long, and you can also set up your own versioning system. So for every coordinate row:family:column,
there can be multiple versions of the value.
L2-23
®
Lesson 2: Apache HBase Data Model
Versioning is built-in. A put is both an insert (create) and an update and each one gets its
own version. Delete gets a tombstone marker. The tombstone marker prevents the data being
returned in queries.
Get requests return specific version(s) based on parameters. If you do not specify any
parameters, the most recent version is returned. You can configure how many versions you
want to keep and this is done per column family. The default is to keep only one version. When
the max number of versions is exceeded extra records will be eventually removed.
The version or timestamp is part of the key and you can access it from a Result object.
You can specify what versions you want to delete.
L2-24
®
Lesson 2: Apache HBase Data Model
L2-25
®
Lesson 2: Apache HBase Data Model
The lower-right part shows the physical storage in files. Column families are stored in separate files.
The entire cell, the row key, column family name, column name, timestamp, and value are stored for
every cell for which you have set a value.
The rows are sorted by row key first, and then by column qualifier/name. if there is more than one
value per cell, then it is sorted by timestamp descending, with the most recent first.
L2-26
®
Lesson 2: Apache HBase Data Model
It is best to think of an HBase table as a map of a map: an outer-sorted map keyed by a row key, and
an inner-sorted map keyed by column name.
L2-27
®
Lesson 2: Apache HBase Data Model
L2-28
®
Lesson 2: Apache HBase Data Model
L2-29
®
Lesson 2: Apache HBase Data Model
L2-30
®
Lesson 2: Apache HBase Data Model
L2-31
®
Lesson 2: Apache HBase Data Model
L2-32
®
Lesson 2: Apache HBase Data Model
L2-33
®
Lesson 2: Apache HBase Data Model
L2-34
®
Lesson 2: Apache HBase Data Model
L2-35
®
Lesson 2: Apache HBase Data Model
L2-36
®
Lesson 2: Apache HBase Data Model
In this section, you will take a look at some basic data model operations.
L2-37
®
Lesson 2: Apache HBase Data Model
Once you have created a table you define column families. Columns may be defined on the fly.
You can define them ahead of the time but that is not necessary. Normally, you will define
rows when you add the data.
L2-38
®
Lesson 2: Apache HBase Data Model
put - Inserts data into rows, both add and update. Put will add new rows to the table if the key
is new or update an existing row if the key already exists.
get - Returns the data from a specified row.
scan - Accesses data from a range of rows. It allows iteration over multiple rows for specified
attributes.
delete – Removes a row from a table. HBase does not modify data in place. Deletes are
handled by creating new markers called tombstones. These are cleaned up along with the
dead values on major compactions.
L2-39
®
Lesson 2: Apache HBase Data Model
L2-40
®
Lesson 2: Apache HBase Data Model
In this section, we will see how to create an HBase table using the HBase shell.
L2-41
®
Lesson 2: Apache HBase Data Model
You can create an HBase table using the HBase shell or the Java API.
L2-42
®
Lesson 2: Apache HBase Data Model
Here is a partial data for the customers of the Big Office Supply Company.
L2-43
®
Lesson 2: Apache HBase Data Model
L2-44
®
Lesson 2: Apache HBase Data Model
Using the create statement creates the table. Here we are specifying where we want to create
the table and that it should be called Customer. We are also defining two column families,
Address and Order.
In the first put statement, we are specifying the row key smithj, adding the value Central Dr
for the street column in the Address column family.
In the next put statement, we are specifying the row key smithj adding the value 2/2/2015 for
the OrderDate column in the Order column family.
The third put statement here is adding the value Columbus to the city column in the Address
column family.
L2-45
®
Lesson 2: Apache HBase Data Model
The get statement will retrieve the latest version in all the columns in all the column families for
smithj.
L2-46
®
Lesson 2: Apache HBase Data Model
L2-47
®
Lesson 2: Apache HBase Data Model
L2-48
®
Lesson 2: Apache HBase Data Model
L2-49
®
Lesson 2: Apache HBase Data Model
L2-50
®
Lesson 2: Apache HBase Data Model
L2-51
®
Lesson 2: Apache HBase Data Model
Open your lab guide, and complete exercises 2.4a and 2.4b.
L2-52
®
Lesson 2: Apache HBase Data Model
L2-53
®
Lesson 3: Apache HBase Architecture
Welcome to DEV 320 – Apache HBase Data Model and Architecture, Lesson 3: Apache
HBase Architecture.
L3-1
®
Lesson 3: Apache HBase Architecture
L3-2
®
Lesson 3: Apache HBase Architecture
Lesson 3 introduces HBase architectural components. We will see how these components
work together and how data is stored in HBase. We will also differentiate between HBase and
MapR-DB.
L3-3
®
Lesson 3: Apache HBase Architecture
L3-4
®
Lesson 3: Apache HBase Architecture
RegionServers serve data for reads and writes. When accessing data, clients communicate
with HBase RegionServers directly.
Region assignment and DDL operations, create and delete tables, are handled by the HBase
Master process.
Hadoop data nodes store the data that the RegionServer is managing. All HBase data is stored
in HDFS files. RegionServers are collocated with the HDFS data nodes, which enables data
locality for the data served by the RegionServers. Data locality means putting the data close to
where it is needed, to reduce the amount of time spent in data transfer.
The NameNode is for HDFS, The NameNode maintains metadata information for all the
physical data blocks that comprise the files.
L3-5
®
Lesson 3: Apache HBase Architecture
HBase tables are divided horizontally by row key range into regions. A region contains all rows
in the table between the start key and end key. Regions are assigned to specific nodes in the
cluster called RegionServers. RegionServers serve data for reads and writes. A RegionServer
can serve about 1,000 regions.
L3-6
®
Lesson 3: Apache HBase Architecture
Region assignment and DDL operations are handled by the HBase HMaster.
An HMaster is responsible for:
• Coordinating the RegionServers:
• assigning regions on startup
• re-assigning regions for recovery or load balancing
• monitoring all RegionServer instances in the cluster by listening for
notifications from ZooKeeper
• Admin functions:
• interface for creating, deleting, and updating tables
L3-7
®
Lesson 3: Apache HBase Architecture
HBase uses ZooKeeper as a distributed coordination service to maintain server state in the
cluster. ZooKeeper maintains which servers are alive and available, and provides server failure
notification. Zookeeper uses consensus to guarantee common shared state. Note that there
should be three or five machines for consensus.
L3-8
®
Lesson 3: Apache HBase Architecture
L3-9
®
Lesson 3: Apache HBase Architecture
L3-10
®
Lesson 3: Apache HBase Architecture
1 – C; 2-A, 3-B
L3-11
®
Lesson 3: Apache HBase Architecture
L3-12
®
Lesson 3: Apache HBase Architecture
Now that we have taken a look at the main architectural components, let us see how they work
together.
L3-13
®
Lesson 3: Apache HBase Architecture
RegionServers and the active HMaster connect to ZooKeeper. The ZooKeeper maintains
ephemeral nodes for active sessions via heartbeats. Each RegionServer creates an ephemeral
node.
The HMaster monitors these nodes to discover available RegionServers, and for server
failures.
HMasters vie to create an ephemeral node. ZooKeeper determines the first one and uses it to
make sure that only one master is active.
The active HMaster sends heartbeats to ZooKeeper. The inactive HMaster listens for
notifications of the active HMaster failure.
If a region server or the active HMaster fails to send a heartbeat, the session is expired and the
corresponding ephemeral node is deleted.
Listeners for updates will be notified of the deleted nodes. The active HMaster listens for
RegionServers, and will recover RegionServers on failure. The inactive HMaster listens for
active HMaster failure and if an active HMaster fails, the inactive HMaster becomes active.
L3-14
®
Lesson 3: Apache HBase Architecture
There is a special HBase Catalog table called the Meta table which holds the location of the
regions in the cluster. ZooKeeper stores the location of the Meta table.
For future reads, the client uses the cache to retrieve the Meta location and previously read
row keys. Over time it does not need to query the Meta table, unless there is a miss because a
region has moved, then it will re-query ZooKeeper, and update the cache.
L3-15
®
Lesson 3: Apache HBase Architecture
This Meta table is an HBase table that keeps a list of all regions in the system. The Meta Table
is like a B-tree. The Key is the table name, the region start key, and the region ID. The Values
are the RegionServers. Given a key, the Meta table will define which RegionServer to go to.
L3-16
®
Lesson 3: Apache HBase Architecture
Startup Sequencing
When HBase starts up, regions are assigned. The HMaster looks at the existing region
assignments in the Meta table, and if needed the Meta table is updated with RegionServer
assignments.
1. the location of hbase:meta is looked up in ZooKeeper
2. hbase:meta is updated with server values
L3-17
®
Lesson 3: Apache HBase Architecture
L3-18
®
Lesson 3: Apache HBase Architecture
L3-19
®
Lesson 3: Apache HBase Architecture
L3-20
®
Lesson 3: Apache HBase Architecture
L3-21
®
Lesson 3: Apache HBase Architecture
L3-22
®
Lesson 3: Apache HBase Architecture
L3-23
®
Lesson 3: Apache HBase Architecture
In this section, we will take a look at regions, how they work and their benefits.
L3-24
®
Lesson 3: Apache HBase Architecture
L3-25
®
Lesson 3: Apache HBase Architecture
When the client issues an Put request the first step is to write the data to the write-ahead log.
Edits are appended to the end of the WAL file that is stored on disk. The WAL is used to
recover not-yet-persisted data in case a server crashes. WAL a file in HDFS, so is replicated 3
times across the cluster.
L3-26
®
Lesson 3: Apache HBase Architecture
Once the data is written to the WAL, it is placed in the MemStore. Then the put request
acknowledgement returns to the client.
L3-27
®
Lesson 3: Apache HBase Architecture
The Memstore stores updates in memory as sorted key-values, the same as it will be stored in
a HFile.
There is one Memstore per column family, and the updates are sorted per column family.
L3-28
®
Lesson 3: Apache HBase Architecture
When the Memstore accumulates enough data, the entire sorted set is written to a new HFile
in HDFS.
HBase uses multiple HFiles per column family, which contain the actual cells, or key-
value instances. These files are created over time, as key-value edits sorted in the Memstores
are flushed as files to disk.
Note this is one reason why there is a limit to the number of column families in HBase. There is
one Memstore per column family, when one is full they all flush. It also saves the last written
sequence number so the system knows what has persisted so far.
The highest sequence number is stored as a meta field in each HFile, to reflect where
persisting has ended and where to continue. On region startup, the sequence number is read
and the highest is used as the sequence number for new edits.
L3-29
®
Lesson 3: Apache HBase Architecture
When the Memstore accumulates enough data, the entire sorted Key-value set is written to a
new HFile in HDFS. This is a sequential write. It is very fast as it avoids moving the disk drive
head.
L3-30
®
Lesson 3: Apache HBase Architecture
An HFile contains a multi-layered index which allows HBase to seek to the data, without having
to read the whole file.
The Trailer points to the meta blocks, and is written at the end of persisting the data to the file.
The trailer also has information like bloom filters and time range info. Bloom filters help to skip
files that do not contain a certain row key. The time range info is useful for skipping the file if it
is not in the time range the read is looking for.
L3-31
®
Lesson 3: Apache HBase Architecture
The Index is loaded when the HFile is opened and kept in memory. This allows lookups to be
performed with a single disk seek.
L3-32
®
Lesson 3: Apache HBase Architecture
We have seen that the key-value cells corresponding to one row can be in multiple places.
Row cells already persisted are in HFiles, recently updated cells are in the Memstore, and
recently read cells are in the BlockCache. When you read a row, key-values are merged from
the BlockCache, Memstore, and HFiles in the following steps:
1. First, the scanner looks for the row cells in the BlockCache. Recently read key-values
are cached here, and the least recently used are evicted when memory is needed.
2. Next, the scanner looks in the Memstore, the write cache in memory containing the most
recent writes.
3. If the scanner does not find all of the row cells in the Memstore and Block Cache, then
HBase will use the BlockCache indexes and Bloom filters to load HFiles into memory,
which may contain the target row cells.
L3-33
®
Lesson 3: Apache HBase Architecture
Read amplification occurs when there are many HFiles written by Memstore flushes. This
causes multiple files to be examined for a read, which can affect the performance.
L3-34
®
Lesson 3: Apache HBase Architecture
HBase ensures consistency by having a single RegionServer responsible for all reads and
writes for a region of data. The region server’s WAL and files are stored on an HDFS node,
which replicates to secondary and tertiary HDFS nodes for availability in case of node failure.
HBase provides strong consistency, meaning that when a write returns, all readers will see the
same value.
HBase also has row atomicity using Multi Version Concurrency Control or (MVCC).
The high level flow of a MVCC write transaction in HBase is described here:
1. Lock the row before a write, to guard against concurrent writes
2. Retrieve the current write number
3. Update the WAL and Memstore, using the acquired write number to tag the key-values
4. Commit the transaction, then roll the read point forward to the write number
5. Unlock the row
L3-35
®
Lesson 3: Apache HBase Architecture
Reads do not lock a row, like a write does. Only those rows that are already committed are
read.
Here is the description of the high level flow of MVCC reads:
1 Open the scanner
2 Get the current read point
3 Filter all scanned key-values with Memstore timestamp > the read point
4 Close the scanner (this is initiated by the client)
L3-36
®
Lesson 3: Apache HBase Architecture
L3-37
®
Lesson 3: Apache HBase Architecture
L3-38
®
Lesson 3: Apache HBase Architecture
1-d
2-c
3-b
4-a
L3-39
®
Lesson 3: Apache HBase Architecture
L3-40
®
Lesson 3: Apache HBase Architecture
Sequence = C->B->D->A
L3-41
®
Lesson 3: Apache HBase Architecture
L3-42
®
Lesson 3: Apache HBase Architecture
L3-43
®
Lesson 3: Apache HBase Architecture
L3-44
®
Lesson 3: Apache HBase Architecture
Sequence = B->C->A
L3-45
®
Lesson 3: Apache HBase Architecture
L3-46
®
Lesson 3: Apache HBase Architecture
We have seen how the architectural components work together and also how read and writes
are done in HBase. In this next section, we will look at major and minor compactions.
L3-47
®
Lesson 3: Apache HBase Architecture
All files in HDFS are write once, and once written they are immutable. Therefore, each
Memstore flush to disk is written to a new HFile.
Flushing Memstores to disk causes more and more HFiles to be created. As data increases,
there may be many HFiles on HDFS. Multiple files may have to be examined for each new
read, which is not good for read performance. To improve read performance, HBase merges
and compacts HFiles into larger new consolidated files.
L3-48
®
Lesson 3: Apache HBase Architecture
HBase will automatically pick some smaller HFiles and rewrite them into fewer bigger HFiles.
This process is called minor compaction.
Minor compaction reduces the number of storage files by rewriting smaller files into fewer but
larger ones, performing a merge sort.
L3-49
®
Lesson 3: Apache HBase Architecture
To avoid reading too many files, there is a background thread that will detect when there are
too many files. The system performs compaction by reading some small files, merge sorting
them in memory, and writing the sorted key-values into a new, larger file. This involves
sequential reads and writes, with sorting done in memory to avoid disk seeks.
L3-50
®
Lesson 3: Apache HBase Architecture
Major compaction merges and rewrites all the HFiles in a region to one HFile per Column
family. During the process, deleted or expired cells are dropped. Major compaction improves
read performance.
Since major compaction rewrites all of the files, lots of disk I/O and network traffic might occur
during the process. This is called write amplification.
Major compactions can be scheduled to run automatically. Due to write amplification, major
compactions are usually scheduled for weekends or evenings.
A major compaction also makes any remote data files local to the region server, such as those
from a server failure or load balancing.
L3-51
®
Lesson 3: Apache HBase Architecture
The above table outlines the difference between minor and major compaction.
L3-52
®
Lesson 3: Apache HBase Architecture
L3-53
®
Lesson 3: Apache HBase Architecture
L3-54
®
Lesson 3: Apache HBase Architecture
L3-55
®
Lesson 3: Apache HBase Architecture
L3-56
®
Lesson 3: Apache HBase Architecture
In this section, we will take a look at how a RegionServer splits regions as a table grows.
L3-57
®
Lesson 3: Apache HBase Architecture
L3-58
®
Lesson 3: Apache HBase Architecture
L3-59
®
Lesson 3: Apache HBase Architecture
Initially there is one region per table. When a region grows too large, it splits into two child
regions. Both child regions, representing one-half of the original region, are opened in parallel
on the same RegionServer, then the split is reported to the HMaster.
For load balancing reasons the HMaster may schedule for new regions to be moved off to
other servers. The Meta table is updated, and the RegionServer updates ZooKeeper.
L3-60
®
Lesson 3: Apache HBase Architecture
Note that when a region splits, all the ColumnFamilies are split based on the row key range.
This is one reason for the recommended 3 ColumnFamily limit for HBase.
Also it is better to have a similar amount of data in different column families in a table, so that
the columns will be balanced for scanning across regions.
L3-61
®
Lesson 3: Apache HBase Architecture
Splitting happens initially on the same region server, but for load balancing reasons the
HMaster may schedule for new regions to be moved off to other servers. This results in the
new RegionServer serving data from a remote HDFS node until a major compaction moves the
data files to the RegionServer’s local node.
HBase data is local when it is written, but when a region is moved, for load balancing or
recovery, it is not local until the next major compaction.
L3-62
®
Lesson 3: Apache HBase Architecture
Based on the target number of regions per RegionServer, one can pre-split the table at
creation time. This is one way to ensure that the table starts out already distributed across
many servers, which helps with load balancing at the start of a new cluster. While this has its
advantages, pre-splitting requires careful planning. You need to consider how your table will
grow based on the row key.
L3-63
®
Lesson 3: Apache HBase Architecture
L3-64
®
Lesson 3: Apache HBase Architecture
L3-65
®
Lesson 3: Apache HBase Architecture
All writes and reads are to and from the primary node.
1. Data writes are first recorded in the WAL
2. The data itself is written to a Memstore.
3. When the Memstore is full, the data is persisted in the background to a disk in an HFile.
What happens if there is a failure when the data is still in memory and not persisted to an
HFile?
The resiliency to failures comes from HDFS. The HFile in HDFS is replicated by default 3
times. The WAL is also replicated 3 times.
L3-66
®
Lesson 3: Apache HBase Architecture
HFile block replication happens automatically. HBase relies on HDFS to provide the data
safety. When data is written in HDFS, one copy is written locally, then it is replicated to a
secondary node, and a third copy is written to tertiary node.
L3-67
®
Lesson 3: Apache HBase Architecture
Incoming data is first stored in a WAL, which is shared across all regions in a RegionServer.
There is 1 WAL per RegionServer.
As WALs grow, they are eventually closed and a new, active WAL file is created to accept
additional edits. This is called rolling the WAL file. By default, the WAL file is rolled when its
size is about 95% of the HDFS block size.
Edits in the WAL have a sequence number. When a Memstore flushes, the highest sequence
number is stored as a meta field in the HFile.
When a region is opened, the sequence number is read and used in the WAL to reflect where
persisting has ended, and where to continue.
Closed WALs with edits that have a sequence number less than the last Memstore flushed
sequence number are deleted.
HBase client never reads from WAL. The WAL is shared by all regions hosted by the same
RegionServer, which acts as a central logging backbone for every modification.
L3-68
®
Lesson 3: Apache HBase Architecture
The WAL file and the HFiles are persisted on disk and replicated. How does HBase recover
the Memstore updates not persisted to HFiles?
L3-69
®
Lesson 3: Apache HBase Architecture
WAL files contain a list of edits, with one edit representing a single put or delete. Edits are
written chronologically, so, for persistence, additions are appended to the end of the WAL file
that is stored on disk.
What happens if there is a failure when the data is still in memory and not persisted to an
HFile?
The WAL is replayed. Replaying a WAL is done by reading the WAL, then adding and sorting
the contained edits to the current Memstore. At the end of the replay, the Memstore is flushed
to write changes to an HFile.
L3-70
®
Lesson 3: Apache HBase Architecture
L3-71
®
Lesson 3: Apache HBase Architecture
HBase is not a fully ACID compliant database, but does guarantee certain ACID
characteristics.
Record-level puts are atomic for a single row, multi record/multi table puts are not.
To learn more about the specifics of HBase ACID compliance, refer to the HBase
documentation.
L3-72
®
Lesson 3: Apache HBase Architecture
L3-73
®
Lesson 3: Apache HBase Architecture
While Apache HBase has many advantages, it also has some limitations.
Recovering a RegionServer after a crash can take 30 minutes or more. RegionServer crash
can cause data to be unavailable during this time, while WALs are replayed for impacted
regions.
Compactions use a lot of resources, and disrupt HBase operations. The I/O bursts during
compaction can overwhelm nodes.
HBase does not have client throttling, and clients can easily overwhelm RegionServers
causing downtime.
Basic administration is complex. Major compactions and splitting often have to be done
manually, and coordinating separate distributed systems is very hard.
L3-74
®
Lesson 3: Apache HBase Architecture
L3-75
®
Lesson 3: Apache HBase Architecture
L3-76
®
Lesson 3: Apache HBase Architecture
L3-77
®
Lesson 3: Apache HBase Architecture
L3-78
®
Lesson 3: Apache HBase Architecture
L3-79
®
Lesson 3: Apache HBase Architecture
L3-80
®
Lesson 3: Apache HBase Architecture
We have looked at HBase architecture. We have seen the advantages and limitations of
HBase. This section describes the differences between HBase and MapR-DB.
L3-81
®
Lesson 3: Apache HBase Architecture
What is MapR-DB?
L3-82
®
Lesson 3: Apache HBase Architecture
HBase on top of HDFS is shown on the left. You see in the diagram there are many layers.
HDFS is separate from the underlying file system, and HBase is separate from HDFS. These
layers and separation, plus the limitations of a write once HDFS file system lead to the HBase
problems we discussed earlier.
MapR-FS maintains compatibility with the Hadoop APIs, as shown here, and HBase can run
on top of MapR-FS, shown here on the right. Compared to HDFS, the MapR Filesystem
implements the storage layer natively in C++, and accesses disks directly. This eliminates a
JVM and the ext3 filesystem layer. The benefits of accelerated disk performance and system
stability are multiplied across all nodes in the cluster.
MapR took the append-only architecture of HDFS, made it a fully read-write system, and gave
it a Network File System mount. MapR-FS also provides high availability for NameNode
functionality, the part of HDFS that acts like a file allocation table in your disk drive,
remembering where all the data is, and that was a single point of failure in Hadoop.
L3-83
®
Lesson 3: Apache HBase Architecture
The diagram shown here compares the application stacks for different HBase
implementations.
MapR-DB exposes the HBase API and the Data model, and stores data structured as a nested
sequence of key/value pairs. The value in one pair serves as the key for another pair. The
MapR-DB implementation integrates table storage into the MapR file system, eliminating all
JVM layers and interacting directly with disks for both file and table storage.
The MapR file system is written in C and optimized for performance. As a result, MapR-FS
runs significantly faster than JVM-based HBase. With MapR-FS, HDFS functionality is pushed
down into a distributed NFS file system, supporting all of the APIs of HDFS. With MapR-DB,
the file system can handle small chunks of data, and also small pieces of HBase tables.
L3-84
®
Lesson 3: Apache HBase Architecture
MapR-DB uses the same data model as HBase, and supports the core HBase API. This
means that you can port your application from HBase to Map-DB without re-compiling and you
are not locked in to a specific vendor.
Also you can use MapR-DB tables as the data store for Hadoop, Hive, and other Hadoop
ecosystem applications, just like you would use HBase. MapR preserves the standard Hadoop
and HBase APIs, so all ecosystem components continue to operate without modification.
Because MapR uses the open-standard HBase API, many legacy HBase applications can
continue to run on MapR without modification.
• The MapR table API works with the core HBase API
• MapR tables implement the HBase feature set
• You can use MapR tables as the data store for Hive applications
L3-85
®
Lesson 3: Apache HBase Architecture
You can run HBase and MapR-DB on a MapR cluster, you can use MapR-DB exclusively, or in a mixed
environment with HBase tables. A mixed environment is recommended when porting applications from HBase
to MapR-DB, and you can use the standard CopyTable tool to copy a table from HBase to MapR-DB.
Using MapR and Apache HBase Tables Together
MapR-DB table storage is independent from HBase table storage, enabling a single MapR cluster to run both
systems. Users typically run both systems concurrently, particularly during the migration phase. Alternately,
you can leave HBase running for existing applications, and use MapR-DB tables for new applications. You can
set up namespace mappings for your cluster to run both MapR tables and Apache HBase tables concurrently,
during migration or on an ongoing basis.
MapR's implementation of the HBase API differentiates between Apache HBase tables and MapR-DB tables,
based on the table name.
• By default, if a table name includes a forward slash, the name is assumed to be a path to a MapR-DB
table. Forward slash is not a valid character for HBase table names.
You can treat a MapR-DB table just as you would a file, specifying a path to a location in a directory. The table
appears in the same namespace as your regular files, without the need to coordinate with a database
administrator.
• MapR-DB has table mapping support, so to the user, the table names can just be names without forward
slash
• Namespace mappings can be setup to distinguish between HBase tables and MapR tables based on
path/table name
• During data migration or other specific scenarios where you need to refer to a MapR table of the same
name as an HBase table in the same cluster, you can map the table namespace to enable that operation
The [Link] property allows you to map HBase table names to MapR tables. This
property is set in the configuration file
/opt/mapr/hadoop/hadoop-<version>/conf/[Link].
In this example, any flat table name foo is treated as a MapR-DB table in the directory /tables_dir/foo.
<property>
<name>[Link]</name>
<value>*:/tables_dir</value>
</property>
L3-86
®
Lesson 3: Apache HBase Architecture
Let us take a look at the key differences in the MapR-DB architecture and what MapR-DB
brings.
Table Cells are stored in Files on MapR-FS. HBase has to work with HDFS. That means that
only large I/O operations are efficient. These limits are imposed by the HDFS architecture. The
result of these architectural limits is that HDFS has to share a commit log (WAL) across all
regions in a region server. MapR’s read/write file system supports efficient small I/O operations
which allows MapR-DB to have multiple micro-WALS per region.
L3-87
®
Lesson 3: Apache HBase Architecture
A container is the fundamental unit of storage within a MapR cluster, and is also the unit of
replication. A container is big, 16GB by default, and can range from 10-30 GB. MapR-FS files
are sharded into chunks that are written into a number of containers. The container contents
are then replicated across the cluster.
L3-88
®
Lesson 3: Apache HBase Architecture
With MapR-DB tables, like with HBase, continuous sequences of rows are divided into regions.
In MapR-DB however, the regions live inside a container. Because the tables are integrated
into the file system, MapR-DB can guarantee data locality. There are multiple regions per
container. Regions are 4GB~6 GB in size and containers are 10-30GB in size. Therefore, there
are about 4-5 regions per container.
L3-89
®
Lesson 3: Apache HBase Architecture
L3-90
®
Lesson 3: Apache HBase Architecture
One powerful feature of MapR is volume management, which allows you to logically segment
portions of your cluster into separate volumes. This helps with multi-tenant applications that
require a separation of data and users in a single cluster.
MapR-DB tables are a file in MapR-FS, and benefit from all built-in MapR-FS volume
capabilities, such as snapshots and mirroring.
HBase and HDFS are separate storage systems. You have limited data management options
on the HBase tables, and you also can't do snapshots, backups, or mirrors of files and tables
the way that you can with the MapR file system.
With MapR-DB, the unstructured data in the file system and the MapR-DB HBase tables of the
file system can be interwoven. The tables and their underlying data files can be grouped
together, snapshotted together, and backed up to mirrors together.
MapR-DB creates a unified data layer that makes the same snapshots, mirroring, namespace,
files, and tables accessible from the same management.
L3-91
®
Lesson 3: Apache HBase Architecture
L3-92
®
Lesson 3: Apache HBase Architecture
HBase gets rid of deleted data or expired data during major compactions.
With MapR-DB, deleted or expired data is purged automatically. This happens whenever the
data is updated.
The big difference is that MapR-DB does not need to do compaction. The merges are smaller
than HBase. The sorted read/write files on disk are 2-4MB, to allow for small purges.
L3-93
®
Lesson 3: Apache HBase Architecture
In MapR-FS, the split operation is automatic and fast, and supports pre-splitting a region.
While pre-splitting is not required, it can help with load-balancing.
L3-94
®
Lesson 3: Apache HBase Architecture
L3-95
®
Lesson 3: Apache HBase Architecture
L3-96
®
Lesson 3: Apache HBase Architecture
L3-97
®
Lesson 3: Apache HBase Architecture
L3-98
®
Lesson 3: Apache HBase Architecture
L3-99
®
Lesson 3: Apache HBase Architecture
Congratulations! You have now completed DEV 320 Lesson 3. You should now have an understanding
of the HBase data model and architecture.
L3-100
®
Lesson 4: Basic Schema Design
Welcome to DEV 325 – Apache HBase Schema Design, Lesson 4: Basic Schema Design.
L4-1
®
Lesson 4: Basic Schema Design
L4-2
®
Lesson 4: Basic Schema Design
In this lesson, we will look at schema design principles for HBase tables. These principles
apply equally to MapR-DB tables.
L4-3
®
Lesson 4: Basic Schema Design
In this first section, we will take a look at the elements of schema design.
L4-4
®
Lesson 4: Basic Schema Design
Schema design matters for the same reasons that design always matters for everything: If you
use the most advanced materials in the world to construct an inefficient car design, you still get
an inefficient car. A properly designed schema can make all the difference in how your
application performs, and either improves or hinders all aspects of the application that rely on
the performance of the datastore.
The importance of design isn’t new. In relational databases, for example, things work better
when you understand your schemas and optimize queries. Similarly with HBase tables,
knowing how you will access data and what kinds of queries you plan to execute, will help get
the best performance.
We have a favorite anecdote at MapR where one of our data scientists worked with a
customer, and in a one-hour conversation about schema design, was able to improve access
performance by a factor of 1,000x. These concepts matter.
L4-5
®
Lesson 4: Basic Schema Design
HBase is sometimes called a schema-less database but this is a misnomer. HBase doesn't
require the same predefined structure as a relational database, but you do have to define the
facets of how you plan to organize your data.
Before we delve into designing schemas in HBase, let us do a quick review of the HBase Data
Model.
L4-6
®
Lesson 4: Basic Schema Design
L4-7
®
Lesson 4: Basic Schema Design
L4-8
®
Lesson 4: Basic Schema Design
• A table has one or more column families. This diagram shows a table with two column
families, namely Address and Order.
• Column Family is often referred to as "family" in the HBase API Javadoc.
• Data in column families are stored separately and can be accessed independently. You can
think of column families as separate tables that have records that use the same set of row
keys.
• In Apache HBase, you don't want to go over three or so column families or performance
suffers dramatically. With MapR-DB, you can use up to 64 column families with no
performance penalty.
• A column family can have any number of associated columns – it could be two or 1000.
• Column families are typically defined when you create a table, whereas columns may be
added and named dynamically.
L4-9
®
Lesson 4: Basic Schema Design
The top graphic shows the logical layout of the data in table format, the lower graphic shows
how the data is actually physically stored in files.
Physically column families are stored in separate files, rows are stored as sorted sets of key-
value cells. The entire cell coordinates, the row key, column family name, column name, and
timestamp are stored for every cell which has a value.
L4-10
®
Lesson 4: Basic Schema Design
HBase tables are sparsely populated. If data doesn’t exist at a column, it’s not stored.
Table cells are versioned uninterpreted arrays of bytes. The version is by default a timestamp
that is a long. You can use the timestamp or set up your own versioning system. So for every
coordinate row:family:column, there can be multiple versions of the value.
Finally, recall that cells can have properties like time-to-live or max-versions, which can cause
the values in certain cells to be purged automatically at some future point.
L4-11
®
Lesson 4: Basic Schema Design
• The physical layout in storage is different than the logical representation of a table.
• Rowkey:ColFamily maps to a sub-map of Columns.
Rowkey:ColFamily:Column maps to a sub-sub-map of versions.
• All values are stored with the full coordinates:
Table:Row:ColFamily:Column:TimestampèValue
• Every cell version is a separate entry in the table, represented as a "row" in this slide.
• Physically data is stored by column family as a sorted map of nested maps.
L4-12
®
Lesson 4: Basic Schema Design
The Cell coordinates or Key consists of the row key, column family name, column name, and
timestamp.
When retrieving data, we can specify more or less information about the coordinates for the
values we want to get back. In broad terms, we can think of the granularity of the query getting
finer as we move to the right, in other words, as we provide more criteria for the cell
coordinates. We could require an exact match for certain coordinates, or we can specify
columns, timestamps or use filters to retrieve all data that matches certain patterns.
• Specifying row key and column family limits disk I/O, since these elements allow the server
to skip over entire chunks of data in files on disk that don't apply.
• Specifying column name and timestamp limits network traffic. The server still has to spin
the disks to find the data, but will strip off the parts you're not interested in before sending it
over the network.
In other words, smart row key design is going to improve efficiency in scanning just the right
rows, and specifying to get a subset of columns cells will reduce network traffic to return your
results.
In the next slide, we will see how the key-value works when retrieving data.
L4-13
®
Lesson 4: Basic Schema Design
As you provide more criteria for the coordinates, you get less data returned.
You can limit the amount of data you read off the disk or transfer over the network. Specifying
the row key lets you get just the exact row you need. Specifying the column family lets you
specify what part of the row to read, sparing reading multiple HFiles if the row spans multiple
families. Specifying the column name lets you save on the number of columns returned to the
client, thereby saving on network I/O.
L4-14
®
Lesson 4: Basic Schema Design
As a reminder, the image in the bottom shows how the data is stored. Since only the row key is
indexed in HBase tables, smart row key design is going to improve efficiency in finding the
right rows for scans.
Specifying the column family limits disk I/O by helping to find the file where the requested data
is stored. If you do a get just providing the row key smithj it will read from two files to get all the
columns in CF. As you provide more criteria for the coordinates, you get less data returned.
L4-15
®
Lesson 4: Basic Schema Design
This table is another way to visualize how query parameters impact system behavior. A yellow
check indicates a relationship.
You see that specifying a column qualifier will limit the network I/O only, but it doesn't do
anything to limit the rows scanned or limit disk I/O. We also see that specifying a column
qualifier has no impact on limiting HFile access since the HFile has to be read in order to
examine the column qualifier.
The timestamp can help you find the file and add more granularity, thereby limiting the disk I/O.
L4-16
®
Lesson 4: Basic Schema Design
Now that we have reviewed HBase data model concepts, let us look at the elements of
schema design.
When we say ”schema design,” these are the design choices we're referring to:
L4-17
®
Lesson 4: Basic Schema Design
Schema design matters because proper design directly affects the bottom line. In HBase, we
model for the questions. The schema should be designed based on:
L4-18
®
Lesson 4: Basic Schema Design
Remember that HBase has been used in three major types of use cases:
2. Information exchange
These cases represent high volume and velocity writes and reads.
3. Content erving
These cases use high volume and velocity reads.
The best schema design for your particular application will depend on your data access
patterns. As with any database design, you have to know how data will arrive and be
accessed. Let us now take a look at an example use case.
L4-19
®
Lesson 4: Basic Schema Design
We are going to look at a concrete example to explore the impact of schema design choices.
Let's contemplate a system that records all trade events on a stock exchange.
In a day, there could be millions of trade events, which we'll represent with a trade object. A
minimal trade object would comprise the details shown here.
L4-20
®
Lesson 4: Basic Schema Design
We will go into more detail in later slides, but to get the discussion going, let's pose the
following questions about our stock trades example:
• How will data be retrieved? By date? By company? è (*) These factors drive at row key
design. If we want to scan based on company name, then we will want to build the company
name into the row key somehow.
• Are Price and Volume data typically accessed together, or are they unrelated? è (*) These
factors drive at column family design. You want to keep together in the same column family
those data that are accessed together.
• Which trade data needs fastest access (or most frequent)? è (*) These factors drive at
row key ordering and possibly design of the timestamp. Scan order of row keys is from
lowest-to-highest, so you might choose to insert the MOST RECENT items with the
LOWEST row key.
• Do all trades need to be saved forever? è (*) This factor drives at purging past cell version
by setting Time-To-Live (TTL) or MaxVersions on a column family.
• What are the needs for atomicity of transactions? è (*) This factor drives at column design.
Atomicity is only guaranteed when putting to one row key, so if you need atomic updates to
some elements in a table, you need to keep all elements on the same row.
L4-21
®
Lesson 4: Basic Schema Design
L4-22
®
Lesson 4: Basic Schema Design
Answer: 2
L4-23
®
Lesson 4: Basic Schema Design
Answers: A - 3, B - 1, C - 2
L4-24
®
Lesson 4: Basic Schema Design
L4-25
®
Lesson 4: Basic Schema Design
L4-26
®
Lesson 4: Basic Schema Design
Row keys are the primary index for a table. There are no secondary indexes out of the box
with HBase. That means you HAVE to use the row key or a column name to get to data, thus
what goes into a row key is important.
Also note that the only way to change row keys once data is written to the table is to delete and
then re-insert data. This could be expensive!
Remember that row keys are sorted when inserted in lexical order, so take this into
consideration when designing your row keys. If you need to keep a more natural order and if
your keys contain digits, then consider padding the keys with zeroes to the left.
L4-27
®
Lesson 4: Basic Schema Design
HBase tables typically use a composite key design, which means that multiple data elements
are included in the row key. We use that composite row key to bound scan ranges and provide
sub-indexing to cell data.
You can include multiple data elements in the row key. For example, date or timestamp, data
source, user, or other metadata can be part of the row key. In extreme cases, the row key
might contain a lot of metadata about a value.
Note that Get operations require you to provide a precise row key. Thus for Gets, the
application logic has to be able to construct a complete row key. Scans, on the other hand, can
use partial row keys to capture all row keys starting with a certain value.
L4-28
®
Lesson 4: Basic Schema Design
Thinking about access patterns ahead of time is probably the single most important
consideration. Designing your schema around how the data will be accessed, both reads and
writes, will directly impact performance.
An important question to ask is how data will be retrieved. Referring to our example with
trades, how we want to retrieve data will impact the row key design. Do we want to retrieve by
date, by hour, or companyId? If yes then we should include these in the row key.
What happens if we use the time stamp in the row key and it is leftmost in the row key?
We know that row keys are sorted in lexical order – so in this case it means our row keys are
sequential by time stamp.
Let us see next what happens when we have sequential row keys written in order.
L4-29
®
Lesson 4: Basic Schema Design
When we have sequential row keys, data is written to the table sequentially. Here we see data
being written to the table in region 1 in region server 1 which is on file server 1. We know that
when a region is full, it splits.
L4-30
®
Lesson 4: Basic Schema Design
Since data is written in sorted order, if the keys only increase with time, then every key is
written to the end of a table. The end of the table will be contained in a single region, served by
a single file server. As you see here, the data is being added to region 1. When it is full, it splits
into two with data from region 1 being divided equally between region 1 and region 2.
L4-31
®
Lesson 4: Basic Schema Design
Since data is written in sorted order, if the keys only increase with time, every key is written to
the end of a table. The end of the table will be contained in a single region, served by a single
file server.
L4-32
®
Lesson 4: Basic Schema Design
The table in region 2 gets larger and eventually region 2 will split.
L4-33
®
Lesson 4: Basic Schema Design
Region 2 then splits into two with all the data from region 2 being divided equally between
region 2 and region 3.
Since records in HBase tables are stored in lexicographical order, using a sequential
generation method for row keys can lead to a hot spot problem, where all Puts will "hot spot" to
the one "hot" server, and no other servers will ever take Puts. In addition to concentrating
activity on a single region, all the other splits remain at half their maximum size. This destroys
the advantages of using a distributed architecture.
Note that with MapR-DB, the cluster handles sequential keys and table splits to keep potential
hot spots moving across nodes, decreasing the intensity and performance impact of the hot
spot.
L4-34
®
Lesson 4: Basic Schema Design
Hot spotting is an issue that occurs when row keys are written in monotonically increasing (or
decreasing) order. Since data is written in sorted order, if the keys only increase with time, it
means that every key is written to the end of a table. The end of the table will be contained in a
single region, served by a single fileserver. Therefore, all Puts will "hot spot" to the one "hot"
server, and no other servers will ever take Puts.
Hot spotting also results in inefficient region splitting, because after a table splits when the
table grows too large for a single region, the first half of the table never gets more data; it
remains forever at half the maximum potential size for a container.
Hot spotting isn't an issue for all applications. For example, if row key leads with a last name
and names arrive in random order, then row keys will naturally spread over A-Z. On the other
hand, writing time-series data has a high potential to hot spot, if the timestamp is used at the
head of the row key.
There are ways to prevent hot spotting, which we will look at next.
L4-35
®
Lesson 4: Basic Schema Design
Random writes will go to different regions, if the table was pre-split or big enough to have split.
Hashing a sequential row key will create a random key which will distribute the writes, however
random row keys are not good for scanning.
The java code here gets an instance of the MessageDigest class with the MD5 algorithm.
This class provides hash algorithm function which takes a byte array and outputs a fixed-length
hash value.
d = [Link]("MD5");
Here the digest method is called and it returns a fixed length random hash of the input:
L4-36
®
Lesson 4: Basic Schema Design
This diagram shows that random row keys are better for writing, but sequential row keys make
it easier and faster to scan ranges of data.
A way to prevent hot spotting is to "spread" or "spray" the row keys over multiple regions for
consecutive writes. Hashing the row key distributes the row keys over regions which improves
write performance but this makes scanning continuous ranges of data impossible based solely
on the row key.
Solutions in between exist, shown in the middle of this diagram, which we will look at next.
L4-37
®
Lesson 4: Basic Schema Design
To guarantee a spread or region row ranges across all region servers, you can prefix the row
key with a salt. You can generate a random salt number by taking the hash code of the
timestamp and taking its modulus with some multiple of the number of region servers:
This involves taking the salt number and putting it in front of the timestamp to generate your
timestamp:
byte[] rowkey = [Link]([Link](salt) \
+ [Link](“_") + [Link](timestamp));
This will distribute will distribute regions based on the first part of the key, which is the random
salt number. Data for consecutive timestamps is distributed across multiple regions. The
disadvantage to this is reads now involve distributing the scans to all the regions and finding
the relevant rows. A better solution is to prefix with a field key or a hashed field key which we
will look at next.
This is example code for salting from HBase the definitive guide:
byte salt = (byte) ([Link](timestamp) % <number of region
servers>);
byte[] rowkey = [Link](salt), timestamp);
L4-38
®
Lesson 4: Basic Schema Design
A better solution than hashing the whole key or a random salt is to prefix the row key with a
shortened hashed field key. You can create a prefix hash based on the original key value so
that when you want to get or scan you can calculate the hash, as opposed to searching each
salted region.
L4-39
®
Lesson 4: Basic Schema Design
You can “prefix” or promote an identifying or searchable value to the front of the row key in
order to spread rows across region servers. This makes it easy to scan by that value. For
example in the stock application, if we want to scan by company name then we can put the
company name in front of the timestamp, this will distribute the key ranges by company name
and you can scan by date after the company name.
L4-40
®
Lesson 4: Basic Schema Design
This table shows the three solutions to hot spotting that we discussed.
The solution for hot spotting is to "spread" or "spray" the row keys over multiple regions for
consecutive writes. As with any design decision, there are trade-offs involved, and choosing
any one of these methods adds complexity to your application.
For example, scanning continuous ranges of data becomes impossible if you hash the
complete row key. Partial solutions exist, such as prefixing, which sprays the data over
regions, but within each region data is sorted and can be scanned in order.
L4-41
®
Lesson 4: Basic Schema Design
Let’s go back to our stock trades example. Thinking again about access patterns, we ask the
following questions:
• Which trade data needs fastest access (or most frequent)? è (*) These factors drive at
row key ordering and possibly design of the timestamp.
• What if you want to retrieve the stocks by symbol and date?
• Then you would compose the key with the stock symbol on the left followed by
the timestamp: symbol_timestamp which is shown in the table.
• Note that the timestamp is increasing over time so that the oldest (by symbol)
are ordered at the top.
• What if you usually want to retrieve the most recent? Then you would want the youngest at
the top instead of the oldest.
L4-42
®
Lesson 4: Basic Schema Design
Recall that row keys are sorted in increasing order. So, if you want to retrieve the most recent,
you would want the youngest at the top instead of the oldest. Since the scan order of row keys
is from lowest-to-highest, you might choose to insert the MOST RECENT items with the
LOWEST row key.
Here's a tip for improving access time to the most recently-written data, if last-in-first-out is
your predominant access pattern.
Design a composite row key that appends the reverse timestamp to the end of the key. In this
example, we append the reverse timestamp to the right of the stock symbol. When you scan
by row key, you will get the most recent trade first for each stock symbol.
Just as an FYI: Long.max_value is a constant that holds the maximum value a long can
have – i.e. 2^63 -1.
L4-43
®
Lesson 4: Basic Schema Design
Since rows are stored in sorted order, you can affect the results of the sort by changing the
ordering of the fields that make up the composite row key. When designing a composite key,
consider how the data will be queried during production use. Place the fields that will be
queried the most often towards the front of the composite key.
L4-44
®
Lesson 4: Basic Schema Design
L4-45
®
Lesson 4: Basic Schema Design
Answer: D
L4-46
®
Lesson 4: Basic Schema Design
Answers: 1 and 2
L4-47
®
Lesson 4: Basic Schema Design
Answers: 1, 2, and 4.
L4-48
®
Lesson 4: Basic Schema Design
Answer: 4
L4-49
®
Lesson 4: Basic Schema Design
L4-50
®
Lesson 4: Basic Schema Design
L4-51
®
Lesson 4: Basic Schema Design
In this section, we will take a look at the HBase table design, including tall vs. wide tables. We
will also see how row key design and column family definition play a part in the table shape.
L4-52
®
Lesson 4: Basic Schema Design
Design of the row key impacts the shape of HBase tables. A table may grow "tall-narrow,"
where most new entries get a unique row key. Tall tables typically use a composite row key,
storing multiple pieces of data in the row key itself.
By contrast, HBase tables can also grow "flat-wide," where many data elements share the
same row key and store data across many columns.
Note that growing horizontally is not possible in a typical relational database, because columns
are predefined. A relational table with many columns and few rows might look flat, but it can
only grow "taller" as data is added.
L4-53
®
Lesson 4: Basic Schema Design
Consider again the access patterns for our stock trades example.
Are Price and Volume data typically accessed together, or are they unrelated? These factors
drive at column family design. You want to keep together in the same column family those data
that are accessed together.
L4-54
®
Lesson 4: Basic Schema Design
Here is a tall-narrow implementation as a possible solution for our stock trades example. To give us
some direction, let's say that we want to be able to pick a company and read back all trades for a span of
time.
• We use a composite row key combining the stock ticker symbol and a reverse timestamp.
• We have only one column family, called CF1, and we use exactly two columns: Price and Vol.
• We don't have to include columns for the trade time or the stock symbol, because this data is
embedded in the row key.
• Every trade is stored with a new row key, so a single Get operation returns data for a single trade.
• It's clear that this table will grow tall as new trades come in. And because the timestamp in the row
key is reversed new trades for a particular company get inserted higher in the list. Scans will retrieve
the most recent data first.
• Does this design hot spot? No. For a given company, say Amazon AMZN, row keys arrive in
decreasing order, so it looks like hot spotting might occur. However, trades for all companies A-to-Z
are happening concurrently in random order. These timestamps are distributed by the company
symbol. So, as shown here, two trades arrive at the same millisecond, but they're not necessarily
hitting the same region server.
In our stock trades example, in a tall configuration, new stock trades will each get a new row key and
grow the table taller. In this tall schema, every row represents one trade. There is only one column
family, with two columns to store Price and Volume values. The composite row key is formed by
combining the stock symbol and a reversed timestamp, (Long.MAX_VALUE - timestamp). For
example: AMZN_98618600888. Because the row key contains timestamp data, the trade time is not
stored anywhere else in the table.
L4-55
®
Lesson 4: Basic Schema Design
Consider the access patterns for our stock trades example. If Price and Volume are not
typically accessed together then we should put them in separate column families.
We are now also going to consider dynamic column names. We will look at the resulting table
shape if we use dynamic columns.
L4-56
®
Lesson 4: Basic Schema Design
In this flat medium wide schema, all trades for an hour are stored in a single row. The row key
is a composite of the company symbol and the date and hour, formatted YYYYMMDDHH. For
example: AMZN_20131020. The column name or qualifier is the seconds since the hour and
is dynamic. Price and Volume values are stored in separate column families. Each column cell
represents one trade.
We are essentially segregating time into buckets. Time is rounded to the hour in the row key.
One row stores a bucket of measurements for an hour.
L4-57
®
Lesson 4: Basic Schema Design
Thinking about the access patterns for a third example schema for our stock trades example,
how many versions do we wish to keep?
L4-58
®
Lesson 4: Basic Schema Design
We'll use the same assumption that we want to be able to pick a company and read back all trades for a
span of time. In this flat wide schema, all trades for each day are stored in a single row.
In this design:
• This time we use a composite row key formed from the stock symbol and the date of the trade.
• We use separate column families for price and volume. This means that we could easily scan for just
price, without having to waste disk cycles reading volume data.
• The stock symbol is embedded in the row key, so we don't have a column for it.
• We want to be able to scan a company for a particular time range. This implementation segregates
time into buckets:
• The date of a trade is stored in the row key. This means that a single Get operation on a row
reads back a whole day's worth of trades.
• We store the hour of the trade as the column name, from 00 to 23. This allows us to return a
specific hour of trades by specifying a column.
• As the hours of the day go by, you can see that the row will grow wider.
• In this example, the column families Price and Vol are set to store Max Version. Every
version of a cell represents one trade, and the cell version (a long) stores the timestamp of
the trade in milliseconds. Within a specific hour, as trades are written, we write more cell
versions into the hour column.
• Does this design hot spot? No. For the same reasons as the tall-narrow example, prefixing the row
key with the company name prevents hot spotting.
Since each row represents a single day's worth of trades, we might also want to store some useful
information about the day right there on the same row. For example, we could create a column family for
daily statistics and keep track of the daily high and low price for a company, or the total trade volume.
What are limitations of this implementation?
• Like our previous example, this model cannot represent two trades in the same millisecond for one
company.
• And here too, it would be inefficient to scan for trades for all companies within a time range.
• In this implementation, because price and volume are stored in separate column families, the lists of
versions become separate maps. The version timestamp acts as an index to correlate each price to a
volume. This introduces challenges in the code to manage the values as a unit.
L4-59
®
Lesson 4: Basic Schema Design
In a tall-narrow table we achieve better query granularity since the row key is rich and we can
query by row key.
Put operations at a row level are atomic and you leverage that in a flat-wide design. So you
give up atomicity with a tall table to gain performance benefits.
Accessing wider rows is more expensive than accessing smaller ones, because the row key is
the dominating component of indexes. Knowing the row key is what gives you all the benefits
of how HBase indexes under the hood, but you give up atomicity in order to gain performance
benefits that come with a tall table.
MapR tables split at the row level, not the column level. For this reason, extremely wide tables
with very large numbers of columns can sometimes reach the recommended size for a table
split at a comparatively small number of rows. In general, design your schema to prioritize
more rows and fewer columns.
L4-60
®
Lesson 4: Basic Schema Design
L4-61
®
Lesson 4: Basic Schema Design
Answers: 1 and 3
L4-62
®
Lesson 4: Basic Schema Design
Answers: 2 and 4
L4-63
®
Lesson X: Lesson Name
L4-64
®
Lesson 4: Basic Schema Design
L4-65
®
Lesson 4: Basic Schema Design
In this section, we will take a look at the properties that we can define at the column family
level.
L4-66
®
Lesson 4: Basic Schema Design
• Are Price and Volume data typically accessed together, or are they unrelated? è (*) These
factors drive at column family design. You want to keep together in the same column family
those data that are accessed together.
• Do all trades need to be saved forever? è (*) This factor drives at purging past cell
versions by setting Time-To-Live (TTL) on a column family.
L4-67
®
Lesson 4: Basic Schema Design
As stated before, data that is accessed together should be kept together in one column family.
Here is an additional guideline to follow: data that compresses well together should be in the
same column family. Since text-only cell data compresses well together, place those columns
in the same column family. On the other hand, mixed binary and text do not compress well
together, so don’t place these columns in the same column family.
L4-68
®
Lesson 4: Basic Schema Design
Continuing with our analysis of the stock trades example, here are some questions regarding
our data access patterns.
• Do all trades need to be saved forever? This factor drives at purging past cell version by
setting Time-To-Live (TTL) or MaxVersions on a column family.
• How many version do we want to keep? The default value for max versions used to be
three, but is now one.
L4-69
®
Lesson 4: Basic Schema Design
Here are properties you can enable at the column family level that affect behavior of a table.
These properties provide tuning mechanisms you should consider when you design your
system.
• Compression - Compression is usually turned on when creating tables and is a good choice
most of the time. You can specify the compression algorithm you want for each column
family at table creation time. MapR supports LZ4, LZF, and ZLIB. If you specify LZO or
SNAPPY (which are valid choices in the Apache HBase API), MapR will use LZ4 under the
hood.
• TTL – Not every stored value needs to live forever. You can specify a Time-To-Live
attribute so that data older than the TTL will be deleted.
• Versioning - How many versions for each cell you keep will affect queries and how much
data is read from disk. The default used to be three versions for each cell, now the default is
one. If you don't need to keep old values, leave the default to one version so that multiple
updates will not store multiple versions.
• You can also specify the minimum versions that are stored for a column family
using the MIN_VERSIONS parameter. This can be used together with the TTL
attribute. When all versions currently stored are older than the TTL, at least the
MIN_VERSIONS number of values will be retained.
• In-Memory – This setting is a suggestion that the server keep a column family's data in
memory for fast retrieval. This works well for column families with few columns, like a map
between username and password, or column families with sparse data. The in-memory
setting is not a guarantee that data will be kept in memory; it is merely elevated priority for
data to be kept in memory.
L4-70
®
Lesson 4: Basic Schema Design
In this lesson, we looked at the basics of schema design, its importance, design guidelines,
and in particular, row key design.
L4-71
®
Lesson 4: Basic Schema Design
L4-72
®
Lesson 4: Basic Schema Design
Answers: 2, 3 & 4
Column family name is also important, as it takes up space in the file storage and network.
L4-73
®
Lesson 4: Basic Schema Design
L4-74
®
Lesson 4: Basic Schema Design
We are now going to take a look at how OpenTSDB has designed their schema.
L4-75
®
Lesson 4: Basic Schema Design
OpenTSDB is an open source project. It is a Time Series Database (or TSDB) created to store,
index and serve metrics from computer systems, like network gear, operating systems, and
applications, at a large scale. It's useful for fine-grained, real-time monitoring. Existing systems
today handle tens of billions of data points per day. The data can be used for charts like this
one to show correlations in behavior between complex systems.
OpenTSDB provides a good example of row key design for time series data. It is also a good
study of row key design in general, because of clever composition of data elements into the
row key. You can read more online at [Link]/schema.
L4-76
®
Lesson 4: Basic Schema Design
A time series is a series of numeric data points of some particular metric over time. A
metric is any particular piece of data, like hits to an Apache hosted file, that you wish to
track over time. Each time series consists of a metric plus zero or more tags associated
with this metric. Tags are name value pairs which further identify a metric, for example,
host=hostname
Here is the use case situation: Need to record values of hundreds of metrics from
thousands of machines sampled every few seconds.
L4-77
®
Lesson 4: Basic Schema Design
This shows the some example data and the queries or questions we would like to retrieve from
the data. For example: What was the response time during peak periods? How many http
hits/hour? When was peak traffic or hits? And so on.
L4-78
®
Lesson 4: Basic Schema Design
To reiterate, you have to know the questions in order to design for performance, such as what
information are you going to retrieve?
L4-79
®
Lesson 4: Basic Schema Design
One of the first things we need to do is design the row key. Recall that using compound keys
will give us control over layout. We will take a look at a few row key design choices next.
L4-80
®
Lesson 4: Basic Schema Design
The row key format here is a composite key consisting of Time, Metric, and Tags in this order.
This means that the sample data will be written in sequential order. Could this lead to any
issues? Yes – remember that using sequential keys can cause hot spotting.
L4-81
®
Lesson 4: Basic Schema Design
In this design, the row key is again a composite key consisting of Tags, Metric, and Time in
that order. Tags are name value pairs which further identify a Metric, for example, host =
hostname. Samples for the same host will be ordered and grouped together.
This row key design would not have a hot spotting problem. However, queries commonly
require data for the same metric across different hosts, so having the data grouped by host is
not ideal. Let us take a look at a third option.
L4-82
®
Lesson 4: Basic Schema Design
This row key is composed of the Metric, Time, and Tags in that order. This means that all
samples for the same metric are grouped together. Queries commonly focus on data for the
same metric or a few metrics at a time, so having the data grouped by metric type would make
scans by metric type easier.
L4-83
®
Lesson 4: Basic Schema Design
The best choice in this case is number three, where the row key design groups the desired
data together by putting most queried information on the left most part of the key.
L4-84
®
Lesson 4: Basic Schema Design
This is how OpenTSDB has designed their row key. It is very similar to the third option that
we saw – MetricID (instead of Metric + Time + Tags). The difference is that OpenTSDB
shortened the Metric name and Tags to Ids in order to put a lot information in the row key
without taking up too much space.
This allows to scan by Metric, scan by Metric and Time, scan by Metric, Time, and Tags.
Also regular expressions can be used with a row key filter.
L4-85
®
Lesson 4: Basic Schema Design
Here is a tip for row key design: Add Tags, key-value pairs, to end of key for
additional information for Scans.
L4-86
®
Lesson 4: Basic Schema Design
Did OpenTSDB pick tall and skinny OR wide and flat? This is determined by the row key
design as well as how column families and columns are defined.
In the tall table example on the left the timestamp in the row key is to the millisecond, there is
one row per cell value. The tall table will grow taller because each value inserts a new row.
In the medium wide table example on the right the timestamp in the row key is rounded off to
the hour, which means one row will bucket an hour of metric values. The dynamic column
name is the offset in seconds from the hour in the row key.
In the wide table, the scan has to filter fewer rows, the tall table would have too much filtering
overhead. OpenTSDB chose the wide solution on the right.
Note that schemas can be very flexible and can even change on the fly.
L4-87
®
Lesson 4: Basic Schema Design
In order to compress the amount of data stored for each data point, a OpenTSDB creates
lookup table which maps metric and tag names to Unique IDs.
The Unique IDs are of a fixed 3-byte width and are used to lookup the name associated with
the IDs used in the row key.
Next, we will show the OpenTSDB key and columns in a little more detail.
L4-88
®
Lesson 4: Basic Schema Design
As mentioned earlier, a lot of data is stored in the row key. The timestamp in row key is
rounded down to the hour. There is one column family t. The column name is the seconds
since the timestamp in the key. One row stores a bucket of measurements for the hour.
OpenTSDB provides maps for Metric ID and Tag IDs, to compress the amount of data stored
in the row key. This makes row keys less readable, but also makes them much smaller for
data that repeats often.
L4-89
®
Lesson 4: Basic Schema Design
The middle path between tall vs. wide is packing data that would be a separate in rows into
columns, for certain rows. OpenTSDB is the best example of this case where a single row
represents an hour time-range and discrete events in that time range are stored as columns.
This has the advantage of being I/O efficient.
L4-90
®
Lesson 4: Basic Schema Design
Let's look at an example data point to get a clearer picture of how this works.
The diagram shows which elements of the data point get embedded into the row key, the
column name, and the cell value itself. Notice that for the tag data, this schema effectively
nests a data structure inside the row key. This nesting of structures is a common design
pattern for HBase. The row key, column names, and values are all variable-length byte arrays,
and can contain whatever data you want, as long as your application can to interpret the
values.
In the example shown, the full data point timestamp is 1292148125, and the row key will store
the base value 1292148000. The column name will provide the offset 125.
Storing multiple observations per row lets filtered scans disqualify more data in a single
exclusion. It also drastically reduces the overall number of rows that must be tracked by the
Bloom Filter on row key.
As an exercise, can you think of a way to nest the tag metadata in the column name, instead of
the row key?
L4-91
®
Lesson 4: Basic Schema Design
In this lesson, we looked at the basics of schema design, its importance, design guidelines and
in particular, row key design. We concluded this lesson by taking a look at a real-world
example. OpenTSDB provides a good example of composite row keys using IDs.
L4-92
®
Lesson 4: Basic Schema Design
L4-93
®
Lesson 4: Basic Schema Design
Answers: 3 and 4
L4-94
®
Lesson 4: Basic Schema Design
Congratulations. You have completed Lesson 4. In the next lesson, we will design schemas for
complex data structures.
L4-95
®
Lesson 5: Design Schemas for Complex Data
Welcome to DEV 325, Apache HBase Schema Design, Lesson 5: Design Schemas for
Complex Data Structures.
L5-1
®
Lesson 5: Design Schemas for Complex Data
L5-2
®
Lesson 5: Design Schemas for Complex Data
In the previous lesson, you have seen how to define a schema to fit the HBase data model. In
this lesson, we will look at transitioning from a relational model to HBase. We are also going to
look at designing for nested entities, using secondary indexes, and designing for other complex
data structures.
We are going to go into more detail about schema design, and using some examples, we will
compare schema design for relational databases to schema design for HBase.
L5-3
®
Lesson 5: Design Schemas for Complex Data
In this section, we will compare the relational model to the HBase model and describe ways to
transition from the relational model to HBase.
L5-4
®
Lesson 5: Design Schemas for Complex Data
There can be situations where it would make sense to move a database defined for RDBMS to
HBase. The following principles of denormalization, duplication, and using intelligence keys are
commonly used when designing schemas for moving a relational model to HBase. These
principles are discussed here.
L5-5
®
Lesson 5: Design Schemas for Complex Data
Designing an HBase schema is different than designing a relational schema. There is no one-
to-one mapping from relational databases to HBase.
In relational design the focus and effort is around describing the entity and its interaction with
other entities. The queries and indexes are designed later. Recall that HBase is designed such
that distributed data is accessed together, for clustering.
You should design your HBase schema to take advantage of the strengths of HBase. Think
about your access patterns and design so that the data that is read together is stored together.
L5-6
®
Lesson 5: Design Schemas for Complex Data
• You don’t have to update multiple copies when an update happens, which makes writes
faster.
• You reduce the storage size by having a single copy instead of multiple copies.
However this causes joins. Since data has to be retrieved from more tables, queries can take
more time to complete. In this example we have an order table which has a one-to-many
relationship with an order items table. The order items table has a foreign key with the ID of the
corresponding order.
L5-7
®
Lesson 5: Design Schemas for Complex Data
Data density has gone up by a factor of one million since the start of relational database
management systems.
In support of denormalizing data, think back to the 1970’s when the push toward normalization
began. Storage was expensive and access was slow.
These economics don't apply anymore. Storage is cheap and HBase provides sparse tables
that consume storage for cells only when you write to them.
L5-8
®
Lesson 5: Design Schemas for Complex Data
In a denormalized datastore, you store in one table what would be multiple indexes in a
relational world. Denormalization can be thought of as a replacement for JOINs.
Often with HBase you denormalize or duplicate data so that data is accessed and stored
together. In this example, the order and related line items are stored together and can be read
together with a get on the row key. This makes the reads a lot faster than joining tables
together.
L5-9
®
Lesson 5: Design Schemas for Complex Data
For big data to scale, you design schemas differently than for a relation database. A general
principle is to denormalize schemas by duplicating data.
Why do we do this? We want to achieve one read per request if possible. To achieve this,
group all data that is needed to process a query in one place. This often means that for
different query flows the same data will be accessed in different combinations. Hence, we
need to duplicate data which provides the ability to easily retrieve data for multiple querying
patterns. This improves read performance. However, when we duplicate the data, it increases
total data volume and update performance decreases.
• Materialization of views.
• To store data about an entity/entities related to it in the same table allowing for retrieval of
data in one read operation, with no joins.
L5-10
®
Lesson 5: Design Schemas for Complex Data
With HBase, the support for sparse, wide tables and column-oriented design often eliminates
the need to normalize data to save space. Therefore JOIN operations are not needed to
aggregate the data at query time. Furthermore, HBase was designed for horizontal scaling and
provides no automatic JOIN capability. Thus designing your schema properly enables you to
scale your application.
Use denormalization and duplicate data in tables that you would typically have to join in a
relational database. Group related data so that it can be read together.
However, denormalization requires design consideration. As with all design decisions, there
are trade-offs. If you have the same data replicated in multiple tables, it now becomes the
responsibility of your application to maintain consistency or handle inconsistency gracefully.
L5-11
®
Lesson 5: Design Schemas for Complex Data
As a modeling example we will use a social app. Here are the use cases:
L5-12
®
Lesson 5: Design Schemas for Complex Data
L5-13
®
Lesson 5: Design Schemas for Complex Data
1. Entities—map to tables.
2. Attributes—map to columns.
3. Relationships—map to foreign-key relationships.
L5-14
®
Lesson 5: Design Schemas for Complex Data
• The posted URL is stored in the Post table with a foreign key to the user that posted it,
and a foreign key to the category for the post (Users can subscribe to another user’s
posts and/or subscribe to categories, so you need to be able to get the list of URLs for a
category and for a User. This links post tables to the User and Category tables with a
foreign key relationship).
• Comments about a post are stored in the comments table with a foreign key to the post
and a foreign key to the user that commented.
L5-15
®
Lesson 5: Design Schemas for Complex Data
As we learned in the previous lesson, design your schema for the questions. In HBase design,
the focus is around how the application will retrieve the data. Also think about how the data is
read!
Next we will look at the HBase tables for our sample app.
L5-16
®
Lesson 5: Design Schemas for Complex Data
There are various approaches to converting entity relationships to fit the underlying
architecture of HBase. You could implement this example in different ways. This is how the
schema could be represented in HBase:
Before we look at nested entities, we are going to compare normalized and denormalized
schemas.
L5-17
®
Lesson 5: Design Schemas for Complex Data
What should you do with Entity attributes? Identify attributes mapping to the row key.
Other non-identifying attributes include password, user name, URL title, for example, anything
that’s not the unique or primary key. These non-unique attributes usually map to columns
which may be defined dynamically in HBase. You may also consider saving them as part of the
value (analog to a SQL blob) if there are a lot of them.
L5-18
®
Lesson 5: Design Schemas for Complex Data
If your tables exist in a parent-child, master-detail, or other strict one-to-many relationship, it’s
possible to model it in HBase as a single row.
A unique feature in HBase is the ability to have dynamic column names. This allows us
to nest, or embed, an entity inside the row of a parent or primary entity. The row key will
correspond to the parent entity ID. The nested values will contain the children, where each
child entity gets one column name into which the ID is stored. The remainder of the non-
identifying attributes are put in the value. This kind of schema design is appropriate if the only
way you get at the child entities is via the parent entity.
The embedding of the comment entity in the post table is an example of this.
L5-19
®
Lesson 5: Design Schemas for Complex Data
• The row key corresponds to the parent entity ID, the OrderId.
• There is one column family for the order data, and one column family for the order items.
• The Order Items are nested, the Order Item IDs are put into the column qualifier and any
non-identifying attributes, into the value.
Once again, this kind of schema design is appropriate if the only way you get at the child
entities is via the parent entity.
L5-20
®
Lesson 5: Design Schemas for Complex Data
Pinterest uses a follow model, where users follow other users. There is a following feed for
every user that gets updated every time a followee creates or updates a pin. This results in
100’s of millions of possible pins per month, and billions of writes per day. The ‘Following
Feed‘ is implemented as a fat wide HBase table, where each user’s following feed is a single
row. In the “Following Feed” table, a user’s following pins are put in one row as nested entities,
the timestamp and PinId are put into the column qualifier. A background queue writes the pins.
L5-21
®
Lesson 5: Design Schemas for Complex Data
L5-22
®
Lesson 5: Design Schemas for Complex Data
Answers: 1 - C; 2 - B, 3 - A
L5-23
®
Lesson 5: Design Schemas for Complex Data
L5-24
®
Lesson 5: Design Schemas for Complex Data
We have taken a look at denormalization and duplication. In this section, we will describe how
to use intelligent keys.
L5-25
®
Lesson 5: Design Schemas for Complex Data
You can use intelligent keys to implement indexing, sorting and optimizing reads. The use of
intelligent keys gives you control over how data is retrieved. Remember, rows are ordered and
partitioned by row keys. Only the keys are indexed in HBase tables. To take advantage of this
storage organization, the key should be designed as a composition of the attributes that are
most often used as search criteria - similar to a multi-column index design in relational
databases.
You can also use composite keys combined with partial key scans as a leading, left-hand
index, with each key field adding to its precision.
L5-26
®
Lesson 5: Design Schemas for Complex Data
In relational databases, a compound key is a key whereby any part of the key is a foreign key.
Example: In an hotel reservation system, a reservation has the compound key, (GuestId,
HotelId). GuestId identifies a Guest, and references the Guests table. HotelId identifies a Hotel,
and references the Hotels table.
In HBase, a composite key is made up of elements that may or may not be foreign keys.
Example: In a table of transaction details, the key is (TransactionId, ItemNumber). A
transaction detail is a sub-entity of a transaction. TransactionId is a foreign key, referencing the
Transactions table. ItemNumber is not a key in and of itself. It only uniquely identifies an item
within the context of a single transaction.
Map multiple elements into a row key. It’s common practice to take multiple attributes and
make them a part of the row key. The key should be designed as a composition of the
attributes that are most often used as search criteria.
Using values of fixed length makes life much easier. Variable lengths mean you need
delimiters and escaping logic in your client code to figure out the composite attributes that form
the key. Fixed length also makes it easier to reason about start and stop keys.
L5-27
®
Lesson 5: Design Schemas for Complex Data
Recall that you can use composite keys combined with partial key scans as a leading, left-
hand index, with each key field adding to its precision. So this also allows us to scan for post
keys by user + category + date.
Setting the nested Comment Column qualifier to be UserId+timestamp will identify nested
comments by user and time.
L5-28
®
Lesson 5: Design Schemas for Complex Data
User-Post lookup (index) table key = UserId + separator + post key. This allows us to scan for
post keys by user. Recall that you can use composite keys combined with partial key scans as
a leading, left-hand index, with each key field adding to its precision. This also allows us to
scan for post keys by user + category + date.
Setting the nested Comment Column qualifier to be UserId + timestamp will identify nested
comments by user and time.
L5-29
®
Lesson 5: Design Schemas for Complex Data
This is the HBase schema again, now with the row keys and column qualifiers. Posted URLs
are stored in the post table.
The user-post table acts as a lookup so that you can quickly find all of the PostIds for a given
user.
The user-post table is replacing the foreign key relationship, making user-related lookups
faster.
The user table stores the user details.
The comments table has been absorbed by the post table, the comments table is nested in the
post table, the comments columns qualifier is the UserId-date.
L5-30
®
Lesson 5: Design Schemas for Complex Data
L5-31
®
Lesson 5: Design Schemas for Complex Data
Answers: 1 and 2
L5-32
®
Lesson 5: Design Schemas for Complex Data
Answer: 2
L5-33
®
Lesson 5: Design Schemas for Complex Data
L5-34
®
Lesson 5: Design Schemas for Complex Data
L5-35
®
Lesson 5: Design Schemas for Complex Data
One solution to implementing secondary indexes is to have a secondary index table, where the
application maintains a lookup/mapping table. Looking up the data referenced from a
secondary index requires a lookup of the main row key and then retrieval of the data in a
second operation.
L5-36
®
Lesson 5: Design Schemas for Complex Data
In the social app example we want to find all URLs for a specific user. A simple approach is to
scan all post records and check the UserId. This could be very expensive obviously, so you
need an index that keeps track of posts by user.
A solution is a user-post lookup/mapping table. The user-post table acts as a lookup so that
you can quickly find all of the PostIds for a given user.
The user-post table is replacing the foreign key index, making user-related lookups faster. This
is a lookup table (basically an index) to find all URLs for a given user. Getting a post by UserId
requires a lookup of the post row key, and then retrieval of the post in a second operation.
Note that this table is not automatically generated, nor maintained by HBase.
L5-37
®
Lesson 5: Design Schemas for Complex Data
Here is another solution for the same example. In the social app example we want to find all
URLs for a specific user.
Another solution is to add a column family to the user table for the posts that this user has
made. Now, to find all the posts by UserId, do a Get on the user table row key, which is the
UserId, to Get all of the posts for this user, then to retrieve a post, you have the PostId to use
to do a Get from the post table.
L5-38
®
Lesson 5: Design Schemas for Complex Data
If an index table is updated as the main table data is updated, then both tables need to be
updated at the same time.
Since there are no cross table transactions for consistency, this could result in data being
stored in the main table, but with no mapping in the secondary index table, because the
operation failed after the main table was updated, but before the index table was written.
A solution can be to write to the secondary index tables first and to the main table second.
Should anything fail in the process, you are left with orphaned index mappings, but those could
be removed by regular background cleanup jobs.
For an existing table, a lookup table could be created by MapReduce jobs.
If using TTL, setup the main table to expire slightly before the index table(s).
L5-39
®
Lesson 5: Design Schemas for Complex Data
L5-40
®
Lesson 5: Design Schemas for Complex Data
Answers: 2, 3, and 4
L5-41
®
Lesson 5: Design Schemas for Complex Data
Answers: 1, 2, and 4
L5-42
®
Lesson 5: Design Schemas for Complex Data
L5-43
®
Lesson 5: Design Schemas for Complex Data
Now we will take a look at designing schemas for other complex data structures such as
hierarchical data.
L5-44
®
Lesson 5: Design Schemas for Complex Data
Data may be organized in a tree-like or hierarchical way. Here are some examples.
The most common solution in SQL is to store the parent ID with a child. For example each
comment is linked to its parent that way. This is known as an adjacency list. You can’t query all
descendants with such a structure.
Another way to query a tree-structure from adjacency list is to retrieve all the rows in the
collection and reconstruct the hierarchy in the application before you can use it like a tree.
Copying a large volume of data from the database to the application before you can analyze it
is grossly inefficient.
Next we will look at some examples for this online running store.
L5-45
®
Lesson 5: Design Schemas for Complex Data
We are now going to look at some ways of storing the tree in HBase.
The first is the child references pattern. Here we store a string in a column that contains the
children of the current row.
Path enumeration: store a string in a column with the children of the current row.
L5-46
®
Lesson 5: Design Schemas for Complex Data
In this example, we store a string in a column with the sequence of the parent and a string with
the sequence with the children in another column for each row. One column shows parents
and the other column shows children.
L5-47
®
Lesson 5: Design Schemas for Complex Data
Here we are storing a string in a column with the sequence of ancestors of the current row in
order from the top of the tree down, just like a UNIX path. This example uses a comma as a
separator. You can find ancestors or descendants as substrings of the path.
L5-48
®
Lesson 5: Design Schemas for Complex Data
In this example, one column shows the parent and the other column shows ancestors.
L5-49
®
Lesson 5: Design Schemas for Complex Data
Here is an example using path enumeration with the path in the row key. You can use the
materialized path in the row key.
L5-50
®
Lesson 5: Design Schemas for Complex Data
Here is an example for an adjacency list or graph, using a separate column for each parent
and child. Each row shows a node. The row key is equal to the node ID. There is a column
family for parent p, and a column family children c.
The column qualifiers are equal to the parent or child node IDs and the value is equal to the
type to node.
You can see there are multiple ways to represent trees, the best way depends on your queries.
HBase is great for sparse data, so design with this sort of data in mind.
L5-51
®
Lesson 5: Design Schemas for Complex Data
Generic data that is schema-less, is often expressed as name value or entity attribute value. In
a relational database this is complicated to represent. The advantage of HBase is that you can
define columns on the fly, put attribute names in column qualifiers, and group data by column
families.
Here is an example of clinical patient event data. The row key is the patient ID plus a time
stamp. The variable event type is put in the column qualifier. The event measurement is put in
the column value. OpenTSDB is an example of variable system monitoring data.
A conventional relational table consists of attribute columns that are relevant for every row in
the table, because every row represents an instance of a similar object. A different set of
attributes represents a different type of object and thus belongs in a different table. When
having to deal with variable attributes, a common SQL solution is to create a second table,
storing attributes as rows:
The Entity: Typically this is a foreign key to a parent table that has one row per entity.
The Attribute: This is simply the name of a column in a conventional table, but in this new
design, we have to identify the attribute on each given row.
The Value: Each entity has a value for each of its attributes.
L5-52
®
Lesson 5: Design Schemas for Complex Data
In modern object-oriented programming models, different object types can be related, for
instance, by extending the same base type. In object-oriented design, these objects are
considered instances of the same base type, as well as instances of their respective subtypes.
We would like to store objects as rows in a single database table to simplify comparisons and
calculations over multiple objects. But we also need to allow objects of each subtype to store
their respective attribute columns, which may not apply to the base type or to other subtypes.
You can’t join to a different table per row in SQL. SQL syntax requires that you name all the
tables literally at the time you submit the query.
L5-53
®
Lesson 5: Design Schemas for Complex Data
In HBase, you can denormalize and/or use column families. For example, put the object
subclass type i.e. check or credit, in the row key or column qualifier. This is one way of
handling variable attributes.
L5-54
®
Lesson 5: Design Schemas for Complex Data
Here is another inheritance example, this time for an online store with different kinds of
products.
L5-55
®
Lesson 5: Design Schemas for Complex Data
In this example, the type of product is a column name and some of the columns are different
and maybe empty depending on the type of product.
L5-56
®
Lesson 5: Design Schemas for Complex Data
In this example, the type of product is a prefix in the row key and some of the columns are
different and maybe empty depending on the type of product. HBase is designed for sparse
data.
L5-57
®
Lesson 5: Design Schemas for Complex Data
L5-58
®
Lesson 5: Design Schemas for Complex Data
The entity tables are as shown here. For an entity table, it is pretty common to have one
column family storing all the entity attributes, and column families to store the links to other
entities.
L5-59
®
Lesson 5: Design Schemas for Complex Data
A self-join is a relationship in which both match fields are defined in the same table. Here we
have a twitter table for Queries:
A possible solution: The UserIds are put in the row key with the relationship type. For example,
Carol follows Steve Jobs and Carol followed by BillyBob.
L5-60
®
Lesson 5: Design Schemas for Complex Data
This is a real world example from Facebook. Facebook stores messages in HBase. The
UserId is the row key, there is one column family for messages, the messages Id is the column
name, and the message is the value. Next we will look at how they index searches.
L5-61
®
Lesson 5: Design Schemas for Complex Data
There is a column family “s” for storing work searches, the column name is the indexed word,
the version is the message Id of where to find the word, the value is the offset of the word in
the message. For example, the word “hi” is in message 17 at offset 0.
L5-62
®
Lesson 5: Design Schemas for Complex Data
Start with listing all of the use cases your application needs to support. Think about the data
you want to capture and the lookups your application needs to do. Since there is no index
concept, you have to plan out carefully how your data is physically sorted. Therefore it is
important to find all your query use cases first.
With HBase, you need to design the row keys and table structure in terms of rows and column
families to match the data access patterns of your application.
1. Start with identifying Entities. For an entity table, it is pretty common to have one column
family storing all the entity attributes, and multiple column families to store the links to other
entities.
2. Identify relationships. Identify the type of relationship that currently exists.
3. Identify Queries. What information is accessed together in one Get. What information needs
scanning.
4. Find identifying Attributes used in queries. Which attributes uniquely identify a particular
instance of the entity? These then could become part of the composite row key.
L5-63
®
Lesson 5: Design Schemas for Complex Data
Secondary Index:
Lookup table, or put in Column Family
Indexing is not an afterthought, anymore
Think about query patterns and indexes upfront.
L5-64
®
Lesson 5: Design Schemas for Complex Data
L5-65
®
Lesson 5: Design Schemas for Complex Data
L5-66
®
Lesson 5: Design Schemas for Complex Data
In this section, we will describe how to manage schemas that need to evolve over time.
L5-67
®
Lesson 5: Design Schemas for Complex Data
L5-68
®
Lesson 5: Design Schemas for Complex Data
There are some tools you can use to migrate a database whether you are changing the
structure or you are changing the data. You can use snapshots or mirrors if you are using
MapR-DB.
L5-69
®
Lesson 5: Design Schemas for Complex Data
Instead of taking the system offline, take advantage of snapshots to migrate data from read-
only source. Snapshots provide a view of a volume at a specific point-in-time.
L5-70
®
Lesson 5: Design Schemas for Complex Data
Instead of taking the system offline take advantage of mirrors to migrate data from read-only
source. Mirrors are read-only copies of a MapR volume.
L5-71
®
Lesson 5: Design Schemas for Complex Data
Using the MapR Control System you can change the following properties on a column family:
A column family's name, minimum and maximum versions, time-to-live, compression, memory
residence status. You can also change column family properties using the HBase shell or the
java API.
L5-72
®
Lesson 5: Design Schemas for Complex Data
Note:
Tables can be altered
Column families can be added
Columns may be added dynamically
Columns may be altered (modifyColumn())
Recommendations:
Keep table and column families simple and generic
Add column families for migration
L5-73
®
Lesson 5: Design Schemas for Complex Data
There is no difference between an insert and an update, only the version differs. Use versions
to keep only what is needed and expire data with TTL. Adding new values to a column may be
a way to migrate values. Recall that row keys cannot be changed once data has been written
to the table.
L5-74
®
Lesson 5: Design Schemas for Complex Data
In this lesson, we have looked at some general guidelines for moving from a relational model
to HBase. We looked at denormalization, duplication, and intelligent keys. This lesson also
went over designing schemas for hierarchical data and evolving schemas over time.
L5-75
®
Lesson 5: Design Schemas for Complex Data
Congratulations. You have completed Lesson 5. Now that you have seen how design
schemas, we are going to see how to query HBase.
L5-76
®
Lesson 6: Query HBase with Hive
Welcome to DEV 325 – Apache HBase Schema Design, Lesson 6: Query HBase with Hive.
L6-1
®
Lesson 6: Query HBase with Hive
L6-2
®
Lesson 6: Query HBase with Hive
L6-3
®
Lesson 6: Query HBase with Hive
We are now going to take a brief look at Hive, which we will use to query HBase tables.
L6-4
®
Lesson 6: Query HBase with Hive
Hive is a data warehouse infrastructure built on top of Hadoop that provides a SQL-like syntax
for performing queries using MapReduce. The HiveQL language provides a set of operations
that are structured similar to SQL, and are transformed into MapReduce programs to produce
the desired query results.
Hive should be used for data warehouse analytic processing where fast response times are
not required.
L6-5
®
Lesson 6: Query HBase with Hive
To best understand Hive, we need to peek under the hood and see the engine it is running on. SQL
usually exists in a land of strict organization, indexes, and reverse indexes that facilitate very efficient
data access, that we’ve discussed as RDBMS’s. In contrast, Hive is more an interface to Hadoop than
being a database.
Let’s step through an example word-count program using MapReduce to count the occurrences of each
word in a set of input text files.
As input, let’s say we have all Lewis Carroll’s books and we want to count the occurrence of every word.
1. Hadoop divides this into “input splits.”
2. One of the nodes contains Tweedledee’s poem, “The time has come to talk of many things” and so
forth. In the case of text input, every line of text is a record. Each record gets fed individually to the
Map function. The key is the byte-offset into the file, the value is the text. In this case, the key isn’t
useful for this program, so we ignore it.
3. The map function tokenizes the input string, and outputs a key-value pair for every word. The key is
the word, and the value is simply 1. As you can see, the word “and” shows up twice, and it produces
two distinct key-value pairs.
4. Then begins the reduce phase, which just has to sum up values from the Mapper.
In this case, some combining occurs before shuffle-and-sort. The combiner aggregates multiple
instances of the same key coming out of the map into a subtotal. So in our case, the two instances
of “and” get combined, and only one record of value 2 goes through shuffle-and-sort.
5. The framework sorts records by key and, for each key, sends all records to a particular reducer. This
particular reducer gets the keys: and, come, has, the and time. You can see that the combined value
2 for “and” shows up here. The other values come from other map tasks.
6. Finally, the framework gathers the output and deposits it in the file system where we can read it. (Of
course, if we really counted all words in a book, there would be a lot more occurrences of these
words than shown here.)
L6-6
®
Lesson 6: Query HBase with Hive
You can use MapReduce workflows to analyze data. The MapReduce workflow shown here
involves many MapReduce jobs. Running multiple jobs in series is very typical when using
MapReduce.
L6-7
®
Lesson 6: Query HBase with Hive
Here is a small sample of code from a reducer class. As you can see, writing MapReduce code
involves java programming which can be complex for non programmers. We will see that Hive
provides a way for those familiar with SQL syntax to easily create MapReduce programs, and
to execute queries on data stored in Hadoop.
L6-8
®
Lesson 6: Query HBase with Hive
Hive provides a language that is very similar in both structure and syntax to SQL, making it
easy to learn given the very wide use of SQL in modern databases. It is an analytics tool that
was designed for ad hoc batch processing of potentially enormous amounts of data by
leveraging MapReduce. Hive allows data analysts an easy way to use Hadoop, without
programming, to facilitate querying and managing large datasets residing in distributed storage
in much the same manner as traditional data warehousing software.
We can use HiveQL statements to access HBase tables for both read (SELECT) and write
(INSERT). It is even possible to combine access to HBase tables with native Hive tables via
joins and unions.
L6-9
®
Lesson 6: Query HBase with Hive
One way to leverage Hive with HBase is when you use the data in the HBase database as
source of your data flow. An example of doing so is a set of well-defined Batch Analytical
Processing queries that you’ve implemented using HiveQL.
L6-10
®
Lesson 6: Query HBase with Hive
Another way to leverage Hive with HBase is when you use the HBase database as a sink for
your data flow. An example of using HBase as a Hive sink application is when you want to use
a batch job to bulk load data from files into your HBase table.
L6-11
®
Lesson 6: Query HBase with Hive
The last way to leverage Hive with HBase is when you use the HBase database as both a
source and sink in your data flow. One example of this use case is when you calculate
summaries across your HBase data and then store those summaries back in the HBase
database. Another example of this use case is to reorganize HBase data into materialized
views for faster reads.
L6-12
®
Lesson 6: Query HBase with Hive
This diagram shows the components that make up Hive and how they interact with the Hadoop
framework.
Hive provides a number of ways to submit queries. Hive provides an interactive command line
shell that resembles a SQL shell. There are also Java DataBase Connectivity (JDBC) and
Open DataBase Connectivity (ODBC) drivers for Hive that allow applications to access Hive
much like they would access any traditional database. Hive also provides an Apache Thrift
client, which enables access to Hive from many different client-side languages, including java,
C++, Python, Ruby, or PHP.
The Driver moves a HiveQL statement through all the phases needed to become a
MapReduce job and returns the results.
The Metastore contains metadata about Hive tables. In order to query data in files or HBase
with Hive you have to define a schema for this data.
The Hive Metastore contains the definitions of tables (table name, columns and data types),
the location of data files and the routines required to parse data (i.e. StorageHandlers,
InputFormats and SerDes). In this example we see the Hive schema for the HBase
trades_tall table.
L6-13
®
Lesson 6: Query HBase with Hive
There are two ways that you can run Hive queries on HBase tables:
• You can create and manage HBase tables from Hive that can be accessed by both
Hive and HBase. These are called Hive Managed tables
Or
• Hive can map to existing HBase tables. These are called external tables. When using
an already existing table you can create multiple Hive tables that point an HBase table.
This can be useful for example when you only want to query one out of multiple
column families.
L6-14
®
Lesson 6: Query HBase with Hive
Here is an example of using Hive's external table functionality to create a table which sources
its data from an existing HBase table. The Hive external table is metadata that is defined over
the HBase table. The metadata maps the table name, column names and types to the HBase
table. Once the Hive external table has been defined, it can be queried using HiveQL.
The above statement registers the HBase table named /usr/user1/trades_tall in the
Hive metastore, accessible from Hive by the name trades.
L6-15
®
Lesson 6: Query HBase with Hive
Here we see the flow of a Hive query. The Parser translates the HiveQL statement into a plan
which consists of series of MapReduce jobs. The driver submits the MapReduce jobs from the
plan to the Execution Engine. Hive uses MapReduce as its execution engine.
L6-16
®
Lesson 6: Query HBase with Hive
Hive maps queries into MapReduce jobs, simplifying the process of querying large datasets in
HDFS. HiveQL statements can be mapped to phases of the MapReduce framework. Selection
and transformation operations occur in map tasks, while aggregation is handled by reducers.
Join operations are flexible: they can be performed in the reducer or mappers depending on
the size of the leftmost table.
L6-17
®
Lesson 6: Query HBase with Hive
Next, we will look at how a Hive query gets transformed into MapReduce jobs. If you have
EXPLAIN before your query, Hive will output the query plan for the MapReduce jobs to run the
query.
L6-18
®
Lesson 6: Query HBase with Hive
This diagram shows an example of SQL transformed into MapReduce jobs, in this case just
one MapReduce job. The scanning, selecting columns, filtering values, happens first during the
map job, results are sent to the reducer where they are aggregated and then output.
L6-19
®
Lesson 6: Query HBase with Hive
Joins are a powerful feature of SQL-based databases and are also implemented in Hive, with
some restrictions.
Joins give the Cartesian product of two or more tables based on the value of a specified pair of
columns being equal.
Here we see the difference between an implicit and explicit join. These queries are functionally
equivalent, but the implicit form is only available in Hive 0.13 and above.
L6-20
®
Lesson 6: Query HBase with Hive
This example shows the query plan or a more complex Hive query with joins. On the left the
query plan will run as three MapReduce jobs, but on the right, the optimization engine has
optimizated the plan to one MapReduce job.
[Link]
L6-21
®
Lesson 6: Query HBase with Hive
L6-22
®
Lesson 6: Query HBase with Hive
Answer: 4
L6-23
®
Lesson 6: Query HBase with Hive
Answer: 1
L6-24
®
Lesson 6: Query HBase with Hive
Answer: 1 and 3
L6-25
®
Lesson 6: Query HBase with Hive
In this lab you will use Hive to query HBase tables. Refer to your lab guide to run the following:
L6-26
®
Lesson 6: Query HBase with Hive
You have now completed Lesson 6. Congratulations! You have completed the course!
L6-27
®
Lesson 7: Java Client API Part 1
Welcome to DEV 330: Developing HBase Applications Basics, Lesson 1: Java Client API, Part
1.
L7-1
®
Lesson 7: Java Client API Part 1
L7-2
®
Lesson 7: Java Client API Part 1
In this lesson you will learn how to access data with the Java HBase API. We will describe a
sample application that we will use in this lecture and lab exercises.
When you have finished with this lesson, you will be able to:
• Connect a client to HBase tables
• Perform standard CRUD operations on an HBase table, which are Create, Read, Update
and Delete, and define the various helper classes using the Java API
• And finally, you will be able to describe the process of cell versioning within the tables
L7-3
®
Lesson 7: Java Client API Part 1
First, we will look at how the client connects to an existing HBase table.
L7-4
®
Lesson 7: Java Client API Part 1
We want to build a shopping cart application for a client, the Big Office Supply Company,
which sells their office supply products through different outlets. They have their own stores
around the country, as well as an online shop.
The company sells office supplies such as pens, notepads, pencils, and erasers. When
building this shopping cart, we need somewhere to store the inventory of items in the store,
collect items in the customer shopping cart as they browse the site, and update the inventory
when they make a purchase.
To build this cart, we will use two HBase Tables: Inventory and Shoppingcart.
L7-5
®
Lesson 7: Java Client API Part 1
The row key for the Inventory table is the item ID, such as pens, notepads or erasers. The
Inventory table has one column family Stock with the dynamic columns for quantity and price
values.
The Shopping Cart table row key is the user ID, such as Mike or John. This table also has one
column family Items, which includes the dynamic columns for storing the amount of each item
in the user’s cart.
L7-6
®
Lesson 7: Java Client API Part 1
As we go through the lessons of this course, we will visit the different parts of this process. In
this lesson, we will focus on the second one, performing operations on existing HBase tables.
L7-7
®
Lesson 7: Java Client API Part 1
The main interfaces included in the HBase client package are the HtableInterface and
HBaseAdmin.
A Java client uses the HTableInterface to communicate with an existing HBase table, similar to
a handle. The client uses the HTableInterface for Create, Read, Update, and Delete type of
operations on table data.
The client uses the HBaseAdmin interface for creating, altering or deleting tables.
L7-8
®
Lesson 7: Java Client API Part 1
L7-9
®
Lesson 7: Java Client API Part 1
To review from DEV 320 HBase Architecture, when a client wants to read or write to an HBase
table, they must first get the region information from ZooKeeper. The first time a client reads or
writes to HBase, it connects to ZooKeeper to get the the location of the HBase Catalog table,
called the Meta table. The Meta table provides the client with the RegionServer corresponding
to row key ranges. Once the client receives this information, it is cached so the client can
return to the RegionServer without the need for additional lookups.
L7-10
®
Lesson 7: Java Client API Part 1
In our Java code, we create a configuration object, HBaseConfiguration, which will read the
location of ZooKeeper from the [Link] configuration file on class path.
For a MapR cluster, this file is located in the directory shown here. Note that the path includes
the current version of HBase on your cluster.
We can get an instance of the configuration object by calling the create method from
HBaseConfiguration. Once you have a configuration object instantiated, you pass it as a
parameter in the HTable constructor, as shown here.
L7-11
®
Lesson 7: Java Client API Part 1
The [Link] file for client applications contains the location of ZooKeeper in the
configuration information. It tells the client how to connect to ZooKeeper and the cluster,
providing such details as the ZooKeeper quorum IP addresses and port number.
L7-12
®
Lesson 7: Java Client API Part 1
In a typical setup for working with an existing table, first we will instantiate an
HBaseConfiguration object.
Next, we will setup components of the table that we are going to work with. These usually
include the table name, and for MapR tables we will also specify the path to the table.
It is a best practice to set up our variables ahead of time rather than calling [Link]
every time we need to specify a table component. This will save resources and is more
efficient. We will convert our Java strings to a byte array, to specify the various components of
a table, including the table name, column families, column names, row key names, and a
value.
L7-13
®
Lesson 7: Java Client API Part 1
Now we are ready to work with the table myTable which we just instantiated.
We can use the HTableInterface to perform standard CRUD operations, Create, Read,
Update, and Delete.
L7-14
®
Lesson 7: Java Client API Part 1
L7-15
®
Lesson 7: Java Client API Part 1
L7-16
®
Lesson 7: Java Client API Part 1
Now we are going to look at CRUD operations from the Java API. As we go through, you will
see some patterns in how you use the various methods.
L7-17
®
Lesson 7: Java Client API Part 1
When we perform CRUD operations using the Java API, we often follow a similar pattern
regardless of which operation we are performing.
For example,
• First we instantiate an object for the operation we are about to execute: put, get, scan or
delete
• Then we add details to that object, and specify what we need from it. We do this by calling
an add method and sometimes a set method.
• Once our object is specified with these attributes we are ready to execute the operation
against a table. To do that we invoke the operation with the object we have prepared.
For example for a put operation we call [Link]() and pass the put object we created as the
parameter.
L7-18
®
Lesson 7: Java Client API Part 1
We will look at how to create or update a row in the Inventory table using HTable put(). In
this example we will use the Inventory table shown here, with the logical model on the left and
the physical model, how it is stored on disk, on the right.
L7-19
®
Lesson 7: Java Client API Part 1
With this put operation, we insert the cell value shown in the diagram.
1. First, we convert the Java strings to byte arrays to specify the various components of the
Inventory table:
• the table name /path/Inventory
• the stock column family
• and the quantity column
2. Next, we create the HTable interface with the configuration object, and the table name
3. Then, we instantiate the put object, in this case with the pens row key
4. Then, call the put add method to add the stock column family, quantity column and the
value of 24 long to the put object
5. Once the put object is specified with these attributes, we are ready to execute the put
operation against the inventory table. We invoke the put operation with the put object by
calling [Link](), and pass the put object we created as the parameter.
L7-20
®
Lesson 7: Java Client API Part 1
After we have instantiated a put object, we need to add the values that we want to insert,
before we call the [Link]() method.
L7-21
®
Lesson 7: Java Client API Part 1
Once we have an instance of a put object for a specified row key, we will need provide some
details, such as what value we want to insert or update. In general we will add a value for a
column that belongs to a column family.
In the constructor for the put object, or in the put add method we can optionally set the
timestamp to any long value, but usually we will let HBase set this.
L7-22
®
Lesson 7: Java Client API Part 1
Next we will look at the code to insert a row into the Shoppingcart table, which has column
values for pens, notepads, and erasers.
L7-23
®
Lesson 7: Java Client API Part 1
We will use a similar process to when we updated the inventory table earlier, except that in this
case we are adding three different column values; 2 erasers, 5 notepads, and 1 pen, to the row
with row key, Mike, using a put object.
Each call to add() specifies the column family name, column name, and column value. For a
put operation we will typically call add for each column in the row which we are updating.
L7-24
®
Lesson 7: Java Client API Part 1
Everything in HBase is stored as bytes. The Bytes class is a utility class that provides methods
to convert Java types to and from byte arrays.
The native Java types supported are String, short, int, long, double, and float.
We can use the Bytes class [Link] method toboolean, to convert Java types for putting
data into HBase. When reading data from HNase, we use the Bytes class [Link](),
[Link] (), etc, for converting HBase bytes into Java types.
The HBase Bytes class is similar to the Java ByteBuffer class, but the HBase class performs
all of its operations without instantiating new classes. Thus, it avoids garbage collection.
Visit the HBase documentation for more details about the Bytes class methods.
L7-25
®
Lesson 7: Java Client API Part 1
When we look at reading data using the get operation, we will see a similar pattern to the put
operation.
• First, we instantiate a get object with the row key for the row we want to read
• Then, we specify what to get by calling add methods on the get object
• Finally, we execute the get operation against a table by calling [Link](), and passing the
get object we created as the parameter.
L7-26
®
Lesson 7: Java Client API Part 1
We will look at the code to read a row from the Shoppingcart table shown here, which has
column values for pens, notepads, and erasers.
L7-27
®
Lesson 7: Java Client API Part 1
The get operation reads in the row with row key Mike, the column family items, and the column
pens, and then prints out the value from that cell, which in this case is 5.
L7-28
®
Lesson 7: Java Client API Part 1
Next we will talk about the add and set methods on the get object.
L7-29
®
Lesson 7: Java Client API Part 1
If we use the get object without specifying what we want to get, then everything in the row will
be returned. Normally we will want to narrow down what columns or cells that are returned.
To do this,
• We call addFamily to get all the columns for a column family
• And call addColumn to get the value in a specific column of the row
If we want to be more precise, then we should call one of the set methods to be more specific.
We can also control what timestamp or time range we are interested in, and how many
versions of the data we want to see. We can even add a filter to the get operation.
L7-30
®
Lesson 7: Java Client API Part 1
This table shows the get add and set method signatures.
The addColumn method will get the column from the specified family,
While the addFamily method will get all columns from the specified family.
setTimeRange means get will return versions of columns only within the specified timestamp
range
setTimeStamp means get will return versions of columns with the specified timestamp
For more information on the get operation, refer to the HBase documentation.
L7-31
®
Lesson 7: Java Client API Part 1
In this get operation, we have narrowed down the request to the pens column. Once we get
the result back from [Link](), we invoke one of the convenience methods on the result
object. In this case we call getValue, to retrieve the value.
L7-32
®
Lesson 7: Java Client API Part 1
When we call get() on a table a result instance containing the requested data is returned. The
result instance wraps cell data from a row returned from get or scan operations as key values.
Calling [Link]() will print out the cell key values in the result and can be useful for
debugging.
In this example, we see the [Link]() output from performing a get on the Shoppingcart
row, with row key Mike.
[Link]() will get a specific column value from the result instance.
The result object provides more methods to manipulate the returned data. You can learn more
about result in the HBase documentation.
L7-33
®
Lesson 7: Java Client API Part 1
Key value is the fundamental HBase type. It represents a value and its key, which is referred
to as the cell coordinates.
When we successfully insert a record using a put operation, we save data that can be
represented in a key value object.
When we perform a get operation, it returns a result object that is backed by key value
instances.
L7-34
®
Lesson 7: Java Client API Part 1
The typical pattern for deleting data from a row with the delete operation includes:
L7-35
®
Lesson 7: Java Client API Part 1
As with the get and put operations, we can refine the cells that are affected by a delete
operation.
L7-36
®
Lesson 7: Java Client API Part 1
Calling delete() with a delete instance will delete an entire row, and all of its columns and
versions.
We can delete a column family, and all of its columns, by using one of the delete family
methods.
We can also delete individual columns by using either the deleteColumn or deleteColumns
methods.
L7-37
®
Lesson 7: Java Client API Part 1
When specifying a timestamp, deleteFamily and deleteColumns will delete all versions with a
timestamp less than or equal to that passed.
If no timestamp is specified, an entry is added with a timestamp of now, where now is the
server's [Link]().
When specifying a timestamp, deleteColumn will delete versions only with a timestamp equal
to the one passed.
If no timestamp is specified, deleteColumn only deletes the latest version of the column
provided. This may be expensive as the server needs first to find the latest version by doing a
get.
L7-38
®
Lesson 7: Java Client API Part 1
L7-39
®
Lesson 7: Java Client API Part 1
What are the operations that can be used to add/change/delete data in HTable?
L7-40
®
Lesson 7: Java Client API Part 1
What are the operations that can be used to add/change/delete data in HTable?
put – correct
get – incorrect
delete - correct
L7-41
®
Lesson 7: Java Client API Part 1
Scan is a very powerful operation that allows us to retrieve data from multiple rows.
The standard pattern for reading data with the HTable Scan operation is as follows:
The result ResultScanner is returned, which lets us iterate over result objects.
L7-42
®
Lesson 7: Java Client API Part 1
We can specify an optional startRow and stopRow for a scan operation. If no rows are
specified, using the empty constructor, scan iterates over all rows, starting at the beginning of
the table.
The start row is always inclusive, meaning the scan will match the first row key that is equal to
or larger than the given start row.
The stop row is exclusive meaning the scan stops when the current row key is equal to or less
than the optional stop row.
L7-43
®
Lesson 7: Java Client API Part 1
Similar to our other operations, scans can also be limited to certain column families or
columns.
L7-44
®
Lesson 7: Java Client API Part 1
The default, empty constructor, will read the entire table, including all column families and their
columns.
Table data is stored in column families, and column families are physically stored in separate
files. When we omit a column family, scan will not read the file for that family. By specifying
which column families to scan, we take advantage of the strength of column-family oriented
architecture, saving time and resources.
L7-45
®
Lesson 7: Java Client API Part 1
A read merges key values from the BlockCache, which is recently read cells in memory, the
Memstore, which is recently inserted/updated cells in memory, and HFiles, which are cells on
disk.
We can reduce the amount of data that we need to read off the disk or transfer over the
network.
• Specify the row key to find the cells, since the row key is indexed
• Specify the column family for what part of the row to read, thereby reducing the number of
HFiles read, if the row spans multiple families
• Specify the column name to reduce the number of columns returned to the client, thereby
saving on network I/O
As we provide more criteria for the coordinates we do less work and return less data.
L7-46
®
Lesson 7: Java Client API Part 1
Let’s look at the ResultScanner object that is returned from a HTable getScanner operation.
A ResultScanner is like a Java iterator that will iterate over result instances.
We can process the results by calling the ResultScanner next() method.
L7-47
®
Lesson 7: Java Client API Part 1
L7-48
®
Lesson 7: Java Client API Part 1
Scans do not ship all the matching rows in one RPC to the client, but instead do this on a row
basis.
The ResultScanner wraps the result instance for each row into an iterator functionality.
The next() calls return a single instance of result, representing the next available row.
We can fetch a larger number of rows using the next(int nbRows) call, which returns an array
of up to nbRows items, each an instance of result, representing a unique row.
Each call to next() will be a separate RPC for each row, even when using the next(int
nbRows).
L7-49
®
Lesson 7: Java Client API Part 1
We can enable scan caching to control the number of rows returned per RPC call. By calling
setScannerCaching(number) on the scan object, the specified number of rows will be returned
in one RPC call and cached on the client side.
The next() calls will then return a single instance of result. This represents the next available
row from the cache, until the cache is empty, then another RPC call will be made.
Scanner caching uses memory on the client. Setting the scanner caching higher will improve
scanning performance most of the time, but setting it too high can cause
an OutOfMemoryException.
L7-50
®
Lesson 7: Java Client API Part 1
In this example of scan with scanner caching, [Link] with a parameter of three,
specifies for three rows to be returned in one RPC call and cached on the client side.
The next() calls will then return a single instance of result. As before, this represents the next
available row from the cache until the cache is empty, then another RPC call will be made.
L7-51
®
Lesson 7: Java Client API Part 1
With MapR-DB, long scans are auto-detected, and the client increases the RPC size to
optimize for them. While this happens automatically with MapR-DB, while we need to set this
value with HBase.
L7-52
®
Lesson 7: Java Client API Part 1
This shows the Lab Exercise Program Structure which is explained in detail in the Lab Guide.
L7-53
®
Lesson 7: Java Client API Part 1
L7-54
®
Lesson 7: Java Client API Part 1
L7-55
®
Lesson 7: Java Client API Part 1
Versioning is built-in with HBase. A put is both an insert, or create, and an update and each
one of these actions gets stored as its own version.
Deleting a row places a tombstone marker on blocks of data. The tombstone marker prevents
the data being returned in queries. The Physical data is deleted later during merge
compactions.
Get requests return a specific version or versions based on parameters. If no parameters are
specified, the most recent version is returned.
We can configure how many versions we want to keep, per column family, with a default of 1
version. When the max number of versions is exceeded, extra records will be eventually
removed. We can specify which extra versions will be deleted.
L7-56
®
Lesson 7: Java Client API Part 1
Table cells are versioned, uninterpreted arrays of bytes. The version is by default a timestamp
that is a long. For every row:family:column coordinate, there can be multiple versions of the
value.
Each update, put or delete, adds a new cell with a new version, which is by default the
current time in milliseconds.
L7-57
®
Lesson 7: Java Client API Part 1
L7-58
®
Lesson 7: Java Client API Part 1
L7-59
®
Lesson 7: Java Client API Part 1
L7-60
®
Lesson 7: Java Client API Part 1
Congratulations! You have finished DEV 330, Lesson 1: Developing HBase Applications
Basics – Java Fundamentals Part 1.
Continue on the Lesson 2 of this course to learn more about working with the Java API in
HBase.
L7-61
®
Lesson 8: Java Client API Part 2
Welcome to DEV 330, Lesson 2: Developing HBase Applications Basics – Java Fundamentals
Part 2.
L8-1
®
Lesson 8: Java Client API Part 2
L8-2
®
Lesson 8: Java Client API Part 2
When you have finished with this lesson, you will be able to:
L8-3
®
Lesson 8: Java Client API Part 2
L8-4
®
Lesson 8: Java Client API Part 2
By default each call to put() is wrapped and sent to the server using a Remote Procedure Call,
or RPC.
As part of the HBase API there is a client side write buffer, which when turned on, will collect
put operations so that they will be sent in one RPC call to the servers.
Using the client-side buffer is controlled by an autoFlush property on the HTable, which is set
to true by default. Therefore, by default each put will be flushed and sent in it’s own RPC call.
This can be changed by calling HTable setAutoFlush(), and setting the parameter to false,
which activates the client-side write buffer to collect put operations.
L8-5
®
Lesson 8: Java Client API Part 2
When updating small records, we will get better performance by grouping puts into fewer
RPCs.
When autoFlush is on, each RPC will be sent one at a time, whether we will use a single put or
a list of put operations.
Disabling autoFlush will prevent having each call being sent separately to the server, and the
put operations will be sent when the write buffer is full instead.
L8-6
®
Lesson 8: Java Client API Part 2
We can activate the client side write buffer by invoking [Link] and setting it to
false. The client-side write buffer will then collect put operations in the write buffer.
When flushCommits() is called, the buffered updates will be sent in one RPC call to the server
or servers. In addition, the client buffer will be flushed whenever the maximum write buffer size
is reached, or when [Link]() is called.
L8-7
®
Lesson 8: Java Client API Part 2
The client-side, write-buffered put instances may span different regions. Under the covers the
put instances are batched to the RegionServer corresponding to the region.
Note that the client-side buffer is held on the client side, so if the client is terminated, for
example it crashes, everything that was in the write buffer is lost.
L8-8
®
Lesson 8: Java Client API Part 2
res1 = [Link](get); gets the row with rowkey rowA. However the result returns
keyvalues=NONE, which is an empty row because flushCommits has not been invoked yet.
[Link](); is called, which will cause the buffered updates to be sent in one
RPC call to the servers.
Result res2 = [Link](get); gets the row with rowkey rowA again. Res2 will display the
contents of rowA, now that flushCommits has been called.
L8-9
®
Lesson 8: Java Client API Part 2
L8-10
®
Lesson 8: Java Client API Part 2
L8-11
®
Lesson 8: Java Client API Part 2
L8-12
®
Lesson 8: Java Client API Part 2
L8-13
®
Lesson 8: Java Client API Part 2
L8-14
®
Lesson 8: Java Client API Part 2
Another way to get better performance with fewer RPCs is to use the HTable putList or Batch
methods.
The HTable put, get, and delete List methods let us pass a list of objects for updating or
reading.
If isAutoFlush is false, the update is buffered until the write buffer is full.
Puts added via [Link](Put) and [Link]( <List> Put) will be in the same write buffer.
If autoFlush = false, these messages are not sent until the write-buffer is filled.
L8-15
®
Lesson 8: Java Client API Part 2
Then we can instantiate as many put objects as we need to, and add them to the list.
When we are done, we call HTable put, with the list as the parameter.
L8-16
®
Lesson 8: Java Client API Part 2
When we use the htable batch() functionality, the included put instances will not be buffered
using the client-side write buffer. The batch() calls are synchronous and send the operations
directly to the servers. No delay nor other intermediate processing is used. This is different
when compared to the put() calls, so choose which one you want to use carefully.
This is similar to the htable putList call. We pass in a List of get, put, or delete objects. Note
that the Row class is the parent class for put, get and delete objects, and we can also pass a
results array.
L8-17
®
Lesson 8: Java Client API Part 2
The void batch method is shown here, with a list of get, put or delete actions, and a results
array as its parameters.
L8-18
®
Lesson 8: Java Client API Part 2
This table shows what you can expect to be returned from HTable batch operations in the
Object[] results parameter.
The results parameter gives access to the results of all succeeded operations, and the remote
exceptions for those that failed. A null in the result array means the action failed even after
retries.
L8-19
®
Lesson 8: Java Client API Part 2
The HTable batch method looks very similar to the example we looked at for a put with a list of
put operations.
Then we instantiate as many put objects as we need to, and add them to the list.
When we are done, we call HTable batch with the list as the parameter.
L8-20
®
Lesson 8: Java Client API Part 2
L8-21
®
Lesson 8: Java Client API Part 2
L8-22
®
Lesson 8: Java Client API Part 2
L8-23
®
Lesson 8: Java Client API Part 2
L8-24
®
Lesson 8: Java Client API Part 2
L8-25
®
Lesson 8: Java Client API Part 2
We want to implement checkout functionality for the Shoppingcart application tables shown
here. There is one Shoppingcart per customer at any given time.
L8-26
®
Lesson 8: Java Client API Part 2
L8-27
®
Lesson 8: Java Client API Part 2
L8-28
®
Lesson 8: Java Client API Part 2
The basic principle is that we read data from the HBase tables. When we want to update any
value, we want to make sure that no other client has changed this value. We use the atomic
check to compare that the value was not modified, and then apply our new value. This
guarantees atomicity for a single row. This method returns a boolean true on success.
This method is similar to the atomic compareAndSet() method in Java. We will typically use
this for concurrent transactions, such as checking if a ID exists before inserting it, or checking
if an amount has changed before updating.
L8-29
®
Lesson 8: Java Client API Part 2
In this example, a check is made on the quantity column with the value 24. if the value is still
24, then the put updates the quantity column to 19 and adds the Mike column with 5.
L8-30
®
Lesson 8: Java Client API Part 2
In this example, the first checkAndPut checks if column1 is null. If it is, it puts value1 into
column1.
The second checkAndPut checks if column1 is equal to value1. If it is equal, it puts value2 into
column2.
ret2 will be equal to true if ret1 was successful, and if no one else changed the value since it
was set.
Also for ret1 the checkAndPut call is an example of checking whether the column did not exist,
or in other words there was no previous value there. We will only insert a value if there isn’t
one there.
L8-31
®
Lesson 8: Java Client API Part 2
In the HTable class, we also have the checkAndDelete method. This is similar to the put
method we saw earlier. The two are often referred to as atomic compare-and-set operations
abbreviated as CAS.
checkAndDelete gives us read-and-modify functionality on the server side. Similar to the put
method, if the check fails, nothing is deleted and false is returned. Conversely if the check
succeeds the delete is executed and the method returns true.
Passing a null value triggers the nonexistence test, meaning, the check is successful if the
column does not exist.
Again this method has atomicity at the row level. Checking on one row key, while the supplied
instance of delete points to another, will throw an exception.
L8-32
®
Lesson 8: Java Client API Part 2
L8-33
®
Lesson 8: Java Client API Part 2
L8-34
®
Lesson 8: Java Client API Part 2
L8-35
®
Lesson 8: Java Client API Part 2
L8-36
®
Lesson 8: Java Client API Part 2
L8-37
®
Lesson 8: Java Client API Part 2
KeyValue objects contain the data as well as the coordinates of one specific cell.
Everything in HBase is stored as byte arrays, each KeyValue wraps the byte array of a
particular cell.
L8-38
®
Lesson 8: Java Client API Part 2
These helper methods allocate new byte arrays and return copies of the cell information.
cloneRow, with row as the parameter, refers to the row key. The key in the diagram refers to
the coordinates of a cell, in their raw, byte array format.
L8-39
®
Lesson 8: Java Client API Part 2
L8-40
®
Lesson 8: Java Client API Part 2
L8-41
®
Lesson 8: Java Client API Part 2
L8-42
®
Lesson 8: Java Client API Part 2
L8-43
®
Lesson 8: Java Client API Part 2
L8-44
®
Lesson 8: Java Client API Part 2
A Result object wraps data from a row returned from a get or a scan operation.
The Result object wraps a row as an array of cell key values, and contains all of the cells for all
of the columns from all of the column families that the get or scan request returned for that
row.
[Link]() can be useful for debugging, to see the key values that the Result contains.
The Result object provides several methods to manipulate the returned data.
L8-45
®
Lesson 8: Java Client API Part 2
In this example we use the Result method, getColumn, to get the key values for the pens
column.
There are three versions stored in this pens column, and one for each of the notepads and
erasers columns. When using the get object, five key values will be returned for Adam.
[Link] is set to 3, so values for all three versions in the pens column will be
returned.
[Link](ITEMS_CF , PENS_COL) will return the last written version for the pens
column, in this case 3.
L8-46
®
Lesson 8: Java Client API Part 2
We will get a result instance from a get operation, or when we call next from a ResultScanner,
from a scan operation.
getColumnCells() returns the key values for the specified column. The returned list contains
zero, when the column has no value for the given row, or one entry, which is the newest
version of the value. We can request more than one value, if we specified a value greater than
one version to be returned.
L8-47
®
Lesson 8: Java Client API Part 2
[Link]() will return 5. There are five cells in this row, and [Link](3) sets the
get to return a maximum of three versions per cell.
L8-48
®
Lesson 8: Java Client API Part 2
rawCells() returns the sorted array of cells for the Result instance
listCells() returns a sorted list of the cells. This provides a convenient way to iterate over the
results.
isEmpty() checks if cell array is empty. It will zero length if it is empty or null if the array is not
empty.
L8-49
®
Lesson 8: Java Client API Part 2
getColumnLatestCell returns the newest cell of the specified column. In contrast to getValue(),
however, it does not return the raw byte array of the value, but the full key value instance
instead. This may be useful when we need more than just the data. In this example
getColumnLatestCell returns the key value for the cell Adam:Items:pens, which is 3
containsColumn is a convenience method to check if there was any cell returned in the
specified column. In this case, it will return false.
L8-50
®
Lesson 8: Java Client API Part 2
getColumnLatest returns the newest cell of the specified column. In contrast to getValue(),
however, it does not return the raw byte array of the value, but the full key value instance
instead. This may be useful when we need more than just the data.
containsColumn is a convenience method to check if there was any cell returned in the
specified column.
L8-51
®
Lesson 8: Java Client API Part 2
Result getMap() returns the entire result row data in a Java map, so we can iterate over it. The
result may include multiple families and multiple versions.
The public interface, NavigableMap, with a key and value, extends SortedMap, getMap
The Result row returned is a map, of a map, of a map. The outer map is keyed by the family
name, and the middle-inner map is keyed by a column name, and the innermost map is keyed
by timestamp.
L8-52
®
Lesson 8: Java Client API Part 2
An HBase table is not a relational table. Instead, it is more like a map of maps, with an outer,
sorted map keyed by a row key, and an inner, sorted map keyed by column name.
The Java representation of the table is seen here, showing how this table is a map, of a map,
of a map.
L8-53
®
Lesson 8: Java Client API Part 2
The Result object getMap() method will return a sorted NavigableMap of maps containing the
row data.
The bottom table shows how the example row data is stored on disk. The code on the top
shows how this row of data is returned in a Java sorted map of maps.
A map is keyed by the column family name, which contains a map keyed by column qualifier,
which in turn contains a map of cells keyed by timestamp.
L8-54
®
Lesson 8: Java Client API Part 2
In this example, the Result getMap() method returns the entire result set in a Java map so we
can iterate over it.
Result getMap() returns a navigable map keyed by the column family name, which contains a
map keyed by column qualifier, which contains a map of cells keyed by timestamp.
• In this example, the column family map gets a map of the columns, keyed by qualifiers,
for the items column family
• For each column qualifier, it gets a map of the cells keyed by timestamps
L8-55
®
Lesson 8: Java Client API Part 2
getFamilyMap returns results for a specific family only, including all versions.
getMap returns the entire result set in a Java map, so we can iterate over it. The Result may
include multiple families and multiple versions.
getNoVersionMap returns the same information as getMap, but includes only the latest version
of a cell for each column.
Whether you use these map methods, or the other methods mentioned earlier, is a matter of
style. Follow your established access patterns. There is no performance penalty in either
technique, and the results have already been moved across the network from the server to
your client process.
L8-56
®
Lesson 8: Java Client API Part 2
• First we get the Result object, which wraps the row for the row key Mike
• Then we get all of the columns in the items column family, as a NavigableMap
• Finally, from the column NavigableMap, we get the values for the columns pens, notepads
and erasers
L8-57
®
Lesson 8: Java Client API Part 2
L8-58
®
Lesson 8: Java Client API Part 2
L8-59
®
Lesson 8: Java Client API Part 2
L8-60
®
Lesson 8: Java Client API Part 2
L8-61
®
Lesson 8: Java Client API Part 2
Congratulations! You have finished DEV 330, Lesson 8: Developing HBase Applications
Basics – Java Fundamentals Part 2.
Continue on the Lesson 9 of this course to learn about the HBase Java API Admin interface.
L8-62
®
Lesson 9: Java Client API for Administrative Features
Welcome to DEV 330, Developing HBase Applications Basics, Lesson 9, Java API Admin
Interface and HBase compatibility.
L9-1
®
Lesson 9: Java Client API for Administrative Features
L9-2
®
Lesson 9: Java Client API for Administrative Features
We talked about manipulating data in tables, similar to Data Manipulation Language, or DML in
SQL. Now we are going to work with tables themselves, similar to Data Definition Language, or
DDL in SQL.
When you have finished with this lesson, you will be able to:
• Define table and column family properties using the table and column family descriptor
objects
• Create, alter, and delete tables using the HBaseAdmin class, which you use with the table
and column family descriptors
• Define the HBase API compatibility with MapR-DB tables
L9-3
®
Lesson 9: Java Client API for Administrative Features
L9-4
®
Lesson 9: Java Client API for Administrative Features
A Java client uses the HBaseAdmin interface to create, drop, list, enable, and disable tables,
as well as to add and drop table column families.
We will use the HBaseAdmin interface with the HTableDescriptor, and with the
HColumnDescriptor to define table properties.
L9-5
®
Lesson 9: Java Client API for Administrative Features
This example shows how the table descriptor, column descriptor, and HBaseAdmin can be
used together to create the shopping application Inventory table.
L9-6
®
Lesson 9: Java Client API for Administrative Features
The HTableDescriptor class lets us control details about a table and its column families.
We can define column families by adding column descriptors to the HTableDescriptor, and
create a table descriptor with a table name.
MapR tables names are specified using a full path name, and we can also create a table with
an existing descriptor.
L9-7
®
Lesson 9: Java Client API for Administrative Features
L9-8
®
Lesson 9: Java Client API for Administrative Features
The table descriptor column family methods let us add, get, or remove column family
properties by adding, getting or removing ColumnDescriptors.
We can:
• Add a family
• Check if the table contains a column family
• Get a list of column families for the table
• Get or remove a specific family
L9-9
®
Lesson 9: Java Client API for Administrative Features
L9-10
®
Lesson 9: Java Client API for Administrative Features
HColumnDescriptor wraps column family settings, and lets us control the details about a
column family.
We add a column descriptor to a table descriptor before creating or modifying a table, and we
create a column descriptor with a family name or with an existing descriptor.
L9-11
®
Lesson 9: Java Client API for Administrative Features
L9-12
®
Lesson 9: Java Client API for Administrative Features
L9-13
®
Lesson 9: Java Client API for Administrative Features
We can set the minimum and maximum number of cell versions to keep for a column family.
The default value is currently 1, before HBase version .98 the default was to store three
versions.
Values that exceed the set maximum will be removed. For example, if we have the max
versions set to 3, and already have three versions stored, then when a new version is added,
the oldest one will be dropped.
minVersions sets the minimum number of versions to keep, and is often used with timeToLive.
L9-14
®
Lesson 9: Java Client API for Administrative Features
HBase provides pluggable compression algorithms that let us choose the best compression for
the data stored in a column family.
LZ4 is the default algorithm. LZ4 is faster than the other options, but gives less compression.
Zlib is slower but gives a higher level of compression. There is a tradeoff between
compression ratio and speed.
To set the compression algorithm for a column family, call setCompressionType, passing in
the enumeration type.
[Link] is an enumeration.
L9-15
®
Lesson 9: Java Client API for Administrative Features
We use Time to Live, or TTL, to control when data should be expired. If a value exceeds its
TTL, it is dropped, based on the timestamp.
We can set the minimum number of versions, to specify how many versions should be kept
even if they are older than the TTL. If we set the minimum to 0, we will save no values.
The TTL is specified in seconds. By default it is set to Integer.Max_Value, which is more than 2
billion seconds.
L9-16
®
Lesson 9: Java Client API for Administrative Features
A read operation has to read cells corresponding to one row from multiple places. Row cells
already persisted are in HFiles, recently updated cells are in the Memstore, and recently read
cells are in the BlockCache.
Bloom filters provide a lightweight, in-memory structure to reduce disk reads by determining
which files do not contain row Cells.
Bloom filters are stored in the meta data of each HFile and are loaded into the BlockCache. A
small amount of cache used for storing Bloom filters, which reduce the number of disk seeks
required for read operations.
L9-17
®
Lesson 9: Java Client API for Administrative Features
Bloom filters reduce disk access by determining whether an HFile might contain data for a
specified row key.
A Bloom filter is a data structure designed to tell us, rapidly and memory-efficiently, whether an
element is present in a set.
For example, Bloom filters allow us to ask a question like, is the row key “Adam” in this file?
Bloom filters will occasionally return a false positive, but will never return a false negative.
In the example on the left, for the question, “Is row key Adam in this file?”, the Bloom filter
would return Maybe. In the example on the right, the bloom filter would return No. This
eliminates reading files when the row cells will not be in this file.
L9-18
®
Lesson 9: Java Client API for Administrative Features
Bloom filters reduce the number of HFiles that need to be read to find the cells for a given row.
Bloom filters are often referred to as a negative test. The algorithm tests for the presence of
data in the index, and returns whether a file contains a particular row key or not.
For HBase the default BloomType value is none. However for MapR-DB, the BloomType is
always row. The MapR-DB value cannot be changed.
L9-19
®
Lesson 9: Java Client API for Administrative Features
L9-20
®
Lesson 9: Java Client API for Administrative Features
HBaseAdmin provides an API for administrative tasks, similar to the DDL (Data Definition
Language) in relational databases.
It provides methods to create tables with specific column families, check for table existence,
alter table and column family definitions, drop tables, and more.
L9-21
®
Lesson 9: Java Client API for Administrative Features
L9-22
®
Lesson 9: Java Client API for Administrative Features
L9-23
®
Lesson 9: Java Client API for Administrative Features
L9-24
®
Lesson 9: Java Client API for Administrative Features
L9-25
®
Lesson 9: Java Client API for Administrative Features
L9-26
®
Lesson 9: Java Client API for Administrative Features
We can create an HBaseAdmin with a configuration object, the same configuration object we
used with the HBaseTable interface.
We will need to call close, to release resources kept by the HBaseAdmin when we are done.
L9-27
®
Lesson 9: Java Client API for Administrative Features
L9-28
®
Lesson 9: Java Client API for Administrative Features
Table operations work with the tables, not the actual schemas inside them.
The first operation listed here, createTable with the HTableDescriptor, is the base method for
creating a new table.
The next creates a new table, and adds an initial set of empty regions defined by the specified
split keys. The total number of regions created will be the number of split keys plus one. The
split keys define the start and end keys of the regions created.
The final operation creates a new table with a specific number of regions. The start key that is
defined will become the end key of the first region of the table, and the end key defined will
become the start key of the last region of the table. The first region has a null start key, and
the last region has a null end key. BigInteger math will be used to divide the key range
specified into enough segments to make the required number of total regions.
L9-29
®
Lesson 9: Java Client API for Administrative Features
The tableExists() method will determine if a table already exists, or if a previous command to
create succeeded.
The listTables() method returns a list of HTableDescriptor instances for every table that MapR
knows about. By default, this will list instances from the user’s home directory.
L9-30
®
Lesson 9: Java Client API for Administrative Features
The deleteTable() method will delete the table specified by its string or byte name.
To alter the structure of a table we can call modifyTable() passing a TableDescriptor. This is an
asynchronous operation to modify table properties after creating a table.
L9-31
®
Lesson 9: Java Client API for Administrative Features
The methods shown here will delete a column or modify a column family. If there is data stored
in the column, it will be deleted when the column is deleted.
L9-32
®
Lesson 9: Java Client API for Administrative Features
L9-33
®
Lesson 9: Java Client API for Administrative Features
L9-34
®
Lesson 9: Java Client API for Administrative Features
L9-35
®
Lesson 9: Java Client API for Administrative Features
L9-36
®
Lesson 9: Java Client API for Administrative Features
L9-37
®
Lesson 9: Java Client API for Administrative Features
L9-38
®
Lesson 9: Java Client API for Administrative Features
The online MapR documentation will provide you with the details of what HBase API methods
and shell commands are supported. For example,
L9-39
®
Lesson 9: Java Client API for Administrative Features
L9-40
®
Lesson 9: Java Client API for Administrative Features
Congratulations! You have finished DEV 330, Lesson 3: Developing HBase Applications
Basics – Java Client API for Administrative Features.
Continue on to DEV 335 to learn more about the HBase Java API.
L9-41
®
Lesson 10: Advanced HBase Java API
L10-1
®
Lesson 10: Advanced HBase Java API
L10-2
®
Lesson 10: Advanced HBase Java API
In this lesson, we will look at more advanced features of the HBase Java API such as using
filters with scan operations to improve results that come back from a scan operation and using
counters which are atomic update operations.
L10-3
®
Lesson 10: Advanced HBase Java API
When you have finished with this lesson, you will be able to:
• Define the different types of filters available when using the Java API, and apply these
filters to reduce the returned results based on column family, qualifier, value and row key.
• Use counters to store and retrieve incremental occurrences to the HBase tables.
L10-4
®
Lesson 10: Advanced HBase Java API
Filters let us narrow down the result set returned from a Scan or a Get, and they provide more
fine-grained features than the add or set methods on a get or scan object. We can filter by row
key, column family, column qualifier, and value. We can also compare using substrings and
regular expressions.
L10-5
®
Lesson 10: Advanced HBase Java API
We define a new instance of the filter that we want to apply and hand it to the Get or Scan
instances, using setFilter(), and passing in the filter as a parameter. This sets the filter on
the client side. Filters are then serialized so they can be sent over the network, deserialized,
and then applied on the server side. On the server side, the filter is used to determine whether
a record should be returned back to the client side.
L10-6
®
Lesson 10: Advanced HBase Java API
The different types of filters available are: dedicated filters, comparison filters, decorating
filters, and finally the filter list.
L10-7
®
Lesson 10: Advanced HBase Java API
This slide shows the table for the stock trades tall schema where every row represents one
trade. There is one column family, with two columns to store Price and Volume values. The
composite row key is formed by combining the stock symbol and a reversed timestamp, which
is calculated using Long.MAX_VALUE and subtracting the current timestamp.
L10-8
®
Lesson 10: Advanced HBase Java API
This example shows a small sample output for putting and then scanning the table without any
filtering.
L10-9
®
Lesson 10: Advanced HBase Java API
Comparison filters are used to filter by comparison. We can filter by comparing a value with the
row key, column family, qualifier, or cell value.
We create a comparison filter with an operator such as equal, greater, not equal, and a
comparator, which specifies what to value compare to, such as a String, SubString, Binary
Value, or Regular Expression.
In this example a valueFilter is used to find values greater than 8000. The EQUAL
operator is used together with a binary comparator.
L10-10
®
Lesson 10: Advanced HBase Java API
Here is the example code for a valueFilter used to find Values >= 8000 long on the volume
column with a scan operation.
The code is the same as for a regular scan operation, we call [Link] passing a
filter as the argument.
We can then narrow down the data filtered by using the addColumn or addColumnFamilies
methods with the scan object, and also by specifying a start and stop key when appropriate.
L10-11
®
Lesson 10: Advanced HBase Java API
Here we see a small sample output for the previous code for scanning the trades table with the
value filter >= 8000 on the volume column.
L10-12
®
Lesson 10: Advanced HBase Java API
A comparison filter is created with an an operator and a comparator. The operator specifies
how to compare, such as equal, greater or not equal and the comparator specifies what value
to compare.
In this example a valueFilter is used to find a specific string. The EQUAL operator is used
together with a sub-string comparator.
L10-13
®
Lesson 10: Advanced HBase Java API
The comparison operator specifies how to compare. They allow us to select the data that we
want as either a range, subset, or exact match.
EQUAL will do an exact match on the value and the provided one.
GREATER_OR_EQUAL will match values that are equal to or greater than the one provided.
GREATER will Only include values greater than the one provided.
LESS will Match values less than the one provided.
LESS_OR_EQUAL will Match values less than or equal to the one provided.
NO_OP will Exclude everything.
NOT_EQUAL will Include everything that does not match the provided value.
For more information on comparison filter operators, refer to the java doc.
L10-14
®
Lesson 10: Advanced HBase Java API
The comparator specifies what value to compare. We can create a comparator with the value
that we want to compare against. Some of these constructors take a byte array to do a binary
comparison and other comparators take a String parameter.
L10-15
®
Lesson 10: Advanced HBase Java API
In this flat schema, all trades for each day are stored in a single row. Trades are grouped by
the hour of the day, using a column for every hour. Price and Volume values are stored in
separate column families.
Every version of a cell represents one trade and the cell version, which is a long, stores the
timestamp of the trade in milliseconds. Because every version of a cell is significant, the Price
and Volume column families are set to keep all versions.
The row key is a composite of the company symbol and the day-of-year, formatted
YYYYMMDD.
L10-16
®
Lesson 10: Advanced HBase Java API
RowFilter gives us the ability to filter based on row keys. We can compare for exact
matches, usually using a binary comparator, and also for substring matches or regular
expression matches.
In the example here, rows with a key equal to or less than GOOG_201308 will be returned.
L10-17
®
Lesson 10: Advanced HBase Java API
The qualifier filter is similar to the row filter but instead of operating on row keys it works on
column qualifiers.
In this example, we want to filter in the flat stock table for columns less than 10, which would
be price and volume columns before 10 o’clock. If we know the exact column name that we are
looking for, then we can add a column to a scan operation, using [Link](byte[],
byte[]) directly rather than a filter. This filter can be wrapped with the
WhileMatchFilter and SkipFilter to add more control.
L10-18
®
Lesson 10: Advanced HBase Java API
This example shows the results of filtering in the flat stock table for columns less than 10,
which would be prices and volume columns before 10 o’clock. The results are for hours less
than 10.
L10-19
®
Lesson 10: Advanced HBase Java API
L10-20
®
Lesson 10: Advanced HBase Java API
L10-21
®
Lesson 10: Advanced HBase Java API
L10-22
®
Lesson 10: Advanced HBase Java API
L10-23
®
Lesson 10: Advanced HBase Java API
Dedicated filters implement specific use cases and generally apply to one of the elements in a
row like a column or a timestamp.
L10-24
®
Lesson 10: Advanced HBase Java API
We must first specify the column that we want to compare to and then an operator and a
comparator value to check against. In this example we want to get the stocks with the volume
column value >= 8000 long
L10-25
®
Lesson 10: Advanced HBase Java API
This shows the output for filtering the stocks with vol >= 8000 in the Stock tall table using the
SingleColumnValue filter.
L10-26
®
Lesson 10: Advanced HBase Java API
The PrefixFilter filters data based on the prefix value of the row key, all rows
that match this prefix are returned to the client and the scan is ended when the filter
encounters a row key that is larger than the prefix.
On a scan operation, combining this filter with a start row improves the performance of the
scan. This example shows a prefix filter on row keys that start with the string highlighted here.
L10-27
®
Lesson 10: Advanced HBase Java API
The InclusiveStopFilter is a filter that stops scanning after the given row key bytes.
When we specify the stop row in the scan constructor, it stops just before the stop row. Also,
with scans, the start and stop row can specify just the leftmost part of a row key, in order to
return all rows starting with this part of the key.
We can use this filter to include the stop row. In this example the start row is set to a userId,
and it sets the filter to stop after the userId. This will only scan row keys starting with this
userId and will be an inclusive stop. Setting the stop row key in the scan constructor would not
work because it would cause it to stop before the userId.
L10-28
®
Lesson 10: Advanced HBase Java API
The timestampsFilter returns only cells whose version timestamp is in the specified list of
version timestamps. This filter allows fine-grained control over the versions that are returned to
the client.
L10-29
®
Lesson 10: Advanced HBase Java API
L10-30
®
Lesson 10: Advanced HBase Java API
L10-31
®
Lesson 10: Advanced HBase Java API
Decorating filters extend the behavior of another filter to provide more control over what data is
returned. The decorator pattern is a design pattern that allows behavior to be added to an
object.
Decorating filters are applied to another filter, like the decorator pattern they wrap an existing
filter, allowing behavior to be added to the filter.
L10-32
®
Lesson 10: Advanced HBase Java API
A SkipFilter wraps a given filter and extends it to exclude an entire row. As soon as the
wrapped filter indicates a value is to be omitted then the entire row is omitted. Without this
filter, the other non-zero valued columns in the row would still be emitted.
This is often used with a ValueFilter as shown in the example code here, to skip a row if
any of its values are zero.
L10-33
®
Lesson 10: Advanced HBase Java API
The WhileMatchFilter aborts the entire scan once a piece of information is filtered.
This works by checking the wrapped filter and seeing if it skips a row by its key, or a column of
a row because of a key-value check.
L10-34
®
Lesson 10: Advanced HBase Java API
The FilterList creates filtering logic by combining several filters in an ordered list. A
FilterList is created with an operator, which will be used with the list of filters to combine
their results.
The default operator is MUST_PASS_ALL, which means results in the final list are included if
they pass all filters. JUST_PASS_ONE means results are included if they pass only one filter.
You can add a filter after you have created an instance of FilterList by calling
addFilter.
L10-35
®
Lesson 10: Advanced HBase Java API
Here we see a small sample output for putting and then scanning this table without any
filtering.
L10-36
®
Lesson 10: Advanced HBase Java API
Here is an example using a FilterList to retrieve stocks for Amazon with a volume >=
1000.
A FilterList is used to combine three filters that we have already looked at before. A
RowFilter filters for the substring “AMZN” to get only amazon stocks. A QualifierFilter
filters for the volume column name. A ValueFilter filters for a value greater than or equal to
1000 long.
L10-37
®
Lesson 10: Advanced HBase Java API
L10-38
®
Lesson 10: Advanced HBase Java API
Here are some tips to improve filter performance. In general, we will want to narrow down the
data that we are filtering on. We can do this by limiting the rows scanned and by limiting the
columns or cells scanned.
L10-39
®
Lesson 10: Advanced HBase Java API
The filters are called in sequence, if we are using a filter list we should put the row key filters
first in the list.
When performing a table scan where only the row keys are needed, no families, qualifiers,
values, nor timestamps, we will add a FilterList with a MUST_PASS_ALL operator to the
scanner using setFilter().
The filter list should include both a FirstKeyOnlyFilter and a KeyOnlyFilter instance.
Using this filter combination will cause the region server to only load the row key of the first
key-value found and return it to the client, resulting in minimized network traffic.
L10-40
®
Lesson 10: Advanced HBase Java API
L10-41
®
Lesson X: Lesson Name
Answer: True
L10-42
®
Lesson X: Lesson Name
L10-43
®
Lesson 10: Advanced HBase Java API
L10-44
®
Lesson 10: Advanced HBase Java API
A counter is a special kind of column used to store a number that incrementally counts the
occurrences of a particular event or process.
L10-45
®
Lesson 10: Advanced HBase Java API
A counter is a long value in a column. When a counter is created the column is added and set
to whatever value we provide.
Counters are used to track activity in general like clicks, page hits, or ad display counts in web
applications. Counters provide a fast atomic increment operation, instead of running a get and
put which would be too expensive and time consuming.
There is a single column counter with the HTable method incrementColumnValue and
there is a multiple column counter with an Increment Object that is similar to the Put, Get
objects.
L10-46
®
Lesson 10: Advanced HBase Java API
L10-47
®
Lesson 10: Advanced HBase Java API
When using the HTable IncrementColumnValue method, we specify the exact column and
the amount to add and the result is returned. In this example the counter is the column called
clicks, the amount 42 long will be added to the clicks column. 1042 long will be returned.
L10-48
®
Lesson 10: Advanced HBase Java API
The increment method effect is based on the provided value. The increment method will
increment the counter when using a positive value, retrieve the current value of the counter
when using zero, and decrease the counter when using a negative value.
We do not need to initialize counters. They are set to the zero, or the specified amount, when
first using a new counter. The first increment call to a new counter will return one or whatever
value is specified.
L10-49
®
Lesson 10: Advanced HBase Java API
L10-50
®
Lesson 10: Advanced HBase Java API
With the Increment Object, an increment may be applied to multiple columns. The Increment
Object is similar to the Put and Get objects.
First, we instantiate an Increment Object with the row key to which the increment will be
applied. Next, we specify each column to increment with an addColumn() call. And finally
call HTable increment, passing the Increment Object.
L10-51
®
Lesson 10: Advanced HBase Java API
Here is an example of an increment operation on multiple columns for the shopping example
inventory table. In this example, we want to subtract from the pens quantity column and add to
the pens Mike column in one operation.
First, we instantiate the increment object with the pens row key. Then, we call addColumn for
the quantity and Mike columns, subtracting from the quantity column the amount that we want
to add to the Mike column.
And finally, we call [Link]() after which the pens quantity column will be 12, and
the Mike column will be one.
L10-52
®
Lesson 10: Advanced HBase Java API
Next, we will look at how to do this for all three rows as shown in the diagram here.
L10-53
®
Lesson 10: Advanced HBase Java API
Here is same example for three rows, pens, notepads, and erasers. For each row we want to
subtract from the quantity column and add to the Mike column. Remember that the atomicity is
by row.
For each row we instantiate the increment object with the row key. Then we call addColumn
for the quantity and Mike columns, subtracting from the quantity column the amount that we
want to add to the Mike column.
Finally we call [Link](). The result adds one pen, three notepads, and two
erasers to the Mike column values, and subtracts these amounts from the quantity column
values.
L10-54
®
Lesson 10: Advanced HBase Java API
Here is another example of an increment operation. We have two counter columns, a hits
column, and a clicks column. In this example we are working with a single column family
called hourly and a single row, rowA.
Assume that rowA is new and remember that a new counter starts off with zero. Remember,
passing no value to the new clicks counter will increment it to one. The result after
[Link](increment1) will be the click column value which is one, and the hits
counter will be 25.
The result after [Link](increment2) will be the clicks column value which is
one, and the hits counter will be 20. Passing zero to the clicks counter just read the value one
and passing a negative value to the hourly counter will decrement it.
L10-55
®
Lesson 10: Advanced HBase Java API
L10-56
®
Lesson 10: Advanced HBase Java API
Answers: 1 – T, 2 – T, 3 – F, 4 – T
L10-57
®
Lesson X: Lesson Name
L10-58
®
Lesson 10: Advanced HBase Java API
Congratulations! You have finished DEV 335, Lesson 10: Advanced HBase Java API.
Continue on the Lesson 11 of this course to learn about working with MapReduce on HBase.
L10-59
®
Lesson 11: Working with MapReduce on HBase
L11-1
®
Lesson 11: Working with MapReduce on HBase
L11-2
®
Lesson 11: Working with MapReduce on HBase
When you have finished this lesson, you will be able to:
• Define MapReduce
• Describe how MapReduce is used on HBase and
• Develop MapReduce programs for HBase
L11-3
®
Lesson 11: Working with MapReduce on HBase
The primary learning objective of this section is to understand the fundamentals of the
MapReduce programming paradigm.
L11-4
®
Lesson 11: Working with MapReduce on HBase
MapReduce was not invented at Google and did not start with Hadoop. For example, map and
reduce functions have been used in Lisp since the 1970s.
This example shows a map of the square function on an input list from 1 to 4. The square
function, since it is mapped, will apply to each of the inputs and produce a single output per
input in this case 1, 4, 9, and 16. The addition function reduces the list and produces a single
output which is the sum of the input.
L11-5
®
Lesson 11: Working with MapReduce on HBase
Google is the poster child for the power of the MapReduce paradigm, which is the engine
behind Hadoop. Google was the 19th search engine to enter a crowded market. You might
recall some of these old search brands, but you might not, because within a few short years,
Google emerged and dominated the search market.
L11-6
®
Lesson 11: Working with MapReduce on HBase
First, we want to crawl the web. This involves using web "spiders" to crawl the web, following
links within web pages to get to other web pages. Overall, this is the most time-consuming
step.
Second, we will sort the pages by URL and third, we remove the junk. A good search engine
removes "junk" from a known list of bad sites, or contextually based on the what is inside a
given page.
Fourth, we rank the results. The URLs are sorted for a given word or set of words based on
criteria like frequency, number of hits or freshness of page.
And finally, we will create an index. For each word, we create a list of URLs that contain that
word.
L11-7
®
Lesson 11: Working with MapReduce on HBase
The word count algorithm shown here is taken directly from the seminal paper on MapReduce
from Dean and Ghemawat. Algorithms for counting words existed before this paper was
published, but the mechanism for doing it using the MapReduce framework was novel, and the
algorithm is quite simple. This is true of most MapReduce programs.
The map method takes as input a key and a value, where the key represents the name of a
document and the value is the contents of the document. The map method loops through each
word in the document and emits a 2-tuple representing word, and 1 in this example.
The reduce method takes as input a key and a list of values, where the key represents a word.
The list of values is the list of counts for that word. In this example, the value is a list of 1’s.
The reduce method loops through the counts and sums them. When the loop is done, the
reduce method emits a 2-tuple representing word, and count.
L11-8
®
Lesson 11: Working with MapReduce on HBase
First, each mapper in the map phase takes an input list, such as the first line “Hello hadoop
world, hello” and maps it to a list of key-value pairs.
The reducer in the reduce phase takes an input of a key and the list of values associated with
that key, such as the two world and 1 pairs from the two different mappers, and emits a single
output for that word, world and the sum 2.
L11-9
®
Lesson 11: Working with MapReduce on HBase
In this high-level summary of the Hadoop MapReduce computational model, we see that there
are actually three phases in MapReduce: map, shuffle, and reduce. The data in the map phase
is split amongst the task tracker nodes where the data is located. Each node in the map phase
emits key-value pairs based on input one record at a time. The shuffle phase is handled by the
Hadoop framework. Output from the mappers is sent to the reducers as partitions. The data in
the reduce phase is divided into the partitions in which each reducer reads a key and iterable
list of values associated with that key. The reducers emit zero or more key-value pairs based
on the application logic.
L11-10
®
Lesson 11: Working with MapReduce on HBase
We collect source data on the Hadoop cluster, either by bulk copying data in, or by simply
accumulating data in the cluster over time. When we kick off a MapReduce job, Hadoop sends
map and reduce tasks to appropriate servers in the cluster, and the framework manages all the
details of data passing between nodes. Much of the compute happens on nodes with data on
local disks, which minimizes network traffic. Finally, we can read back the result from the
cluster.
L11-11
®
Lesson 11: Working with MapReduce on HBase
We start off with our input, maybe one file, maybe many files.
• The framework logically breaks up the input into “splits”. Each split contains many records. Records
can be any type of information: text, audio data, structured records, or any other type of data we want
to process. Each split typically corresponds to a block of data on a node where the data resides, but
the programmer doesn’t need to be aware of this.
• Each split is processed by a map task. Each record in a split is passed, independently of other
records, to the map() method. The map() method receives each record as a key-value pair, although
in some cases it might only be interested in either the keys or the values. The mapper emits key-
value pairs in response to the input record. It can emit zero records, or it can emit lot of records.
• The map output is partitioned such that all records of a particular key go to the same reduce task.
This phase involves a lot of copying and coordination between nodes in the cluster, but the
programmer does not have worry about these details.
• If the reduce method is like addition, where summing subtotals of terms is equivalent to summing all
individual terms, it is more efficient to split the reduce step and do some of it before shuffle. In this
case, a combiner method is used, which is often the same method as the reducer. This cuts down the
number of records that need to be copied from node to node.
• Whether or not a combiner is invoked, the framework will send the intermediate results from the
mappers to the reducers. The reduce() method receives a single key and a list of all values
associated with that key. The reducer, based on your logic, will emit 0 or more key- value pairs which
constitute the final results of this map-reduce job.
• Finally, the framework collects the reducer outputs so you can access the results.
Most of the parts here are handled by the framework. We only have to provide the code in the map and
reduce columns.
Note that, at this level of detail, the boxes do not represent nodes in the cluster. We are talking only
about logical flow of data. The framework issues map tasks and reduce tasks in parallel, and for
example, several map tasks might run concurrently on a single node.
L11-12
®
Lesson 11: Working with MapReduce on HBase
Let’s step through an example word-count program using MapReduce to count the occurrences of each
word in a set of input text files.
WordCount is the “Hello World” program for MapReduce, though counting strings may be of real use in
your programs.
As input, let’s say we have all Lewis Carroll’s books, and we want to count the occurrence of every word.
One of the nodes contains Tweedledee’s poem, “The time has come to talk of many things” and so forth.
In the case of text input, every line of text is a record. Each record gets fed individually to the map
method as key-value pairs. The key is the byte-offset into the file, the value is the text.
The map method tokenizes the input string, and outputs a key-value pair for every word. In this case, the
key is the word, and the value is simply 1. As you can see, the word “and” shows up twice, and it
produces two distinct key-value pairs
Some combining occurs before shuffle-and-sort. The combiner aggregates multiple instances of the
same key coming out of the mapper into a subtotal. In our case, the two instances of the string “and” get
combined, and only one record of value 2 goes through the shuffle.
The framework sorts records by key and, for each key, sends all records to a particular reducer. For
example, one particular reducer may get the keys: and, come, has, the & time. You can see that the
combined value 2 for “and” shows up here. The other values come from other map tasks.
Then begins the reduce phase, which just has to sum up values from the Mappers.
Finally, the framework gathers the output and deposits it in the file system where we can read it.
L11-13
®
Lesson 11: Working with MapReduce on HBase
L11-14
®
Lesson 11: Working with MapReduce on HBase
The primary learning objective of this section is to describe how MapReduce may be used in
HBase deployments.
L11-15
®
Lesson 11: Working with MapReduce on HBase
One way to leverage MapReduce with HBase is using the data in the HBase database as the
source of your data flow. An example of doing so is a set of well-defined Batch Analytical
Processing queries that we implement using MapReduce.
L11-16
®
Lesson 11: Working with MapReduce on HBase
Another way to leverage MapReduce in HBase is to use the HBase database as a sink for our
data flow. The input for the MapReduce application can come from any variety of sources,
including files. For example, we can use HBase in a MapReduce application with the
ImportTsv utility to bulk load data.
When writing a lot of data to an HBase table from a MapReduce job, such as
with TableOutputFormat, and specifically where Puts are being emitted from the mapper, we
will skip the reducer step. When a reducer step is used, all of the output (Puts) from the
mapper will get spooled to disk, then sorted/shuffled to other reducers that will most likely be
off-node. It is far more efficient to just write directly to HBase.
L11-17
®
Lesson 11: Working with MapReduce on HBase
The last way to leverage MapReduce in HBase is to use the HBase database as both a source
and sink in our data flow. One example of this use case is to calculate summaries across the
HBase data and then store those summaries back in the HBase database. For summary jobs
where HBase is used as a source and a sink, then writes will be coming from the reducer step.
L11-18
®
Lesson 11: Working with MapReduce on HBase
When we want to import a large set of data into an HBase table we can read the data using a
mapper, and after aggregating it on a per key basis, use a reducer and finally write it into an
HBase table. This involves all steps in MapReduce, including the shuffle and sort of
intermediate files.
But what if we know that the data already has a unique key? Should we go through the extra
step of copying and sorting when there is always just exactly one key-value pair? Wouldn't it
be better if we could skip that whole reduce stage? We can do that, and when we do, we will
harvest the pure computational power of all CPU's to crunch the data and writing it at top I/O
speed to its final target.
As seen in the matrix shown here, there are quite a few scenarios where we can decide if we
want Map only or both, map and reduce. When it comes to handling HBase tables as sources
and targets, there are a few exceptions to this rule.
L11-19
®
Lesson 11: Working with MapReduce on HBase
In this example, HBase is used as both the source and sink for the MapReduce processing.
The data is split based on regions and all map tasks that process data from the same region
are sent to that file server. Intermediate results from the mappers are shuffled into partitions
such that all the intermediate results with the same key belong to the same partition. There is
one partition per reducer and there may be more than one key in the same partition.
L11-20
®
Lesson 11: Working with MapReduce on HBase
L11-21
®
Lesson 11: Working with MapReduce on HBase
The primary learning objective of this section is to describe how to write MapReduce
applications, where HBase is a source, a sink, or both.
L11-22
®
Lesson 11: Working with MapReduce on HBase
The table above identifies the three choices you have to develop MapReduce applications for
HBase. Note that you’ll have more choices if you leverage other sources or sinks in your data
flow. It is not uncommon, for example, to read in data from HBase, perform MapReduce
operations on that data, and then store the results in an external RDBMS database.
L11-23
®
Lesson 11: Working with MapReduce on HBase
The graphic above illustrates the programming and execution model when using
TableMapReduceUtil for MapReduce jobs in HBase. Note that the Map() and Reduce() steps
identify the code we have to write. The rest of it is handled by the TableMapReduceUtil class
and the underlying Hadoop framework. Specifically, the table scan is performed by
TableMapReduceUtil, and the resulting rows from HBase are fed to Map() calls.
L11-24
®
Lesson 11: Working with MapReduce on HBase
This shows the input and output for the TableMapper class map method and the TableReducer
class reduce method for reading from and to HBase.
A scan result object and row key are sent to the mapper map method one row at a time, one or
more key-value pairs are output by the map method. The reducer reduce method receives a
key and an iterable list of corresponding values and outputs a Put object.
L11-25
®
Lesson 11: Working with MapReduce on HBase
This shows the skeleton code for how to write your mapper class extending the TableMapper
class which we looked at in the previous slide. The TableMapper class extends the
base mapper class to add the required input key and value classes.
L11-26
®
Lesson 11: Working with MapReduce on HBase
This shows the skeleton code for how to write your reducer class extending the TableReducer
class.
The TableReducer class extends the basic Reducer class to add the required key and value
input/output classes. The output value must be either a Put or a Delete instance when using
this class.
L11-27
®
Lesson 11: Working with MapReduce on HBase
This shows the TableMapReduceUtil init methods that we will use in the driver class for
initializing the mapper and reducer jobs. With these methods we can set the input and output
tables, the classes, input types, and job.
L11-28
®
Lesson 11: Working with MapReduce on HBase
The slide above describes the Flat-Wide schema used in the code examples that follow. Note
there are three column families: price, vol, and stats in the table. The prices are written as cell
versions in each hour from “00,” midnight, to “23,” 11pm.
L11-29
®
Lesson 11: Working with MapReduce on HBase
This slide above summarizes the code example we will walk through in the next few slides.
First, we will talk about Driver class.
L11-30
®
Lesson 11: Working with MapReduce on HBase
The StockDriver class implements the Tool interface and implements the run method.
Tool is the standard for any MapReduce tool/application. The driver class delegates the
handling of standard command-line options to the [Link](Tool, String[]) method.
The driver class contains a main method which calls the run method with the arguments
passed to the command line.
Next, we will look at the details for the run method shown here.
L11-31
®
Lesson 11: Working with MapReduce on HBase
First, we will set up the job. To do this, we will set the jar file to the StockDriver class. We then
instantiate a scan object, and request all cell versions and the price column family to be
returned.
Next, we initialize the map and reduce classes, the input and output table, and the parameters
using the TableMapReduce Util methods. The scan object is passed to the
initTableMapperJob() method.
Finally, we launch the job. waitForCompletion will launch the job if it’s not already running.
L11-32
®
Lesson 11: Working with MapReduce on HBase
TableMapReduceUtil instantiates a ResultScanner before calling the map method and passes
it the iterable results from the scan.
One map task is launched for every region in the HBase table. In other words, the map tasks
are partitioned such that each map task reads from a region independently.
L11-33
®
Lesson 11: Working with MapReduce on HBase
Next, we will look at the code for the TableMapper class, which processes records from
the scan and emits intermediate key-value pairs.
L11-34
®
Lesson 11: Working with MapReduce on HBase
This again is the HBase table schema for our example, showing the data that will be in the
iterable results from the scan, which are passed to the map method. When we then
instantiated the scan object, we requested all cell versions and the price column family to be
returned.
L11-35
®
Lesson 11: Working with MapReduce on HBase
The StockMapper class extends the TableMapper class from the TableMapReduceUtil
package.
Input to the map method includes the key and row from the scan result, along with the job
context for this map job. The map method outputs a key-value by calling the job context write
method with the output key and value corresponding to the input row key.
L11-36
®
Lesson 11: Working with MapReduce on HBase
This shows the details for creating the output key-value pairs in the map method. When we
instantiated the scan, we requested all cell versions to be part of the scan result. This code
iterates over all of the cell versions for every column in the row to emit a stock symbol and
price for each cell in the input row.
L11-37
®
Lesson 11: Working with MapReduce on HBase
Next we will go over the unit test for the StockMapper class. MRUnit makes it easy to test
MapReduce jobs including the HBase ones.
This slide shows the setup for the unit test. The map driver is a harness that allows us to test a
mapper instance. To set up the MapDriver with pass in our mapper class which we want to
test.
L11-38
®
Lesson 11: Working with MapReduce on HBase
This shows the implementation of the Unit test for the StockMapper class.
The map driver is a harness that allows us to test a mapper instance. First we set up the input
key and value that should be sent to the mapper, in this case a row key and result object. We
then call the MapDriver method passing the input for the mapper.
The harness will deliver the input to the mapper and return the mapper output. Next ,we test
the map result with the expected output.
L11-39
®
Lesson 11: Working with MapReduce on HBase
L11-40
®
Lesson 11: Working with MapReduce on HBase
TableMapReduceUtil and the MapReduce framework takes care of sorting the keys, sending a
key and corresponding values to the reducers, and writing the output from the reducers to the
HBase table.
L11-41
®
Lesson 11: Working with MapReduce on HBase
Remember the reducer will output the Put row objects with the value for the stats column
family min column.
L11-42
®
Lesson 11: Working with MapReduce on HBase
The reduce method has three inputs: the input key, an iterable list of prices associated with
that key, and the job context associated with the reduce job.
This reduce method outputs a row key and Put object with the context write() method. This Put
object will be written to the table by the framework. Next, we will look at the details of
calculating the min value and creating the Put object.
L11-43
®
Lesson 11: Working with MapReduce on HBase
This reduce method iterates over the input prices to find the minimum value. Other statistics
could be calculated in a similar fashion. The reduce method then constructs a Put object with
the row key and adds the minimum value to the stats column family min column. Then it
outputs the put the [Link] method.
L11-44
®
Lesson 11: Working with MapReduce on HBase
This diagram shows that the constructed Put object value is for the min column in the stats
column family.
L11-45
®
Lesson 11: Working with MapReduce on HBase
Here is the setup for testing the StockReducer: we instantiate the StockReducer and pass it to
the ReduceDriver class’s newreducedriver() method.
L11-46
®
Lesson 11: Working with MapReduce on HBase
First, we provide a row key and a set of price values corresponding to inputs that should be
sent to the reducer, as if they came from a mapper. By calling the ReduceDriver run method,
the harness will deliver the input to the reducer and return its outputs.
Next, we check the output from the reducer against the expected results, in this case the
output should be a put object with the min value corresponding to the input prices.
L11-47
®
Lesson 11: Working with MapReduce on HBase
The slide above defines some hints and tips for writing MapReduce applications in an HBase environment.
Limit child JVM memory
Number of concurrent tasks is limited by per machine RAM
Speculative execution is a feature of Hadoop wherein a map or reduce task is scheduled to multiple data nodes. This is a performance enhancement that
will take the results from the first data node to produce the results. However, when there is an outside sink for your data (i.e. HBase), you may find that
results have been written multiple times. Therefore, it is recommended to turn off speculative execution when running MapReduce applications with HBase.
By default, the Hadoop framework creates one split per region. If you wish to split the data in a custom way, then you need to override the
[Link]() method of the TableMapReduceUtil class.
The ToolRunner utility in Hadoop allows users to run MapReduce jobs as java programs (rather than using the Hadoop command directly). It also allows
you to leverage the Hadoop GenericOptionsParser if you have multiple command line arguments. The GenericOptionsParser enables any ordering of
command-line arguments (rather than relying on the user to know the implicit ordering of args[0], args[1], … etc).
Be judicious with your data types. Constructing String objects, for example, is expensive. You must use subclasses of writable for how data gets stored in
your HBase database, but you are free to use whatever makes sense in your MapReduce applications as data types local to your map and reduce programs
when you are transforming your data.
Scan Caching
If HBase is used as an input source for a MapReduce job, make sure that the input Scan instance to the MapReduce job has setCaching set to something
greater than the default (which is one). Using the default value means that the map-task will make call back to the region-server for every record processed.
Setting this value to 500, for example, will transfer 500 rows at a time to the client to be processed. There is a cost/benefit to have the cache value be large
because it costs more in memory for both client and RegionServer, so bigger isn't always better.
Scan settings in MapReduce jobs deserve special attention. Timeouts can result (e.g. UnknownScannerException) in Map tasks if it takes longer to process
a batch of records before the client goes back to the RegionServer for the next set of data. This problem can occur because there is non-trivial processing
occurring per row. If you process rows quickly, set caching higher. If you process rows more slowly (e.g. lots of transformations per row, writes), then set
caching lower.
Whenever a Scan is used to process large numbers of rows (and especially when used as a MapReduce source), be aware of which attributes are selected.
If [Link] is called then all of the attributes in the specified Column Family will be returned to the client. If only a small number of the available
attributes are to be processed, then only those attributes should be specified in the input scan because attribute over-selection is a non-trivial performance
penalty over large datasets.
When writing a lot of data to an HBase table from a MapReduce job (e.g., with TableOutputFormat), and specifically where Puts are being emitted from the
Mapper, skip the Reducer step. When a Reducer step is used, all of the output (Puts) from the Mapper will get spooled to disk, then sorted/shuffled to other
Reducers that will most likely be off-node. It's far more efficient to just write directly to HBase.
For summary jobs where HBase is used as a source and a sink, then writes will be coming from the Reducer step (e.g., summarize values then write out
result). This is a different processing problem than from the above case.
Here is how to turn off speculative execution:
hadoop jar /opt/mapr/hadoop/hadoop-0.20.2/[Link] terasort \
-[Link]=300 \
-[Link]=false \
-[Link]=false \
-[Link]=false \
L11-48
®
Lesson X: Lesson Name
L11-49
®
Lesson X: Lesson Name
Congratulations, you have completed Lesson 11. Continue on to Lesson 12 to learn about the
bulk loading of data.
L11-50
®
Lesson 12: Bulk Loading of Data
Welcome to DEV 340, Lesson 12, Bulk Loading Data into MapR Tables.
L12-1
®
Lesson 12: Bulk Loading of Data
L12-2
®
Lesson 12: Bulk Loading of Data
In this lesson, we are going to discuss several techniques to import data in bulk with an
emphasis on importing from a SQL database.
MapR extended the HBase HFileOutputFormat class to write to a MapR-DB table using
bulk load. Any HBase MapReduce program which works with HFileOutputFormat will also
work with MapR bulk load. Standard HBase MapReduce jobs for bulkload work with MapR-DB
as well.
L12-3
®
Lesson 12: Bulk Loading of Data
L12-4
®
Lesson 12: Bulk Loading of Data
L12-5
®
Lesson 12: Bulk Loading of Data
Here is a review of the HBase write steps before we look at bulk loading. Every update writes
to the write ahead log on disk, shown here as WAL, and to memory, referred to as the
MemStore. The MemStore stores updates in memory as sorted key-values, the same way that
it will be stored when it is flushed to disk into an HFile.
L12-6
®
Lesson 12: Bulk Loading of Data
1. Transform the source data into the native file format used by MapR-DB tables.
2. Notify the database of the location of the resulting files.
A full bulk load operation can only be performed to an empty table and skips the write-ahead
log typical of Apache HBase and MapR-DB table operations. This results in increased
performance. Incremental bulk load operations DO use the write-ahead log.
L12-7
®
Lesson 12: Bulk Loading of Data
We can bulk load using a MapReduce job to load data directly into MapR-DB, with the
following steps:
1. Transform the source data into the native file format used by MapR tables.
• The map task emits key-value pairs.
• The MapReduce framework collects and sorts the keys for partition and
sends them to the reducers.
• Each reducer create files in MapR-DB table format.
L12-8
®
Lesson 12: Bulk Loading of Data
Bulk loading can be performed as a full bulk load or as an incremental bulk load. A full bulk
load offers the best performance advantage for empty tables. Incremental bulk loads can add
data to existing tables concurrently with other table operations. This will provide better
performance than Put operations, but is allowed only at table creation time.
L12-9
®
Lesson 12: Bulk Loading of Data
Apache HBase needs two stages for bulk loading: First, to generate HFiles and second, to load
HFiles into the table.
MapR-DB needs only one stage, which is to directly load data into table.
Bulk loading is supported for the following tools, which can be used for both full or incremental
bulk load operations:
L12-10
®
Lesson 12: Bulk Loading of Data
After completing a full bulk load operation, take the table out of bulk load mode to restore
normal client operations. You can do this from the command line or the HBase shell with the
commands shown here:
L12-11
Lesson 12: Bulk Loading of Data
This table shows the performance of MapR-DB bulk load compared to HBase.
L12-12
®
Lesson 12: Bulk Loading of Data
L12-13
®
Lesson 12: Bulk Loading of Data
Now, let’s learn about how to use the ImportTsv bulk load tool.
L12-14
®
Lesson 12: Bulk Loading of Data
ImportTsv is a very efficient tool to load text data into MapR-DB or HBase.
• We will first need to export the data from the RDBMS to a TSV file using our standard tools.
This is faster than executing SQL on RDBMS for large amounts of data.
• We can then run ImportTsv on the TSV file. ImportTsv runs a MapReduce job to perform
the import.
L12-15
®
Lesson 12: Bulk Loading of Data
ImportTsv is a utility provided by the HBase package. The TSV file must contain a field
representing the row key of the HBase table row. Usually the format is: row key \tab value1
\tab value2…up to valueN.
From a SQL store, dump data into a text file and then use ImportTsv. This is faster than
executing SQL on RDBMS for large amounts of data.
The ImportTsv tool will only read data from MapR-FS or HDFS. The TSV file we want to import
must first be on our cluster file system. This usage will load the data via Puts (non-bulk
loading).
L12-16
®
Lesson 12: Bulk Loading of Data
Before we start the job, we need to create the MapR-DB table that we want to import into. We
will also need to create the column families and set the column family properties for our
schema. Once the job is started, we can monitor it via the web UI.
Usage:
- Imports the given input directory of TSV data into the specified table.
- The ImportTsv tool itself is a Java class included in the HBase JAR file. We will therefore
run the tool by executing the Hadoop JAR command. This command will start the Java
process and add all dependencies to it.
- The JAR to run is specified by the first parameter of the Hadoop JAR command, and
includes the version of HBase we have installed in the name.
- Invoking ImportTsv without any arguments will show what the various options are.
-[Link]
- Comma-separated column names of TSV data, where each column name is either a
simple column qualifier, or a columnfamily:qualifier.
- HBASE_ROW_KEY is used to designate that this column should be used as the row key
for each imported record.
- Specify exactly one column to be the row key and specify a column name for every
column that exists in the input data.
- Including the -[Link] will generate files for loading (i.e. bulk loading)
Map phase of the job, it reads and parses rows from TSV files under the specified input
directory and Puts rows into the HBase table using the column mapping information. The Read
and Put operations are executed in parallel on multiple servers, so it is much faster than
loading data from a single client. By default, there is no reduce phase in the job.
L12-17
®
Lesson 12: Bulk Loading of Data
As before, after we complete a full bulk load operation, we need to take the table out of bulk
load mode to restore normal client operations.
L12-18
®
Lesson 12: Bulk Loading of Data
This process of using ImportTsv works well when you are bulk loading large amounts of data
as it is using MapReduce framework to process (shard) the input file to divide the file/task into
many mapping tasks. It is fast to use ImportTsv to input data into HBase once we have
converted it into a TSV file.
L12-19
®
Lesson X: Lesson Name
Next, let’s work on the lab exercises to use the ImportTsv tool and the CopyTable method for
Lab 12.2.
L12-20
®
Lesson 12: Bulk Loading of Data
L12-21
®
Lesson 12: Bulk Loading of Data
L12-22
®
Lesson 12: Bulk Loading of Data
When we use the ImportTsv utility to bulk load data from our HDFS or MapR-FS file system, it
runs a MapReduce application to load the data into the table. We can write a custom
MapReduce job to perform bulk loading if ImportTsv does not meet all of your requirements.
L12-23
®
Lesson 12: Bulk Loading of Data
Custom MapReduce jobs can use bulk loads with the configureIncrementalLoad() method from
the HFileOutputFormat class.
The HFileOutputFormat class on MapR clusters distinguishes between Apache HBase tables
and MapR tables, behaving appropriately for each type. Existing workflows that rely on
the HFileOutputFormat class, such as the ImportTsv and copytable tools, support both types of
tables without further configuration.
Any HBase MapReduce program which works with HFileOutputFormat will also work with
MapR bulk load. Standard HBase MapReduce jobs for bulkload work with MapR-DB as well.
L12-24
®
Lesson 12: Bulk Loading of Data
The code in this example is similar to what we saw in the MapReduce with HBase lesson.
Custom MapReduce jobs can use bulk loads with the configureIncrementalLoad() method from
the HFileOutputFormat class as shown here highlighted in red.
In this example, the reducer class is null because there isn't actually a reducer step.
Remember when Puts are being emitted from the mapper, you can skip the reducer step since
the framework is already sorting the key-values from the mapper.
Next, we will look at the code for the mapper which will create a Put object and emit it, the
TableOutputFormat class from the framework will take care of sending the Put to the target
table.
L12-25
®
Lesson 12: Bulk Loading of Data
This code is similar to what we covered earlier when we first looked at MapReduce, except the
Put is being emitted from the mapper instead of the reducer.
Here we assume that the records are line oriented so extracting the data consists in calling
substring on each line to retrieve elements that go into the row key, the column names, and
values. The row key, column names, and values, constructed from the input, are used to
create the Put object, then the Put object is emitted with the row key. The
TableOutputFormat class from the framework will take care of sending the Put to the
target table.
L12-26
®
Lesson X: Lesson Name
Open your lab guide to complete Lab 12.3: Use a custom MapReduce program that reads a
text file, processes the data in map function and the uses Put to write to the HBase table.
L12-27
®
Lesson 12: Bulk Loading of Data
L12-28
®
Lesson 12: Bulk Loading of Data
L12-29
®
Lesson 12: Bulk Loading of Data
As a reminder, HBase tables are split into sequences of rows, by key range, called regions.
L12-30
®
Lesson 12: Bulk Loading of Data
When a region grows too large, it splits into two child regions. It is also possible to pre-split a
table by key range when you create it.
L12-31
®
Lesson 12: Bulk Loading of Data
When you create a table you can pre-split it. There are three ways you can do this, as
discussed before you can use the Java HBase admin API, you also can use the MapR Control
System, or the HBase shell. Here is an example of using the HBase shell to pre-split a table.
You can specify the split keys in an array as shown or you can read the split keys from a file.
This creates and pre-splits a table with an initial set of empty regions defined by the specified
split keys. The total number of regions created will be the number of split keys plus one. They
form the start and end keys of the regions created. Here the split keys are e, m, and s, which
creates four regions, with e as the end key for region one and u as the start key for region four.
L12-32
®
Lesson 12: Bulk Loading of Data
Here the split keys are e, m, and u, which creates four regions, with e as the end key for region
one and u as the start key for region four.
L12-33
®
Lesson 12: Bulk Loading of Data
We have discussed several techniques to import data in bulk with an emphasis on importing
from a SQL database.
• We looked at a simple client using the HBase API and the java JDBC API to transfer data
from a SQL database to MapR-DB,
• We looked at using ImportTsv, a built-in HBase tool to import text data,
• We saw how we can use a MapReduce task and took a look at some other options/clients
like Thrift, Hive, Pig, Rest, and CLI,
• Finally, we saw how to optimize by pre-splitting table before importing data into it.
L12-34
®
Lesson 12: Bulk Loading of Data
Congratulations, you have finished DEV 340 Lesson 12, Bulk Loading Data into MapR Tables.
Continue to Lesson 13 to learn about Performance.
L12-35
®
Lesson 13: Performance
Welcome to DEV 340, HBase Applications: Bulk Loading, Security, and Performance, Lesson
13: Performance.
L13-1
®
Lesson 13: Performance
L13-2
®
Lesson 13: Performance
When you have completed this lesson, you will be able to:
L13-3
®
Lesson 13: Performance
L13-4
®
Lesson 13: Performance
L13-5
®
Lesson 13: Performance
Before we talk about performance, here is a review of some terms we will be using:
Online systems have low-latency requirements, because there is a person waiting for a quick
response.
The intent to be an online or an offline system influences many technology decisions when
implementing an application. HBase is an online system. Its tight integration with Hadoop
MapReduce makes it equally capable of offline access as well.
For online systems, low latency or a fast response time is the priority.
For batch systems, a high throughput is the priority.
L13-6
®
Lesson 13: Performance
When HBase is used as the backend store for a web application, the data access pattern is
read heavy and the priority is fast, low latency reads.
L13-7
®
Lesson 13: Performance
Reading from memory is really fast. If the data is frequently read and not frequently updated
then caching will give you faster reads. You can set a column family in memory property for
frequently read data.
This is good for Gets and short Scans, however this will not be faster for tablewide Scans
since the cache will become full. For fast Gets and short Scans the row key design is critical.
For nodes that have very large memory and with very high MapR-DB performance
requirements, increasing memory cache allocation as high as 70% or more may be beneficial.
L13-8
®
Lesson 13: Performance
A distributed messaging application using HBase as the backend will be read and write heavy
and the priority is low latency writes and reads.
L13-9
®
Lesson 13: Performance
For the use case of machine generated data or sensors being stored in HBase, the data
access pattern will be write heavy low latency writes.
L13-10
®
Lesson 13: Performance
For fast writes it is important to distribute the writes across the cluster so that writes can
happen in parallel to different region servers. The row key needs to be designed to not hot spot
on one region server.
L13-11
®
Lesson 13: Performance
To provide fast reads for online web sites or an online view of data for data analysis,
MapReduce jobs can bulkload and/or reorganize the data into precomputed views or
materialized views.
Remember, designing for reads means aggressively denormalizing data so that the data that is
read together is stored together.
L13-12
®
Lesson 13: Performance
For fast reads, the data that will be read together should be stored together. In order to group
the data together you can:
For fast Gets put related data for a Get in a single row. For Scans put related data in
contiguous rows. You need to think about the row key!
L13-13
®
Lesson 13: Performance
For faster write throughput you should make sure to use batch Puts. Tall skinny tables will
allow you to write more rows per second. Pre-splitting the table and using compression can
also speed up throughput.
L13-14
®
Lesson 13: Performance
Offline HBase ETL data access patterns, such as MapReduce or Hive, are characterized by
high latency reads and high throughput writes. Offline systems don’t expect a response
immediately.
L13-15
®
Lesson 13: Performance
Nathan Marz came up with the term Lambda Architecture (LA) for a generic, scalable, and
fault-tolerant data processing architecture.
The Lambda Architecture has three layers: the batch layer, the serving layer, and the speed
layer.
2. The serving layer provides a precomputed view for fast reads. The serving layer updates
whenever the batch layer finishes precomputing a batch view.
3. The speed layer only produces views on recent data, the speed layer is for functions
computed on data in the few hours not covered by the batch. The speed layer updates the
real time view as it receives new data.
L13-16
®
Lesson 13: Performance
L13-17
®
Lesson 13: Performance
L13-18
®
Lesson 13: Performance
We have seen that with HBase you have to think about how your data will be read up front.
You have to design your schema based on your access patterns, you can not rely on queries
with secondary indexes and joins like with a relational database. This means more planning for
data access patterns is needed, but on the other hand, HBase can scale across a cluster,
unlike a relational database.
When you are designing your HBase application for performance there are three significant
areas:
1. The row key design is critical in order to find and read data efficiently.
2. The column family design allows you to group the columns together that most typically will
be read together and separate those which will not.
3. In-memory column families allow for super fast reads for data that is frequently read and
not too big to cache.
L13-19
®
Lesson 13: Performance
Your schema design will impact performance directly when you read and write data. You
should think about your use cases and data access patterns compared to the ones we just
discussed. Are your priorities low latency reads or writes or high throughput?
L13-20
®
Lesson 13: Performance
For fast Gets, it’s possible to model one-to-many relationships in HBase as a single row. In this
example, the order and related line items are stored together and can be read together with a
single get on the row key.
L13-21
®
Lesson 13: Performance
For fine grained scanning you can put a lot of information in the row key in a tall table, like in
the OpenTSDB example shown here, which we discussed in the schema design lecture.
L13-22
®
Lesson 13: Performance
You should make sure your row key distributes writes, to avoid hot spotting.
L13-23
®
Lesson 13: Performance
Make sure your key groups related data together for scanning in order to minimize seeks. You
may have to test to make sure that your key design actually works with usage.
L13-24
®
Lesson 13: Performance
Row keys determine data locality since HBase data is stored in key-value format in files sorted
by the key. The row key is very significant for read performance, since reading data that is
stored together is fast. Your row key must be unique and should have useful meaning for
finding data.
L13-25
®
Lesson 13: Performance
Row keys determine data locality, grouping related data together is super fast for data that
should be read together. Examples of this are when you want to scan by a partial key of
related web URLs like [Link] to read the counter of likes per page.
L13-26
®
Lesson 13: Performance
Grouping and ordering is also good when you want a sorted retrieval, for example by stock
symbol and most recent transaction time.
L13-27
®
Lesson 13: Performance
Grouping is also good for location related questions like what restaurants or hotels are near
this city.
L13-28
®
Lesson 13: Performance
Grouping related data that is frequently read together is super fast for caching, reading from
memory is always faster than from disk. In-memory column families will remain in memory with
the Least Recently Used being taken out when the cache is full. Composite keys are good for
fast range scans because full table scans will cause the memory to get full and will not benefit
from caching.
Grouping data together is not good when your data is not well distributed across your cluster,
for example when the number of Gets/Puts you are observing are not spread out across region
servers. An example of this is for Facebook’s messaging system, where Zuckerberg got way
more messages than the average user.
L13-29
®
Lesson 13: Performance
L13-30
®
Lesson 13: Performance
L13-31
®
Lesson 13: Performance
When updating small records, you will get better performance by grouping puts into fewer
RPCs.
Whether you use a single Put or a list of Put operations, each call will be sent one at a time
because autoflush is on by default. Disabling autoflush will prevent having each call be sent
separately to the server. By turning it off, the Put operations will be sent when the write buffer
is full instead.
L13-32
®
Lesson 13: Performance
If you are making a lot of Put calls then disabling autoflush will prevent having each call be
sent separately to the server, because autoflush is on by default. By turning it off, with
[Link](false) the Put operations will be sent when the write buffer is full
instead. You can either explicitly call flushCommits() to flush messages or
flushCommits() will be called implicitly when you close the table.
L13-33
®
Lesson 13: Performance
As discussed before Network roundtrips are expensive. Another way to get better performance
with fewer RPCs is to use the HTable Put List or Batch methods, shown here. These methods
let you pass a list of objects for updating or reading, which will be sent in one RPC call.
L13-34
®
Lesson 13: Performance
Here are some optimizations you should consider when using the API.
Remember that with a Get or Scan you should narrow down the data returned, otherwise all of
the row will be returned.
If you are only interested in some of the columns in a column family, you will get better
performance by specifying the columns with addColumn(), this will transfer less data to the
client.
L13-35
®
Lesson 13: Performance
As a review, you can use filters to reduce the amount of data transferred to the client. On the
server side the filter is used to determine whether a record should be returned back to the
client side.
L13-36
®
Lesson 13: Performance
Reducing object allocation for converting bytes for reading/writing to HBase can make a huge
difference in performance. You can reduce object allocation by setting up such variables once
with “public static final“ instead of calling [Link] every time you need to specify a table
component. This saves resources and is more efficient.
L13-37
®
Lesson 13: Performance
You should close the ResultScanner to release resources which are no longer needed. This
affects things on the server more than on the client side and you should do this so that you
don’t create performance issues on the server side.
To avoid performance problems always have ResultScanner processing enclosed in the finally
of a try/catch blocks, this means it will always be called, if an exception is thrown or not.
L13-38
®
Lesson 13: Performance
You can use the MapR control system, the HBase shell, or the Java API to set properties for
column families.
You can set the Max Min versions, compression type, time-to-live, and the in-memory setting,
whether the CF is kept in memory or not.
Time-to-live TTL:
Specified in seconds and is by default set to Integer.MAX_VALUE or 2,147,483,647
seconds. That’s 2 billion 147 million etc. 2^31.
If a value exceeds its TTL it is dropped and this is based on the timestamp. This is
in addition to the number of versions that get kept.
In-memory
Defaults to false.
Setting it to true is not a guarantee that values are loaded into memory nor that they
stay there. It’s more like an elevated priority. This setting is useful for small column families
with few values, like passwords in a user table.
L13-39
®
Lesson 13: Performance
You can set the compression type on the column family, lz4, the default, is faster but gives less
compression, Zlib is slower but gives max compression. There is a tradeoff between
compression ratio and speed. Compressed data uses disk space and less bandwidth on the
network than uncompressed data, however for scanning files it is slower for reads.
L13-40
®
Lesson 13: Performance
L13-41
®
Lesson 13: Performance
L13-42
®
Lesson 13: Performance
As a cell value passes through the system, it'll be accompanied by its row, column name, and
timestamp. They take up space in the RPC calls, in-memory, indexes, and on disk.
ColumnFamilies, column qualifiers, and row keys could be repeated several billion times.
• You should keep row keys as short as is reasonable such that they can still be useful for
required data access.
• Keep the ColumnFamily names as small as possible, preferably one character, for
example, "d" for data.
• Keep the column names as small as possible, preferably one-three characters,
abbreviate!
• You can use a lookup table to keep names short and meaningful, like in the openTSDB
example.
For write throughput you can put more data with fewer columns verses lots of columns with
small values. Note, MapR-DB does not store the full coordinates for each cell on disk like
HBase, but size still matters. MapR-DB does not repeat the key or the CF details for anything,
while HBase does. So, the number of bytes transferred from disk to find a subset of columns in
a row is far less compared to HBase. Consequently, the in-memory footprint in MapR-DB is
much tinier, leading to more efficient use of the memory. MapR-DB does store the column
qualifier as part of data.
L13-43
®
Lesson 13: Performance
A container is 32 GB when full. MapR-DB regions are 4 to 7 GB, more than one region may fit
in a container. The average size of a container is 25 GB, assuming it's only partially full.
L13-44
®
Lesson 13: Performance
The limit for the number of Column Families for a MapR-DB Table is 64 , with HBase 3 is the
recommendation. You can have billions of rows.
The row size limit is enforced at the RPC level (per Put/Get).
The Sweet Spot for row size is between 100 bytes to 50k
The Default max row size is 16MB. Marshalling/unmarshalling giant objects is not a good idea,
otherwise Performance will drop dramatically .
MapR-DB comes with a read/write file-system built-in. For larger rows, you can put the data
into a file and put the file-name into the row. Unlike HBASE with HDFS , MapR-DB has no limit
on the number of files, so create as many as you like.
L13-45
®
Lesson 13: Performance
MapR tables support rows up to 2 GB in size. Rows in excess of 100MB may show decreased
performance. You can configure this maximum by changing the value of the
[Link] value.
L13-46
®
Lesson 13: Performance
You can increase memory settings in [Link]. This is controlled by the three
parameters in the [Link] file shown here.
The target percent for MFS Cache to use is typically 35%, but for MapR-DB table specific
usages, especially on nodes that have very large memory and with very high performance
requirements, increasing memory cache allocation as high as 70% or more may be beneficial.
L13-47
®
Lesson 13: Performance
MapR uses very efficient caching techniques for all of its file system and MapR-DB data:
• There is no duplicate caching like in HBase - data is cached in the filesystem, and shared
with MapR-DB.
• Paging of cached data happens using a Least Recently Used being evicted. Key data
structures such as indexes are kept in-memory.
• Using these key data structure in-memory means out-of-memory data accesses require
only one disk seek.
• Since all reads happen from the container master node, the replicas do minimal caching
of MapR-DB data.
L13-48
®
Lesson 13: Performance
You should monitor that your data is spread out over regions for good concurrency. If you don’t
have enough regions, you should split your table to spread out your data for better
concurrency.
L13-49
®
Lesson 13: Performance
L13-50
®
Lesson 13: Performance
1. The HBase table is too small and did not grow enough to split across all the servers.
This can happen when a server was added later to the cluster after the table has data in
it. If the table is not growing, the table will not be splitting regions to the new server: The
result will be an unbalanced distribution of regions and requests to regions.
2. Another cause of hot-spotting can be when the the row key was not well designed:
• In the schema design lecture we discussed how a row key with a sequential
Id like a timestamp will cause the rows to be inserted in sequential order,
which will result in hot-spotting.
• Also a row key with a UserId for a social application like messaging, where
one user is a lot more active than others could cause an unequal distribution
on the region server for very active users, which is what Facebook messaging
experienced.
L13-51
®
Lesson 13: Performance
Another possible cause is when the client was not designed well, for example when a client
processes large batches of key ordered requests and the client does not have a good spread
across the entire table. This can create hot zones, since the batch is going to the same key
range, and therefore region.
L13-52
®
Lesson 13: Performance
1. Check the number of Puts and Gets per second per node. This should be similar across
nodes on your MapR-DB cluster.
2. Look at the pattern of Gets and Puts, this should be distributed.
3. Check the number of regions per node, this should be similar on all nodes.
4. If you are adding new nodes, the existing table’s data needs to grow before it can spread
out automatically.
5. Think about the key design. Will it spread keys across regions? Review the schema
design lecture if you are having problems.
L13-53
®
Lesson 13: Performance
You can use the MCS Machine Performance pane to display the number of Get, Put, and Scan
operations performed during various time intervals to check for hot zones.
L13-54
®
Lesson 13: Performance
• Smaller regions will give you a better spread across nodes which can be better for parallel
processing across nodes.
• Larger regions will give you less key range location meta data to cache on the client side.
Next, we will look at how you can manually tune your region size, which you should not have to
do if you have a good row key design and application architecture from the beginning, but you
may have to do otherwise.
L13-55
®
Lesson 13: Performance
These are the commands which you can use to manually tune region size. See the MapR
documentation online for more details.
[Link]
L13-56
®
Lesson 13: Performance
L13-57
®
Lesson 13: Performance
L13-58
®
Lesson 13: Performance
We have been talking about tuning performance and we looked at some specifics. But just like
any application there are some general guidelines you should keep in mind and that you
should follow when you are looking into performance issues.
First, you should fully understand the architecture of the system and that includes all of its
components. As mentioned earlier, hardware and network may have an impact on
performance. If you are running into performance issues then first work on stabilizing the
system, which means fix critical bugs before you tackle performance issues. Once you have a
stable system, then you can measure the current performance. This will provide you with
numbers that make up a baseline.
You should set performance targets so you know what you are goals are and how far or close
your are to reaching them. With the numbers from the baseline you will be able to determine
how close to the targets you are.
Next, identify bottlenecks: use logging, profilers, monitoring tools like MCS. Fix the root cause
of bottlenecks.
This is an iterative process so integrate performance at the beginning of the development effort
if possible and keep an eye on it regularly. A way to do this is to build integration tests that
record performance for known operations or transactions.
A lot of these guidelines may seem obvious, but often times development teams fall into some
common traps. Let’s look at these next.
L13-59
®
Lesson 13: Performance
A popular tool for measuring performance is YCSB. We are going to run it in the lab as well.
Yahoo! funded research to come up with a standard performance-testing tool that could be
used to compare different databases. The company called it Yahoo! Cloud Serving Benchmark
(YCSB). YCSB is the closest there is to a standard benchmarking tool that can be used to
measure and compare the performance of different distributed databases. Although YCSB is
built for comparing systems, you can use it to test the performance of any of the databases it
supports.
L13-60
®
Lesson 13: Performance
The goal of the YCSB project is to develop a framework and common set of workloads for
evaluating the performance of different “key-value” and “cloud” serving stores.
YCSB consists of the YCSB client, which is an extensible workload generator, and the core
workloads, which are a set of workloads that comes prepackaged and can be generated by the
YCSB client.
The Client is extensible so that you can define new and different workloads to examine system
aspects, or application scenarios, not adequately covered by the core workload.
L13-61
®
Lesson 13: Performance
L13-62
®
Lesson 13: Performance
This shows the different prepackaged workloads that you can run with YCSB. As you see you
can test for various workloads from read only, to read/write 50/50.
L13-63
®
Lesson 13: Performance
While benchmarking you monitor usage for CPU, RAM, disk I/O, and network.
L13-64
®
Lesson 13: Performance
This table shows a YCSB benchmark comparing MapR-DB to Cloudera HBase. The last
column show the observed MapR-DB advantage in performance.
L13-65
®
Lesson 13: Performance
This shows read latency over time, a smaller read latency means faster response time, so
smaller is better. MapR-DB is shown in blue and Cloudera HBase in orange. The spikes for
Cloudera HBase are because of the impact of garbage collection and compactions on
performance. During the spikes the region server is experiencing garbage collection and
compaction causing I/O storms.
L13-66
®
Lesson 13: Performance
L13-67
®
Lesson 13: Performance
L13-68
Lesson 13: Performance
Here are some typical questions an admin would ask when monitoring and managing a MapR
cluster. We will glance over monitoring with MCS next, for more information you should take
the MapR Administrator training.
L13-69
®
Lesson 13: Performance
The DB Gets, Puts, Scans pane displays the number of Gets, Puts, Scan operations
performed during various time intervals.
The Machine Performance pane displays the following information about the node's
performance and resource usage since it last reported to the CLDB:
L13-70
®
Lesson 13: Performance
• Number of rows
• Start key and end key
• The physical size and logical size
• Which node the region is on
L13-71
®
Lesson 13: Performance
With MapR-DB there is no need for settings for region splits or data compaction. In a MapR-
DB table regions panel you can see the size and location of table data on the cluster.
L13-72
®
Lesson 13: Performance
Select a table to open the display à Initial view is the table regions tab
L13-73
®
Lesson 13: Performance
Primary node is the region’s original source for data and computation in an MapR-DB table.
The secondary nodes provide the replicas for data.
L13-74
®
Lesson 13: Performance
You can use the MapR control system to set properties for column families. You can set the
Max Min versions, Compression type, time-to-live, and the in-memory setting, whether the CF
is kept in memory or not.
L13-75
®
Lesson 13: Performance
L13-76
®
Lesson 13: Performance
Compression and in-memory is configurable at the column family level, which can help
performance.
L13-77
®
Lesson 13: Performance
You can use other tools to monitor performance besides MCS. We have discussed OpenTSDB
and you can use it to monitor performance by collecting information from your infrastructure
logs.
Nagios, Ganglia, and JMX are tools for monitoring actions in the cluster. They're not
specifically geared for analyzing jobs, but they may help you identify slow performance in the
cluster, which you can trace back to poor job structure.
More classical monitoring tools like Nagios can provide useful metrics.
Ganglia is an open source system for monitoring distributed systems.
JMX the Java Management Extensions framework can also be used for monitoring.
You can Mbeans to expose specific attributes and operations from your system to JMX. You
can then monitor things from a JMX client like the java console. Most projects in the Hadoop
ecosystem expose metrics via JMX and they can be enabled via configuration.
L13-78
Lesson 13: Performance
This is a screen shot of monitoring with Ganglia, which is a popular open source system for
monitoring distributed systems.
L13-79
®
Lesson 13: Performance
[Link]
L13-80
®
Lesson 13: Performance
L13-81
®
Lesson 13: Performance
Congratulations, you have finished DEV 340 Lesson 13, Performance. Continue to Lesson 14
to learn about Security.
L13-82
®
Lesson 14: Security
Welcome to DEV 340, HBase Applications: Bulk Loading, Security, and Performance, Lesson
14: Security.
L14-1
®
Lesson 14: Security
L14-2
®
Lesson X: Lesson Name
L14-3
®
Lesson X: Lesson Name
L14-4
®
Lesson 14: Security
L14-5
®
Lesson 14: Security
The core component of user authentication in MapR is the ticket. A ticket is an object that
contains specific information about a user, an expiration time, and a key. Tickets uniquely
identify a user and are encrypted to protect their contents. Tickets are used to establish
sessions between a user and the cluster.
MapR supports two methods of authenticating a user and generating a ticket: a
username/password pair and Kerberos. Both of these methods are mediated by
the maprlogin utility. When you authenticate with a username/password pair, the system
verifies credentials using Pluggable Authentication Modules (PAM). You can configure the
cluster to use any registry that has a PAM module.
L14-6
®
Lesson 14: Security
L14-7
®
Lesson 14: Security
L14-8
®
Lesson 14: Security
Permissions for MapR tables, column families, and columns are defined by Access Control
Expressions (ACEs).
An ACE (Access Control Expression) is a boolean expression of roles, users, groups and
boolean operators “&” “|” “!”, used to control MapR-DB access at table, column family, and
column level.
When a user, group, or role requests to read data from, or write data to a column, MapR-DB
checks whether that user, group, or role has read or write permission for the column family and
read or write permission for the column.
This screen shot shows the Edit Table Permissions screen which allows you to set
permissions for tables when you create or edit tables.
L14-9
®
Lesson 14: Security
Access Control Expressions can be defined on MapR-DB table Data from the table to the
column level, and on MapR-DB Table Operations such as add/delete column family,
split/merge, pack operations.
An ACE is defined by a combination of user, group, or role tokens with operators. !Negation
operator, & AND operator, |OR operator.
An example definition is (user 1001 or role engineering), which restricts access to the user with
ID 1001 or to any user with the role engineering.
In the next example, (group admin OR group qa), members of the group admin are given
access, and so are members of the group qa.
L14-10
®
Lesson 14: Security
The MCS GUI provides an expression builder that validates the correctness of the settings in
real-time.
L14-11
®
Lesson 14: Security
Here we see two groups GroupB and GroupA with users u1, u3, in GroupB. u2, u1 in GroupA,
and u4 in no group as shown.
A warning about the NOT operator: NOT implies a very large universe. For example "!groupA"
is everyone that isn't in groupA which is a lot of people. NOT is best used to limit some other
constraint. For example "groupB&!groupA“ which would be user u3 in this example.
L14-12
®
Lesson 14: Security
Table level ACEs mainly deal with restricting users from modifying table attributes, that is
adding/renaming/removing column families.
A new table's permissions default to the UID of the user creating the table. The creator of the
table has all rights by default, others have none.
L14-13
®
Lesson 14: Security
You can set default permissions for column families when you create or edit tables and you
can override these defaults when you create column families.
This shows the column family default permissions screen, which allows you to specify default
column family permissions when you are creating a table. These ACEs get inherited by column
families when they are created. These permissions default to the creating user, as shown here
user MapR, but this screen allows you to set the default to another ACE.
L14-14
®
Lesson 14: Security
As we said before, you can set default permissions for column families when you create or edit
tables, which will be inherited, you can also override these defaults when you create column
families.
Column family permissions are inherited when they are created, from the default column family
permissions, which you can set in the previous screen (column family default permissions).
The column family permissions screen shown here allows you to explicitly set ACEs for a
column family which will override the defaults.
L14-15
®
Lesson 14: Security
Unlike column families, columns are not predefined since users can dynamically add any
column during a put operation.
Column access is an AND of the permissions on the column family AND column, that is if a
user wants to Get or Put data from or to a particular column, he needs to pass access tests of
both ColumnFamily and Column. For example when a user, group, or role requests to read
data from, or write data to a column, MapR-DB checks whether that user, group, or role has
read or write permission for the column family AND read or write permission for the column.
For example, suppose user carol tries to write data to columns col1 and col2 in column
family cf1. MapR-DB checks whether carol has write permission on cf1 AND col1 AND col2.
If carol does not have all three permissions, MapR-DB returns an error that says access for the
write is denied.
If this user were to try to read from the same two columns, MapR-DB would simply not return
the data. If the user tried to read from those two columns and additional columns on which she
had read permissions, the results would contain the data for those additional columns but
exclude the data for col1 and col2.
L14-16
®
Lesson 14: Security
A role is a name or label that defines a common task or set of behaviors related to permissions
for an application, for example admin, staff.
1. You can define a logical role in an ACE, such as Admin, and give permissions for that
role.
2. Then you can map this role to a set of user’s userIds, giving those users the permissions
for that role, in this case Admin role permissions.
Roles enable you to use functionality similar to Unix groups for your users without requiring
you to alter your system's existing group hierarchy.
L14-17
®
Lesson 14: Security
L14-18
®
Lesson 14: Security
Congratulations! You have finished DEV 340, HBase Applications: Bulk Loading, Security, and
Performance.
L14-19