數(shù)據(jù)庫(kù)界的《延禧攻略》來(lái)了,不看你就輸了
原創(chuàng)【51CTO.com原創(chuàng)稿件】我本人巨愛(ài)這類題材,所以癡迷得不得了。(好像暴露了自己沒(méi)有更博的真正原因哈哈)。
宮廷類的劇,都是后宮嬪妃之間的爾虞吾詐,勾心斗角,有你沒(méi)我,有我沒(méi)你的殘酷事實(shí)。
勝者為王,敗者為寇這種思想好像從古代就一直延續(xù)到今日。非要分出個(gè)勝負(fù),分出個(gè)誰(shuí)好,誰(shuí)壞才罷休。
在數(shù)據(jù)庫(kù)領(lǐng)域也會(huì)有此類問(wèn)題,我混跡開(kāi)源數(shù)據(jù)庫(kù)圈多年。MySQL 數(shù)據(jù)庫(kù)占領(lǐng)著開(kāi)源數(shù)據(jù)庫(kù)的頭把交椅,MongoDB 占領(lǐng)著 NoSQL 數(shù)據(jù)庫(kù)的***位。
我們來(lái)看下數(shù)據(jù)庫(kù)的整體排名情況:
兩者都是***,所以總會(huì)拿來(lái)比較。也會(huì)經(jīng)常被人問(wèn)及到諸如此類的問(wèn)題 MongoDB 4.0 已經(jīng)問(wèn)世了,而且支持事務(wù)了,是不是將來(lái)可以取代 MySQL 了。
MySQL 和 MongoDB 哪個(gè)數(shù)據(jù)庫(kù)好用?今天想通過(guò)這篇文章,帶著大家全方位解讀 MySQL 與 MongoDB 的區(qū)別。讓有困惑的老鐵們明白,沒(méi)有誰(shuí)替代誰(shuí),只有哪個(gè)場(chǎng)景更適合誰(shuí)。
只有更了解彼此,才能更好地利用它們的功能性,下面我從四個(gè)方向依次闡明兩者的區(qū)別:
- 數(shù)據(jù)庫(kù)概述
- 日常運(yùn)維管理維度
- 集群架構(gòu)層面
- 應(yīng)用場(chǎng)景角度
數(shù)據(jù)庫(kù)概述
我們先來(lái)了解一下 MySQL 數(shù)據(jù)庫(kù),如下圖:
接下來(lái)學(xué)習(xí)一下 MySQL 數(shù)據(jù)庫(kù)的特點(diǎn),如下圖:
MySQL 了解完后,我們?cè)賮?lái)了解 MongoDB 及其特點(diǎn)的介紹:
MongoDB 特點(diǎn)介紹,如下圖:
根據(jù)上文圖解,我們對(duì)兩者數(shù)據(jù)庫(kù)都有了一定的認(rèn)識(shí),接下來(lái)我們從運(yùn)維的角度來(lái)檢驗(yàn)兩者的不同。
日常運(yùn)維管理維度
術(shù)語(yǔ)和概念的差異
從上圖可以看出,關(guān)系型數(shù)據(jù)庫(kù)中的“表”,在 MongoDB 中叫做集合。“行”在 MongoDB 中叫做文檔。所以我們管 MongoDB 叫做文檔型數(shù)據(jù)庫(kù)。
存儲(chǔ)數(shù)據(jù)結(jié)構(gòu)的差異
在關(guān)系型數(shù)據(jù)庫(kù)中設(shè)計(jì)表,有些信息需要多表記錄。而在 MongoDB 中,上面的三張表,就變成下面的這一段代碼就可以實(shí)現(xiàn)了。
- {
- _id:"M416",
- name:"zhangsu",
- phone:[1234,5678],
- .....
- }
MongoDB 表設(shè)計(jì)的特點(diǎn)如下:
- 數(shù)據(jù)聚合
- 數(shù)據(jù)嵌套
- 數(shù)組結(jié)構(gòu)
啟動(dòng)配置文件格式差異
MySQL 數(shù)據(jù)庫(kù)的配置叫做 my.cnf,我們來(lái)看下它的記錄方式,代碼如下:
- [client]
- port = 3306
- socket = /data/mysql/mysql.sock
- [mysql]
- prompt="\u@db \R:\m:\s [\d]> "
- no-auto-rehash
- [mysqld]
- user = mysql
- port = 3306
- basedir = /usr/local/mysql
- datadir = /data/mysql/
- socket = /data/mysql/mysql.sock
- pid-file = db.pid
- character-set-server = utf8mb4
- skip_name_resolve = 1
- open_files_limit = 65535
- back_log = 1024
- max_connections = 512
- max_connect_errors = 1000000
- table_open_cache = 1024
- table_definition_cache = 1024
- table_open_cache_instances = 64
- thread_stack = 512K
- external-locking = FALSE
- max_allowed_packet = 32M
- sort_buffer_size = 4M
- join_buffer_size = 4M
- thread_cache_size = 768
- #query_cache_size = 0
- #query_cache_type = 0
- interactive_timeout = 600
- wait_timeout = 600
- tmp_table_size = 32M
- max_heap_table_size = 32M
- slow_query_log = 1
- slow_query_log_file = /data/mysql/slow.log
- log-error = /data/mysql/error.log
- long_query_time = 0.1
- server-id = 3306101
- log-bin = /data/mysql/mybinlog
- sync_binlog = 1
- binlog_cache_size = 4M
- max_binlog_cache_size = 1G
- max_binlog_size = 1G
- expire_logs_days = 7
- master_info_repository = TABLE
- relay_log_info_repository = TABLE
- gtid_mode = on
- enforce_gtid_consistency = 1
- log_slave_updates=1
- binlog_format = row
- relay_log_recovery = 1
- relay-log-purge = 1
- key_buffer_size = 32M
- read_buffer_size = 8M
- read_rnd_buffer_size = 4M
- bulk_insert_buffer_size = 64M
- #myisam_sort_buffer_size = 128M
- #myisam_max_sort_file_size = 10G
- #myisam_repair_threads = 1
- lock_wait_timeout = 3600
- explicit_defaults_for_timestamp = 1
- innodb_thread_concurrency = 0
- innodb_sync_spin_loops = 100
- innodb_spin_wait_delay = 30
- secure_file_priv=''
- super_read_only=0
- transaction_isolation = REPEATABLE-READ
- #innodb_additional_mem_pool_size = 16M
- innodb_buffer_pool_size = 1024M
- innodb_buffer_pool_instances = 8
- innodb_buffer_pool_load_at_startup = 1
- innodb_buffer_pool_dump_at_shutdown = 1
- innodb_data_file_path = ibdata1:100M:autoextend
- innodb_flush_log_at_trx_commit = 1
- innodb_log_buffer_size = 32M
- innodb_log_file_size = 2G
- innodb_log_files_in_group = 2
- innodb_max_undo_log_size = 4G
- innodb_io_capacity = 4000
- innodb_io_capacity_max = 8000
- innodb_flush_neighbors = 0
- innodb_write_io_threads = 8
- innodb_read_io_threads = 8
- innodb_purge_threads = 4
- innodb_page_cleaners = 4
- innodb_open_files = 65535
- innodb_max_dirty_pages_pct = 50
- innodb_flush_method = O_DIRECT
- innodb_lru_scan_depth = 4000
- innodb_checksum_algorithm = crc32
- #innodb_file_format = Barracuda
- #innodb_file_format_max = Barracuda
- innodb_lock_wait_timeout = 10
- innodb_rollback_on_timeout = 1
- innodb_print_all_deadlocks = 1
- innodb_file_per_table = 1
- innodb_online_alter_log_max_size = 4G
- internal_tmp_disk_storage_engine = InnoDB
- innodb_stats_on_metadata = 0
- innodb_status_file = 1
- [mysqldump]
- quick
- max_allowed_packet = 32M
MongoDB 配置文件使用 Yaml 格式,如下圖:
增刪改查操作的差異
事務(wù)支持的差異
但隨著 MongoDB 4.0 的問(wèn)世,它將支持多文檔事務(wù),屆時(shí) MongoDB 將成為唯一能夠同時(shí)支持速度,靈活性,JSON 文檔模型和 ACID 數(shù)據(jù)完整性保證的數(shù)據(jù)庫(kù)。
所謂的多文檔事務(wù),可以理解為關(guān)系型數(shù)據(jù)庫(kù)的多行事務(wù)。在關(guān)系型的事務(wù)支持中,大家?guī)缀鯚o(wú)一例外支持同一事務(wù)內(nèi)操作的原子性,即要么全部提交,要么全部回滾。
這個(gè)同一事務(wù)內(nèi)可以有多個(gè)操作,針對(duì)于多個(gè)表,或者是同一個(gè)表內(nèi)的多行數(shù)據(jù)。
總結(jié):隨著事務(wù)支持的增加,MongoDB 功能上更接近于關(guān)系型數(shù)據(jù)庫(kù),但是和關(guān)系型還是有本質(zhì)上的區(qū)別。
MySQL 是基于關(guān)系模型的數(shù)據(jù)庫(kù),對(duì)各種數(shù)據(jù)多變的場(chǎng)景如物聯(lián)網(wǎng)或社交化并沒(méi)有 MongoDB 支持得好。
MongoDB 的 JSON 模型則具有動(dòng)態(tài)靈活,數(shù)據(jù)庫(kù)無(wú)須下線就可以進(jìn)行模式變遷升級(jí),在這種場(chǎng)景下面,選擇 MongoDB 會(huì)特別合適。
備份上的差異
MySQL備份方式,如下圖:
MongoDB備份方式(邏輯備份與恢復(fù)):
- mongodump
- mongorestore
- mongoexport
- mongoimport
注:MongoDB 目前為止還沒(méi)有像 xtrabackup 這樣好用的備份工具。所以一般來(lái)說(shuō),都是使用邏輯備份方式來(lái)進(jìn)行操作。
從運(yùn)維角度我們對(duì)它們有了更深的認(rèn)識(shí)之后,我們來(lái)從集群架構(gòu)的維度出發(fā),去探究更深的不同之處。
集群架構(gòu)層面
集群架構(gòu)層面上的差異
我們先從 MySQL 復(fù)制的角度入手,然后再介紹 MySQL 高可用集群架構(gòu)。
MySQL 主從復(fù)制原理圖如下:
MySQL 復(fù)制種類總結(jié)
異步復(fù)制:通常沒(méi)說(shuō)明指的都是異步,即主庫(kù)執(zhí)行完 Commit 后,在主庫(kù)寫入 Binlog 日志后即可成功返回客戶端,無(wú)需等 Binlog 日志傳送給從庫(kù),一旦主庫(kù)宕機(jī),有可能會(huì)丟失日志。
半同步復(fù)制:MySQL 5.5 版本之后引入了半同步復(fù)制功能,主從服務(wù)器必須同時(shí)安裝半同步復(fù)制插件,才能開(kāi)啟該復(fù)制功能。
在該功能下,確保從庫(kù)接收完主庫(kù)傳遞過(guò)來(lái)的 Binlog 內(nèi)容已經(jīng)寫入到自己的 Relay Log 里面了,才會(huì)通知主庫(kù)上面的等待線程,該操作完畢。
如果等待超時(shí),超過(guò) rpl_semi_sync_master_timeout 參數(shù)設(shè)置的時(shí)間,則關(guān)閉半同步復(fù)制,并自動(dòng)轉(zhuǎn)換為異步復(fù)制模式,直到至少有一臺(tái)從庫(kù)通知主庫(kù)已經(jīng)接收到 Binlog 信息了為止。
多源復(fù)制:所謂多源復(fù)制,就是把多臺(tái)主庫(kù)的數(shù)據(jù)同步到一臺(tái)從庫(kù)服務(wù)器上,從庫(kù)會(huì)創(chuàng)建通往每個(gè)主庫(kù)的管道。
在 MySQL 5.7 之前的版本中,只能實(shí)現(xiàn)一主一從、一主多從或者多主多從的復(fù)制架構(gòu),如果想要實(shí)現(xiàn)多主一從的復(fù)制,只能使用 MariaDB。MySQL 5.7 版本已經(jīng)可以實(shí)現(xiàn)多主一從的復(fù)制。
并行復(fù)制:使用 MySQL 5.7 的并行復(fù)制功能。在 5.6 版本中就有了并行的概念,但它的并行復(fù)制是基于庫(kù)級(jí)別的,即 slave_parallel_type=database。在這種模式下,只是基于多庫(kù)少表的情況,并不適用于真實(shí)的生產(chǎn)環(huán)境。
在 MySQL 5.7 版本中,真正實(shí)現(xiàn)了基于組提交的并行復(fù)制,簡(jiǎn)單說(shuō)就是主庫(kù)并行執(zhí)行 SQL 語(yǔ)句,從庫(kù)也可以通過(guò)多個(gè) Workers 線程并發(fā)執(zhí)行 Relay Log 中主庫(kù)提交的事務(wù)。
想要開(kāi)啟 MySQL 5.7 的并行復(fù)制可以在從庫(kù)設(shè)置參數(shù) slave_parallel_workers > 0。
并把 5.7 版本中新添加的 slave_parallel_type 參數(shù)設(shè)置為 LOGICAL_CLOCK。
該參數(shù)有 DATABASE 和 LOGICAL_CLOCK 兩個(gè)值。MySQL 5.6 默認(rèn)是 DATABASE。
MySQL 高可用集群架構(gòu)
MySQL 高可用集群架構(gòu)分類圖如下:
MHA
MHA 集群架構(gòu)圖
MHA 的目的在于維持 MySQL Replication 中 Master 庫(kù)的高可用性,它***特點(diǎn)是可以修復(fù)多個(gè) Slave 之間的差異日志,最終使所有 Slave 保持?jǐn)?shù)據(jù)一致,然后從中選擇一個(gè)充當(dāng)新的 Master,并將其他 Slave 指向它。
當(dāng) Master 出現(xiàn)故障時(shí),可以通過(guò)對(duì)比 Slave 之間 I/O thread 讀取主庫(kù) Binlog 的 Position 號(hào),選取最接近的 Slave 作為備選主庫(kù)(備胎)。其他的從庫(kù)可以通過(guò)與備選主庫(kù)對(duì)比生成差異的中繼日志。
在備選主庫(kù)上應(yīng)用從原來(lái) Master 保存的 Binlog,同時(shí)將備選主庫(kù)提升為 Master。***在其他 Slave 上應(yīng)用相應(yīng)的差異中繼日志并從新的 Master 開(kāi)始復(fù)制。
雙主+Keepalived
企業(yè)中小型規(guī)模的時(shí)候,采用這種架構(gòu)是最省事的。兩個(gè)節(jié)點(diǎn)可以采用簡(jiǎn)單的一主一從模式,或者雙主模式。
并且,它們放置于同一個(gè) VLAN 中,在 Master 節(jié)點(diǎn)發(fā)生故障后,利用 Keepalived / Heartbeat 的高可用機(jī)制實(shí)現(xiàn)快速切換到 Slave 節(jié)點(diǎn)。
PXC 集群
PXC 是基于 Galera 協(xié)議的 MySQL 高可用集群架構(gòu)。Galera 產(chǎn)品是以 Galera Cluster 方式為 MySQL 提供高可用集群解決方案的。Galera Cluster 就是集成了 Galera 插件的 MySQL 集群。
Galera replication 是 Codership 提供的 MySQL 數(shù)據(jù)同步方案,具有高可用性,方便擴(kuò)展。
并且它可以實(shí)現(xiàn)多個(gè) MySQL 節(jié)點(diǎn)間的數(shù)據(jù)同步復(fù)制與讀寫,可保障數(shù)據(jù)庫(kù)的服務(wù)高可用及數(shù)據(jù)強(qiáng)一致性。
MGR 架構(gòu)
MySQL 官方在 5.7.17 版本正式推出組復(fù)制(MySQL Group Replication,簡(jiǎn)稱MGR)。Master1,Master2,Master3,所有成員獨(dú)立完成各自的事務(wù)。
當(dāng)客戶端先發(fā)起一個(gè)更新事務(wù),該事務(wù)先在本地執(zhí)行,執(zhí)行完成之后就要發(fā)起對(duì)事務(wù)的提交操作了。
在還沒(méi)有真正提交之前需要將產(chǎn)生的復(fù)制寫集廣播出去,復(fù)制到其他成員。如果沖突檢測(cè)成功,組內(nèi)決定該事務(wù)可以提交,其他成員可以應(yīng)用,否則就回滾。
最終,這意味著所有組內(nèi)成員以相同的順序接收同一組事務(wù)。因此組內(nèi)成員以相同的順序應(yīng)用相同的修改,保證組內(nèi)數(shù)據(jù)強(qiáng)一致性。
MongoDB 的復(fù)制情況
MongoDB 復(fù)制集,如下圖:
三副本架構(gòu)是最基礎(chǔ)的復(fù)制集的架構(gòu),一主兩備模式。主節(jié)點(diǎn)接受外界的讀寫請(qǐng)求,向備節(jié)點(diǎn)進(jìn)行數(shù)據(jù)同步。當(dāng)主節(jié)點(diǎn)宕掉,會(huì)自動(dòng)切換到備節(jié)點(diǎn),不影響線上業(yè)務(wù),防止單點(diǎn)故障。
MongoDB 復(fù)制集自動(dòng)切換,如下圖:
副本集的所有成員都可以接受讀取操作。 但是,默認(rèn)情況下,應(yīng)用程序?qū)⑵渥x取操作指向 Primary。
副本集可以有至多一個(gè) Primary 節(jié)點(diǎn),Primary 節(jié)點(diǎn)宕機(jī)后,集群會(huì)觸發(fā)選舉以選出新的 Primary 節(jié)點(diǎn)。
在以下三成員節(jié)點(diǎn)副本集架構(gòu)中,Primary 宕機(jī)后,觸發(fā)了一次選舉,從剩下的兩個(gè) Secondary 節(jié)點(diǎn)里,選舉出了一個(gè)新的 Primary 節(jié)點(diǎn)。
MongoDB 復(fù)制集讀寫分離設(shè)置,如下圖:
Read Preference 決定 MongoDB 客戶端從哪個(gè)節(jié)點(diǎn)上讀取數(shù)據(jù)。默認(rèn)情況下,應(yīng)用程序?qū)⑵渥x取操作指向副本集中的 Primary 節(jié)點(diǎn)。
指定 Read Preference 選項(xiàng)時(shí)要注意:因?yàn)槭褂卯惒綇?fù)制,復(fù)制延遲會(huì)導(dǎo)致 Secondary 上的數(shù)據(jù)可能不是***的。
默認(rèn)情況下,復(fù)制集的所有讀請(qǐng)求都發(fā)到 Primary,Driver 可通過(guò)設(shè)置 Read Preference 來(lái)將讀請(qǐng)求路由到其他的節(jié)點(diǎn):
- Primary: 默認(rèn)規(guī)則,所有讀請(qǐng)求發(fā)到 Primary。
- PrimaryPreferred:Primary 優(yōu)先,如果 Primary 不可達(dá),請(qǐng)求 Secondary。
- Secondary: 所有的讀請(qǐng)求都發(fā)到 Secondary。
- SecondaryPreferred:Secondary 優(yōu)先,當(dāng)所有 Secondary 不可達(dá)時(shí),請(qǐng)求 Primary。
- Nearest:讀請(qǐng)求發(fā)送到最近的可達(dá)節(jié)點(diǎn)上(通過(guò) Ping 探測(cè)得出最近的節(jié)點(diǎn))。
MongoDB 分片架構(gòu)如下圖:
分片是一種在多臺(tái)機(jī)器上分配數(shù)據(jù)的方法。MongoDB 使用分片架構(gòu)有助于您去管理非常大數(shù)量的數(shù)據(jù)集和高吞吐量操作的集群。
大數(shù)據(jù)量和高吞吐量的業(yè)務(wù)情況對(duì)單臺(tái)服務(wù)器來(lái)講是具備很大的挑戰(zhàn)性的。例如,高查詢率可能耗盡服務(wù)器的 CPU 容量。
工作集大小超過(guò)系統(tǒng)內(nèi)存,那么壓力會(huì)給到磁盤上,這對(duì) IO 來(lái)講不是我們所希望看到的。MongoDB 支持通過(guò)分片進(jìn)行水平縮放。
總結(jié):MySQL 的復(fù)制種類很多,集群架構(gòu)在選擇性上來(lái)說(shuō)也比較多。但橫向擴(kuò)展能力上,沒(méi)有 MongoDB 的分片架構(gòu)擴(kuò)展能力強(qiáng)。
***,我們通過(guò) MySQL 與 MongoDB 不同的應(yīng)用場(chǎng)景來(lái)對(duì)兩種數(shù)據(jù)庫(kù)做一個(gè)總結(jié)。
應(yīng)用場(chǎng)景角度
正如開(kāi)篇介紹 MySQL 特點(diǎn)時(shí)所說(shuō)的,MySQL 使用得覆蓋率已經(jīng)接近 100%。
從大型 BAT,電商平臺(tái),游戲公司,甚至諸多傳統(tǒng)行業(yè)也無(wú)不例外都在往 MySQL 數(shù)據(jù)庫(kù)方向靠攏,達(dá)到逐漸壟斷的趨勢(shì)。
對(duì)于 MongoDB 的應(yīng)用也已經(jīng)蔓延到各個(gè)領(lǐng)域,比如游戲、物流、電商、內(nèi)容管理、社交、物聯(lián)網(wǎng)、視頻直播等:
游戲領(lǐng)域:使用 MongoDB 存儲(chǔ)游戲用戶信息,用戶的裝備、積分等直接以內(nèi)嵌文檔的形式存儲(chǔ),方便查詢、更新。
物流場(chǎng)景:使用 MongoDB 存儲(chǔ)訂單信息,訂單狀態(tài)在運(yùn)送過(guò)程中會(huì)不斷更新,以 MongoDB 內(nèi)嵌數(shù)組的形式來(lái)存儲(chǔ),一次查詢就能將訂單所有的變更讀取出來(lái)。
社交場(chǎng)景:使用 MongoDB 存儲(chǔ)用戶信息,以及用戶發(fā)表的朋友圈信息,通過(guò)地理位置索引實(shí)現(xiàn)附近的人、地點(diǎn)等功能。
物聯(lián)網(wǎng)場(chǎng)景:使用 MongoDB 存儲(chǔ)所有接入的智能設(shè)備信息,以及設(shè)備匯報(bào)的日志信息,并對(duì)這些信息進(jìn)行多維度的分析。
我 2009 年開(kāi)始接觸 MySQL,在 2012 年接觸 MongoDB 的***個(gè)版本 2.1,對(duì)于這兩個(gè)數(shù)據(jù)庫(kù)真的是手心手背都是肉。
在我孤獨(dú)寂寞的時(shí)候,都是它們一直陪伴著我,感謝技術(shù)給我們帶來(lái)的簡(jiǎn)單快樂(lè)。
無(wú)論未來(lái)發(fā)展如何,沒(méi)有所謂的誰(shuí)會(huì)替代誰(shuí),只是說(shuō)它們各自都有不同的特點(diǎn),促使在不同的應(yīng)用場(chǎng)景下,我們使用誰(shuí)更合適而已。
這里沒(méi)有宮廷內(nèi)斗,沒(méi)有爾虞我詐,只有那份最簡(jiǎn)單地做技術(shù)的心,是現(xiàn)實(shí)版的《延禧攻略》!
張甦, 數(shù)據(jù)庫(kù)領(lǐng)域的專家和知名人士、圖書《MySQL王者晉級(jí)之路》作者,51CTO 專家博主。近 10 年互聯(lián)網(wǎng)線上處理及培訓(xùn)經(jīng)驗(yàn),專注于 MySQL 數(shù)據(jù)庫(kù),對(duì) MongoDB、Redis 等 NoSQL 數(shù)據(jù)庫(kù)以及 Hadoop 生態(tài)圈相關(guān)技術(shù)有深入研究,具備非常豐富的理論與實(shí)戰(zhàn)經(jīng)驗(yàn)。
【51CTO原創(chuàng)稿件,合作站點(diǎn)轉(zhuǎn)載請(qǐng)注明原文作者和出處為51CTO.com】