溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何運用LIST和RANGE與HASH分區(qū)解決熱點數(shù)據(jù)的分散

發(fā)布時間:2021-09-14 09:56:22 來源:億速云 閱讀:189 作者:柒染 欄目:大數(shù)據(jù)

本篇文章為大家展示了如何運用LIST和RANGE與HASH分區(qū)解決熱點數(shù)據(jù)的分散,內(nèi)容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。

熱點數(shù)據(jù)通俗的講是指被高頻使用到的數(shù)據(jù),比如某些熱點事件,由于網(wǎng)絡的發(fā)酵效應,短時間能夠達到幾十萬甚至上百萬的并發(fā)量,針對這類場景,我們需要分析系統(tǒng)瓶頸所在,以及應對的技術(shù)實現(xiàn)

方案講解
  • 大并發(fā)架構(gòu)演進
    如何運用LIST和RANGE與HASH分區(qū)解決熱點數(shù)據(jù)的分散
    1、圖1和圖2的區(qū)別是中間會有一層web緩存服務器,該服務它可以由nginx+lua+redis進行設計完成,緩存層的熱點數(shù)據(jù)分散,將會在后續(xù)的‘高并發(fā)度’章節(jié)做介紹。
    2、熱點數(shù)據(jù)肯定能在web層的緩存服務器被攔截住,防止把大量的請求打到應用服務器,但是對于非熱點的數(shù)據(jù)穿透緩存后會請求至DB,這部分數(shù)據(jù)每秒幾千的QPS對DB造成的壓力也是非常大的,這個時候我們需要一定的方案,保證請求的時效性,就是如何降低DB層面的IO次數(shù)

  • 場景分類
    熱點數(shù)據(jù)并發(fā)分為讀和寫兩種場景,日常高并發(fā)遇到的大多數(shù)都是讀場景,無論是采用何種的架構(gòu)設計,都需要在緩存層和DB層面做熱點數(shù)據(jù)的分散,本章著重介紹后者

  • 原理分析
    大家都知道對熱點數(shù)據(jù)分散后,系統(tǒng)的性能會有顯著提升,是什么原理導致的,接下來我們探討一下db存儲的一些關(guān)鍵知識,上面兩個是大家經(jīng)常用到的兩種mysql存儲引擎,尤其是后者,基本上筆者在工作中遇到的絕大多數(shù)的表都定義成了innodb引擎,兩者的差異在哪里?使用場景的區(qū)別在什么地方?
    1、讀數(shù)據(jù)
    myisam:與innodb一樣都采用BTREE實現(xiàn),myisam是非聚集索引,索引文件和數(shù)據(jù)文件分離,它對讀的效率非常好,為什么呢,是因為它的存儲結(jié)構(gòu)決定的,數(shù)據(jù)順序存儲,樹的葉子節(jié)點指向的是文件物理地址,所以查詢起來效率較高
    innodb:它是通過聚集索引實現(xiàn),按照主鍵聚集,所以innodb引擎必須要擁有一個唯一標識這列數(shù)據(jù)的標識,對于聚集索引它的葉子節(jié)點存放的是數(shù)據(jù),對于innodb的輔助索引它的葉子節(jié)點是主鍵的值,所以查詢的時候增加了二次查找,為了避免這種情況,可以直接使用聚集索引去查,但是現(xiàn)實情況是大多數(shù)的業(yè)務場景我們依然需要借助于輔助索引
    2、寫數(shù)據(jù)
    myisam:不支持事物,且寫優(yōu)先級高于讀優(yōu)先級,多線程讀可并發(fā),讀和插入通過優(yōu)化參數(shù)可并發(fā),讀和更新不可并發(fā),鎖的級別是表級別鎖
    innodb:支持事物,可以實現(xiàn)讀寫并發(fā),行級別鎖,寫性能優(yōu)于myisam的引擎
    3、數(shù)據(jù)頁
    是innodb數(shù)據(jù)存儲的一個基本單位,可以通過優(yōu)化innodb_page_size參數(shù)進行修改,默認是16K,根據(jù)上面提到的聚集索引的原理,索引的大小、單條數(shù)據(jù)的大小決定了該數(shù)據(jù)頁所能包含的記錄條數(shù),包含的數(shù)據(jù)記錄越多,需要做翻頁的幾率就越小,進行IO的次數(shù)相對就會減小

實現(xiàn)方式
  • 垂直分表
    垂直分表是對列做拆解,可以根據(jù)業(yè)務功能或者冷熱去拆解,比如對用戶表根據(jù)使用冷熱場景進行拆解的示意圖如下:
    如何運用LIST和RANGE與HASH分區(qū)解決熱點數(shù)據(jù)的分散
    垂直分表的意義是在于將熱表進一步拆分,降低數(shù)據(jù)表的因為單行長度過大,導致的多頁查詢,引起的IO過多問題

  • 水平分表
    水平分表是對行做拆解,拆完以后單張表的數(shù)據(jù)量會更小
    比如對5000萬的數(shù)據(jù)量,做水平分表,原來單表5000萬的數(shù)據(jù)記錄,拆分為10張表以后,每張表則為500萬記錄
    每個索引頁的大小固定默認16K,所以在單頁大小固定的情況下,單表記錄越多,索引頁的頁數(shù)越多,查詢期間分頁的概率和頻次就會增加
    水平分表就是解決這個問題,分表的實現(xiàn)方式:分區(qū)、分表/分庫,具體參考下面的介紹。

  • 最佳實踐
    1、冷熱數(shù)據(jù)分離
    以文章內(nèi)容系統(tǒng)為例:標題、作者、分類、創(chuàng)建時間、點贊數(shù)、回復數(shù)、最近回復時間
    1.1、冷數(shù)據(jù):可以理解成偏靜態(tài)的數(shù)據(jù),會頻繁的被讀取,但是幾乎或者很少被改變,這類數(shù)據(jù)對讀的性能要求較高,數(shù)據(jù)存儲可以使用myisam引擎
    1.2、熱數(shù)據(jù):數(shù)據(jù)內(nèi)容被頻繁改變,這類數(shù)據(jù)對并發(fā)讀寫要求較高,我們可以使用innodb引擎存儲
    根據(jù)具體的使用場景使用不同的存儲引擎,以達到性能的相對最優(yōu),將文章內(nèi)容系統(tǒng)的表結(jié)構(gòu)進行冷熱拆分,拆分后的表結(jié)構(gòu)如下:
    如何運用LIST和RANGE與HASH分區(qū)解決熱點數(shù)據(jù)的分散
    1.3、拆分前后性能比對
    插入100000條數(shù)據(jù),對拆分前后的文章表做查詢,性能比對的趨勢如下,同樣都是模擬50個并發(fā),一共2500次請求,每個線程50個請求,采用ID隨機,這樣更貼近真實的查詢場景,很明顯拆分后的效果更勝一籌:
    拆分后單表測試:

    mysqlslap -h227.0.0.1 -uroot -P3306 -p --concurrency=50 --iterations=1    --engine=myisam --number-of-queries=2500 --query='select * from cms_blog_static where id=RAND()*1000000' --create-schema=test

    如何運用LIST和RANGE與HASH分區(qū)解決熱點數(shù)據(jù)的分散
    未拆分測試:

    mysqlslap -h227.0.0.1 -uroot -P3306 -p --concurrency=50 --iterations=1    --engine=innodb --number-of-queries=2500 --query='select * from cms_blog where id=RAND()*1000000' --create-schema=test

    如何運用LIST和RANGE與HASH分區(qū)解決熱點數(shù)據(jù)的分散
    2、減少單行數(shù)據(jù)大小
    對于拆分以后的數(shù)據(jù)表,我們能否進一步降低單行數(shù)據(jù)的大小呢,總結(jié)起來常用的方法如下:
    2.1、設置合理的字段長度
    大家都知道不同的字段類型占用的存儲空間不同,如下圖:

    類型長度(字節(jié))定長/非定長
    TINYINT1定長
    SMALLINT2定長 
    MEDIUMINT3定長
    INT4定長
    BIGINT8定長
    FLOAT(m)4字節(jié)(m<=24)、8字節(jié)(m>=24 and m<=53)非定長
    FLOAT4定長
    DOUBLE8定長
    DOUBLE PRECISION8定長
    DECIMAL(m,d)m字節(jié)(m>d)、d+2字節(jié)(m<d)非定長
    NUMBER(m,d)m字節(jié)(m>d)、d+2字節(jié)(m<d)非定長
    DATE3定長
    DATETIME8定長
    TIMESTAMP4定長
    TIME3定長
    YEAR1定長
    CHAR(m)m非定長
    VARCHAR(m)l字節(jié),l就是實際存儲字節(jié)(l<=m)非定長
    BLOB, TEXTl+2字節(jié),l就是實際存儲字節(jié)非定長
    LONGBLOB, LONGTEXTl+4字節(jié),l就是實際存儲字節(jié)非定長

    我們在實際使用中,需要根據(jù)實際的需要選擇合理的類型,能有效的減小單行數(shù)據(jù)的大小,比如,user_status,一般我們定義成tinyint(1)即可,沒必要定義成int,白白多占用3個字節(jié)
    2.2、設置合理的索引長度
    2.2.1、對于需要建索引的字段,如果字段占用的空間越大,對于索引來說,建立索引的長度就越長,索引頁大小不變的情況下,數(shù)據(jù)條數(shù)就越少,查詢需要做IO的次數(shù)就越頻繁
    2.2.2、對于某些索引字段,如果我們可以通過前綴字段能達到很好的區(qū)分度,則可以控制創(chuàng)建索引的長度,目的是索引頁的含的數(shù)據(jù)行數(shù)更多,減少IO,方式如下:

    //如下,我們根據(jù)字段1和字段2,指定的組合索引的長度
    alter table table_name add index index_name (field1(length2),field2(length3))

    2.2.3、索引的選擇性
    索引本身是由開銷的,首先是存儲資源,然后插入和更新帶來的對B+Tree樹的維護,數(shù)據(jù)更新帶來的性能下降,所以對于我們的原則是:索引該不該建,以及用什么字段建
    數(shù)據(jù)量少-則不建,區(qū)分度或者選擇性不高-則不建,數(shù)據(jù)量少大家很容易理解,小于1W條數(shù)據(jù)全表掃描也能接收,選擇性或者區(qū)分度是指,數(shù)據(jù)的分散程度,比如某個用戶表,有一個性別字段,數(shù)據(jù)量越大它的索引選擇性越差,計算公式如下:

    //返回值范圍(0,1],該值越大,索引選擇性越高
    select distinct(col)/count(*) from table_name

    同理,對于需要控制索引長度的字段,計算選擇性如下:

    select distinct(left(col,n))/count(*) from table_name

    2.2.4、性能比較
    通過對100w的數(shù)據(jù),對數(shù)據(jù)行大小做優(yōu)化,前后性能比對結(jié)果如下:
    優(yōu)化前后的表結(jié)構(gòu)定義如下:

    --優(yōu)化前
    CREATE TABLE `cms_blog` (
      `id` bigint(20) NOT NULL auto_increment,
      `title` varchar(60) NOT NULL,
      `creator` varchar(20) NOT NULL,
      `blog_type` tinyint(1) not NULL,
      `reply_praise` int(10) UNSIGNED,
      `reply_count` int(10) UNSIGNED,
      `create_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
      `update_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
      PRIMARY KEY (`id`)
    ) ENGINE=innodb DEFAULT CHARSET=utf8;
    --創(chuàng)建索引
    alter table cms_blog add index idx_reply_count (reply_count);
    --優(yōu)化后reply_praise和reply_count
    CREATE TABLE `cms_blog_v2` (
      `id` bigint(20) NOT NULL auto_increment,
      `title` varchar(60) NOT NULL,
      `creator` varchar(20) NOT NULL,
      `blog_type` tinyint(1) not NULL,
      `reply_praise` MEDIUMINT(10) UNSIGNED,
      `reply_count` MEDIUMINT(10) UNSIGNED,
      `create_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
      `update_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00' ON UPDATE CURRENT_TIMESTAMP,
      PRIMARY KEY (`id`)
    ) ENGINE=innodb DEFAULT CHARSET=utf8;
    --創(chuàng)建索引
    alter table cms_blog_v2 add index idx_reply_count (reply_count);

    壓測腳本如下:

    --壓測腳本,舊表
    mysqlslap -h227.0.0.1 -uroot -P3306 -p --concurrency=50 --iterations=1    --engine=innodb --number-of-queries=2500 --query='select * from cms_blog where reply_count>999990' --create-schema=test
    --壓測腳本,新表
    mysqlslap -h227.0.0.1 -uroot -P3306 -p --concurrency=50 --iterations=1    --engine=innodb --number-of-queries=2500 --query='select * from cms_blog_v2  where reply_count>999990' --create-schema=test

    性能表現(xiàn),舊表和新表壓測表現(xiàn)如下:
    如何運用LIST和RANGE與HASH分區(qū)解決熱點數(shù)據(jù)的分散
    新表優(yōu)化后性能明顯有提升:
    如何運用LIST和RANGE與HASH分區(qū)解決熱點數(shù)據(jù)的分散
    2.3、主鍵的選擇
    盡量使用保持單調(diào)性的自增主鍵,避免使用uuid、hash方式、業(yè)務自定義主鍵,減少索引重建對性能的影響
    3、分散數(shù)據(jù)頁查詢
    3.1、數(shù)據(jù)分區(qū)
    數(shù)據(jù)分區(qū)可以有效的提升查詢的性能,充分利用不同分區(qū)所關(guān)聯(lián)的IO存儲,在邏輯上是屬于同一張表,物理上可以分散到不同的磁盤存儲,缺點是跨分區(qū)查詢的性能稍差,所以互聯(lián)網(wǎng)公司在實際當中很少用到數(shù)據(jù)分區(qū),一般建議物理分表的方式實現(xiàn)

  • CREATE TABLE table_name (
      id INT AUTO_INCREMENT,
      customer_surname VARCHAR(30),
      store_id INT,
      salesperson_id INT,
      order_date DATE,
      note VARCHAR(500),
      INDEX idx (id)
    ) ENGINE = INNODB
      PARTITION BY LIST(store_id) (
      PARTITION p1
      VALUES IN (1, 3, 4, 17)
      INDEX DIRECTORY = '/var/path3'
      DATA DIRECTORY = '/var/path2';

    3.1.1、分區(qū)方式
    3.1.1.1、RANGE partitioning

    CREATE TABLE employees (
        id INT NOT NULL,
        fname VARCHAR(30),
        lname VARCHAR(30),
        hired DATE NOT NULL DEFAULT '1970-01-01',
        separated DATE NOT NULL DEFAULT '9999-12-31',
        job_code INT NOT NULL,
        store_id INT NOT NULL
    )
    PARTITION BY RANGE (store_id) (
        PARTITION p0 VALUES LESS THAN (6),
        PARTITION p1 VALUES LESS THAN (11),
        PARTITION p2 VALUES LESS THAN (16),
        PARTITION p3 VALUES LESS THAN (21)
    );

    3.1.1.2、LIST Partitioning

    CREATE TABLE employees (
        id INT NOT NULL,
        fname VARCHAR(30),
        lname VARCHAR(30),
        hired DATE NOT NULL DEFAULT '1970-01-01',
        separated DATE NOT NULL DEFAULT '9999-12-31',
        job_code INT,
        store_id INT
    )
    PARTITION BY LIST(store_id) (
        PARTITION pNorth VALUES IN (3,5,6,9,17),
        PARTITION pEast VALUES IN (1,2,10,11,19,20),
        PARTITION pWest VALUES IN (4,12,13,14,18),
        PARTITION pCentral VALUES IN (7,8,15,16)
    );

    3.1.1.3、COLUMNS Partitioning

    --range columns
    CREATE TABLE rc1 (
        a INT,
        b INT
    )
    PARTITION BY RANGE COLUMNS(a, b) (
        PARTITION p0 VALUES LESS THAN (5, 12),
        PARTITION p3 VALUES LESS THAN (MAXVALUE, MAXVALUE)
    );
    --list columns
    CREATE TABLE customers_1 (
        first_name VARCHAR(25),
        last_name VARCHAR(25),
        street_1 VARCHAR(30),
        street_2 VARCHAR(30),
        city VARCHAR(15),
        renewal DATE
    )
    PARTITION BY LIST COLUMNS(city) (
        PARTITION pRegion_1 VALUES IN('Oskarshamn', 'H?gsby', 'M?nster?s'),
        PARTITION pRegion_2 VALUES IN('Vimmerby', 'Hultsfred', 'V?stervik'),
        PARTITION pRegion_3 VALUES IN('N?ssj?', 'Eksj?', 'Vetlanda'),
        PARTITION pRegion_4 VALUES IN('Uppvidinge', 'Alvesta', 'V?xjo')
    );

    3.1.1.4、HASH Partitioning

    CREATE TABLE employees (
        id INT NOT NULL,
        fname VARCHAR(30),
        lname VARCHAR(30),
        hired DATE NOT NULL DEFAULT '1970-01-01',
        separated DATE NOT NULL DEFAULT '9999-12-31',
        job_code INT,
        store_id INT
    )
    PARTITION BY HASH(store_id)
    PARTITIONS 4;

    3.1.1.5、KEY Partitioning

    CREATE TABLE tk (
        col1 INT NOT NULL,
        col2 CHAR(5),
        col3 DATE
    )
    PARTITION BY LINEAR KEY (col1)
    PARTITIONS 3;

    3.2、數(shù)據(jù)分表/分庫
    數(shù)據(jù)分表解決的問題,提升單表的并發(fā)能力,文件分布在不同的表文件,對IO性能進一步提升,另外對讀寫鎖影響的數(shù)據(jù)量變少,插入數(shù)據(jù)需要做索引重建的數(shù)據(jù)減少,insert或update性能會更好
    3.2.1、分表和分庫方式
    3.2.1.1:哈希取模方式,hash(關(guān)鍵字)%N
    3.2.1.2:按照時間,如按照年或者月分表
    3.2.1.3、按照業(yè)務,以訂單業(yè)務為例,平臺訂單、三方訂單
    3.2.2、分庫分表中間件
    整體來說分為在客戶端實現(xiàn),和代理端實現(xiàn),比如:cobar、sharding-jdbc、mycat等,具體使用可以自行檢索

上述內(nèi)容就是如何運用LIST和RANGE與HASH分區(qū)解決熱點數(shù)據(jù)的分散,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關(guān)注億速云行業(yè)資訊頻道。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI