Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
oSQL 數(shù)據(jù)庫最近一段時間都是很受追捧的,也許已經(jīng)是 Node.js 應(yīng)用程序的首選后端了。不過,你不應(yīng)該只是根據(jù)潮流來選擇拿什么技術(shù)構(gòu)建下一個項目,使用什么數(shù)據(jù)庫類型要取決于項目的特定需求。如果你的項目涉及到動態(tài)表的創(chuàng)建,實時的插入等等,那么 NoSQL 就是不錯的技術(shù)路線,而另一方面,如果項目中要處理復(fù)雜的查詢和事務(wù),那么 SQL 數(shù)據(jù)庫就更加合適了。
在本教程中,我們會向你介紹如何使用 MySQL 模塊 - 這是一個用 JavaScript 編寫的運行在 Node.js 之上的 MySQL 驅(qū)動程序。我會向你解釋如何使用該模塊連接到 MySQL 數(shù)據(jù)庫,執(zhí)行常規(guī)的 CRUD 操作,之后就是對存儲的過程進行檢查,以及對用戶的輸入進行轉(zhuǎn)義這些技術(shù)。
這個頗受歡迎的教程在 2017 年 11 月 07 日進行了更新。其中的修改包括將語法更新到了 ES6,解決了node-mysql 模塊被重新命名的問題,增加了更多對初學者友好的文字說明,并在 ORM 上新增加了一個部分。
快速入門:如何在Node 中使用MySQL
也許你來這是就是為了找到一個快速的法門。如果你是想用盡可能少的時間在 Node 中啟動并運行 MySQL,我們能滿足你的需求!
以下5個簡單步驟告訴你如何在 Node 中使用 MySQL:
創(chuàng)建一個新項目:mkdir mysql-test && cd mysql-test
創(chuàng)建一個 package.json 文件:npm init -y
安裝mysql模塊: npm install mysql –save
創(chuàng)建一個app.js文件并將下面的代碼段復(fù)制進去。
運行該文件: node app.js。會看到一條 “Connected!”(已連接上了)消息。
安裝 mysql 模塊
現(xiàn)在讓我們細化到第一步。首先,我們使用命令行創(chuàng)建一個新目錄進進入這個目錄。然后我們使用 npm init -y 命令創(chuàng)建 package.json 文件。-y 參數(shù)表示 npm 會使用默認值而不會問你各種問題。
這一步假設(shè)你已經(jīng)在系統(tǒng)上安裝了 Node 和 npm。如果還沒安裝,請閱讀 SitePoint 上的這篇文章,它會指導你:使用 nvm 安裝 Node.js 的多個版本。
然后,我們從 npm 安裝 mysql 模塊并將其保存為項目的依賴項。項目的 dependencies (相對于 dev-dependencies) 是運行程序所需要的包。你可以閱讀了解兩者的區(qū)別。
如果你深入學習使用 npm,可以閱讀這個指南,或者在我們的論壇上提問。
入門
在我們連接到數(shù)據(jù)庫之前,有一件重要的事情就是要在你的機器上安裝和配置 MySQL。如果這件事情還沒做完,那就看看軟件主頁上的安裝說明自己去裝一個吧。
接下來我們需要做的就是創(chuàng)建一個數(shù)據(jù)庫和一個數(shù)據(jù)庫表。你可以使用一個圖形用戶界面來做到這一點,比如說 phpMyAdmin,或者就使用命令行。 對于我們這篇文章,使用的是一個名為 sitepoint 的數(shù)據(jù)庫和一個名為 employees 的表。如果你希望跟著一起操作的話,這里有一個數(shù)據(jù)庫的轉(zhuǎn)儲文件,方便你可以快速地啟動并運行起來:
連接到數(shù)據(jù)庫
現(xiàn)在,我們在 mysql-test 目錄下創(chuàng)建一個名為 app.js 的文件,來看看如何從 Node.js 連接到 MySQL。
現(xiàn)在打開一個終端并輸入 node app.js。在連接成功建立之后,你應(yīng)該能夠在控制臺中看到“Connection established”(連接已經(jīng)建立好了)這條消息了。 如果出現(xiàn)了什么問題(例如輸入了錯誤的密碼),程序就會觸發(fā)一個回調(diào),該事件會傳遞出一個 JavaScript Error 對象(err)的實例。 你可以嘗試將其打印到控制臺以查看其中包含的有用信息以調(diào)試程序。
使用 Grunt 來監(jiān)視文件的更改
每當我們對代碼進行更改時,手動運行 node app.js 命令會變得有點乏味,所以讓我們來把這個操作自動化吧。 這一節(jié)并不需要跟本教程的其余部分并沒有依賴關(guān)系,不過如果照著做的話肯定會為你節(jié)省一些麻煩事兒。
我們首先得安裝幾個包:
Grunt 是有名的 JavaScript 任務(wù)執(zhí)行程序,每當監(jiān)聽到有文件發(fā)生修改時,grunt-contrib-watch 都會運行已經(jīng)預(yù)定義好的任務(wù),并且會使用 grunt-execute 來運行 node app.js 命令。
安裝完成之后,在項目根中創(chuàng)建一個名為Gruntfile.js的文件,然后在里面添加上如下代碼。
現(xiàn)在運行 grunt watch 然后修改一下 app.js 文件。Grunt 就應(yīng)該會檢測到我們修改了文件并重新運行 node app.js 命令。
執(zhí)行查詢
讀取
現(xiàn)在你知道如何在 Node.js 中建立 MySQL 連接了,再來看看如何執(zhí)行 SQL 查詢。我們從這里開始:建立使用 createConnection 命令連接到名為 sitepoint 的數(shù)據(jù)庫。
連接建立后我們要使用連接變量來對數(shù)據(jù)庫中的 employees 表進行查詢。
現(xiàn)在運行 app.js (通過 grunt-watch 或者在終端輸入 node app.js),你可以看到終端輸出從數(shù)據(jù)庫返回的數(shù)據(jù)。
從 MySQL 數(shù)據(jù)庫返回的數(shù)據(jù)可以通過遍歷 rows 對象來進行解析。
創(chuàng)建
你可以在數(shù)據(jù)庫中執(zhí)行 insert 查詢,像這樣:
請注意到我們是如何通過回調(diào)參數(shù)來獲得剛插入那條記錄的 ID 的。
更新
類似地,在執(zhí)行 update 查詢的時候,通過 result.affectedRows 可得到受影響的行數(shù):
刪除
delete 查詢的操作也差不多:
高級用法
我希望有辦法通過 mysql 模塊來處理存儲過程,以及轉(zhuǎn)義用戶輸入。
存儲過程
簡單的說,存儲過程是存儲在數(shù)據(jù)庫中,可以由數(shù)據(jù)庫引擎和連接上數(shù)據(jù)的程序語言調(diào)用的程序(例如,SQL 程序)。如果你需要復(fù)習,請看看這篇不錯的文章。
先來為我們的 sitepoint 數(shù)據(jù)庫創(chuàng)建一個存儲過程,它用于獲取所有員工的詳情。我們把它命名為 sp_getall。為了做這件事,你需要某種數(shù)據(jù)庫接操作界面。我使用 phpMyAdmin。在 sitepoint 數(shù)據(jù)庫中運行下面的查詢:
它會將程序保存在 information_schema 數(shù)據(jù)庫的 ROUTINGS 表中。
下一步,建立連接并使用連接對象調(diào)用存儲過程,像這樣:
保存修改并運行。運行的時候你可以看到從數(shù)據(jù)庫返回的數(shù)據(jù)。
這些數(shù)據(jù)包括一些附加信息,比如影響的行數(shù),insertId 等。你需要對返回數(shù)據(jù)的第 0 個元素進行遍歷以獲取員工詳情信息。
現(xiàn)在考慮一個需要輸入?yún)?shù)的存儲過程。
我們可以在調(diào)用存儲過程的時候傳入?yún)?shù):
多數(shù)時候,如果我們想在數(shù)據(jù)庫中插入一條記錄,需要將插入記錄的 ID 作為輸出參數(shù)返回出來。考慮接下來用于插入數(shù)據(jù)的存儲過程,它有一個輸出參數(shù):
為了調(diào)用含有輸出參數(shù)的存儲過程,我們需要在創(chuàng)建連接時調(diào)用多個程序。因此,修改連接,設(shè)置執(zhí)行多個語句為 true。
然后在調(diào)用存儲過程的時候,設(shè)置并傳入一個輸出參數(shù)。
在上面的代碼中,我們設(shè)置了輸出參數(shù) @employee_id 并在調(diào)用存儲過程的時候?qū)⑵鋫魅搿R坏┱{(diào)用完成,我們需要使用 select 查詢輸出參數(shù)來獲取返回的 ID。
運行 app.js。如果執(zhí)行成功你可以看到 select 查詢的輸出參數(shù)和各種其它信息。通過 rows[2] 可獲得輸出參數(shù)的值。
轉(zhuǎn)義用戶輸入
為了避免 SQL 注入攻擊,你應(yīng)該總是轉(zhuǎn)義來自用戶的任何數(shù)據(jù),然后再把它用于 SQL 查詢。來演示一下為什么:
這看起來并沒有什么問題,它會返回正確的結(jié)果:
不過,如果我們將 userLandVariable 改為:
居然訪問了整個數(shù)據(jù)集。如果我們再改為這樣:
這下麻煩大了!
好消息是有辦法處理這類問題。你只需要使用 mysql.escape 方法:
或者使用問號占位符,就像我們在文章一開始提到的那個示例一樣:
為什么不簡單地使用 ORM?
你可能注意到了,評論中有人建議使用 ORM。在詳述這個方法的優(yōu)缺點之前,我先看看 ORM 是什么。下面是來自 Stack Overflow 的回答。
對象關(guān)系映射(Object-Relational Mapping, ORM) 是一種允許人們使用面向?qū)ο蠓缎蛠聿樵兒筒僮鲾?shù)據(jù)庫數(shù)據(jù)的技術(shù)。在談到 ORM 的時候,多數(shù)人是指實現(xiàn)了 ORM 技術(shù)的某個庫,所以會使用 “an ORM” 這樣的短語。
因此,這種方法基本上意味著你會使用 ORM 領(lǐng)域相關(guān)的語言來編寫數(shù)據(jù)庫邏輯,而不是我們一直在討論的普通方法。下面以 Sequelize 為例:
對比:
使用 ORM 對你是否有意義,取決于很多與你工作相關(guān)的因素,比如你在做什么以及為誰做。一方面,ORM 的形式使開發(fā)更為高效,從某種程序上來說,它抽象了大部分的 SQL 因而不需要團隊中的每個人都去了解如何編寫高效的數(shù)據(jù)庫查詢。它也很容易遷移到不同的數(shù)據(jù)庫軟件,因為你是在抽象層次上進行開發(fā)。
然而,從另一方面來說,由于不理解 ORM 是如何做的,所以可能會編寫出一些混亂和低效的 SQL。性能也會是一個大問題,畢竟優(yōu)化不通過 ORM 的查詢要容易得多。
到底采用哪一種方法,決定權(quán)在你,但是如果正在做這個決定,請看看這個 Stack Overflow 的帖子:為什么應(yīng)該使用 ORM?,以及 SitePoint 上的:你可能不知道的 3 個 JavaScript ORM。
小結(jié)
本教程中只涉及到了 MySQL 客戶端的皮毛。我推薦你去閱讀官方文檔以了解更詳細的信息。當然也有別的選擇,比如 node-mysql2 和 node-mysql-libmysqlclient。
你是否已經(jīng)在 Node.js 中用過這些庫來連接到 MySQL?我很想聽人說說這些庫。請在下面的評論中告訴我們你的想法、建議以及更正意見!
文章來源:開源中國
【燈塔大數(shù)據(jù)】微信公眾號介紹:中國電信北京研究院通過整合電信自有數(shù)據(jù)、互聯(lián)網(wǎng)數(shù)據(jù)和線下數(shù)據(jù),創(chuàng)建了業(yè)內(nèi)領(lǐng)先的“燈塔”大數(shù)據(jù)行業(yè)應(yīng)用平臺,致力于與行業(yè)合作伙伴共同打造大數(shù)據(jù)行業(yè)應(yīng)用生態(tài)圈。目前我們面向市場研究、廣告、汽車、金融、人力資源等諸多行業(yè)領(lǐng)域,提供零售研究、消費者研究、店鋪選址、精準營銷、泛義征信等服務(wù),助力企業(yè)在大數(shù)據(jù)時代楊帆遠航。
微信公眾號【燈塔大數(shù)據(jù)】關(guān)鍵字信息:
【人工智能】獲取人工智能時代的發(fā)展思考 ppt
【半月刊】下載大數(shù)據(jù)瞭望半月刊
【網(wǎng)絡(luò)安全】獲取國民網(wǎng)絡(luò)安全報告全文
【23個理由】下載《大數(shù)據(jù)讓你興奮的23個理由》電子書
【思維導圖】下載12種工具的獲取方式
【 燈塔 】 查看更多關(guān)鍵字回復(fù)
言
嘮嗑一下。都在說去O或者開源,但是對于數(shù)據(jù)庫選型來說,很多人卻存在著誤區(qū)。例如,去O,狹義上講,是去Oracle數(shù)據(jù)庫。但是從廣義上來說,是去Oracle公司產(chǎn)品或者具有漂亮國壟斷地位和需要商業(yè)授權(quán)的數(shù)據(jù)庫產(chǎn)品。
去O,目前國內(nèi)有一個現(xiàn)象,就是很多公司或個人聽到去O,第一反應(yīng)是改用Mysql,實際上Mysql也是Oracle公司的。而且Mysql雖然是開源的,但是需要遵循GPL開源協(xié)議,這個協(xié)議里面(大概意思)含有這么兩點就可以窺見一斑:
1、如果用Mysql源碼進行二次修改,修改后的產(chǎn)品也必須開源,例如目前國產(chǎn)分布式數(shù)據(jù)庫TiDB就遵循該協(xié)議進行開源;
2、如果要對Mysql二次封裝或者修改后進行實現(xiàn)商業(yè)版本,就必須取得甲骨文公司授權(quán)。以上這兩條,就足以讓Mysql這款開源數(shù)據(jù)庫并不具備“開源優(yōu)勢”,將來該被制裁還是會被制裁。
目前去O,還有一款備選開源數(shù)據(jù)庫是PostgreSQL,它是基于BSD開源協(xié)議的,該開源協(xié)議是四大開源協(xié)議里面最“開放”和自由的,不會受到商業(yè)版權(quán)化影響,并且組織或個人也可以通過它的源碼進行二次封裝或者進行發(fā)行商業(yè)版,例如華為的OpenGuass是基于該開源版本進行二次開發(fā)的,并且基于PostgreSQL或者基于OpenGuass進行二次封裝成商業(yè)版本的數(shù)據(jù)庫(國產(chǎn)、非國產(chǎn)等)也比比皆是。
以上只是吐個槽,本篇文章主要是想通過.NET6+EF CORE + 三大數(shù)據(jù)庫,進行一個在同等環(huán)境下的簡單的讀寫性能測試。
【備注】由于各種原因,接下來的測試結(jié)果可能會不準確,以下僅供學習或參考使用。
數(shù)據(jù)庫運行環(huán)境:Cent OS 7.5
PostgreSQL版本:14
MySQL數(shù)據(jù)庫版本:8.0
Oracle數(shù)據(jù)庫:12C 64位
客戶端環(huán)境:WIN 10 專業(yè)版
運行時環(huán)境:.NET 6
ORM:EF CORE
開發(fā)語言:C#
CentOS環(huán)境安裝PostgreSQL
遠程服務(wù)器上已有授權(quán)的Oracle環(huán)境和Mysql環(huán)境,所以具體安裝細節(jié)不再進行描述,如果感興趣的小伙伴也可以自行百度一下Oracle和Mysql的安裝教程,應(yīng)該非常多。由于服務(wù)器上暫時還沒有PostgreSQL環(huán)境,我暫且也把安裝PostgreSQL的安裝步驟也順手記錄下。
PostgreSQL安裝:
下載地址:
https://www.postgresql.org/download/linux/redhat/
選擇版本以后,會有對應(yīng)提示的安裝方式命令,就不發(fā)出來了,可自行參考。
以下是安裝以后的一些配置。
安裝完畢,并且啟動pgsql服務(wù)以后,此處我先創(chuàng)建一個測試用的數(shù)據(jù)庫:testdb
使用命令:su - postgres 可以進行默認的登錄,默認無密碼。
登陸以后使用命令:psql 可以進入到可執(zhí)行SQL的命令的頁面,以postgres=# 開頭。其他命令和有關(guān)創(chuàng)建用戶的SQL語句如圖所示。
修改配置文件: /var/lib/pgsql/14/data/postgresql.conf
將注釋的listen_addresses打開,設(shè)置值為 ‘*’
路徑上的14代表版本,如果是13版本就是13,以此類推,下同。
修改/var/lib/pgsql/14/data/pg_hba.conf配置文件,對IPV4訪問新增一行配置如下:
然后要重啟pgsql服務(wù),以用于生效。
由于pgsql默認的端口是5432,為了可以跨遠程訪問,此處把遠程服務(wù)器上的端口開放出來。命令:firewall-cmd --zone=public --add-port=5432/tcp --permanent
然后重載防火墻,命令:firewall-cmd --reload
測試數(shù)據(jù)庫有關(guān)表結(jié)構(gòu)。以下表均沒有設(shè)置索引,僅單表測試,結(jié)果僅供參考。
Mysql表結(jié)構(gòu):
PostgreSQL表結(jié)構(gòu):
Oracle表結(jié)構(gòu):
.NET 6開發(fā)測試代碼
先創(chuàng)建一個minimal api項目,以及一個服務(wù)類庫項目。類庫引用需要操作Oracle數(shù)據(jù)庫、MySQL數(shù)據(jù)庫以及Postgresql數(shù)據(jù)庫有關(guān)的組件。
對服務(wù)類設(shè)置為啟動項,然后新增三個文件夾(MyModel,OraModel和PgModel),用于分別存放三個數(shù)據(jù)庫的實體類。然后在程序包管理控制臺上,通過命令:
Scaffold-DbContext “mysql連接字符串" Pomelo.EntityFrameworkCore.MySql -OutputDir MyModel -Force
自動生成指定的mysql數(shù)據(jù)庫實體類。其中,MyModel是需要生成的目標目錄文件夾。
通過命令:
Scaffold-DbContext "Oracle連接字符串" Oracle.EntityFrameworkCore -OutputDir OraModel -Force
自動生成Oracle數(shù)據(jù)庫實體類。
通過命令:
Scaffold-DbContext "pgsql連接字符串" Npgsql.EntityFrameworkCore.PostgreSQL -OutputDir PgModel -Force
自動生成PostgreSQL數(shù)據(jù)庫實體類。
新建一個測試服務(wù)類DatabaseTestService,提供簡單插入和更新功能:
在minimai api項目里,新增兩個簡單的測試API用于測試。為了簡單,就直接實例化一下進行訪問,然后返回執(zhí)行結(jié)果。
以上方法可能執(zhí)行適合會導致耗時而失敗,為了直觀一點,改成控制臺里面輸出。
實現(xiàn)里面也做點調(diào)整。
測試插入和更新
運行程序以后,對三個數(shù)據(jù)庫分別插入數(shù)據(jù)并計時。
先看Oracle物理表情況。
插入總共數(shù)據(jù)條數(shù):
部分數(shù)據(jù)結(jié)果集:
然后是mysql物理表數(shù)據(jù)。
插入數(shù)據(jù)總數(shù):
部分數(shù)據(jù)結(jié)果集:
最后是PostgreSQL。插入總條數(shù):
部分數(shù)據(jù)結(jié)果集:
以下是通過EF CORE進行插入的結(jié)果:
接下來進行一輪更新操作,為了防止數(shù)據(jù)量太大,所以只進行批量更新10000條數(shù)據(jù)。結(jié)果如下:
看下數(shù)據(jù)更新結(jié)果是不是正常。
Oracle數(shù)據(jù):
MySQL數(shù)據(jù):
PGSQL數(shù)據(jù):
數(shù)據(jù)庫數(shù)據(jù)清空,屏蔽掉C#代碼一些實體賦值時間,重新執(zhí)行兩次僅統(tǒng)計批量插入數(shù)據(jù)庫部分的執(zhí)行的時間進行重新測試,僅測試批量插入耗時結(jié)果。
第一回測試結(jié)果:
接下來不刪除數(shù)據(jù),重新執(zhí)行一輪。
Oracle估計哪兒有問題,數(shù)據(jù)讓人很尷尬啊。接下來只比較MySQL和PgSQL
來一波批量插入:
再來一波三次的批量更新:
有關(guān)代碼(最后測試使用):
public class DatabaseTestService
{
public String TestInsert()
{
StringBuilder sb=new StringBuilder();
Console.WriteLine("*************************開始插入測試************************");
for(int i=1; i < 5; i++)
{
// Console.WriteLine(TestOracleInsert(i));
Console.WriteLine(TestMysqlInsert(i));
Console.WriteLine(TestPostgreSQLInsert(i));
}
return sb.ToString();
}
public String TestUpdate()
{
StringBuilder sb=new StringBuilder();
Console.WriteLine("*************************開始更新測試************************");
// Console.WriteLine(TestOracleUpdate());
for (int i=0;i<3;i++) {
Console.WriteLine(TestMysqlUpdate(i));
Console.WriteLine(TestPostgreSQLUpdate(i));
}
return sb.ToString();
}
private String TestOracleInsert(int loop)
{
StringBuilder sb=new();
Stopwatch stopwatch=new();
List<OraModel.TestTable> tables=new();
for (int i=1; i <=50000; i++)
{
OraModel.TestTable table=new();
table.Id=Guid.NewGuid().ToString("N");
table.Message=$"第{loop}輪測試數(shù)據(jù){i}";
table.CurrentTime=DateTime.Now;
table.Code=(loop * 5000) + i;
tables.Add(table);
}
using (var context=new OraModel.ModelContext())
{
try {
stopwatch.Start();
context.Database.BeginTransaction();
context.TestTables.AddRange(tables);
context.SaveChanges();
context.Database.CommitTransaction();
stopwatch.Stop();
sb.Append($"第{loop}輪插入50000條到【Oracle】數(shù)據(jù)庫【成功】:耗時{stopwatch.ElapsedMilliseconds} ms...");
}
catch(Exception ex)
{
context.Database.RollbackTransaction();
stopwatch.Stop();
sb.Append($"第{loop}輪插入50000條到【Oracle】數(shù)據(jù)庫【失敗】:耗時{stopwatch.ElapsedMilliseconds} ms...");
}
finally
{
}
}
return sb.ToString();
}
private String TestMysqlInsert(int loop)
{
StringBuilder sb=new();
Stopwatch stopwatch=new();
List<MyModel.TestTable> tables=new();
for (int i=1; i <=100000; i++)
{
MyModel.TestTable table=new();
table.Id=Guid.NewGuid().ToString("N");
table.Message=$"第{loop}輪測試數(shù)據(jù){i}";
table.CurrentTime=DateTime.Now;
table.Code=i;
tables.Add(table);
}
using (var context=new MyModel.testdbContext())
{
try
{
stopwatch.Start();
context.Database.BeginTransaction();
context.TestTables.AddRange(tables);
context.SaveChanges();
context.Database.CommitTransaction();
stopwatch.Stop();
sb.Append($"第{loop}輪插入100000條到【MySQL】數(shù)據(jù)庫【成功】:耗時{stopwatch.ElapsedMilliseconds} ms...");
}
catch (Exception ex)
{
context.Database.RollbackTransaction();
stopwatch.Stop();
sb.Append($"第{loop}輪插入100000條到【MySQL】數(shù)據(jù)庫【失敗】:耗時{stopwatch.ElapsedMilliseconds} ms...");
}
finally
{
}
}
return sb.ToString();
}
private String TestPostgreSQLInsert(int loop)
{
StringBuilder sb=new();
Stopwatch stopwatch=new();
List<PgModel.TestTable> tables=new();
for (int i=1; i <=100000; i++)
{
PgModel.TestTable table=new();
table.Id=Guid.NewGuid().ToString("N");
table.Message=$"第{loop}輪測試數(shù)據(jù){i}";
table.CurrentTime=DateTime.Now;
table.Code=i;
tables.Add(table);
}
using (var context=new PgModel.testdbContext())
{
try
{
stopwatch.Start();
context.Database.BeginTransaction();
context.TestTables.AddRange(tables);
context.SaveChanges();
context.Database.CommitTransaction();
stopwatch.Stop();
sb.Append($"第{loop}輪插入100000條到【PostgreSQL】數(shù)據(jù)庫【成功】:耗時{stopwatch.ElapsedMilliseconds} ms...");
}
catch (Exception ex)
{
context.Database.RollbackTransaction();
stopwatch.Stop();
sb.Append($"第{loop}輪插入100000條到【PostgreSQL】數(shù)據(jù)庫【失敗】:耗時{stopwatch.ElapsedMilliseconds} ms...");
}
finally
{
}
}
return sb.ToString();
}
private String TestOracleUpdate()
{
StringBuilder sb=new();
Stopwatch stopwatch=new();
using (var context=new OraModel.ModelContext())
{
var datas=context.TestTables.OrderBy(x=>x.Code).Take(10000);
context.Database.BeginTransaction();
foreach (var value in datas)
{
value.Message=$"數(shù)據(jù)變更,code={value.Code}";
}
try
{
stopwatch.Start();
context.TestTables.UpdateRange(datas);
context.SaveChanges();
context.Database.CommitTransaction();
stopwatch.Stop();
sb.Append($"批量更新【Oracle】數(shù)據(jù)庫10000條【成功】:耗時{stopwatch.ElapsedMilliseconds} ms...");
}
catch (Exception ex)
{
context.Database.RollbackTransaction();
stopwatch.Stop();
sb.Append($"批量更新【Oracle】數(shù)據(jù)庫10000條【失敗】:耗時{stopwatch.ElapsedMilliseconds} ms...");
}
finally
{
}
}
return sb.ToString();
}
private String TestMysqlUpdate(int loop)
{
StringBuilder sb=new();
Stopwatch stopwatch=new();
using (var context=new MyModel.testdbContext())
{
var datas=context.TestTables.OrderBy(x=> x.Code).Skip(loop*50000).Take(50000);
context.Database.BeginTransaction();
foreach (var value in datas)
{
value.Message=$"數(shù)據(jù)變更,code={value.Code}";
}
try
{
stopwatch.Start();
context.TestTables.UpdateRange(datas);
context.SaveChanges();
context.Database.CommitTransaction();
stopwatch.Stop();
sb.Append($"批量更新【MySQL】數(shù)據(jù)庫50000條【成功】:耗時{stopwatch.ElapsedMilliseconds} ms...");
}
catch (Exception ex)
{
context.Database.RollbackTransaction();
stopwatch.Stop();
sb.Append($"批量更新【MySQL】數(shù)據(jù)庫50000條【失敗】:耗時{stopwatch.ElapsedMilliseconds} ms...");
}
finally
{
}
}
return sb.ToString();
}
private String TestPostgreSQLUpdate(int loop)
{
StringBuilder sb=new();
Stopwatch stopwatch=new();
using (var context=new PgModel.testdbContext())
{
var datas=context.TestTables.OrderBy(x=> x.Code).Skip(loop * 50000).Take(50000);
context.Database.BeginTransaction();
foreach (var value in datas)
{
value.Message=$"數(shù)據(jù)變更,code={value.Code}";
}
try
{
stopwatch.Start();
context.TestTables.UpdateRange(datas);
context.SaveChanges();
context.Database.CommitTransaction();
stopwatch.Stop();
sb.Append($"第{loop}輪 批量更新【PostgreSQL】數(shù)據(jù)庫50000條【成功】:耗時{stopwatch.ElapsedMilliseconds} ms...");
}
catch (Exception ex)
{
context.Database.RollbackTransaction();
stopwatch.Stop();
sb.Append($"第{loop}輪 批量更新【PostgreSQL】數(shù)據(jù)庫50000條【失敗】:耗時{stopwatch.ElapsedMilliseconds} ms...");
}
finally
{
}
}
return sb.ToString();
}
}
以上測試至此就結(jié)束了。結(jié)論可能有點尷尬,也許跟環(huán)境配置有關(guān),也可能跟ef core操作數(shù)據(jù)庫的支持與實現(xiàn)有關(guān)。并且當前僅在單表環(huán)境下測試,并沒有通過多表測試、存過測試、壓力測試等,結(jié)果僅供娛樂和參考。同時歡迎各位大佬們提供更多測試內(nèi)容,也歡迎各位大佬轉(zhuǎn)發(fā)或評論或點贊等一鍵三連。
本文原鏈接:https://www.cnblogs.com/weskynet/p/16097151.html
果你不了解python,可以先了解python的簡單用法。不過人郵君相信,在座的各位都是大佬,我們直接介紹操作。
python 與 mysql 實現(xiàn)交互的過程,通常分為:建立連接、把sql語句定義為字符串,提交指令、關(guān)閉連接。
核心的技能在于 sql語句;除了定義sql語句,其余3個處理都是固定的寫法。接下來,人郵君結(jié)合《MySQL是怎樣運行的》這本書,以Linux環(huán)境為主,為大家進行說明。
MySQL是怎樣運行的 從根兒上理解MySQL
首先來看第一步,安裝 MySQL 數(shù)據(jù)庫:
如果你想要使用python操作MySQL數(shù)據(jù)庫,就必須先要安裝pymysql庫,這個庫的安裝很簡單;
第二步,pymysql 模塊安裝與使用:
MySQL-python驅(qū)動,是python 操作mysql必不可少的模塊。
下載MySQL-python-1.2.5.zip 文件之后直接解壓。進入MySQL-python-1.2.5目錄:
>>python setup.py install
下載地址:https://pypi.python.org/pypi/MySQL-python/
第三步,python與mysql的交互實現(xiàn):
1)連接
pymysql .connect () 函數(shù):連接數(shù)據(jù)庫
使用 pymysql 的 connect() 方法連接數(shù)據(jù)庫,涉及到幾個參數(shù),具體代表意義如下:
host:MySQL服務(wù)的地址,若數(shù)據(jù)庫在本地上,使用 localhost 或者127.0.0.1。如果在其它服務(wù)器上,則寫對應(yīng)的 IP地址
port:服務(wù)的端口號,默認為3306,不寫則為默認值。
user:登錄數(shù)據(jù)庫的用戶名
passwd:登錄 MySQL 的密碼
db:數(shù)據(jù)庫名
charset:設(shè)置為 utf8 編碼,解決存漢字亂碼問題
eg:
# 導入模塊
import pymysql
# 打開數(shù)據(jù)庫連接
conn=pymysql.connect(
host="127.0.0.1",
user="root",
password="123456",
database="test_db",
charset="utf8")
print(conn)
print(type(conn))
輸出結(jié)果顯示如下:表面數(shù)據(jù)庫連接成功
詳細可以參考
https://www.cnblogs.com/qjj19931230/p/12550384.html?utm_source=tuicool
這里要強調(diào)的是,除了上面的連接方式,還有其他的連接。在《MySQL是怎樣運行的》這本書中,介紹到,mysql連接分為內(nèi)連接和外連接。內(nèi)外連接的根本區(qū)別是在驅(qū)動表中記錄不符合ON子句中的連接條件時,內(nèi)連接不會把該記錄加入到最后的結(jié)果集中,而外連接會。外連接分為左(外)連接和右(外)連接。
三種鏈接方式如下圖所示:
2)獲取游標
conn.cursor():獲取游標
對數(shù)據(jù)庫進行操作,只連接數(shù)據(jù)庫是不夠的,還需要獲取操作數(shù)據(jù)庫的游標,才能進行后續(xù)的操作。游標的主要作用是用來接收數(shù)據(jù)庫操作后的返回結(jié)果,比如數(shù)據(jù)查詢、插入和刪除等。通過獲取到的數(shù)據(jù)庫連接實例 conn 下的 cursor() 方法來創(chuàng)建游標,如下:
# 導入模塊
import pymysql
# 打開數(shù)據(jù)庫連接
conn=pymysql.connect(
host="127.0.0.1",
user="root",
password="123456",
database="test_db",
charset="utf8")
# print(conn)
# print(type(conn))
# 獲取連接下的游標
cursor_test=conn.cursor()
print(cursor_test)
3)數(shù)據(jù)庫操作
import pymysql
# 打開數(shù)據(jù)庫連接
conn=pymysql.connect(
host="127.0.0.1",
user="root",
password="123456",
database="test_db",
charset="utf8")
# 獲取連接下的游標
cursor_test=conn.cursor()
# 使用 execute() 方法執(zhí)行 SQL,如果表存在則刪除
cursor_test.execute("DROP TABLE IF EXISTS EMPLOYEE")
# 使用預(yù)處理語句創(chuàng)建表
sql="""CREATE TABLE user1 (
FIRST_NAME CHAR(20) NOT NULL,
LAST_NAME CHAR(20),
AGE INT,
SEX CHAR(1),
INCOME FLOAT )"""
cursor_test.execute(sql)
# 關(guān)閉數(shù)據(jù)庫連接
conn.close()
如下所示數(shù)據(jù)庫表創(chuàng)建成功:
mysql> desc user1;
+------------+----------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+----------+------+-----+---------+-------+
| FIRST_NAME | char(20) | NO | | NULL | |
| LAST_NAME | char(20) | YES | | NULL | |
| AGE | int(11) | YES | | NULL | |
| SEX | char(1) | YES | | NULL | |
| INCOME | float | YES | | NULL | |
+------------+----------+------+-----+---------+-------+
5 rows in set (0.00 sec)
import pymysql
# 打開數(shù)據(jù)庫連接
conn=pymysql.connect(
host="127.0.0.1",
user="root",
password="123456",
database="test_db",
charset="utf8")
# 獲取連接下的游標
cursor_test=conn.cursor()
# 使用預(yù)處理語句創(chuàng)建表
sql="""INSERT INTO user1(FIRST_NAME,
LAST_NAME, AGE, SEX, INCOME)
VALUES ('Fei', 'Fei', 20, 'M', 1000)"""
try:
# 執(zhí)行sql語句
cursor_test.execute(sql)
# 提交到數(shù)據(jù)庫執(zhí)行
conn.commit()
except:
# 如果發(fā)生錯誤則回滾
conn.rollback()
# 關(guān)閉數(shù)據(jù)庫連接
conn.close()
import pymysql
# 打開數(shù)據(jù)庫連接
conn=pymysql.connect(
host="127.0.0.1",
user="root",
password="123456",
database="test_db",
charset="utf8")
# 獲取連接下的游標
cursor_test=conn.cursor()
sql="""
select * from user1"""
try:
# 執(zhí)行 sql 語句
cursor_test.execute(sql)
# 顯示出所有數(shù)據(jù)
data_result=cursor_test.fetchall()
for row in data_result:
fname=row[0]
lname=row[1]
age=row[2]
sex=row[3]
income=row[4]
# 打印結(jié)果
print("fname=%s,lname=%s,age=%s,sex=%s,income=%s" % \
(fname, lname, age, sex, income))
except:
print("Error: unable to fetch data")
# 關(guān)閉數(shù)據(jù)庫連接
conn.close()
# 導入模塊
import pymysql
# 打開數(shù)據(jù)庫連接
conn=pymysql.connect(
host="127.0.0.1",
user="root",
password="123456",
database="test_db",
charset="utf8")
# print(conn)
# print(type(conn))
# 獲取連接下的游標
cursor_test=conn.cursor()
sql="DELETE * FROM user1"
try:
# 執(zhí)行SQL語句
cursor_test.execute(sql)
# 提交到數(shù)據(jù)庫執(zhí)行
conn.commit()
except:
# 發(fā)生錯誤時回滾
conn.rollback()
# 關(guān)閉數(shù)據(jù)庫連接
conn.close()
在《MySQL是怎樣運行的》,作者小孩子4919強調(diào),嵌套循環(huán)連接算法是指驅(qū)動表只訪問一次,但被驅(qū)動表卻可能會訪問多次,訪問次數(shù)取決于驅(qū)動表執(zhí)行單表查詢后的結(jié)果集中有多少條記錄,大致過程如下:
步驟1,選取驅(qū)動表,使用與驅(qū)動表相關(guān)的過濾條件,選取代價最低的單表訪問方法來執(zhí)行對驅(qū)動表的單表查詢;
步驟2,對步驟1中查詢驅(qū)動表得到的結(jié)果集中的每一條記錄,都分別到被驅(qū)動表中查找匹配的記錄。
由于被驅(qū)動表可能會訪問多次,因此可以為被驅(qū)動表建立合適的索引以加快查詢速度。
所以,如果被驅(qū)動表非常大,即需要完成大量的數(shù)據(jù)交換,多次訪問被驅(qū)動表可能導致很多次的磁盤I/O讀取操作,此時可以使用基于塊的嵌套循環(huán)連接算法來緩解由此造成的性能損耗。Mysql的設(shè)計者,提出了名為Join Buffer(連接緩沖區(qū))的概念:
有興趣的同學,建議根據(jù)書中詳細描述走一遍。
此外,人郵君特別建議大家看看《MySQL是怎樣運行的》,它解決了“為什么這個SQL語句執(zhí)行得這么慢?為什么我明明建立了索引,但是查詢計劃顯示沒用?為什么IN查詢中的參數(shù)一多就不使用索引了?為什么我的數(shù)據(jù)顯示成了亂碼?”等等每一位DBA和后端開發(fā)人員在與MySQL打交道時,所遇到的很多常見問題。除此之外,索引結(jié)構(gòu)、MVCC、隔離級別的實現(xiàn)、鎖的使用等知識,也是求職人員在MySQL面試中躲不過去的高頻問題,作者都在書中給出了很詳細的介紹。
MySQL是怎樣運行的 從根兒上理解MySQL
這本書的初稿最初是以小冊的形式發(fā)布在掘金平臺上的,一經(jīng)發(fā)布便得到大家的青睞,十分火爆!歷經(jīng)兩年,現(xiàn)在終于成書,有興趣的小伙伴也可以去掘金圍觀~(小孩子4919 的個人主頁)
從底層到應(yīng)用,從基礎(chǔ)到進階,關(guān)于MySQL的一切,作者都在書中講解得非常清楚,幫助你從根兒上理解MySQL。
*請認真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。