A well-designed table schema enables advanced features and significantly improves the performance, maintainability, and scalability of a database system. Therefore, table schema design is critical. This topic introduces the key table properties to consider when you design a table schema in ApsaraDB for SelectDB. This information helps you choose the right table design for your business scenario.
Overview of important table properties
When you use SelectDB for your business, it is critical to design key table properties based on your business scenario. This practice helps you build a high-performance and easy-to-maintain table schema. The following table provides a quick overview of the important table properties in SelectDB.
Table property | Required | Primary role of properties | For more information |
Data model | Yes | Different data models suit different business scenarios. The Unique model supports primary key uniqueness constraints and meets requirements for flexible and efficient data updates. The Duplicate model uses an append-write mode and is suitable for high-performance analysis of detailed data. The Aggregate model supports data pre-aggregation and focuses on data aggregation and statistics scenarios. | |
Bucketing | Yes | Bucketing distributes data to different nodes in a cluster. This leverages the divide-and-conquer capability of a distributed system to manage and query large amounts of data. | |
Partition | No | Partitioning divides a table into multiple sub-tables based on specified fields, such as time or region. This helps manage and query the data by partition. It also uses partition pruning to improve query speed. | |
Index | No | Indexes quickly filter or locate data. This greatly improves query performance. |
Data models
Selecting the right data model is critical for meeting the functional and performance requirements of your data analytics scenarios. Different models are suitable for different business scenarios. This section introduces each model to help you understand and choose the right one. For more information, see Data models.
Basic concepts
In SelectDB, data is logically organized and managed in tables. Each table consists of rows and columns. A row represents a single record. A column describes a field within that record.
Columns are divided into two main types:
Key columns: These are columns specified by the
UNIQUE KEY,AGGREGATE KEY, orDUPLICATE KEYkeywords in a CREATE TABLE statement.Value columns: All columns other than key columns are value columns.
Model selection guide
In SelectDB, there are three data models for tables: Unique, Duplicate, and Aggregate.
The data model is determined when you create a table and cannot be modified.
If you do not specify a data model when you create a table, the Duplicate model is used by default. The first three columns are automatically selected as key columns.
In the Unique, Duplicate, and Aggregate models, data is sorted and stored by key columns.
Model type | Characteristics | Scenarios | Disadvantages |
Unique | The key value of each row is unique. If rows have the same key values, the value columns of the latest row overwrite the previous ones. | Suitable for scenarios that require a unique primary key or efficient updates, such as data analytics for e-commerce orders and user attribute information. |
|
Duplicate | Multiple rows can have the same key values. Rows with the same key values are stored together in the system. | Offers high data write and query efficiency. Suitable for scenarios where all raw data records must be retained, such as detailed data analytics for logs and bills. |
|
Aggregate | The key value of each row is unique. If rows have the same key values, their value columns are pre-aggregated based on the aggregation method specified when the table was created. | Similar to the Cube model in a traditional data warehouse. Suitable for aggregate statistics scenarios that improve query performance through pre-aggregation, such as data analytics for website traffic and custom reports. |
|
Quick start with models
Unique model
In the Unique model, if rows have the same key values, the value columns of the latest row overwrite the previous ones. The Unique model offers two implementations: Merge on Read (MOR) and Merge on Write (MOW).
We recommend that you prioritize the Merge on Write (MOW) implementation because it is mature, stable, and provides excellent query performance. This topic provides a brief introduction to the MOW implementation for the Unique model. For detailed information about Merge on Read, see Merge on Read (MOR).
Notes
When you create a table that uses the Unique model with the MOW implementation, note the following points.
Use
UNIQUE KEYto specify the unique primary key fields.Add the property to enable MOW in the PROPERTIES section.
"enable_unique_key_merge_on_write" = "true"
Example
The following statement creates the orders table. It specifies the Unique model for the orders table, sets a composite primary key that consists of order_id and order_time, and enables MOW mode.
CREATE TABLE IF NOT EXISTS orders
(
`order_id` LARGEINT NOT NULL COMMENT "Order ID",
`order_time` DATETIME NOT NULL COMMENT "Order time",
`customer_id` LARGEINT NOT NULL COMMENT "User ID",
`total_amount` DOUBLE COMMENT "Total order amount",
`status` VARCHAR(20) COMMENT "Order status",
`payment_method` VARCHAR(20) COMMENT "Payment method",
`shipping_method` VARCHAR(20) COMMENT "Shipping method",
`customer_city` VARCHAR(20) COMMENT "User's city",
`customer_address` VARCHAR(500) COMMENT "User's address"
)
UNIQUE KEY(`order_id`, `order_time`)
PARTITION BY RANGE(`order_time`) ()
DISTRIBUTED BY HASH(`order_id`)
PROPERTIES (
"enable_unique_key_merge_on_write" = "true",
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "DAY",
"dynamic_partition.start" = "-7",
"dynamic_partition.end" = "3",
"dynamic_partition.prefix" = "p",
"dynamic_partition.create_history_partition" = "true",
"dynamic_partition.buckets" = "16"
);Duplicate model
In the Duplicate model, rows with the same key values are stored together in the system. This model has no pre-aggregation or primary key uniqueness constraints.
For example, if you want to record and analyze log data from a business system and sort the data by log time, log type, and error code, you can choose this model. The following statement creates the log table. It specifies that the log table uses the Duplicate model and that data is sorted by log_time, log_type, and error_code.
CREATE TABLE IF NOT EXISTS log
(
`log_time` DATETIME NOT NULL COMMENT "Log time",
`log_type` INT NOT NULL COMMENT "Log type",
`error_code` INT COMMENT "Error code",
`error_msg` VARCHAR(1024) COMMENT "Error details",
`op_id` BIGINT COMMENT "Owner ID",
`op_time` DATETIME COMMENT "Processing time"
)
DUPLICATE KEY(`log_time`, `log_type`, `error_code`)
PARTITION BY RANGE(`log_time`) ()
DISTRIBUTED BY HASH(`log_type`)
PROPERTIES (
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "DAY",
"dynamic_partition.start" = "-7",
"dynamic_partition.end" = "3",
"dynamic_partition.prefix" = "p",
"dynamic_partition.create_history_partition" = "true",
"dynamic_partition.buckets" = "16"
);Aggregate model
Notes
In the Aggregate model, if rows have the same key values, their value columns are pre-aggregated based on the aggregation method specified when the table was created. Therefore, when you create a table that uses the Aggregate model, pay special attention to the following points.
Use
AGGREGATE KEYto specify the key columns. Rows with the same key column values will be aggregated.Specify the aggregation method for the value columns. The following aggregation methods are supported:
Aggregation type
Description
SUM
Calculates the sum of the values in multiple rows. This type is applicable to numeric values.
MIN
Calculates the minimum value. This type is applicable to numeric values.
MAX
Calculates the maximum value. This type is applicable to numeric values.
REPLACE
Replaces the previous values with the newly imported values. For the rows that contain the same data in dimension columns, the values in metric columns are replaced with the newly imported values based on the order in which values are imported.
REPLACE_IF_NOT_NULL
Replaces values except for null values with the newly imported values. Different from the REPLACE type, this type does not replace null values. When you use this type, you must specify a null value instead of an empty string as the default value for fields. If you specify an empty string as the default value for fields, this type replaces the empty string with another one.
HLL_UNION
Aggregates columns of the HyperLogLog (HLL) type by using the HLL algorithm.
BITMAP_UNION
Aggregates columns of the BITMAP type, which performs a union aggregation of bitmaps.
Example
For example, if you want to perform statistical analysis on user behavior and record the last visit time, total cost, maximum dwell time, and minimum dwell time, you can choose this model. The following statement creates the user_behavior table. It specifies that when multiple data records have the same key column values (user ID, data write date, user's city, user's age, and user's gender), the value columns are pre-aggregated based on the following rules:
User's last visit time: The maximum value of the last_visit_date field is used.
Total User Consumption: The total consumption value aggregated from multiple data records.
User's maximum dwell time: The maximum value of the max_dwell_time field is used.
User's minimum dwell time: The minimum value of the min_dwell_time field is used.
CREATE TABLE IF NOT EXISTS user_behavior
(
`user_id` LARGEINT NOT NULL COMMENT "User ID",
`date` DATE NOT NULL COMMENT "Date and time of data write",
`city` VARCHAR(20) COMMENT "User's city",
`age` SMALLINT COMMENT "User's age",
`sex` TINYINT COMMENT "User's gender",
`last_visit_date` DATETIME REPLACE DEFAULT "1970-01-01 00:00:00" COMMENT "User's last visit time",
`cost` BIGINT SUM DEFAULT "0" COMMENT "User's total cost",
`max_dwell_time` INT MAX DEFAULT "0" COMMENT "User's maximum dwell time",
`min_dwell_time` INT MIN DEFAULT "99999" COMMENT "User's minimum dwell time"
)
AGGREGATE KEY(`user_id`, `date`, `city`, `age`, `sex`)
PARTITION BY RANGE(`date`) ()
DISTRIBUTED BY HASH(`user_id`)
PROPERTIES (
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "DAY",
"dynamic_partition.start" = "-7",
"dynamic_partition.end" = "3",
"dynamic_partition.prefix" = "p",
"dynamic_partition.create_history_partition" = "true",
"dynamic_partition.buckets" = "16"
);Data partitioning overview
SelectDB supports two layers of data partitioning, as shown in the following figure. The first layer is the partition, which logically divides the data. A partition is the smallest unit for data management. The second layer is the tablet, which physically divides the data. A tablet is the smallest unit for data operations, such as data distribution and data movement.
Relationship between partitions and tablets
A bucket belongs to a single partition, while a partition can contain multiple buckets.
When you create a table, if you use partitions, the table is first divided according to the partitioning rules. Then, within each partition, the data is further divided according to the specified bucketing rules. If you do not use partitions, the table is directly divided according to the specified bucketing rules.
During data writes, data is first divided into the corresponding partition. Then, within that partition, the data is written to different tablets based on the bucketing rules. Bucketing is a subdivision of partitioned data. The purpose of bucketing is to distribute data more evenly to improve query efficiency.
Partitions (Partition)
In the storage engine of SelectDB, partitioning is a data organization method that divides data in a table into multiple independent parts based on user-defined rules. This process implements a logical division of data, which improves query efficiency and makes data management more flexible. This topic introduces partitioning to help you understand and select a partitioning strategy. For more information, see Partitioning and Dynamic Partitioning.
Partitioning selection guide
SelectDB supports two partitioning methods: Range partitioning and List partitioning. It also provides an easy-to-use dynamic partitioning feature for automated partition management. Different partitioning methods are suitable for different business scenarios.
Partitioning method | Supported column types | Method to specify partition information | Scenarios |
Range | Column types: DATE, DATETIME, TINYINT, SMALLINT, INT, BIGINT, LARGEINT | Supports four syntaxes:
| Suitable for managing data division by intervals. A typical scenario is partitioning by time. |
List | Column types: BOOLEAN, TINYINT, SMALLINT, INT, BIGINT, LARGEINT, DATE, DATETIME, CHAR, VARCHAR | Supports using | Suitable for data management based on existing categories or fixed characteristics of the data. The partition key column is usually an enumerated value, such as partitioning data based on the user's region. |
Notes
SelectDB tables can be partitioned or non-partitioned. This property is optional and is determined when you create a table. It cannot be changed later. For partitioned tables, you can add or delete partitions later. For non-partitioned tables, you cannot add partitions.
Partition key columns must be key columns. You can specify one or more columns.
Regardless of the partition key column's data type, enclose the partition value in double quotation marks ("").
Theoretically, there is no upper limit on the number of partitions.
When you create partitions, ensure that the value ranges of the partitions do not overlap.
Use partitions
Range partitioning
Range partitioning divides and manages data based on the range of a partition field. It is the most common partitioning method. A typical scenario is to partition data by time. This makes it easier to manage and optimize queries on large amounts of time-series data.
The ultimate goal of partitioning and bucketing is to divide data reasonably. The main criteria for a reasonable partitioning rule are as follows:
The data volume of each tablet should be between 1 GB and 10 GB.
Determine the partition granularity based on your data management needs. For example, in a log scenario, you typically need to delete historical data daily. In this case, partitioning by day is appropriate.
In a log scenario, you often filter and query data by time range and need to delete historical partitions by time. You can specify the log_time field as the partition key column and partition the data by day, as shown in the following example.
CREATE TABLE IF NOT EXISTS log
(
`log_time` DATETIME NOT NULL COMMENT "Log time",
`log_type` INT NOT NULL COMMENT "Log type",
`error_code` INT COMMENT "Error code",
`error_msg` VARCHAR(1024) COMMENT "Error details",
`op_id` BIGINT COMMENT "Owner ID",
`op_time` DATETIME COMMENT "Processing time"
)
DUPLICATE KEY(`log_time`, `log_type`, `error_code`)
PARTITION BY RANGE(`log_time`)
(
PARTITION `p20240201` VALUES [("2024-02-01"), ("2024-02-02")),
PARTITION `p20240202` VALUES [("2024-02-02"), ("2024-02-03")),
PARTITION `p20240203` VALUES [("2024-02-03"), ("2024-02-04"))
)
DISTRIBUTED BY HASH(`log_type`);After the table is created, you can run the following SQL statement to view the table's partition information.
SHOW partitions FROM log;p20240201: [("2024-02-01"), ("2024-02-02"))
p20240202: [("2024-02-02"), ("2024-02-03"))
p20240203: [("2024-02-03"), ("2024-02-04"))When you run the following statement to query data, it hits the partition p20240202: [("2024-02-02"), ("2024-02-03")). The system does not scan the data in the other two partitions, which improves query speed.
SELECT * FROM orders WHERE order_time = '2024-02-02';List partitioning
List partitioning divides and manages data based on the enumerated values of a partition field. When you query a list-partitioned table, the system can quickly perform partition pruning based on filter conditions to improve query performance.
You can choose the list partition column based on the fields you commonly use to operate on business data. Note that the data volume should be evenly distributed among the partitions to avoid significant data skew.
For example, in an e-commerce scenario, the volume of order data is usually very large. In some scenarios, you often need to query and analyze this data based on the city of the user who placed the order. To manage and query data more easily, you can specify the customer_city field as the partition key column. Assume the data is distributed by region as follows:
Beijing, Shanghai, and Hong Kong (China): 6 GB
New York and San Francisco: 5 GB
Tokyo: 5 GB
In this case, you can partition the data as shown in the following example.
CREATE TABLE IF NOT EXISTS orders
(
`order_id` LARGEINT NOT NULL COMMENT "Order ID",
`order_time` DATETIME NOT NULL COMMENT "Order time",
`customer_city` VARCHAR(20) COMMENT "User's city",
`customer_id` LARGEINT NOT NULL COMMENT "User ID",
`total_amount` DOUBLE COMMENT "Total order amount",
`status` VARCHAR(20) COMMENT "Order status",
`payment_method` VARCHAR(20) COMMENT "Payment method",
`shipping_method` VARCHAR(20) COMMENT "Shipping method",
`customer_address` VARCHAR(500) COMMENT "User's address"
)
UNIQUE KEY(`order_id`, `order_time`, `customer_city`)
PARTITION BY LIST(`customer_city`)
(
PARTITION `p_cn` VALUES IN ("Beijing", "Shanghai", "Hong Kong"),
PARTITION `p_usa` VALUES IN ("New York", "San Francisco"),
PARTITION `p_jp` VALUES IN ("Tokyo")
)
DISTRIBUTED BY HASH(`order_id`) BUCKETS 16
PROPERTIES (
"enable_unique_key_merge_on_write" = "true"
);After the table is created, you can run the following SQL statement to view the table's partition information. The following three partitions are automatically generated.
SHOW partitions FROM orders;p_cn: ("Beijing", "Shanghai", "Hong Kong")
p_usa: ("New York", "San Francisco")
p_jp: ("Tokyo")When you run the following statement to query data, it hits the partition p_jp: ("Tokyo"). The system does not scan the data in the other two partitions, which improves query speed.
SELECT * FROM orders WHERE customer_city = 'Tokyo';Use dynamic partitioning
In a production environment, a data table may have many partitions. Managing these partitions manually can be tedious and adds maintenance costs for database administrators. SelectDB lets you define dynamic partitioning rules when you create a table for automated partition management.
For example, in e-commerce, for an order information table, you often filter and query data by time range and need to dump and archive historical orders. You can specify the order_time field as the partition key column and set dynamic partitioning properties in the PROPERTIES section. For example, in the PROPERTIES section, you can set the partitions to be created daily (dynamic_partition.time_unit), retain only the partitions from the last 180 days (dynamic_partition.start), and create partitions for the next 3 days in advance (dynamic_partition.end), as shown in the following example.
In the following statement, the parentheses () at the end of PARTITION BY RANGE(`order_time`) () are not a syntax error. If you want to use dynamic partitioning, these parentheses are required by the syntax.
CREATE TABLE IF NOT EXISTS orders
(
`order_id` LARGEINT NOT NULL COMMENT "Order ID",
`order_time` DATETIME NOT NULL COMMENT "Order time",
`customer_id` LARGEINT NOT NULL COMMENT "User ID",
`total_amount` DOUBLE COMMENT "Total order amount",
`status` VARCHAR(20) COMMENT "Order status",
`payment_method` VARCHAR(20) COMMENT "Payment method",
`shipping_method` VARCHAR(20) COMMENT "Shipping method",
`customer_city` VARCHAR(20) COMMENT "User's city",
`customer_address` VARCHAR(500) COMMENT "User's address"
)
UNIQUE KEY(`order_id`, `order_time`)
PARTITION BY RANGE(`order_time`) ()
DISTRIBUTED BY HASH(`order_id`)
PROPERTIES (
"enable_unique_key_merge_on_write" = "true",
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "DAY",
"dynamic_partition.start" = "-180",
"dynamic_partition.end" = "3",
"dynamic_partition.prefix" = "p",
"dynamic_partition.create_history_partition" = "true",
"dynamic_partition.buckets" = "16"
);If you expect a table to have many partitions, we strongly recommend that you learn about dynamic partitioning. For more information, see Dynamic partitioning.
Bucketing (Tablet)
In the storage engine of SelectDB, data is divided into different buckets (tablets) based on the hash value of a specified column. These buckets are managed by different nodes in a cluster. This approach leverages the capabilities of a distributed system to manage and query large amounts of data. When you create a table, you can configure bucketing using the DISTRIBUTED BY HASH(`<bucketing column>`) BUCKETS <number of buckets> clause. For more information about bucketing, see Bucketing.
Notes
If you use partitions when you create a table, the
DISTRIBUTED...clause describes the data division rule within each partition. If you do not use partitions, it describes the data division rule for the entire table.You can specify multiple bucketing columns.
For the Aggregate and Unique models, bucketing columns must be key columns. For the Duplicate model, there is no restriction on bucketing columns.
Choose high-cardinality columns as bucketing columns to distribute data evenly and avoid data skew.
Theoretically, there is no upper limit on the number of tablets.
Theoretically, there is no upper or lower limit on the data volume of a single tablet, but we recommend a range of 1 GB to 10 GB.
If a single tablet's data volume is too small, it can lead to too many tablets, which increases the pressure on metadata management.
If a single tablet's data volume is too large, it hinders replica migration and the full utilization of the distributed cluster. It also increases the cost of retrying failed operations, such as schema changes or index creation, because the granularity of these retries is at the tablet level.
Bucketing column selection guide
In table design, the choice of bucketing columns significantly impacts query performance and concurrency. The following are the principles for selecting bucketing columns. If your business has multiple query requirements, your bucketing column requirements may conflict. In this case, prioritize the requirements of your primary query.
Selection principle | Effect |
Prioritize even data distribution by choosing high-cardinality columns or a combination of columns. | This ensures a more balanced data distribution across cluster nodes. For queries with poor filtering that scan large amounts of data, this approach can fully leverage the resources of the distributed system to improve query performance. |
Choose columns that are frequently used in query filter conditions to accelerate queries through data pruning. | Data with the same bucketing column values is grouped together. For point queries that use the bucketing column as a filter condition, this approach can quickly prune data to improve query concurrency. Note Point queries are typically used to retrieve a small amount of data from a database under specific conditions. These queries use specific conditions, such as filtering by a primary key or a high-cardinality column, to precisely locate and retrieve a small amount of data that meets the specified conditions. |
Example
In an e-commerce scenario, many queries filter by order dimension, while some queries perform statistical analysis on all order data. In this case, you can choose the high-cardinality column order_id from the key columns of the order information table as the bucketing column. This ensures that data is evenly distributed to each bucket and that data for a single order_id is grouped together. This meets the performance requirements for both query types. The specific table creation statement is as follows.
CREATE TABLE IF NOT EXISTS orders
(
`order_id` LARGEINT NOT NULL COMMENT "Order ID",
`order_time` DATETIME NOT NULL COMMENT "Order time",
`customer_id` LARGEINT NOT NULL COMMENT "User ID",
`total_amount` DOUBLE COMMENT "Total order amount",
`status` VARCHAR(20) COMMENT "Order status",
`payment_method` VARCHAR(20) COMMENT "Payment method",
`shipping_method` VARCHAR(20) COMMENT "Shipping method",
`customer_city` VARCHAR(20) COMMENT "User's city",
`customer_address` VARCHAR(500) COMMENT "User's address"
)
UNIQUE KEY(`order_id`, `order_time`)
PARTITION BY RANGE(`order_time`) ()
DISTRIBUTED BY HASH(`order_id`)
PROPERTIES (
"enable_unique_key_merge_on_write" = "true",
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "DAY",
"dynamic_partition.start" = "-7",
"dynamic_partition.end" = "3",
"dynamic_partition.prefix" = "p",
"dynamic_partition.create_history_partition" = "true",
"dynamic_partition.buckets" = "16"
);Indexes
Indexes are critical in database design, and proper indexes can significantly improve query performance. However, indexes may occupy additional storage space and reduce write performance. This section describes commonly used indexes to help you understand and select the right ones. For more information, see Index acceleration.
Design guide
You should specify the most frequently used filter conditions as key columns to automatically create a prefix index, because it provides the best filtering effect. However, a table can have only one prefix index. Therefore, you should apply it to the most frequent filter conditions.
For other filtering acceleration needs, inverted indexes are the recommended choice. They are widely applicable and support combinations of multiple conditions. For equality and LIKE match queries on strings, you can consider lightweight BloomFilter or NGram BloomFilter indexes.
Index selection guide
In SelectDB, indexes can be built-in or custom. Built-in indexes are created automatically by the system. You must create custom indexes as needed, either when you create the table or afterward.
Creation method | Index type | Supported query types | Unsupported query types | Advantages | Disadvantages |
Built-in | Prefix index |
|
| Prefix indexes use relatively little space and can be fully cached in memory. This allows for quick location of data blocks, significantly improving query efficiency. | A table can have only one prefix index. |
Custom | Inverted index (recommended) |
| None | Supports a wide range of query types. You can create indexes on demand when you create a table or afterward, and you can delete them. | Index storage space is relatively large. |
BloomFilter index | Equality query |
| Index creation uses few computing and storage resources. | Supports few query types. Only supports equality queries. | |
NGram BloomFilter index | LIKE queries |
| Improves LIKE query speed. Index creation uses few computing and storage resources. | Only accelerates LIKE queries. |
Quick start index
Inverted index
SelectDB supports inverted indexes. You can use inverted indexes to perform full-text searches on text fields and equality or range queries on ordinary fields. This lets you quickly retrieve data that meets specific conditions from a large amount of data. This topic describes how to create an inverted index. For more information, see Inverted indexes.
Create an index when you create a table
In an e-commerce scenario, querying order information by user ID and user address keywords is a frequent operation. In this case, you can create an inverted index on the customer_id and customer_address fields to improve query speed. The specific table creation statement is as follows.
CREATE TABLE IF NOT EXISTS orders
(
`order_id` LARGEINT NOT NULL COMMENT "Order ID",
`order_time` DATETIME NOT NULL COMMENT "Order time",
`customer_id` LARGEINT NOT NULL COMMENT "User ID",
`total_amount` DOUBLE COMMENT "Total order amount",
`status` VARCHAR(20) COMMENT "Order status",
`payment_method` VARCHAR(20) COMMENT "Payment method",
`shipping_method` VARCHAR(20) COMMENT "Shipping method",
`customer_city` VARCHAR(20) COMMENT "User's city",
`customer_address` VARCHAR(500) COMMENT "User's address",
INDEX idx_customer_id (`customer_id`) USING INVERTED,
INDEX idx_customer_address (`customer_address`) USING INVERTED PROPERTIES("parser" = "chinese")
)
UNIQUE KEY(`order_id`, `order_time`)
PARTITION BY RANGE(`order_time`) ()
DISTRIBUTED BY HASH(`order_id`)
PROPERTIES (
"enable_unique_key_merge_on_write" = "true",
"dynamic_partition.enable" = "true",
"dynamic_partition.time_unit" = "DAY",
"dynamic_partition.start" = "-7",
"dynamic_partition.end" = "3",
"dynamic_partition.prefix" = "p",
"dynamic_partition.create_history_partition" = "true",
"dynamic_partition.buckets" = "16"
);Create an index on an existing table
In an e-commerce scenario, querying order information by user ID is a frequent operation. If you did not create an inverted index for the customer_id field when you created the table, you can use the following statement to add one.
ALTER TABLE orders ADD INDEX idx_customer_id (`customer_id`) USING INVERTED;Prefix index
A prefix index is built on one or more prefix key columns and relies on the underlying data being sorted by those key columns. It is essentially a binary search based on the sorted nature of the data. Prefix indexes are built-in indexes that SelectDB creates automatically after table creation.
There is no special syntax to define a prefix index. The system selects the fields that can be covered by the first 36 bytes as the prefix index, based on the order of key columns defined during table creation. However, when a VARCHAR type is encountered, the prefix index is truncated, and subsequent key columns are not added to the prefix index.
The order of fields defined during table creation is very important. It determines which fields will be used for the prefix index. We strongly recommend that you determine the order of key columns based on the following principles:
Place high-cardinality key columns that are frequently used for filtering before other fields. For example, in the log scenario in the Duplicate model section, the log time
log_timeis placed before the error codeerror_code.Place key columns for equality filters before key columns for range filters. For example, in the e-commerce scenario in the Inverted index section, the time
order_timeis usually filtered by range and is placed after the order IDorder_id.Place regular type fields before VARCHAR type fields. For example, place INT type key columns before VARCHAR type key columns.
Example
In the e-commerce scenario in the Inverted index section, the prefix index for the order information table is order_id+order_time. When the query condition is a prefix of the prefix index (that is, the query condition includes order_id, or both order_id and order_time), the query speed can be significantly increased. As shown in the following two examples, the query in Example 1 is much faster than the query in Example 2.
Example 1
SELECT * FROM orders WHERE order_id = 1829239 and order_time = '2024-02-01';Example 2
SELECT * FROM orders WHERE order_time = '2024-02-01';Next steps
After you complete the first three steps of this tutorial, you will have a basic understanding of SelectDB and be able to design database tables that meet your business requirements. Next, you can learn about specific features relevant to your business, such as data migration, querying external data sources, and version upgrades. For information about more features, see What to do next.