Partition là việc chia dữ liệu từ table đơn với một tệp dữ liệu vật lý thành nhiều phần khác và được lưu trữ trong các tệp dữ liệu vật lý khác nhau.
Một số ưu điểm của partition
Cho phép lưu được nhiều dữ liệu hơn vào một table khi partition khi so sánh với việc lưu trữ dữ liệu table vào một tệp tin hoặc một disk đơn. Nghĩa là dữ liệu partition có thể lưu trữ ở nhiều disk vật lý khác nhau.
Việc query có thể tối ưu hơn khi thực hiện query dữ liệu trên một hoặc một vài partition thay vì phải query toàn bộ dữ liệu table.
Một số hạn chế của partition
Một số cấu trúc sau không được sử dụng trong partition:
Stored procedures, stored functions, UDFs, or plugins
Declared variables or user variables
Toán tử số học và logic:
Toán tử +, – , * được phép sử dụng trong partition tuy nhiên kết quả phải là integer hoặc NULL (ngoại trừ sử dụng kiểu KEY partitioning).
Toán tử DIV được hỗ trợ nhưng toán tử / không được phép.
Các toán tử |, &, ^, <<, >>, and ~ không được phép sử dụng trong các biểu thức partition.
Query cache không được hỗ trợ trong partition
…
2. Partition types
Range partition: Loại partition này sẽ phân các row thành các partition với phạm vi (range) được định nghĩa.
List partition: Phân các row thành các partition dựa trên các column đúng với một trong một tập giá trị rời rạc
Hash partition: Mỗi partition trong kiểu hash dựa trên giá trị được trả về bởi một biểu thức do người dùng định nghĩa.
Key partition: Kiểu partition này giống với hash partition, ngoại trừ việc chỉ cung cấp một hoặc nhiều column được đánh giá và MySQL server cung cấp hàm hash riêng của nó.
II. How to create partitions
1. How to add partitions to a table in mariadb / mysql
Thực hiện tạo cấu trúc bảng và đồng thời tạo partitions như sau:
CREATE TABLE `crm_realtime` (
`id` varchar(200) NOT NULL,
`name` varchar(200) DEFAULT NULL,
`status` int(11) DEFAULT '0',
`type` int(11) DEFAULT '0',
`user_id` int(11) DEFAULT NULL,
`registed_at` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `sales_team1` (`sales_team`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
PARTITION BY RANGE COLUMNS (registed_at) (
PARTITION p0 VALUES LESS THAN ('2020-04-01'),
PARTITION p1 VALUES LESS THAN ('2020-05-01'),
PARTITION p2 VALUES LESS THAN ('2020-06-01'),
PARTITION p3 VALUES LESS THAN ('2020-07-01'),
PARTITION p4 VALUES LESS THAN ('2020-08-01'),
PARTITION p5 VALUES LESS THAN ('2020-09-01'),
PARTITION p6 VALUES LESS THAN ('2020-10-01'),
PARTITION p7 VALUES LESS THAN ('2020-11-01'),
PARTITION p8 VALUES LESS THAN ('2020-12-01'),
PARTITION p9 VALUES LESS THAN ('2021-01-01'),
PARTITION p10 VALUES LESS THAN ('2021-02-01'),
PARTITION p11 VALUES LESS THAN ('2021-03-01'),
PARTITION p12 VALUES LESS THAN (MAXVALUE)
);
2. How to add partitions to an existing table in mariadb / mysql
Khi table này crm_realtime phình to ra, chúng ta có thể thực hiện chia bảng thành nhiều partitions để cho phép thực hiện query nhanh hơn.
Nếu tạo partition với range từng month theo trường registed_at kiểu datetime, yêu cầu các bước sau:
Cập nhật các giá trị NULL của registed_at row đến các giá trị NOT NULL
Thiết lập registed_at field là NOT NULL
Thiết lập registed_at field là PRIMARY KEY
Thực hiện alter bảng và tạo các partitions với type RANGE và dùng function COLUMN cho kiểu dữ liệu datetime (hoặc function UNIX_TIMESTAMP với kiểu dữ liệu timestamp)
Chúng ta thực hiện cụ thể như sau:
mysql>use crm;
mysql>update table crm_realtime set registed_at = updated_at where registed_at is NULL;
mysql>alter table crm_realtime modify column registed_at datetime NOT NULL;
mysql>alter table crm_realtime drop PRIMARY KEY, add primary key (`id`, `registed_at`);
msyql>alter table crm_realtime
PARTITION BY RANGE COLUMNS (registed_at) (
PARTITION p202003 VALUES LESS THAN ('2020-04-01'),
PARTITION p202004 VALUES LESS THAN ('2020-05-01'),
PARTITION p202005 VALUES LESS THAN ('2020-06-01'),
PARTITION p202006 VALUES LESS THAN ('2020-07-01'),
PARTITION p202007 VALUES LESS THAN ('2020-08-01'),
PARTITION p202008 VALUES LESS THAN ('2020-09-01'),
PARTITION p202009 VALUES LESS THAN ('2020-10-01'),
PARTITION p202010 VALUES LESS THAN ('2020-11-01'),
PARTITION p202011 VALUES LESS THAN ('2020-12-01'),
PARTITION p202012 VALUES LESS THAN ('2021-01-01'),
PARTITION p202101 VALUES LESS THAN ('2021-02-01'),
PARTITION p202102 VALUES LESS THAN ('2021-03-01')
);
III. Partition Management
Show thông tin các partition của crm_realtime table
Thêm mới partition từ table đã có sẵn các partitions
mysql> ALTER TABLE crm_realtime ADD PARTITION (
-> PARTITION p202103 VALUES LESS THAN ('2021-04-01')
-> );
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0
Thay đổi tên partition
Thông tin việc tạo các partition nên để theo quy tắc nhất định để cho phép select dữ liệu ở một hoặc một số partition dễ dàng hơn. Chẳng hạn select dữ liệu theo mẫu pYYMM chẳng hạn.
mysql>
ALTER TABLE crm_realtime REORGANIZE PARTITION p1 INTO ( PARTITION p202003 VALUES LESS THAN ('2020-04-01') );
ALTER TABLE crm_realtime REORGANIZE PARTITION p2 INTO ( PARTITION p202004 VALUES LESS THAN ('2020-05-01') );
ALTER TABLE crm_realtime REORGANIZE PARTITION p3 INTO ( PARTITION p202005 VALUES LESS THAN ('2020-06-01') );
ALTER TABLE crm_realtime REORGANIZE PARTITION p4 INTO ( PARTITION p202006 VALUES LESS THAN ('2020-07-01') );
ALTER TABLE crm_realtime REORGANIZE PARTITION p5 INTO ( PARTITION p202007 VALUES LESS THAN ('2020-08-01') );
ALTER TABLE crm_realtime REORGANIZE PARTITION p6 INTO ( PARTITION p202008 VALUES LESS THAN ('2020-09-01') );
Remove partition without delete data
ALTER TABLE table-name REMOVE PARTITIONING;
IV. How to use
Show thông tin dữ liệu trong một partition được chỉ định
mysql> select id,name,registed_at from crm_realtime partition(p202103) limit 2;
+------------+------+---------------------+
| id | name | registed_at |
+------------+------+---------------------+
| 1000000009 | abc | 2021-03-10 02:00:41 |
| 1000101010 | def | 2021-03-12 02:00:58 |
+------------+------+---------------------+
2 rows in set (0.00 sec)
Show thông tin dữ liệu trong một số partition
mysql> select id,name,registed_at from crm_realtime partition(p202102,p202103) limit 5;
+------------+--------------------+---------------------+
| id | name | registed_at |
+------------+--------------------+---------------------+
| 083878492 | nguyen van A | 2021-02-20 10:43:33 |
| 1000010000 | nguyen van B | 2021-02-19 02:00:32 |
| 1009944888 | nguyen van C | 2021-02-26 02:01:02 |
| 1011239922 | nguyen van D | 2021-02-17 16:58:04 |
| 1033355978 | nguyen van E | 2021-02-01 20:06:27 |
+------------+--------------------+---------------------+
5 rows in set (0.01 sec)
Xóa dữ liệu trong partition
mysql> delete from crm_realtime partition(p202104) where name = "keepwalking";
Query OK, 1 row affected (0.00 sec)
NOTE
Việc đánh partition table làm cho table phân thành nhiều mảnh vật lý. Việc query cũng sẽ lựa chọn một hoặc một số partition tùy thuộc vào nhu cầu. Và query cả table (không sử dụng partition) dữ liệu vẫn toàn vẹn.
Hệ thống được triển khai trên 5 nodes, gồm 14 instances (02 instances cho mongos, 03 instances cho config server, 03 instances cho shard1, 03 instances cho shard2, 03 instances cho shard3)
Note: Chúng ta khởi tạo replica set con config server với tham số cấu hình configsvr: true
4. Setup Shards
Thực hiện cấu hình các bước sau trên shard server
Ở đây, trên mỗi server chúng ta tạo 03 instance cho mongod với các port 27017,27018,27019 tương ứng cho các shard1, shard2, shard3. Nghĩa là, chúng tạo 03 shard, với mỗi shard là một replica set của 3 instances, vì thế chúng ta sẽ tạo 09 instance cho mongod service
Trên mỗi server, chúng ta thực hiện các bước sau để cấu hình các shard
Step1: Cấu hình cho shard1
Tạo các tệp cấu hình /etc/mongo-shard1.conf
systemLog:
destination: file
logAppend: true
logRotate: reopen
path: /var/log/mongodb/shard1.log
storage:
dbPath: /data/shard1
journal:
enabled: true
processManagement:
fork: true # fork and run in background
pidFilePath: /var/run/mongodb/mongod-shard1.pid # location of pidfile
timeZoneInfo: /usr/share/zoneinfo
net:
port: 27017
bindIp: 0.0.0.0 # Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.
replication:
replSetName: "shard01"
sharding:
clusterRole: shardsvr
Trong tệp cấu hình này, chúng đặt tên cho replication là “shard01” và clusterRole cho sharding là “shardsvr”.
Tương tự như vậy, chúng ta tạo tệp cấu hình cho shard2 và shard3
Tạo các tệp cấu hình /etc/mongo-shard2.conf
systemLog:
destination: file
logAppend: true
logRotate: reopen
path: /var/log/mongodb/shard2.log
storage:
dbPath: /data/shard2
journal:
enabled: true
processManagement:
fork: true # fork and run in background
pidFilePath: /var/run/mongodb/mongod-shard2.pid # location of pidfile
timeZoneInfo: /usr/share/zoneinfo
net:
port: 27018
bindIp: 0.0.0.0 # Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.
replication:
replSetName: "shard02"
sharding:
clusterRole: shardsvr
Tạo các tệp cấu hình /etc/mongo-shard3.conf
systemLog:
destination: file
logAppend: true
logRotate: reopen
path: /var/log/mongodb/shard3.log
storage:
dbPath: /data/shard3
journal:
enabled: true
processManagement:
fork: true # fork and run in background
pidFilePath: /var/run/mongodb/mongod-shard3.pid # location of pidfile
timeZoneInfo: /usr/share/zoneinfo
net:
port: 27019
bindIp: 0.0.0.0 # Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.
replication:
replSetName: "shard03"
sharding:
clusterRole: shardsvr
Step2: Start các mongod service
Thực hiện start các instance mongod service trên các server3, server4, server5
Trên mỗi replica set, chúng ta sẽ ưu tiên trên mỗi server chỉ chỉ set một vai trò master trên đó để đảm bảo việc read/write dữ liệu vào các server cân bằng hơn.
Cấu hình replica set cho shard1
Thực hiện truy cập mongo shard và khởi tạo replica set shard1 trên server3
[root@node01 ~]# mongo -port 27017
MongoDB shell version v4.0.23
connecting to: mongodb://127.0.0.1:27017/?gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("ced31d75-f290-40dd-a54c-0c584b8e7d96") }
MongoDB server version: 4.0.23
shard01:PRIMARY>
[root@node02 ~]# mongo --port 27018
MongoDB shell version v4.0.23
connecting to: mongodb://127.0.0.1:27018/?gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("6b942cd1-207f-4dc7-9ef3-526f56808a9e") }
MongoDB server version: 4.0.23
shard02:PRIMARY>
[root@node03 ~]# mongo --port 27019
MongoDB shell version v4.0.23
connecting to: mongodb://127.0.0.1:27019/?gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("068a5068-57b0-4f38-9ea3-3fbbfdbc24b0") }
MongoDB server version: 4.0.23
shard03:PRIMARY>
5. Setup Query Router ( mongos)
Phần này, chúng ta thực hiện cấu hình trên mongos instance đầu tiên.
Step1: Tệp tệp cấu hình /etc/mongod.conf
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongos.log
processManagement:
fork: true # fork and run in background
pidFilePath: /var/run/mongodb/mongod.pid # location of pidfile
timeZoneInfo: /usr/share/zoneinfo
net:
port: 27017
bindIp: 0.0.0.0 # Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.
sharding:
configDB: "replconfig01/mongo-config-1:27020,mongo-config-2:27020,mongo-config-3:27020"
Step2: Start mongos
mongos -f /etc/mongod.conf
Nếu chạy mongos như systemd, khi đó tạo tệp tin /usr/lib/systemd/system/mongos.service
mongos> sh.status()
--- Sharding Status ---
sharding version: {
"_id" : 1,
"minCompatibleVersion" : 5,
"currentVersion" : 6,
"clusterId" : ObjectId("6077fa4c673f0ec1ec4ba9d1")
}
shards:
{ "_id" : "shard01", "host" : "shard01/mongo-config-1:27017,mongo-config-2:27017,mongo-config-3:27017", "state" : 1 }
{ "_id" : "shard02", "host" : "shard02/mongo-config-1:27018,mongo-config-2:27018,mongo-config-3:27018", "state" : 1 }
{ "_id" : "shard03", "host" : "shard03/mongo-config-1:27019,mongo-config-2:27019,mongo-config-3:27019", "state" : 1 }
active mongoses:
"4.0.24" : 1
autosplit:
Currently enabled: yes
balancer:
Currently enabled: yes
Currently running: no
Failed balancer rounds in last 5 attempts: 0
Migration Results for the last 24 hours:
No recent migrations
databases:
{ "_id" : "config", "primary" : "config", "partitioned" : true }
mongos>
Ở đây, chúng ta có thể thấy một số thông tin như thông tin các instance của các shard, trạng thái shard
Step4: Enable Sharding at Database Level
Chúng ta thử enable sharding cho một database mới
mongo --host 192.168.1.11 --port 27017
mongos> use example
switched to db example
mongos> sh.enableSharding("example")
{
"ok" : 1,
"operationTime" : Timestamp(1618940724, 4),
"$clusterTime" : {
"clusterTime" : Timestamp(1618940724, 4),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
mongos>
Step5: Enable Sharding at Collection Level
ở bước trên chúng ta đã enable shard cho database. Bước này chúng ta sẽ enable sharding cho một collection.
Để thực hiện sharding cho collection, chúng ta cần xác định sẽ partition theo range based hay hash based (Mặc định sharding theo range based).
Trong phần này chúng ta sẽ shard theo hash based để nhìn rõ hơn quá trình phân vùng dữ liệu qua các shard.
mongo --host 192.168.1.11 --port 27017
mongos> use example
mongos> db.exCollection.ensureIndex({ _id : "hashed" })
mongos> sh.shardCollection("example.exCollection", {"_id" : "hashed"})
mongos>
Ở đây, Chúng ta tạo collection exCollection trong example database và cần thực hiện tạo index cho trường _id với kiểu hashed. Trường _id mặc định được tạo và đánh index với unique index
Note: Việc chọn shard key ảnh hưởng cho việc tạo và phân tán chunks qua các shard có sẵn. Việc này ảnh hưởng trực tiếp đến hiệu quả và hiệu suất hoạt động trong shard cluster. Chẳng hạn với việc chọn field _id ở trên để đánh index thì chỉ có ý nghĩa về mặt phân tán dữ liệu, trong thực tế query dữ liệu hiếm khi sử dụng filter theo trường _id.
Step7: Insert data và kiểm tra quá trình partition
Từ client thực hiện insert dữ liệu qua mongos
mongos>use example
mongos> for (var i = 1; i <= 10000; i++) db.exCollection.insert( { x : i } )
WriteResult({ "nInserted" : 1 })
mongos>
Khi đó check dữ liệu phân tán ở các shard như nào
mongos> use example
mongos> db.exCollection.getShardDistribution()
Shard shard03 at shard03/mongo-config-1:27019,mongo-config-2:27019,mongo-config-3:27019
data : 108KiB docs : 3364 chunks : 2
estimated data per chunk : 54KiB
estimated docs per chunk : 1682
Shard shard01 at shard01/mongo-config-1:27017,mongo-config-2:27017,mongo-config-3:27017
data : 105KiB docs : 3263 chunks : 2
estimated data per chunk : 52KiB
estimated docs per chunk : 1631
Shard shard02 at shard02/mongo-config-1:27018,mongo-config-2:27018,mongo-config-3:27018
data : 108KiB docs : 3373 chunks : 2
estimated data per chunk : 54KiB
estimated docs per chunk : 1686
Totals
data : 322KiB docs : 10000 chunks : 6
Shard shard03 contains 33.63% data, 33.63% docs in cluster, avg obj size on shard : 33B
Shard shard01 contains 32.63% data, 32.63% docs in cluster, avg obj size on shard : 33B
Shard shard02 contains 33.73% data, 33.73% docs in cluster, avg obj size on shard : 33B
mongos>
6. Adding a new Query Router ( mongos)
Để hệ thống có high availability & scalability, chúng ta cần triển khai thêm mongos instance.
Về phương pháp triển khai thêm mongos instance, chúng ta chỉ cần cài đặt mongos chạy như mongos instance đầu tiên. Thông tin shard cluster nó sẽ nhận từ các Config Server.
Step1: Tệp tệp cấu hình /etc/mongod.conf
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongos.log
processManagement:
fork: true # fork and run in background
pidFilePath: /var/run/mongodb/mongod.pid # location of pidfile
timeZoneInfo: /usr/share/zoneinfo
net:
port: 27017
bindIp: 0.0.0.0 # Enter 0.0.0.0,:: to bind to all IPv4 and IPv6 addresses or, alternatively, use the net.bindIpAll setting.
sharding:
configDB: "replconfig01/mongo-config-1:27020,mongo-config-2:27020,mongo-config-3:27020"
Step2: Start mongos
mongos -f /etc/mongod.conf
Nếu chạy mongos như systemd, khi đó tạo tệp tin /usr/lib/systemd/system/mongos.service
[root@mongos02 ~]# mongo --host 192.168.1.12 --port 27017
MongoDB shell version v4.0.24
connecting to: mongodb://192.168.1.12:27017/?gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("e415c22d-b605-4571-8cfa-c45c0d891852") }
MongoDB server version: 4.0.24
Server has startup warnings:
2021-04-21T17:54:15.437+0000 I CONTROL [main]
2021-04-21T17:54:15.437+0000 I CONTROL [main] ** WARNING: Access control is not enabled for the database.
2021-04-21T17:54:15.437+0000 I CONTROL [main] ** Read and write access to data and configuration is unrestricted.
2021-04-21T17:54:15.437+0000 I CONTROL [main]
mongos>
MongoDB hỗ trợ chứng thực giữa các member trong shard cluster với 2 phương thức
Keyfile
X.509
Trong phần này chúng sẽ cấu hình chứng thực shard cluster với keyfile và access control.
Bước 1: Tạo Keyfile
Nội dung của keyfile giống như chia sẻ mật khẩu chung giữa các member trong shard cluster. Yêu cầu độ dài của key nằm giữa 6-102 ký tự và có thể chứa các ký tự trong tập base64.
Thực hiện copy keyfile đã tạo ở trên đến các member server trong shard cluster. Cho rằng đường dẫn chứa keyfile mặc định của các member server là /var/lib/mongo/keyfile
Bước 2: Sửa nội dung tệp cấu hình
Thêm nội dung sau vào tệp cấu hình của các member server trong shard cluster
Một số chế độ read mà Lettuce route đến Redis cluster:
Mặc định Lettuce route thao tác read đến master nodes. Lettuce-core sử dụng ReadFrom để thiết lập cách Lettuce-core route đến các nodes.
Setting
Description
MASTER
Default mode. Read from the current master node.
MASTER_PREFERRED
Read from the master, but if it is unavailable, read from replica nodes.
REPLICA
Read from replica nodes.
REPLICA_PREFERRED
Read from the replica nodes, but if none is unavailable, read from the master.
NEAREST
Read from any node of the cluster with the lowest latency.
Dựa vào 5 read modes đó, chúng ta có thể chọn mode REPLICA_PREFERRED để ưu tiên read từ các slave nodes và nếu không có slave nodes thì nó sẽ read từ master nodes.
Ví dụ cấu hình kết nối redis cluster với mode read từ slave nodes
Trong bài viết này, tôi sẽ giới thiệu một sample đơn giản mà nodejs client sử dụng module **ioredis** kết nối với redis cluster và thực hiện read từ slave nodes.
Một số chế độ read mà ioredis route đến Redis cluster
Mặc định ioredis route thao tác read đến master nodes. ioredis sử dụng scaleRead để thiết lập cách ioredis route đến các nodes và thiết lập thao tác read đến các nodes.
Setting
Description
All
Read queries to masters or slaves randomly.
Slave
Read queries to slaves
a custom
A custom function(nodes, command): node. Custom function to select to which node to send read queries
Dưới đây là một ví dụ nodejs kết nối redis với robust ioredis