You get a bonus - 1 coin for daily activity. Now you have 1 coin

Designing data warehouse performance — indexing

Lecture



As already noted in the previous lecture, one of the most important tasks in developing the physical data model of a data warehouse (DW) is ensuring that the DW delivers the required level of performance. In that same lecture we examined the main approach to solving this task — denormalizing the tables of the DW physical data model within the relational model approach. In the present lecture we will continue discussing methods for tackling DW performance and focus our attention on DBMS-specific means for solving this task: indexes, partitions, and clusters.

Improving query performance: indexes

Indexing

Indexingis a way of ensuring fast access to the values of a column or a combination of columns. Physically, new rows are added at the end of the table, resulting in an unordered placement of values within columns. Without some method of ordering the data, the only way for the DBMS to view a column's value is to scan every row sequentially from the beginning of the table to its end — the so-called table scan. The performance of such a scan is proportional to the size of the table, the size of the database's physical page, and the length of the table row.

One way of introducing an ordering relation over column values without disturbing the physical arrangement of table rows is to create a relational DBMS object — an index. An index is an object in a relational database intended to organize fast access to table rows by the values of one or more of their columns.

Conceptually, an index works as follows. It contains an ordered list of values of a column or combination of columns, together with information about the location on disk of the table rows corresponding to those values. The column values in the index are sorted. Even though the order of rows in the table is arbitrary, the index can be scanned quickly to find a specific value. An ordered index can be scanned many times faster than an unordered table. The higher the degree of distinctness of the key values in a column, the faster access to that table's rows will be.

For instance, when a new record is inserted into a table, the primary key's uniqueness check is not implemented by actually scanning the database table, but rather by enforcing a uniqueness requirement on the values of the primary key column within the index. Thus, an index is a database object that can substantially reduce the time needed to find the required rows in a table.

Indexes take up space in the database. When new data is inserted or existing data is deleted, the DBMS has to update both the tables and the indexes. This can slow down data-modification operations, especially for tables with a large number of rows, as is typical in a DW. This gives rise to a conflict between the speed of updating data in a table and the speed of reading it. The following rule of thumb should be followed when resolving this issue: create indexes for primary key columns and other columns frequently used in queries whose data selection relies on logical criteria. If this results in degraded update performance, removing some indexes may be considered.

Every database table can have one or more indexes. Indexes are created on one column or on several columns of a table. The columns included in an index are commonly called key fields, or keys. Indexes can be unique or non-unique. Uniqueness of an index means there are no two rows with the same index key value. A non-unique index can have several keys with identical values.

The main goals of creating indexes in a database are:

  • speeding up the search for rows in tables;
  • ensuring a unique value in columns;
  • retrieving rows in a given order based on the values of indexed columns (which is justified only for very large tables, when using an ORDER clause degrades performance).

At the stage of creating the DW physical data model, a number of important decisions must be made about what and how to index; it is important to clearly formulate indexing rules. For every DW IT project, indexing rules must be developed and documented in writing as part of the overall performance-assurance rules. Maintaining and supporting indexes during DW operation is largely the responsibility of the database administrator. When addressing performance issues, the DBA will raise the question of redesigning the physical structure (reverse design tasks), including the question of dropping and creating new indexes. The DBA may handle these tasks independently. It is therefore all the more important to know by what rules and for what reasons indexes of one type or another were created. Developing such rules will significantly improve the quality of DW operation from a performance standpoint.

To handle these tasks, the designer must know how an index works, what types of indexes the DBMS supports, and understand the meaning of indexing methods.

First we will describe the types of indexes together with the indexing methods for each type, then we will examine how an index works, and finally give some recommendations for creating and using indexes.

B-Tree index

An index built on a balanced hierarchical structure (or a B-Tree index, balanced tree structured object) resembles a tree when viewed from the bottom up. When the DBMS works with this structure, it first reads the topmost block — the root node (root), then a block at the next level — a branch block (branch), and so on, until a leaf block (leaf) containing the indexed column(s) of the row is retrieved. Note that the values of the indexed columns are stored in the index (fig. 20.1).

This structure allows the number of I/O operations to be minimized. Obtaining the indexed columns of a row typically requires a single visit to a leaf block, i.e., to the physical page of the database's file structure allocated for the index.

Designing data warehouse performance — indexing

enlarge image
Fig. 20.1. Conceptual organization of a B-Tree index

Two cases should be noted in which, after retrieving the indexed columns of a row from the index, several visits to the index's physical page may be required:

  1. when a row's length exceeds one physical page of the database's file structure — the so-called chained row;
  2. when a row, having grown during its lifetime in the database, was moved from its original page to another page — the so-called migrated row.

A B-Tree index is characterized by the number of levels in the index – its height. The fewer the levels, the higher the performance.

A B-Tree index — is a physical object of a relational database organized on the principle of a balanced hierarchical structure and possessing a set of properties. Let us formulate some properties of B-Tree structured indexes.

  • The number of I/O operations needed to obtain a row's identifier depends on the number of branching levels of the tree. As the index grows through the addition of new data, the DBMS adds new levels to it in order to keep the tree balanced. In practice, however, there are rarely more than four such levels.
  • The root node and branch nodes of the index are compressed and therefore contain only as many leading bytes of the key value as are needed to distinguish it from other values. Leaf nodes contain the full key value.
  • Values in the index are ordered by key value, and the index's physical pages are organized into a doubly linked list. This provides sequential access to the index and allows the index to be used for performing the ORDER BY operation in a query.
  • An index can be used to search for both an exact match and a range of values.
  • Indexes can be built on several columns of a table — a so-called composite index. The DBMS uses composite indexes to execute queries in which the leading part of the composite key is specified. For example, the composite index {"Surname" (Ename), "Job" (Job)} will not be used to process the query SELECT * FROM EMPLOYEE WHERE Job='Engineer';.
  • The DBMS usually decides on its own whether to use an index or not.
  • NULL column values are not indexed. If an index is built on such columns, the DBMS will refuse to use it in certain operations, for example ORDER BY.

In the MS SQL Server family of DBMSs, all indexes are organized on the basis of a balanced hierarchical structure. In addition to having the properties of uniqueness ( UNIQUE ) and non-uniqueness, indexes in the MS SQL Server family can be clustered ( CLUSTERED ) or non-clustered ( NONCLUSTERED ), the latter being the default index type.

A clustered indexis an index in which the logical order of the key values determines the physical order of the corresponding rows in the table. At the bottom (leaf) level of such an index, the table's actual data rows are stored. At any point in time, a table or view can have only one clustered index.

A non-clustered indexis an index that defines a logical ordering for a table. The logical order of rows in a non-clustered index does not affect their physical order. Up to 999 non-clustered indexes can be created for each table, regardless of how they are created: implicitly, via PRIMARY KEY and UNIQUE constraints, or explicitly, via the CREATE INDEX command, which was covered in "Getting to Know the CASE Tool".

In a clustered index, the leaf nodes contain the data pages of the underlying table. The index pages of the root node and intermediate nodes hold index rows. Each index row contains a key value and a pointer either to a page at an intermediate level of the balanced tree, or to a data row at the leaf level of the index. Pages at each level are linked into a doubly linked list.

By default, a clustered index occupies one partition of disk space. If a clustered index spans several partitions, each partition includes a balanced tree containing the data of that partition. For example, if a clustered index spans four partitions, there are four balanced trees: one per partition.

SQL Server descends the index to find the row matching the clustered index key. To find a range of keys, SQL Server first locates the starting key value of the range, and then scans the data pages using the pointers to the next and previous pages. To find the first page in the chain of data pages, SQL Server follows the leftmost pointers from the root of the index.

Depending on the data types, each clustered index structure consists of one or more allocation units, which are used to store and manage the partition's data. For each partition, a clustered index contains at least one IN_ROW_DATA allocation unit. To store large object ( LOB ) columns, a clustered index requires one LOB_DATA allocation unit per partition. In addition, to store variable-length rows that exceed the row-size limit of 8,060 bytes, one ROW_OVERFLOW_DATA allocation unit is required per partition.

In their organization, non-clustered indexes differ from clustered ones as follows: the data rows in the underlying table are not sorted and are stored in an order that is not based on their non-clustered keys; the leaf level of a non-clustered index consists of index pages instead of data pages.

Non-clustered indexes can be defined on a table or view that has a clustered index, or on a heap. Each row of a non-clustered index contains a non-clustered key value and a row pointer. This pointer identifies the data row of the clustered index or heap that contains the key value.

Row pointers in non-clustered index rows are either a row pointer or the clustered index key for the row, as described below.

If a table is a heap (that is, it does not contain a clustered index), then the row pointer is a pointer to the row. The pointer is constructed from a file identifier ( ID ), page number, and row number on the page. The entire pointer is called the row identifier ( RID ).

If the table has a clustered index, or the index is built on an indexed view, then the row pointer is the clustered index key for the row. If the clustered index is not a unique index, SQL Server makes any duplicate keys unique by adding an internally generated value called a uniqueifier. This is a four-byte value invisible to users. It is used whenever the clustered key needs to be made unique for use in non-clustered indexes. SQL Server retrieves the data row by searching the clustered index using the clustered index key stored in the leaf row of the non-clustered index.

By default, a non-clustered index includes one partition. If it consists of several partitions, each partition has a balanced tree structure that contains the index rows for that particular partition. For example, if a non-clustered index contains four partitions, there are four balanced tree structures, one for each partition.

Depending on the data types in a non-clustered index, each of its structures will contain one or more allocation units storing data for a given partition. Each non-clustered index will have at least one IN_ROW_DATA allocation unit per partition, storing the pages of the index's balanced tree. A non-clustered index will also contain one LOB_DATA allocation unit per partition if the index has large object (LOB) type columns. In addition, a non-clustered index will include one ROW_OVERFLOW_DATA allocation unit per partition if the index has variable-length columns that exceed the maximum row size of 8,060 bytes.

Example 20.1.

In the examples that follow, unless stated otherwise, we will use the "Employee" (EMPLOYEE) table (fig. 20.2) from "Designing the DW Model from a Logical Model". Let us repeat it here.

Table 20.1. Description of the fields of the "Employee" (EMPLOYEE) table
No. Attribute name Column name
1 Personal card number EMPNO (PK)
2 Surname ENAME
3 First name LNAME
4 Department number DEPNO
5 Job title JOB
6 Date of birth AGE
7 Seniority HIREDATE
8 Bonuses COMM
9 Salary SAL
10 Fines FINE
11 Biography Biog
12 Photo Foto
Designing data warehouse performance — indexing

Fig. 20.2. The "Employee" (EMPLOYEE) table

Let us create a composite index on the "Employee" (EMPLOYEE) table for the columns "Surname" (Ename) and "Job" (Job).

CREATE INDEX emp_ndx2 ON EMPLOYEE (Ename, Job)
GO

Index-organized tables and other B-Tree-based index types

An index-organized table can be created based on the values of one or more columns. If a query's data requirements can be satisfied from the information contained in the index associated with that data, the underlying table is not accessed.

Some DBMSs, Oracle in particular, support index-organized tables. An index-organized table is a B-Tree type database index that simultaneously serves the role of a table. All of that table's data is stored in the index. The advantage of creating fully indexed tables is savings in disk storage space and a reduction in I/O volume, since the key columns do not need to be stored again in the table. The query result is obtained based on the data stored in the index table.

An index-organized table in Oracle is created using the SQL CREATE TABLE command, as shown in example 20.2.

Example 20.2.

Suppose we need to store and track, in a separate database table, problems arising during the execution of all projects, and also to record who reported them and how often problems occurred. Let us create the index-organized table "Indexed Project" (Proj_Index) to solve this task, as shown below:

CREATE TABLE Proj_Index
( projno	char(8) NOT NULL,
 t_person	char(32) NOT NULL,
t_frequency	integer,
t_problem	varchar2(512),
   CONSTRAINT pk_ndx PRIMARY KEY( projno, t_person) )
ORGANIZATION INDEX
TABLESPACE ts_ndx1
PCTTHRESHOLD 20
INCLUDING   t_frequency
OVERFLOW TABLESPACE ts__of_ndx1;

The CREATE TABLE command is no different from other table-creation commands — until the ORGANIZATION INDEX clause is encountered, which instructs the DBMS to create an index-organized table. A tablespace is specified for placing the index on disk. The PCTTHRESHOLD parameter specifies that the remaining part of the row should be stored in the given tablespace — the overflow segment — if the row exceeds the physical database page size by the given percentage. The INCLUDING parameter defines the name of the column at which the index table row is split into two parts: the index part and the overflow part. This column may be part of the table's primary key or a non-key column. All non-key columns following the specified column are placed in the overflow segment, which is defined by the OVERFLOW keyword.

In the MS SQL Server family of DBMSs there is no built-in capability to create index-organized tables. However, in a number of practical cases, this family of DBMSs allows creating structures analogous to index-organized tables. For this purpose, a non-clustered index with included columns can be used (the INCLUDE option of the CREATE INDEX command).

The INCLUDE option specifies non-key columns to be added at the leaf level of a non-clustered index. A non-clustered index can be unique or non-unique. Column names in the INCLUDE list cannot be repeated and cannot be used simultaneously as key and non-key columns.

For example 20.2, the commands to create a structure similar in purpose to an index-organized table, in the MS SQL Server SQL dialect, look as shown in the example below.

Example 20.3.

First we create the "Indexed Project" (Proj_Index) table, as in fig. 20.3.

CREATE TABLE Proj_Index (
   projno	char(8) NOT NULL,
   t_person	char(32) NOT NULL,
   t_frequency	integer,
   t_problem	varchar(512),
   CONSTRAINT pk_ndx PRIMARY KEY( projno, t_person) )
GO
Designing data warehouse performance — indexing

Fig. 20.3. The "Indexed Project" (Proj_Index) table

Next, we create a non-clustered index with the key "Project number" (projno) and three non-key columns: "Employee surname" (t_person), "Problem frequency" (t_frequency), and "Problem description" (t_problem).

CREATE NONCLUSTERED INDEX IX_Proj
    ON Proj_Index (projno)
    INCLUDE (t_person, t_frequency, t_problem);
GO

The Oracle DBMS family provides several additional index types that enhance the traditional B-Tree structured indexes common to all DBMSs. Besides index-organized tables, these variants include bitmap indexes, reverse-key indexes, and function-based indexes.

Each bit of a so-called bitmap index corresponds to the row identifier ROWID (which in Oracle is created and stored for every row and used in the internal organization of indexes) in a table object. If a given row contains the key value in question, a one is stored in the index for that value. This organization can, in some cases, significantly improve query performance, since to retrieve rows with particular index values the DBMS need only find all the ones matching the key. Physically, such an index is organized on the basis of a B-Tree structure, but the task reduces to finding the given row through a single read of the bitmap index structure. This index type is very effective for indexing columns with low cardinality — gender, color, and so on. If a column has many distinct values, the amount of I/O will increase.

Example 20.4.

For our study example, we can build a bitmap index on the "Employee" (EMPLOYEE) table for the "Department number" (DEPNO) column:

CREATE BITMAP INDEX emp_ndx ON EMPLOYEE (DEPNO);

In the MS SQL Server family of DBMSs, there is no capability to create bitmap indexes using the SQL dialect.

A reverse-key index applies byte reversal to the values of a numeric indexed column. This technique allows column values to be distributed evenly among the leaf blocks of a B-Tree structured index, which works well for indexing columns with sequential numbering or numbering with a fixed step. Note that such indexes are used only for returning individual rows, and cannot be used to search for values within a range. In Oracle, the REVERSE option cannot be applied to bitmap indexes or to index-organized tables.

Example 20.5.

Numeric keys containing sequential numbers exist, in particular, in the "Employee" (EMPLOYEE) table – "Employee number" (EMPNO). We can create an additional reverse-key index for this table to retrieve an employee's record. Note that a primary key index already exists for this column.

CREATE INDEX dep_ndx ON EMPLOYEE  (EMPNO) REVERSE;

During operation, the database administrator can rebuild this index using the ALTER INDEX command:

ALTER INDEX EMPLOYEE  REBUILD NOREVERSE;

In the MS SQL Server family of DBMSs, there is no capability to create reverse-key indexes using the SQL dialect.

If a function is applied to an indexed column in a WHERE clause, the DBMS usually does not use that index when organizing access to the table's rows. But when a function-based index is created using the same function as in the WHERE clause, both the Oracle and MS SQL Server families of DBMSs use such an index to read the rows satisfying the selection criterion.

In the MS SQL Server family of DBMSs, computed columns can have the PERSISTED property. This means the Database Engine stores the computed values in the table and updates them whenever any columns on which the computed column depends are updated. The Database Engine uses these materialized values when it builds an index on the column and when a query accesses the index.

For a computed column to be indexable, it must be deterministic and precise. If the PERSISTED property is used, the list of indexable computed column types is extended to include the following types:

  1. computed columns based on Transact-SQL expressions, CLR functions, and CLR user-defined type methods that the user has marked as deterministic;
  2. computed columns based on expressions that the Database Engine determines to be deterministic but not precise.

Example 20.6.

As an example of using indexes on a computed column, let us look at an example of simulating a hash index in MS SQL Server.

Suppose no index has been created for the "Surname" (ENAME) column in the "Employee" (EMPLOYEE) table. Then, if the WHERE clause of a SELECT command specifies a search predicate on the "Surname" (ENAME) column, the DBMS will scan the table. If the table contains around 1,000,000 rows, the scan operation will increase the query's processing time.

Let us create a hash index for the "Surname" (ENAME) column using the checksum() function; to do this, we add a computed column to the table, as shown below.

alter table EMPLOYEE
add ENameHash as checksum(EName);
go

Next let us create an index on this column, as shown below.

create index IDX_ENames on EMPLOYEE (ENameHash);
go

With such an index present in the database, the DBMS will use it when processing the query described in this example.

In the MS SQL Server family of DBMSs, filtered indexes can be created and used; to create such indexes, the CREATE INDEX command provides the option of using a WHERE clause.

Using a WHERE clause creates a filtered index by specifying which rows to include in the index. A filtered index must be a non-clustered index on the table. Filtered statistics are also created for the data rows of the filtered index.

The filter predicate uses simple comparison logic and cannot reference a computed column, a user-defined type column, a spatial data type column, or a hierarchyID type column. Comparisons using NULL literals with comparison operators are not allowed. Instead, the IS NULL and IS NOT NULL operators are used.

A filtered index is well suited for columns with clearly defined subsets of data.

Example 20.7.

Let us create a filtered index on the "Employee" (EMPLOYEE) table for the "Fines" (FINE) column, to quickly retrieve lists of fined employees.

CREATE NONCLUSTERED INDEX IDX_FINE
    ON EMPLOYEE (EName, FINE)
    WHERE FINE > 0;
GO

In this section we examined various types of indexes and examples of their creation. In the next section we will look in more detail at index design parameters.

On some index design parameters

When designing indexes, one needs some mechanism for evaluating the quality of a prospective index. Let us introduce a few concepts that can be used to roughly assess the quality of a potential index.

A table column's cardinality is the number of discrete distinct values of the column that occur in the table's rows. For example, if in the "Employee" (EMPLOYEE) table we add a column to indicate gender – "Sex" (SEX) – then this column's cardinality is 2, since in nature people have only two sexes — male and female. For a primary key column, the cardinality equals the number of rows in the table.

The reason column cardinality matters for index design is that the cardinality of an indexed column determines the number of unique entries that must be stored in the index, i.e. the number of records in the index. For example, the indexed column "Sex" (SEX) will have only two unique entries, each of which will be repeated many times in the index. Assuming an equal distribution of employee sex over 100,000 rows in the "Employee" (EMPLOYEE) table, each index entry will be repeated 50,000 times. A DBMS is unlikely to choose to use such an index when building a query plan.

Determining the cardinality of a potential indexing column in an existing database table is quite simple.

SELECT COUNT (DISTINCT колонка) FROM таблица;

When designing a new data warehouse database, the cardinality of all potential indexed columns in all tables of the data warehouse's physical data model should be estimated based on the available documentation.

The way a DBMS evaluates the effect of cardinality is by using the selectivity factor. The selectivity factor of an index is defined as the inverse of the cardinality of the indexed column:

Designing data warehouse performance — indexing

The selectivity factor estimates the potential volume of I/O operations. The smaller the selectivity factor, the fewer I/O operations are required to obtain the result set of table rows. The DBMS evaluates this value to decide whether or not to use an index to access the table's rows.

To conclude this section, let us list rules for identifying columns that are good and bad candidates for indexing. These rules can be used when deciding on building indexes for a relational database.

Good candidates for indexing are generally:

  • primary key columns. By definition, primary key columns must have a unique index;
  • foreign key columns. They make good indexes for two reasons. First, they are often used to perform joins with parent tables. Second, they can be used by the DBMS to maintain referential integrity when deleting rows in the parent and child tables;
  • any columns that contain unique values;
  • columns used in queries or joins that reference from 5 to 10% of the table's rows;
  • columns that are frequently used as arguments to aggregate functions;
  • columns that are frequently used to validate data entered in data entry/editing programs.

Factors that reduce index effectiveness:

  • small tables. Indexes should not be created for tables smaller than five physical pages. For such small tables, the cost of maintaining an index is higher than the cost of scanning the entire table. Of course, a unique index is still required for the primary key and to support referential integrity;
  • heavy batch updates of tables. Such tables typically suffer from index overflow problems under intensive table modification. If an index is required for such a table, it is more practical to drop it before the update and re-create it afterward;
  • skewness of key values. If the distribution of key values is significantly skewed, the index's cardinality may turn out to be quite high, and, due to the low selectivity factor, the DBMS will often choose to use this index. However, the result of using the index will be unsatisfactory, because a significant portion of the table's rows share the same key value, which negates the cost benefit of using the index compared to scanning the whole table.

Bad candidates for indexing are generally:

  • low-cardinality columns. They produce a high selectivity factor, and the DBMS generally avoids using them when processing queries. The cost of maintaining an index on a low-cardinality column is comparable to the cost of scanning the whole table, since accessing the data

продолжение следует...

Продолжение:


Часть 1 Designing data warehouse performance — indexing
Часть 2 Improving query performance: partitioning - Designing data warehouse performance —
Часть 3 Improving query performance: clusters - Designing data warehouse performance —
Часть 4 Summary - Designing data warehouse performance — indexing

Comments

To leave a comment

If you have any suggestion, idea, thanks or comment, feel free to write. We really value feedback and are glad to hear your opinion.
To reply

Lectures and tutorial on "Databases, knowledge and data warehousing. Big data, DBMS and SQL and noSQL"

Terms: Databases, knowledge and data warehousing. Big data, DBMS and SQL and noSQL