千萬級別數據的mysql數據表優化

出發點

好比,如今有這麼一個問題,常見的一個面試題:
有一張users表,數據量在五千萬以上,存在一條查詢語句:mysql

SELECT * FROM users WHERE name LIKE '%明%' AND sex='男' AND age=32 AND created_at BETWEEN 1388505600 AND 1420041600;面試

就是說從users表中找出建立時間在14年到15年之間的年齡在32歲,名字中帶有‘明’字的男性用戶spring

追溯到數據表的設計,sql查詢語句的調優,而且須要考慮到這種大表的數據插入時候的注意事項,數據分頁及將來數據表管理時可能碰見的問題及解決方案sql


本文將會從存儲層,設計層,sql語句層和架構層進行優化:數據庫

存儲層

  1. 存儲引擎的選取

    • 這裏只展開InnoDB和MyIsam(選擇哪一個存儲引擎須要判斷當前的業務場景)segmentfault

    • MyIsam表:

      • 支持表級鎖,鎖的顆粒度比較大,因此被鎖定的資源的爭用狀況會比其餘的鎖定級別會多,會下降併發處理的能力。 解決方法是能夠設置 Concurrent Insert(併發插入)windows

      • 數據表的存儲位置:在mysql安裝目錄/data/數據庫名稱下,分爲數據表結構,數據表索引,數據表數據三個文件分別進行存儲。這種索引和數據相分離,是經過物理地址進行關聯的,這種索引結構也叫作‘非聚合型索引’,因此能夠選擇直接拷貝對應表的文件進行數據表的備份。在進行數據查詢的時候,是先查找索引文件中數據對應的記錄地址而後在根據該地址找到對應的數據文件中對應的數據。所以這種索引的方式也叫作「非彙集的」。緩存

      • 支持數據表的壓縮,犧牲數據表的更新操做,換取快速的查找速度。在CMD中執行myisamchk.exe -rq 表名。 使用命令myisamchk.exe --unpack 表名 進行解壓縮。而後使用flush table 表名進行刷新數據表服務器

      • 支持全文索引架構

      • 不支持外鍵

      • 表的具體行數:保存有表的具體行數。在針對一個140萬的數據表的時候,count(*)的速度很快:0.0003秒

      • 總結:myisam是mysql最爲古老的存儲引擎之一,對於以讀爲主的非事務性系統來講,myisam無疑是最優先考慮的對象。可是對於存在必定的併發量的系統,仍是不建議使用Myisam的,由於目前隨着Innodb引擎的成熟,Myisam對於高併發時表鎖的消耗太大

    • InnoDB表

      • 支持行級鎖,帶來的性能方面的鎖比如較大,可是總體的併發性能卻遠遠優於MyIsam表。可是若是使用不當可能會發生死鎖,關於避免死鎖的問題,並無深刻研究過,大致的發生死鎖的緣由就是:由多個併發事務;每一個事物都持有了鎖;每一個事務爲了完成相關的邏輯都須要繼續持有鎖;多個事務之間產生加鎖的循環等待->最終造成死鎖

      • 支持外鍵,支持事務

      • 在進行select count(*)的時候,若是數據庫中只存在一個primary key 的時候,執行count的時候速度會很慢,一張140萬的數據表,執行的速度是6s多。可是myisam的count時間卻只有0.00幾秒。可是若是在InnoDB表中在添加一個索引的時候,速度就會比較快。詳情看:https://segmentfault.com/a/11...

      • 數據表內容的存儲位置:.frm文件用來存儲表結構定義相關的元數據,表的索引和數據存放在一塊兒。用戶能夠自定義,默認的初始化存儲位置(windows)是在c:/programData/mysql 下。種數據和索引存儲在一個文件中的索引結構叫作‘聚合型’索引

  2. 字段的存儲的類型的類型推薦

    • 字段應該優先選擇 數值型進行存儲,整形數據比起字符型處理的開銷會更小,好比性別,是否這些可使用enum進行存儲。ip地址,時間等字段也存儲成整形。可使用數值型和枚舉類型的字段進行不使用字符型進行存儲

    • 越小的數據類型越好。越小的數據類型在硬盤,cpu緩存和內存上的使用空間都更小,處理起來會更快

      • 儘可能避免使用null。在建立數據表的時候應該指定列爲 NOT NULL ,而後設定默認值

設計層

  1. 適當的添加索引

    • 添加索引能夠增快數據查詢的速度,可是對應的在數據的寫入的時候須要去維護索引的數據,因此在數據的插入和更新等操做時速度回變慢。因此添加索引須要注意在經常使用的查詢字段上面進行添加

    •  這對於這裏的需求來說,建立一個name,age,created_at字段上的聯合索引,在後面的查詢條件的排列的地方先查詢name字段,而後是age字段,而後created_at字段,最後在是sex字段

    • 具體的關於索引的內容能夠看 https://segmentfault.com/a/11...

  2. 適時的進行分表和分區

  • 分區

    • 分區就是將整個的業務模塊分散到不一樣的服務器上進行存儲,好比說用戶模塊,文章模塊,相冊模塊等分別存在不一樣的服務器上完成分區

  • 水平分表

    • 物理分表:能夠根據求餘的方式或者hash或根據當前月份等方式進行

      • 手動建立多個數據表,主要是根據當前記錄的索引值進行判斷該數據所在的位置

        • 數據查詢

          $id = $_GET['id'];
          $mod = $id%5;
          $sql = "SELECT * from goods_$mod WHERE id=$id";
        • 新增數據 
          //在新增數據的時候須要一張臨時表去判斷當前表中的最大id值爲多少,選擇對應的數據存儲的數據表

          $sql = "INSERT INTO `臨時表` values null";
          $new_id = "SELECT mysql_insert_id()";
          $mod = $new_id%5;
          $sql = "INSERT INTO goods_$mod VALUES ($new_id, 內容1, 內容2)";
    • 邏輯分表(嚴格上來說,是在數據庫的邏輯層進行分區)

      • 爲了保證分區時的查詢效率,必須保證添加的分區字段爲主鍵或unique key

      • 在新建了分區以後,在查看數據表的存儲文件的結構能夠發現,數據表的索引和數據內容已經被單獨拿出去存儲了

      • 在訪問分區表時,在where條件中必定要帶上分區列,即便是看似多餘的,由於查詢優化器會根據該列鎖定數據所在的分區,否則會對全部數據掃描

      • key分區(求餘)

        CREATE TABLE test_key(
             id int not null auto_increment,
             title varchar(32) not null default '',
             price decimal(10,2) not null default 0,
             created_at datetime not null,
             PRIMARY  KEY (id,created_at)
         ) engine=myisam charset=utf8
         partition by key(id) partitions 5;

        1

      • hash分區(求餘)

        CREATE TABLE test_hash(
             id int not null auto_increment,
             title varchar(32) not null default '',
             price decimal(10,2) not null default 0,
             created_at datetime not null ,
             PRIMARY  KEY (id,created_at)
         ) engine=myisam charset=utf8
         partition by hash(month(created_at)) partitions 5;
      • list分區(範圍)

        CREATE TABLE test_list(
             id int not null auto_increment,
             title varchar(32) not null default '',
             price decimal(10,2) not null default 0,
             created_at datetime NOT NULL,
             PRIMARY  KEY (id,created_at)
         ) engine=myisam charset=utf8
         partition by list(month(created_at))(
             partition spring values in (3,4,5),
             partition summer values in (6,7,8),
             partition autumn values in (9,10,11),
             partition winter values in (12,1,2)
         );
      • range分區(範圍)

        CREATE TABLE test_list(
             id int not null auto_increment,
             title varchar(32) not null default '',
             price decimal(10,2) not null default 0,
             created_at datetime NOT NULL,
             PRIMARY  KEY (id,created_at)
         ) engine=myisam charset=utf8
         partition by range(year(created_at))(
             partition oldest values less than 1980,
             partition old values less than 1990,
             partition middle values less than 2010,
             partition new values less than 2010
         );
  • 垂直分表

    • 對於一張很大的數據表,好比user表,username,password,age等字段是常常被使用到的字段,能夠放在一張表中,表中其餘不太經常使用的字段(不會拿來看成查詢條件的字段),如person_info,profile等能夠拿出到一張單獨的表中進行存儲,這樣能夠保證主表在數據量很大的時候性能降低不會太嚴重。

架構層

1.配置mysql集羣,完成數據的讀寫分離

  •  基本原理:1.master記錄本身改變了的記錄的二進制文件(binlog),在每一個事務執行完畢以後,將這些改變記錄在二進制文件中;2.在slave上存在兩個進程:讀取master上的二進制文件到本身的中繼文件中,在中繼文件中讀取更新的事件內容並同步到本身的數據庫中

  • 可使用mysql官方提供的代理層產品完成MysqlProxy相關的功能:https://segmentfault.com/a/11...

sql語句層進行優化

  • 從給定的題目出發

  • 字段:在進行數據的查詢的時候,在查詢字段的選擇上,使用 對應的字段代替 select *,這樣作對查詢速度不會有明顯的提高,可是能夠節省內存

  • 條件:在sql語句的條件的書寫的時候,條件的排列順序應該是以字段上的數據差別性較大的列排列在最左端,也就是最可以區分出更少數據的列優先排列在最左端。好比說在常規的業務邏輯下,age字段應該在sex 字段的左側

  • 分頁:在後臺的數據進行分頁的時候,每頁顯示150條數據,在查看10000頁的數據的時候確定會很慢,使用 where id > 1500000 limit 150的寫法代替 limit 10000,150 .這樣能夠極大的提升查詢的速度

  • 在只查詢一條數據的時候,使用limit 1,這樣mysql在進行搜索的時候,找到了一條數據就不會在鄉下進行搜索

  • 子查詢:在執行一條資查詢:select ... from t1 where t1.uid IN (select uid from t2 ),這條子查詢至關於select ... from t1 where exists(select 1 from t2 where t2.uid=t1.id).這樣一來至關於將兩個結果集中的數據作乘法,相比於鏈接查詢,速度會很慢

  • 連表查詢:將複雜的JOIN查詢語句改寫成針對於單表的sql查詢語句。在JOIN多個表的時候,可能致使更多的鎖定和堵塞

  • 注意數據的飲食轉換。好比說查詢的字段的類型爲varchar,那麼在寫where條件的時候,where name='11' 會比 where name=11更快,由於傳入的字段類型是int,會致使程序進行全表掃描

關於MyIsam切換到InnoDB:http://blog.itpub.net/1267930...

相關文章
相關標籤/搜索