Python-100天(三)-數(shù)據庫基礎和進階

關系數(shù)據庫入門

關系數(shù)據庫概述

  1. 數(shù)據持久化 - 將數(shù)據保存到能夠長久保存數(shù)據的存儲介質中昆禽,在掉電的情況下數(shù)據也不會丟失毛雇。

  2. 數(shù)據庫發(fā)展史 - 網狀數(shù)據庫还最、層次數(shù)據庫、關系數(shù)據庫塌碌、NoSQL數(shù)據庫。

    1970年旬盯,IBM的研究員E.F.Codd在Communication of the ACM上發(fā)表了名為A Relational Model of Data for Large Shared Data Banks的論文台妆,提出了關系模型的概念,奠定了關系模型的理論基礎胖翰。后來Codd又陸續(xù)發(fā)表多篇文章接剩,論述了范式理論和衡量關系系統(tǒng)的12條標準,用數(shù)學理論奠定了關系數(shù)據庫的基礎萨咳。

  3. 關系數(shù)據庫特點懊缺。

    • 理論基礎:集合論和關系代數(shù)。
    • 具體表象:用二維表(有行和列)組織數(shù)據培他。
    • 編程語言:結構化查詢語言(SQL)鹃两。
  4. ER模型(實體關系模型)和概念模型圖。
    ER模型舀凛,全稱為實體關系模型(Entity-Relationship Model)俊扳,由美籍華裔計算機科學家陳品山先生提出,是概念數(shù)據模型的高層描述方式.

    • 實體 - 矩形框
    • 屬性 - 橢圓框
    • 關系 - 菱形框
    • 重數(shù) - 1:1(一對一) / 1:N(一對多) / M:N(多對多)

    實際項目開發(fā)中猛遍,我們可以利用數(shù)據庫建模工具(如:PowerDesigner)來繪制概念數(shù)據模型(其本質就是ER模型)馋记,然后再設置好目標數(shù)據庫系統(tǒng),將概念模型轉換成物理模型懊烤,最終生成創(chuàng)建二維表的SQL(很多工具都可以根據我們設計的物理模型圖以及設定的目標數(shù)據庫來導出SQL或直接生成數(shù)據表)梯醒。

  5. 關系數(shù)據庫產品。

    • Oracle - 目前世界上使用最為廣泛的數(shù)據庫管理系統(tǒng)腌紧,作為一個通用的數(shù)據庫系統(tǒng)茸习,它具有完整的數(shù)據管理功能;作為一個關系數(shù)據庫壁肋,它是一個完備關系的產品逮光;作為分布式數(shù)據庫,它實現(xiàn)了分布式處理的功能墩划。在Oracle最新的12c版本中涕刚,還引入了多承租方架構,使用該架構可輕松部署和管理數(shù)據庫云乙帮。
    • DB2 - IBM公司開發(fā)的杜漠、主要運行于Unix(包括IBM自家的AIX)、Linux、以及Windows服務器版等系統(tǒng)的關系數(shù)據庫產品驾茴。DB2歷史悠久且被認為是最早使用SQL的數(shù)據庫產品盼樟,它擁有較為強大的商業(yè)智能功能。
    • SQL Server - 由Microsoft開發(fā)和推廣的關系型數(shù)據庫產品锈至,最初適用于中小企業(yè)的數(shù)據管理晨缴,但是近年來它的應用范圍有所擴展,部分大企業(yè)甚至是跨國公司也開始基于它來構建自己的數(shù)據管理系統(tǒng)峡捡。
    • MySQL - MySQL是開放源代碼的击碗,任何人都可以在GPL(General Public License)的許可下下載并根據個性化的需要對其進行修改。MySQL因為其速度们拙、可靠性和適應性而備受關注稍途。
    • PostgreSQL - 在BSD許可證下發(fā)行的開放源代碼的關系數(shù)據庫產品。

MySQL簡介

MySQL最早是由瑞典的MySQL AB公司開發(fā)的一個開放源碼的關系數(shù)據庫管理系統(tǒng)砚婆,該公司于2008年被昇陽微系統(tǒng)公司(Sun Microsystems)收購械拍。在2009年,甲骨文公司(Oracle)收購昇陽微系統(tǒng)公司装盯,因此在這之后MySQL成為了Oracle旗下產品坷虑。

MySQL在過去由于性能高、成本低埂奈、可靠性好迄损,已經成為最流行的開源數(shù)據庫,因此被廣泛地應用于中小型網站開發(fā)挥转。隨著MySQL的不斷成熟,它也逐漸被應用于更多大規(guī)模網站和應用共屈,比如維基百科绑谣、谷歌(Google)、臉書(Facebook)拗引、淘寶網等網站都使用了MySQL來提供數(shù)據持久化服務借宵。

甲骨文公司收購后昇陽微系統(tǒng)公司,大幅調漲MySQL商業(yè)版的售價矾削,且甲骨文公司不再支持另一個自由軟件項目OpenSolaris的發(fā)展壤玫,因此導致自由軟件社區(qū)對于Oracle是否還會持續(xù)支持MySQL社區(qū)版(MySQL的各個發(fā)行版本中唯一免費的版本)有所擔憂,MySQL的創(chuàng)始人麥克爾·維德紐斯以MySQL為基礎哼凯,成立分支計劃MariaDB(以他女兒的名字命名的數(shù)據庫)。有許多原來使用MySQL數(shù)據庫的公司(例如:維基百科)已經陸續(xù)完成了從MySQL數(shù)據庫到MariaDB數(shù)據庫的遷移。

  1. 安裝和配置

    說明:下面的安裝和配置都是以CentOS Linux環(huán)境為例醇王,如果需要在其他系統(tǒng)下安裝MySQL朵你,讀者可以自行在網絡上查找對應的安裝教程)。

    • 剛才說過,MySQL有一個分支版本名叫MariaDB她渴,該數(shù)據庫旨在繼續(xù)保持MySQL數(shù)據庫在GNU GPL下開源达址。如果要使用MariaDB作為MySQL的替代品,可以使用下面的命令進行安裝趁耗。

      yum install mariadb mariadb-server
      
    • 如果要安裝官方版本的MySQL沉唠,可以在MySQL官方網站下載安裝文件。首先在下載頁面中選擇平臺和版本苛败,然后找到對應的下載鏈接满葛。下面以MySQL 5.7.26版本和Red Hat Enterprise Linux為例,直接下載包含所有安裝文件的歸檔文件著拭,解歸檔之后通過包管理工具進行安裝纱扭。

      wget https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.26-1.el7.x86_64.rpm-bundle.tar
      tar -xvf mysql-5.7.26-1.el7.x86_64.rpm-bundle.tar
      

      如果系統(tǒng)上有MariaDB相關的文件,需要先移除MariaDB相關的文件儡遮。

      yum list installed | grep mariadb | awk '{print $1}' | xargs yum erase -y
      

      接下來可以按照如下所示的順序用RPM(Redhat Package Manager)工具安裝MySQL乳蛾。

      rpm -ivh mysql-community-common-5.7.26-1.el7.x86_64.rpm
      rpm -ivh mysql-community-libs-5.7.26-1.el7.x86_64.rpm
      rpm -ivh mysql-community-client-5.7.26-1.el7.x86_64.rpm
      rpm -ivh mysql-community-server-5.7.26-1.el7.x86_64.rpm
      

      可以使用下面的命令查看已經安裝的MySQL相關的包。

      rpm -qa | grep mysql
      
    • 配置MySQL鄙币。

      MySQL的配置文件在/etc目錄下肃叶,名為my.cnf,默認的配置文件內容如下所示十嘿。如果對這個文件不理解并沒有關系因惭,什么時候用到這個配置文件什么時候再了解它就行了。

      cat /etc/my.cnf
      
      # For advice on how to change settings please see
      # http://dev.mysql.com/doc/refman/5.7/en/server-configuration-defaults.html
      
      [mysqld]
      #
      # Remove leading # and set to the amount of RAM for the most important data
      # cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.
      # innodb_buffer_pool_size = 128M
      #
      # Remove leading # to turn on a very important data integrity option: logging
      # changes to the binary log between backups.
      # log_bin
      #
      # Remove leading # to set options mainly useful for reporting servers.
      # The server defaults are faster for transactions and fast SELECTs.
      # Adjust sizes as needed, experiment to find the optimal values.
      # join_buffer_size = 128M
      # sort_buffer_size = 2M
      # read_rnd_buffer_size = 2M
      datadir=/var/lib/mysql
      socket=/var/lib/mysql/mysql.sock
      
      # Disabling symbolic-links is recommended to prevent assorted security risks
      symbolic-links=0
      
      log-error=/var/log/mysqld.log
      pid-file=/var/run/mysqld/mysqld.pid
      
    • 啟動MySQL服務绩衷。

      可以使用下面的命令來啟動MySQL蹦魔。

      service mysqld start
      

      在CentOS 7中,更推薦使用下面的命令來啟動MySQL咳燕。

      systemctl start mysqld
      

      啟動MySQL成功后勿决,可以通過下面的命令來檢查網絡端口使用情況,MySQL默認使用3306端口招盲。

      netstat -ntlp | grep mysql
      

      也可以使用下面的命令查找是否有名為mysqld的進程低缩。

      pgrep mysqld
      
    • 使用MySQL客戶端工具連接服務器。

      命令行工具:

      mysql -u root -p
      

      說明:啟動客戶端時曹货,-u參數(shù)用來指定用戶名咆繁,MySQL默認的超級管理賬號為root-p表示要輸入密碼(用戶口令)顶籽;如果連接的是其他主機而非本機玩般,可以用-h來指定連接主機的主機名或IP地址。

      如果是首次安裝MySQL礼饱,可以使用下面的命令來找到默認的初始密碼壤短。

      cat /var/log/mysqld.log | grep password
      

      上面的命令會查看MySQL的日志帶有password的行设拟,在顯示的結果中root@localhost:后面的部分就是默認設置的初始密碼。

      修改超級管理員(root)的訪問口令為123456久脯。

      set global validate_password_policy=0;
      set global validate_password_length=6;
      alter user 'root'@'localhost' identified by '123456';
      

      說明:MySQL較新的版本默認不允許使用弱口令作為用戶口令纳胧,所以我們通過上面的前兩條命令修改了驗證用戶口令的策略和口令的長度。事實上我們不應該使用弱口令帘撰,因為存在用戶口令被暴力破解的風險跑慕。近年來,攻擊數(shù)據庫竊取數(shù)據和劫持數(shù)據庫勒索比特幣的事件屢見不鮮摧找,要避免這些潛在的風險核行,最為重要的一點是不要讓數(shù)據庫服務器暴露在公網上(最好的做法是將數(shù)據庫置于內網,至少要做到不向公網開放數(shù)據庫服務器的訪問端口)蹬耘,另外要保管好root賬號的口令芝雪,應用系統(tǒng)需要訪問數(shù)據庫時,通常不使用root賬號進行訪問综苔,而是創(chuàng)建其他擁有適當權限的賬號來訪問惩系。

      再次使用客戶端工具連接MySQL服務器時,就可以使用新設置的口令了如筛。在實際開發(fā)中堡牡,為了方便用戶操作,可以選擇圖形化的客戶端工具來連接MySQL服務器杨刨,包括:

      • MySQL Workbench(官方提供的工具)
      • Navicat for MySQL(界面簡單優(yōu)雅晤柄,功能直觀強大)
      • SQLyog for MySQL(強大的MySQL數(shù)據庫管理員工具)
  2. 常用命令。

    • 查看服務器版本妖胀。

      select version();
      
    • 查看所有數(shù)據庫芥颈。

      show databases;
      
    • 切換到指定數(shù)據庫。

      use mysql;
      
    • 查看數(shù)據庫下所有表赚抡。

      show tables;
      
    • 獲取幫助爬坑。

      ? contents;
      ? functions;
      ? numeric functions;
      ? round;
      
      ? data types;
      ? longblob;
      

SQL詳解

基本操作

我們通常可以將SQL分為三類:DDL(數(shù)據定義語言)怕品、DML(數(shù)據操作語言)和DCL(數(shù)據控制語言)妇垢。DDL主要用于創(chuàng)建(create)巾遭、刪除(drop)肉康、修改(alter)數(shù)據庫中的對象,比如創(chuàng)建灼舍、刪除和修改二維表吼和;DML主要負責插入數(shù)據(insert)、刪除數(shù)據(delete)骑素、更新數(shù)據(update)和查詢(select)炫乓;DCL通常用于授予權限(grant)和召回權限(revoke)。

說明:SQL是不區(qū)分大小寫的語言,為了書寫方便末捣,下面的SQL都使用了小寫字母來書寫侠姑。

  1. DDL(數(shù)據定義語言)

    -- 如果存在名為school的數(shù)據庫就刪除它
    drop database if exists school;
    
    -- 創(chuàng)建名為school的數(shù)據庫并設置默認的字符集和排序方式
    create database school default charset utf8;
    
    -- 切換到school數(shù)據庫上下文環(huán)境
    use school;
    
    -- 創(chuàng)建學院表
    create table tb_college
    (
    collid       int auto_increment comment '編號',
    collname     varchar(50) not null comment '名稱',
    collintro    varchar(500) default '' comment '介紹',
    primary key (collid)
    );
    
    -- 創(chuàng)建學生表
    create table tb_student
    (
    stuid        int not null comment '學號',
    stuname  varchar(20) not null comment '姓名',
    stusex       boolean default 1 comment '性別',
    stubirth     date not null comment '出生日期',
    stuaddr  varchar(255) default '' comment '籍貫',
    collid       int not null comment '所屬學院',
    primary key (stuid),
    foreign key (collid) references tb_college (collid)
    );
    
    -- 創(chuàng)建教師表
    create table tb_teacher
    (
    teaid        int not null comment '工號',
    teaname  varchar(20) not null comment '姓名',
    teatitle     varchar(10) default '助教' comment '職稱',
    collid       int not null comment '所屬學院',
    primary key (teaid),
    foreign key (collid) references tb_college (collid)
    );
    
    -- 創(chuàng)建課程表
    create table tb_course
    (
    couid        int not null comment '編號',
    couname  varchar(50) not null comment '名稱',
    coucredit    int not null comment '學分',
    teaid        int not null comment '授課老師',
    primary key (couid),
    foreign key (teaid) references tb_teacher (teaid)
    );
    
    -- 創(chuàng)建選課記錄表
    create table tb_record
    (
    recid        int auto_increment comment '選課記錄編號',
    sid      int not null comment '選課學生',
    cid      int not null comment '所選課程',
    seldate  datetime default now() comment '選課時間日期',
    score        decimal(4,1) comment '考試成績',
    primary key (recid),
    foreign key (sid) references tb_student (stuid),
    foreign key (cid) references tb_course (couid),
    unique (sid, cid)
    );
    

    上面的DDL有幾個地方需要強調一下:

    • 創(chuàng)建數(shù)據庫時,我們通過default charset utf8指定了數(shù)據庫默認使用的字符集箩做,我們推薦使用該字符集莽红,因為utf8能夠支持國際化編碼。如果將來數(shù)據庫中用到的字符可能包括類似于Emoji這樣的圖片字符邦邦,也可以將默認字符集設定為utf8mb4(最大4字節(jié)的utf-8編碼)安吁。查看MySQL支持的字符集可以執(zhí)行下面的語句。

      show character set;
      
      +----------+---------------------------------+---------------------+--------+
      | Charset  | Description                     | Default collation   | Maxlen |
      +----------+---------------------------------+---------------------+--------+
      | big5     | Big5 Traditional Chinese        | big5_chinese_ci     |      2 |
      | dec8     | DEC West European               | dec8_swedish_ci     |      1 |
      | cp850    | DOS West European               | cp850_general_ci    |      1 |
      | hp8      | HP West European                | hp8_english_ci      |      1 |
      | koi8r    | KOI8-R Relcom Russian           | koi8r_general_ci    |      1 |
      | latin1   | cp1252 West European            | latin1_swedish_ci   |      1 |
      | latin2   | ISO 8859-2 Central European     | latin2_general_ci   |      1 |
      | swe7     | 7bit Swedish                    | swe7_swedish_ci     |      1 |
      | ascii    | US ASCII                        | ascii_general_ci    |      1 |
      | ujis     | EUC-JP Japanese                 | ujis_japanese_ci    |      3 |
      | sjis     | Shift-JIS Japanese              | sjis_japanese_ci    |      2 |
      | hebrew   | ISO 8859-8 Hebrew               | hebrew_general_ci   |      1 |
      | tis620   | TIS620 Thai                     | tis620_thai_ci      |      1 |
      | euckr    | EUC-KR Korean                   | euckr_korean_ci     |      2 |
      | koi8u    | KOI8-U Ukrainian                | koi8u_general_ci    |      1 |
      | gb2312   | GB2312 Simplified Chinese       | gb2312_chinese_ci   |      2 |
      | greek    | ISO 8859-7 Greek                | greek_general_ci    |      1 |
      | cp1250   | Windows Central European        | cp1250_general_ci   |      1 |
      | gbk      | GBK Simplified Chinese          | gbk_chinese_ci      |      2 |
      | latin5   | ISO 8859-9 Turkish              | latin5_turkish_ci   |      1 |
      | armscii8 | ARMSCII-8 Armenian              | armscii8_general_ci |      1 |
      | utf8     | UTF-8 Unicode                   | utf8_general_ci     |      3 |
      | ucs2     | UCS-2 Unicode                   | ucs2_general_ci     |      2 |
      | cp866    | DOS Russian                     | cp866_general_ci    |      1 |
      | keybcs2  | DOS Kamenicky Czech-Slovak      | keybcs2_general_ci  |      1 |
      | macce    | Mac Central European            | macce_general_ci    |      1 |
      | macroman | Mac West European               | macroman_general_ci |      1 |
      | cp852    | DOS Central European            | cp852_general_ci    |      1 |
      | latin7   | ISO 8859-13 Baltic              | latin7_general_ci   |      1 |
      | utf8mb4  | UTF-8 Unicode                   | utf8mb4_general_ci  |      4 |
      | cp1251   | Windows Cyrillic                | cp1251_general_ci   |      1 |
      | utf16    | UTF-16 Unicode                  | utf16_general_ci    |      4 |
      | utf16le  | UTF-16LE Unicode                | utf16le_general_ci  |      4 |
      | cp1256   | Windows Arabic                  | cp1256_general_ci   |      1 |
      | cp1257   | Windows Baltic                  | cp1257_general_ci   |      1 |
      | utf32    | UTF-32 Unicode                  | utf32_general_ci    |      4 |
      | binary   | Binary pseudo charset           | binary              |      1 |
      | geostd8  | GEOSTD8 Georgian                | geostd8_general_ci  |      1 |
      | cp932    | SJIS for Windows Japanese       | cp932_japanese_ci   |      2 |
      | eucjpms  | UJIS for Windows Japanese       | eucjpms_japanese_ci |      3 |
      | gb18030  | China National Standard GB18030 | gb18030_chinese_ci  |      4 |
      +----------+---------------------------------+---------------------+--------+
      41 rows in set (0.00 sec)
      

      如果要設置MySQL服務啟動時默認使用的字符集燃辖,可以修改MySQL的配置并添加以下內容

      [mysqld]
      character-set-server=utf8
      
    • 在創(chuàng)建表的時候鬼店,我們可以在右圓括號的后面通過engine=XXX來指定表的存儲引擎,MySQL支持多種存儲引擎黔龟,可以通過show engines命令進行查看妇智。MySQL 5.5以后的版本默認使用的存儲引擎是InnoDB,它正好也就是我們推薦大家使用的存儲引擎(因為InnoDB更適合互聯(lián)網應用對高并發(fā)捌锭、性能以及事務支持等方面的需求)俘陷。

      show engines\G
      
      *************************** 1. row ***************************
            Engine: InnoDB
           Support: DEFAULT
           Comment: Supports transactions, row-level locking, and foreign keys
      Transactions: YES
                XA: YES
        Savepoints: YES
      *************************** 2. row ***************************
            Engine: MRG_MYISAM
           Support: YES
           Comment: Collection of identical MyISAM tables
      Transactions: NO
                XA: NO
        Savepoints: NO
      *************************** 3. row ***************************
            Engine: MEMORY
           Support: YES
           Comment: Hash based, stored in memory, useful for temporary tables
      Transactions: NO
                XA: NO
        Savepoints: NO
      *************************** 4. row ***************************
            Engine: BLACKHOLE
           Support: YES
           Comment: /dev/null storage engine (anything you write to it disappears)
      Transactions: NO
                XA: NO
        Savepoints: NO
      *************************** 5. row ***************************
            Engine: MyISAM
           Support: YES
           Comment: MyISAM storage engine
      Transactions: NO
                XA: NO
        Savepoints: NO
      *************************** 6. row ***************************
            Engine: CSV
           Support: YES
           Comment: CSV storage engine
      Transactions: NO
                XA: NO
        Savepoints: NO
      *************************** 7. row ***************************
            Engine: ARCHIVE
           Support: YES
           Comment: Archive storage engine
      Transactions: NO
                XA: NO
        Savepoints: NO
      *************************** 8. row ***************************
            Engine: PERFORMANCE_SCHEMA
           Support: YES
           Comment: Performance Schema
      Transactions: NO
                XA: NO
        Savepoints: NO
      *************************** 9. row ***************************
            Engine: FEDERATED
           Support: NO
           Comment: Federated MySQL storage engine
      Transactions: NULL
                XA: NULL
        Savepoints: NULL
      9 rows in set (0.00 sec)
      

      下面的表格對MySQL幾種常用的數(shù)據引擎進行了簡單的對比。

      特性 InnoDB MRG_MYISAM MEMORY MyISAM
      存儲限制 沒有
      事務 支持
      鎖機制 行鎖 表鎖 表鎖 表鎖
      B樹索引 支持 支持 支持 支持
      哈希索引 支持
      全文檢索 支持(5.6+) 支持
      集群索引 支持
      數(shù)據緩存 支持 支持
      索引緩存 支持 支持 支持 支持
      數(shù)據可壓縮 支持
      內存使用
      存儲空間使用
      批量插入性能
      是否支持外鍵 支持

      通過上面的比較我們可以了解到观谦,InnoDB是唯一能夠支持外鍵拉盾、事務以及行鎖的存儲引擎,所以我們之前說它更適合互聯(lián)網應用豁状,而且它也是較新的MySQL版本中默認使用的存儲引擎捉偏。

    • 在定義表結構為每個字段選擇數(shù)據類型時,如果不清楚哪個數(shù)據類型更合適泻红,可以通過MySQL的幫助系統(tǒng)來了解每種數(shù)據類型的特性夭禽、數(shù)據的長度和精度等相關信息。

      ? data types
      
      You asked for help about help category: "Data Types"
      For more information, type 'help <item>', where <item> is one of the following
      topics:
         AUTO_INCREMENT
         BIGINT
         BINARY
         BIT
         BLOB
         BLOB DATA TYPE
         BOOLEAN
         CHAR
         CHAR BYTE
         DATE
         DATETIME
         DEC
         DECIMAL
         DOUBLE
         DOUBLE PRECISION
         ENUM
         FLOAT
         INT
         INTEGER
         LONGBLOB
         LONGTEXT
         MEDIUMBLOB
         MEDIUMINT
         MEDIUMTEXT
         SET DATA TYPE
         SMALLINT
         TEXT
         TIME
         TIMESTAMP
         TINYBLOB
         TINYINT
         TINYTEXT
         VARBINARY
         VARCHAR
         YEAR DATA TYPE
      
      ? varchar
      
      Name: 'VARCHAR'
      Description:
      [NATIONAL] VARCHAR(M) [CHARACTER SET charset_name] [COLLATE
      collation_name]
      
      A variable-length string. M represents the maximum column length in
      characters. The range of M is 0 to 65,535. The effective maximum length
      of a VARCHAR is subject to the maximum row size (65,535 bytes, which is
      shared among all columns) and the character set used. For example, utf8
      characters can require up to three bytes per character, so a VARCHAR
      column that uses the utf8 character set can be declared to be a maximum
      of 21,844 characters. See
      http://dev.mysql.com/doc/refman/5.7/en/column-count-limit.html.
      
      MySQL stores VARCHAR values as a 1-byte or 2-byte length prefix plus
      data. The length prefix indicates the number of bytes in the value. A
      VARCHAR column uses one length byte if values require no more than 255
      bytes, two length bytes if values may require more than 255 bytes.
      
      *Note*:
      
      MySQL follows the standard SQL specification, and does not remove
      trailing spaces from VARCHAR values.
      
      VARCHAR is shorthand for CHARACTER VARYING. NATIONAL VARCHAR is the
      standard SQL way to define that a VARCHAR column should use some
      predefined character set. MySQL uses utf8 as this predefined character
      set. http://dev.mysql.com/doc/refman/5.7/en/charset-national.html.
      NVARCHAR is shorthand for NATIONAL VARCHAR.
      
      URL: http://dev.mysql.com/doc/refman/5.7/en/string-type-overview.html
      

      在數(shù)據類型的選擇上谊路,保存字符串數(shù)據通常都使用VARCHAR和CHAR兩種類型讹躯,前者通常稱為變長字符串,而后者通常稱為定長字符串缠劝;對于InnoDB存儲引擎潮梯,行存儲格式沒有區(qū)分固定長度和可變長度列,因此VARCHAR類型好CHAR類型沒有本質區(qū)別惨恭,后者不一定比前者性能更好秉馏。如果要保存的很大字符串,可以使用TEXT類型脱羡;如果要保存很大的字節(jié)串萝究,可以使用BLOB(二進制大對象)類型免都。在MySQL中,TEXT和BLOB又分別包括TEXT帆竹、MEDIUMTEXT绕娘、LONGTEXT和BLOB、MEDIUMBLOB栽连、LONGBLOB三種不同的類型业舍,它們主要的區(qū)別在于存儲數(shù)據的最大大小不同。保存浮點數(shù)可以用FLOAT或DOUBLE類型升酣,而保存定點數(shù)應該使用DECIMAL類型舷暮。如果要保存時間日期,DATETIME類型優(yōu)于TIMESTAMP類型噩茄,因為前者能表示的時間日期范圍更大下面。

  2. DML

    -- 插入學院數(shù)據
    insert into tb_college (collname, collintro) values 
    ('計算機學院', '創(chuàng)建于1956年是我國首批建立計算機專業(yè)。學院現(xiàn)有計算機科學與技術一級學科和網絡空間安全一級學科博士學位授予權绩聘,其中計算機科學與技術一級學科具有博士后流動站沥割。計算機科學與技術一級學科在2017年全國第四輪學科評估中評為A;2019 U.S.News全球計算機學科排名26名凿菩;ESI學科排名0.945‰机杜,進入全球前1‰,位列第43位衅谷。'),
    ('外國語學院', '1998年浙江大學椒拗、杭州大學、浙江農業(yè)大學获黔、浙江醫(yī)科大學四校合并蚀苛,成立新的浙江大學。1999年原浙江大學外語系玷氏、原杭州大學外國語學院堵未、原杭州大學大外部、原浙江農業(yè)大學公外部盏触、原浙江醫(yī)科大學外語教學部合并渗蟹,成立浙江大學外國語學院。2003年學院更名為浙江大學外國語言文化與國際交流學院赞辩。'),
    ('經濟管理學院', '四川大學經濟學院歷史悠久雌芽、傳承厚重,其前身是創(chuàng)辦于1905年的四川大學經濟科,距今已有100多年的歷史诗宣。已故著名經濟學家彭迪先膘怕、張與九想诅、蔣學模召庞、胡寄窗岛心、陶大鏞、胡代光篮灼,以及當代著名學者劉詩白等曾先后在此任教或學習忘古。在長期的辦學過程中,學院堅持以馬克思主義的立場诅诱、觀點髓堪、方法為指導,圍繞建設世界一流經濟學院的奮斗目標娘荡,做實“兩個偉大”深度融合干旁,不斷提高黨的建設質量與科學推進一流事業(yè)深度融合。');
    
    -- 插入學生數(shù)據
    insert into tb_student (stuid, stuname, stusex, stubirth, stuaddr, collid) values
    (1001, '楊逍', 1, '1990-3-4', '四川成都', 1),
    (1002, '任我行', 1, '1992-2-2', '湖南長沙', 1),
    (1033, '王語嫣', 0, '1989-12-3', '四川成都', 1),
    (1572, '岳不群', 1, '1993-7-19', '陜西咸陽', 1),
    (1378, '紀嫣然', 0, '1995-8-12', '四川綿陽', 1),
    (1954, '林平之', 1, '1994-9-20', '福建莆田', 1),
    (2035, '東方不敗', 1, '1988-6-30', null, 2),
    (3011, '林震南', 1, '1985-12-12', '福建莆田', 3),
    (3755, '項少龍', 1, '1993-1-25', null, 3),
    (3923, '楊不悔', 0, '1985-4-17', '四川成都', 3),
    (4040, '隔壁老王', 1, '1989-1-1', '四川成都', 2);
    
    -- 刪除學生數(shù)據
    delete from tb_student where stuid=4040;
    
    -- 更新學生數(shù)據
    update tb_student set stuname='楊過', stuaddr='湖南長沙' where stuid=1001;
    
    -- 插入老師數(shù)據
    insert into tb_teacher (teaid, teaname, teatitle, collid) values 
    (1122, '張三豐', '教授', 1),
    (1133, '宋遠橋', '副教授', 1),
    (1144, '楊逍', '副教授', 1),
    (2255, '范遙', '副教授', 2),
    (3366, '韋一笑', '講師', 3);
    
    -- 插入課程數(shù)據
    insert into tb_course (couid, couname, coucredit, teaid) values 
    (1111, 'Python程序設計', 3, 1122),
    (2222, 'Web前端開發(fā)', 2, 1122),
    (3333, '操作系統(tǒng)', 4, 1122),
    (4444, '計算機網絡', 2, 1133),
    (5555, '編譯原理', 4, 1144),
    (6666, '算法和數(shù)據結構', 3, 1144),
    (7777, '經貿法語', 3, 2255),
    (8888, '成本會計', 2, 3366),
    (9999, '審計學', 3, 3366);
    
    -- 插入選課數(shù)據
    insert into tb_record (sid, cid, seldate, score) values 
    (1001, 1111, '2017-09-01', 95),
    (1001, 2222, '2017-09-01', 87.5),
    (1001, 3333, '2017-09-01', 100),
    (1001, 4444, '2018-09-03', null),
    (1001, 6666, '2017-09-02', 100),
    (1002, 1111, '2017-09-03', 65),
    (1002, 5555, '2017-09-01', 42),
    (1033, 1111, '2017-09-03', 92.5),
    (1033, 4444, '2017-09-01', 78),
    (1033, 5555, '2017-09-01', 82.5),
    (1572, 1111, '2017-09-02', 78),
    (1378, 1111, '2017-09-05', 82),
    (1378, 7777, '2017-09-02', 65.5),
    (2035, 7777, '2018-09-03', 88),
    (2035, 9999, default, null),
    (3755, 1111, default, null),
    (3755, 8888, default, null),
    (3755, 9999, '2017-09-01', 92);
    
    -- 查詢所有學生信息
    select * from tb_student;
    
    -- 查詢所有課程名稱及學分(投影和別名)
    select couname, coucredit from tb_course;
    select couname as 課程名稱, coucredit as 學分 from tb_course;
    
    -- 查詢所有學生的姓名和性別(條件運算)
    select stuname as 姓名, case stusex when 1 then '男' else '女' end as 性別 from tb_student;
    select stuname as 姓名, if(stusex, '男', '女') as 性別 from tb_student;
    
    -- 查詢所有女學生的姓名和出生日期(篩選)
    select stuname, stubirth from tb_student where stusex=0;
    
    -- 查詢所有80后學生的姓名炮沐、性別和出生日期(篩選)
    select stuname, stusex, stubirth from tb_student where stubirth>='1980-1-1' and stubirth<='1989-12-31';
    select stuname, stusex, stubirth from tb_student where stubirth between '1980-1-1' and '1989-12-31';
    
    -- 查詢姓"楊"的學生姓名和性別(模糊)
    select stuname, stusex from tb_student where stuname like '楊%';
    
    -- 查詢姓"楊"名字兩個字的學生姓名和性別(模糊)
    select stuname, stusex from tb_student where stuname like '楊_';
    
    -- 查詢姓"楊"名字三個字的學生姓名和性別(模糊)
    select stuname, stusex from tb_student where stuname like '楊__';
    
    -- 查詢名字中有"不"字或"嫣"字的學生的姓名(模糊)
    select stuname, stusex from tb_student where stuname like '%不%' or stuname like '%嫣%';
    
    -- 查詢沒有錄入家庭住址的學生姓名(空值)
    select stuname from tb_student where stuaddr is null;
    
    -- 查詢錄入了家庭住址的學生姓名(空值)
    select stuname from tb_student where stuaddr is not null;
    
    -- 查詢學生選課的所有日期(去重)
    select distinct seldate from tb_record;
    
    -- 查詢學生的家庭住址(去重)
    select distinct stuaddr from tb_student where stuaddr is not null;
    
    -- 查詢男學生的姓名和生日按年齡從大到小排列(排序)
    select stuname as 姓名, datediff(curdate(), stubirth) div 365 as 年齡 from tb_student where stusex=1 order by 年齡 desc;
    
    -- 查詢年齡最大的學生的出生日期(聚合函數(shù))
    select min(stubirth) from tb_student;
    
    -- 查詢年齡最小的學生的出生日期(聚合函數(shù))
    select max(stubirth) from tb_student;
    
    -- 查詢男女學生的人數(shù)(分組和聚合函數(shù))
    select stusex, count(*) from tb_student group by stusex;
    
    -- 查詢課程編號為1111的課程的平均成績(篩選和聚合函數(shù))
    select avg(score) from tb_record where cid=1111;
    
    -- 查詢學號為1001的學生所有課程的平均分(篩選和聚合函數(shù))
    select avg(score) from tb_record where sid=1001;
    
    -- 查詢每個學生的學號和平均成績(分組和聚合函數(shù))
    select sid as 學號, avg(score) as 平均分 from tb_record group by sid;
    
    -- 查詢平均成績大于等于90分的學生的學號和平均成績
    -- 分組以前的篩選使用where子句 / 分組以后的篩選使用having子句
    select sid as 學號, avg(score) as 平均分 from tb_record group by sid having 平均分>=90;
    
    -- 查詢年齡最大的學生的姓名(子查詢/嵌套的查詢)
    select stuname from tb_student where stubirth=( select min(stubirth) from tb_student );
    
    -- 查詢年齡最大的學生姓名和年齡(子查詢+運算)
    select stuname as 姓名, datediff(curdate(), stubirth) div 365 as 年齡 from tb_student where stubirth=( select min(stubirth) from tb_student );
    
    -- 查詢選了兩門以上的課程的學生姓名(子查詢/分組條件/集合運算)
    select stuname from tb_student where stuid in ( select stuid from tb_record group by stuid having count(stuid)>2 );
    
    -- 查詢學生姓名争群、課程名稱以及成績(連接查詢)
    select stuname, couname, score from tb_student t1, tb_course t2, tb_record t3 where stuid=sid and couid=cid and score is not null;
    
    -- 查詢學生姓名、課程名稱以及成績按成績從高到低查詢第11-15條記錄(內連接+分頁)
    select stuname, couname, score from tb_student inner join tb_record on stuid=sid inner join tb_course on couid=cid where score is not null order by score desc limit 5 offset 10;
    
    select stuname, couname, score from tb_student inner join tb_record on stuid=sid inner join tb_course on couid=cid where score is not null order by score desc limit 10, 5;
    
    -- 查詢選課學生的姓名和平均成績(子查詢和連接查詢)
    select stuname, avgmark from tb_student, ( select sid, avg(score) as avgmark from tb_record group by sid ) temp where stuid=sid;
    
    select stuname, avgmark from tb_student inner join ( select sid, avg(score) as avgmark from tb_record group by sid ) temp on stuid=sid;
    
    -- 查詢每個學生的姓名和選課數(shù)量(左外連接和子查詢)
    select stuname, ifnull(total, 0) from tb_student left outer join ( select sid, count(sid) as total from tb_record group by sid ) temp on stuid=sid;
    

    上面的DML有幾個地方需要加以說明:

    1. MySQL中支持多種類型的運算符大年,包括:算術運算符(+换薄、-、*翔试、/轻要、%)、比較運算符(=垦缅、<>冲泥、<=>、<壁涎、<=柏蘑、>、>=粹庞、BETWEEN...AND...咳焚、IN、IS NULL庞溜、IS NOT NULL革半、LIKE、RLIKE流码、REGEXP)又官、邏輯運算符(NOT、AND漫试、OR六敬、XOR)和位運算符(&、|驾荣、^外构、~普泡、>>、<<)审编,我們可以在DML中使用這些運算符處理數(shù)據撼班。

    2. 在查詢數(shù)據時,可以在SELECT語句及其子句(如WHERE子句垒酬、ORDER BY子句砰嘁、HAVING子句等)中使用函數(shù),這些函數(shù)包括字符串函數(shù)勘究、數(shù)值函數(shù)矮湘、時間日期函數(shù)、流程函數(shù)等口糕,如下面的表格所示板祝。

      常用字符串函數(shù)。

      函數(shù) 功能
      CONCAT 將多個字符串連接成一個字符串
      FORMAT 將數(shù)值格式化成字符串并指定保留幾位小數(shù)
      FROM_BASE64 / TO_BASE64 BASE64解碼/編碼
      BIN / OCT / HEX 將數(shù)值轉換成二進制/八進制/十六進制字符串
      LOCATE 在字符串中查找一個子串的位置
      LEFT / RIGHT 返回一個字符串左邊/右邊指定長度的字符
      LENGTH / CHAR_LENGTH 返回字符串的長度以字節(jié)/字符為單位
      LOWER / UPPER 返回字符串的小寫/大寫形式
      LPAD / RPAD 如果字符串的長度不足走净,在字符串左邊/右邊填充指定的字符
      LTRIM / RTRIM 去掉字符串前面/后面的空格
      ORD / CHAR 返回字符對應的編碼/返回編碼對應的字符
      STRCMP 比較字符串券时,返回-1、0伏伯、1分別表示小于橘洞、等于、大于
      SUBSTRING 返回字符串指定范圍的子串

      常用數(shù)值函數(shù)说搅。

      函數(shù) 功能
      ABS 返回一個數(shù)的絕度值
      CEILING / FLOOR 返回一個數(shù)上取整/下取整的結果
      CONV 將一個數(shù)從一種進制轉換成另一種進制
      CRC32 計算循環(huán)冗余校驗碼
      EXP / LOG / LOG2 / LOG10 計算指數(shù)/對數(shù)
      POW 求冪
      RAND 返回[0,1)范圍的隨機數(shù)
      ROUND 返回一個數(shù)四舍五入后的結果
      SQRT 返回一個數(shù)的平方根
      TRUNCATE 截斷一個數(shù)到指定的精度
      SIN / COS / TAN / COT / ASIN / ACOS / ATAN 三角函數(shù)

      常用時間日期函數(shù)炸枣。

      函數(shù) 功能
      CURDATE / CURTIME / NOW 獲取當前日期/時間/日期和時間
      ADDDATE / SUBDATE 將兩個日期表達式相加/相減并返回結果
      DATE / TIME 從字符串中獲取日期/時間
      YEAR / MONTH / DAY 從日期中獲取年/月/日
      HOUR / MINUTE / SECOND 從時間中獲取時/分/秒
      DATEDIFF / TIMEDIFF 返回兩個時間日期表達式相差多少天/小時
      MAKEDATE / MAKETIME 制造一個日期/時間

      常用流程函數(shù)。

      函數(shù) 功能
      IF 根據條件是否成立返回不同的值
      IFNULL 如果為NULL則返回指定的值否則就返回本身
      NULLIF 兩個表達式相等就返回NULL否則返回第一個表達式的值

      其他常用函數(shù)弄唧。

      函數(shù) 功能
      MD5 / SHA1 / SHA2 返回字符串對應的哈希摘要
      CHARSET / COLLATION 返回字符集/校對規(guī)則
      USER / CURRENT_USER 返回當前用戶
      DATABASE 返回當前數(shù)據庫名
      VERSION 返回當前數(shù)據庫版本
      FOUND_ROWS / ROW_COUNT 返回查詢到的行數(shù)/受影響的行數(shù)
      LAST_INSERT_ID 返回最后一個自增主鍵的值
      UUID / UUID_SHORT 返回全局唯一標識符
  3. DCL

    -- 創(chuàng)建可以遠程登錄的root賬號并為其指定口令
    create user 'root'@'%' identified by '123456';
    
    -- 為遠程登錄的root賬號授權操作所有數(shù)據庫所有對象的所有權限并允許其將權限再次賦予其他用戶
    grant all privileges on *.* to 'root'@'%' with grant option;
    
    -- 創(chuàng)建名為hellokitty的用戶并為其指定口令
    create user 'hellokitty'@'%' identified by '123123';
    
    -- 將對school數(shù)據庫所有對象的所有操作權限授予hellokitty
    grant all privileges on school.* to 'hellokitty'@'%';
    
    -- 召回hellokitty對school數(shù)據庫所有對象的insert/delete/update權限
    revoke insert, delete, update on school.* from 'hellokitty'@'%';
    

    說明:創(chuàng)建一個可以允許任意主機登錄并且具有超級管理員權限的用戶在現(xiàn)實中并不是一個明智的決定适肠,因為一旦該賬號的口令泄露或者被破解,數(shù)據庫將會面臨災難級的風險候引。

索引

索引是關系型數(shù)據庫中用來提升查詢性能最為重要的手段侯养。關系型數(shù)據庫中的索引就像一本書的目錄,我們可以想象一下澄干,如果要從一本書中找出某個知識點逛揩,但是這本書沒有目錄,這將是意見多么可怕的事情(我們估計得一篇一篇的翻下去麸俘,才能確定這個知識點到底在什么位置)辩稽。創(chuàng)建索引雖然會帶來存儲空間上的開銷,就像一本書的目錄會占用一部分的篇幅一樣从媚,但是在犧牲空間后換來的查詢時間的減少也是非常顯著的逞泄。

MySQL中,所有數(shù)據類型的列都可以被索引,常用的存儲引擎InnoDB和MyISAM能支持每個表創(chuàng)建16個索引喷众。InnoDB和MyISAM使用的索引其底層算法是B-tree(B樹)各谚,B-tree是一種自平衡的樹,類似于平衡二叉排序樹侮腹,能夠保持數(shù)據有序。這種數(shù)據結構能夠讓查找數(shù)據稻励、順序訪問父阻、插入數(shù)據及刪除的操作都在對數(shù)時間內完成。

接下來我們通過一個簡單的例子來說明索引的意義望抽,比如我們要根據學生的姓名來查找學生加矛,這個場景在實際開發(fā)中應該經常遇到,就跟通過商品名稱查找商品道理是一樣的煤篙。我們可以使用MySQL的explain關鍵字來查看SQL的執(zhí)行計劃斟览。

explain select * from tb_student where stuname='林震南'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tb_student
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 11
     filtered: 10.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec)

在上面的SQL執(zhí)行計劃中,有幾項值得我們關注:

  1. type:MySQL在表中找到滿足條件的行的方式辑奈,也稱為訪問類型苛茂,包括:ALL(全表掃描)、index(索引全掃描)鸠窗、range(索引范圍掃描)妓羊、ref(非唯一索引掃描)、eq_ref(唯一索引掃描)稍计、const/system躁绸、NULL。在所有的訪問類型中臣嚣,很顯然ALL是性能最差的净刮,它代表了全表掃描是指要掃描表中的每一行才能找到匹配的行。
  2. possible_keys:MySQL可以選擇的索引硅则,但是有可能不會使用淹父。
  3. key:MySQL真正使用的索引。
  4. rows:執(zhí)行查詢需要掃描的行數(shù)怎虫,這是一個預估值弹灭。

從上面的執(zhí)行計劃可以看出,當我們通過學生名字查詢學生時實際上是進行了全表掃描揪垄,不言而喻這個查詢性能肯定是非常糟糕的穷吮,尤其是在表中的行很多的時候。如果我們需要經常通過學生姓名來查詢學生饥努,那么就應該在學生姓名對應的列上創(chuàng)建索引出刷,通過索引來加速查詢。

create index idx_student_name on tb_student(stuname);

再次查看剛才的SQL對應的執(zhí)行計劃萄焦。

explain select * from tb_student where stuname='林震南'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tb_student
   partitions: NULL
         type: ref
possible_keys: idx_student_name
          key: idx_student_name
      key_len: 62
          ref: const
         rows: 1
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)

可以注意到,在對學生姓名創(chuàng)建索引后缠诅,剛才的查詢已經不是全表掃描而是基于索引的查詢,而且掃描的行只有唯一的一行乍迄,這顯然大大的提升了查詢的性能管引。MySQL中還允許創(chuàng)建前綴索引,即對索引字段的前N個字符創(chuàng)建索引闯两,這樣的話可以減少索引占用的空間(但節(jié)省了空間很有可能會浪費時間褥伴,時間和空間是不可調和的矛盾),如下所示漾狼。

create index idx_student_name_1 on tb_student(stuname(1));

上面的索引相當于是根據學生姓名的第一個字來創(chuàng)建的索引重慢,我們再看看SQL執(zhí)行計劃。

explain select * from tb_student where stuname='林震南'\G
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: tb_student
   partitions: NULL
         type: ref
possible_keys: idx_student_name
          key: idx_student_name
      key_len: 5
          ref: const
         rows: 2
     filtered: 100.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec)

不知道大家是否注意到逊躁,這一次掃描的行變成了2行似踱,因為學生表中有兩個姓“林”的學生,我們只用姓名的第一個字作為索引的話核芽,在查詢時通過索引就會找到這兩行。

如果要刪除索引酵熙,可以使用下面的SQL狞洋。

alter table tb_student drop index idx_student_name;

或者

drop index idx_student_name on tb_student;

我們簡單的為大家總結一下索引的設計原則:

  1. 最適合索引的列是出現(xiàn)在WHERE子句和連接子句中的列。
  2. 索引列的基數(shù)越大(取值多重復值少)绿店,索引的效果就越好吉懊。
  3. 使用前綴索引可以減少索引占用的空間,內存中可以緩存更多的索引假勿。
  4. 索引不是越多越好借嗽,雖然索引加速了讀操作(查詢),但是寫操作(增转培、刪恶导、改)都會變得更慢,因為數(shù)據的變化會導致索引的更新浸须,就如同書籍章節(jié)的增刪需要更新目錄一樣惨寿。
  5. 使用InnoDB存儲引擎時,表的普通索引都會保存主鍵的值删窒,所以主鍵要盡可能選擇較短的數(shù)據類型裂垦,這樣可以有效的減少索引占用的空間,利用提升索引的緩存效果肌索。

最后蕉拢,還有一點需要說明,InnoDB使用的B-tree索引,數(shù)值類型的列除了等值判斷時索引會生效之外晕换,使用>午乓、<、>=闸准、<=益愈、BETWEEN...AND... 、<>時夷家,索引仍然生效蒸其;對于字符串類型的列,如果使用不以通配符開頭的模糊查詢瘾英,索引也是起作用的枣接,但是其他的情況會導致索引失效颂暇,這就意味著很有可能會做全表查詢缺谴。

視圖

視圖是關系型數(shù)據庫中將一組查詢指令構成的結果集組合成可查詢的數(shù)據表的對象。簡單的說耳鸯,視圖就是虛擬的表湿蛔,但與數(shù)據表不同的是,數(shù)據表是一種實體結構县爬,而視圖是一種虛擬結構阳啥,你也可以將視圖理解為保存在數(shù)據庫中被賦予名字的SQL語句。

使用視圖可以獲得以下好處:

  1. 可以將實體數(shù)據表隱藏起來财喳,讓外部程序無法得知實際的數(shù)據結構察迟,讓訪問者可以使用表的組成部分而不是整個表,降低數(shù)據庫被攻擊的風險耳高。
  2. 在大多數(shù)的情況下視圖是只讀的(更新視圖的操作通常都有諸多的限制)扎瓶,外部程序無法直接透過視圖修改數(shù)據。
  3. 重用SQL語句泌枪,將高度復雜的查詢包裝在視圖表中概荷,直接訪問該視圖即可取出需要的數(shù)據;也可以將視圖視為數(shù)據表進行連接查詢碌燕。
  4. 視圖可以返回與實體數(shù)據表不同格式的數(shù)據误证,

創(chuàng)建視圖。

create view vw_avg_score 
as 
    select sid, round(avg(score), 1) as avgscore 
    from tb_record group by sid;

create view vw_student_score 
as 
    select stuname, avgscore 
    from tb_student, vw_avg_score 
    where stuid=sid;

提示:因為視圖不包含數(shù)據修壕,所以每次使用視圖時愈捅,都必須執(zhí)行查詢以獲得數(shù)據,如果你使用了連接查詢慈鸠、嵌套查詢創(chuàng)建了較為復雜的視圖改鲫,你可能會發(fā)現(xiàn)查詢性能下降得很厲害。因此,在使用復雜的視圖前像棘,應該進行測試以確保其性能能夠滿足應用的需求稽亏。

使用視圖。

select stuname, avgscore from vw_student_score order by avgscore desc;
+--------------+----------+
| stuname      | avgscore |
+--------------+----------+
| 楊過         |     95.6 |
| 任我行       |     53.5 |
| 王語嫣       |     84.3 |
| 紀嫣然       |     73.8 |
| 岳不群       |     78.0 |
| 東方不敗     |     88.0 |
| 項少龍       |     92.0 |
+--------------+----------+

既然視圖是一張?zhí)摂M的表缕题,那么視圖的中的數(shù)據可以更新嗎截歉?視圖的可更新性要視具體情況而定,以下類型的視圖是不能更新的:

  1. 使用了聚合函數(shù)(SUM烟零、MIN瘪松、MAX、AVG锨阿、COUNT等)宵睦、DISTINCT、GROUP BY墅诡、HAVING壳嚎、UNION或者UNION ALL的視圖。
  2. SELECT中包含了子查詢的視圖末早。
  3. FROM子句中包含了一個不能更新的視圖的視圖烟馅。
  4. WHERE子句的子查詢引用了FROM子句中的表的視圖。

刪除視圖然磷。

drop view vw_student_score;

說明:如果希望更新視圖郑趁,可以先用上面的命令刪除視圖,也可以通過create or replace view來更新視圖姿搜。

視圖的規(guī)則和限制寡润。

  1. 視圖可以嵌套,可以利用從其他視圖中檢索的數(shù)據來構造一個新的視圖舅柜。視圖也可以和表一起使用梭纹。
  2. 創(chuàng)建視圖時可以使用order by子句,但如果從視圖中檢索數(shù)據時也使用了order by业踢,那么該視圖中原先的order by會被覆蓋栗柒。
  3. 視圖無法使用索引,也不會激發(fā)觸發(fā)器(實際開發(fā)中因為性能等各方面的考慮知举,通常不建議使用觸發(fā)器瞬沦,所以我們也不對這個概念進行介紹)的執(zhí)行。

存儲過程

存儲過程是事先編譯好存儲在數(shù)據庫中的一組SQL的集合雇锡,調用存儲過程可以簡化應用程序開發(fā)人員的工作逛钻,減少與數(shù)據庫服務器之間的通信,對于提升數(shù)據操作的性能也是有幫助的锰提。其實迄今為止曙痘,我們使用的SQL語句都是針對一個或多個表的單條語句芳悲,但在實際開發(fā)中經常會遇到某個操作需要多條SQL語句才能完成的情況。例如边坤,電商網站在受理用戶訂單時名扛,需要做以下一系列的處理。

  1. 通過查詢來核對庫存中是否有對應的物品以及庫存是否充足茧痒。
  2. 如果庫存有物品肮韧,需要鎖定庫存以確保這些物品不再賣給別人, 并且要減少可用的物品數(shù)量以反映正確的庫存量旺订。
  3. 如果庫存不足弄企,可能需要進一步與供應商進行交互或者至少產生一條系統(tǒng)提示消息。
  4. 不管受理訂單是否成功区拳,都需要產生流水記錄拘领,而且需要給對應的用戶產生一條通知信息。

我們可以通過存儲過程將復雜的操作封裝起來樱调,這樣不僅有助于保證數(shù)據的一致性约素,而且將來如果業(yè)務發(fā)生了變動,只需要調整和修改存儲過程即可本涕。對于調用存儲過程的用戶來說业汰,存儲過程并沒有暴露數(shù)據表的細節(jié)伙窃,而且執(zhí)行存儲過程比一條條的執(zhí)行一組SQL要快得多菩颖。

下面的存儲過程實現(xiàn)了查詢某門課程的最高分、最低分和平均分为障。

drop procedure if exists sp_score_by_cid;

delimiter $$

create procedure sp_score_by_cid(
    courseId int, 
    out maxScore decimal(4,1), 
    out minScore decimal(4,1),
    out avgScore decimal(4,1)
)
begin
    select max(score) into maxScore from tb_record 
        where cid=courseId;
    select min(score) into minScore from tb_record 
        where cid=courseId;
    select avg(score) into avgScore from tb_record 
        where cid=courseId;
end $$

delimiter ;

call sp_score_by_cid(1111, @a, @b, @c);
select @a, @b, @c;

說明:在定義存儲過程時晦闰,因為可能需要書寫多條SQL,而分隔這些SQL需要使用分號作為分隔符鳍怨,如果這個時候呻右,仍然用分號表示整段代碼結束,那么定義存儲過程的SQL就會出現(xiàn)錯誤鞋喇,所以上面我們用delimiter $$將整段代碼結束的標記定義為$$声滥,那么代碼中的分號將不再表示整段代碼的結束,需要馬上執(zhí)行侦香,整段代碼在遇到end $$時才輸入完成并執(zhí)行落塑。在定義完存儲過程后,通過delimiter ;將結束符重新改回成分號罐韩。

上面定義的存儲過程有四個參數(shù)憾赁,其中第一個參數(shù)是輸入參數(shù),代表課程的編號散吵,后面的參數(shù)都是輸出參數(shù)龙考,因為存儲過程不能定義返回值蟆肆,只能通過輸出參數(shù)將執(zhí)行結果帶出,定義輸出參數(shù)的關鍵字是out晦款,默認情況下參數(shù)都是輸入參數(shù)炎功。

調用存儲過程。

call sp_score_by_cid(1111, @a, @b, @c);

獲取輸出參數(shù)的值缓溅。

select @a as 最高分, @b as 最低分, @c as 平均分;

刪除存儲過程亡问。

drop procedure sp_score_by_cid;

在存儲過程中,我們可以定義變量肛宋、條件州藕,可以使用分支和循環(huán)語句,可以通過游標操作查詢結果酝陈,還可以使用事件調度器床玻,這些內容我們暫時不在此處進行介紹。雖然我們說了很多存儲過程的好處沉帮,但是在實際開發(fā)中锈死,如果過度的使用存儲過程,將大量復雜的運算放到存儲過程中穆壕,也會導致占用數(shù)據庫服務器的CPU資源待牵,造成數(shù)據庫服務器承受巨大的壓力。為此喇勋,我們一般會將復雜的運算和處理交給應用服務器缨该,因為很容易部署多臺應用服務器來分攤這些壓力。

幾個重要的概念

范式理論 - 設計二維表的指導思想

  1. 第一范式:數(shù)據表的每個列的值域都是由原子值組成的川背,不能夠再分割贰拿。
  2. 第二范式:數(shù)據表里的所有數(shù)據都要和該數(shù)據表的鍵(主鍵與候選鍵)有完全依賴關系。
  3. 第三范式:所有非鍵屬性都只和候選鍵有相關性熄云,也就是說非鍵屬性之間應該是獨立無關的膨更。

數(shù)據完整性

  1. 實體完整性 - 每個實體都是獨一無二的

    • 主鍵(primary key) / 唯一約束 / 唯一索引(unique)
  2. 引用完整性(參照完整性)- 關系中不允許引用不存在的實體

    • 外鍵(foreign key)
  3. 域完整性 - 數(shù)據是有效的

    • 數(shù)據類型及長度

    • 非空約束(not null)

    • 默認值約束(default)

    • 檢查約束(check)

      說明:在MySQL數(shù)據庫中,檢查約束并不起作用缴允。

數(shù)據一致性

  1. 事務:一系列對數(shù)據庫進行讀/寫的操作荚守,這些操作要么全都成功,要么全都失敗练般。

  2. 事務的ACID特性

    • 原子性:事務作為一個整體被執(zhí)行矗漾,包含在其中的對數(shù)據庫的操作要么全部被執(zhí)行,要么都不執(zhí)行
    • 一致性:事務應確保數(shù)據庫的狀態(tài)從一個一致狀態(tài)轉變?yōu)榱硪粋€一致狀態(tài)
    • 隔離性:多個事務并發(fā)執(zhí)行時踢俄,一個事務的執(zhí)行不應影響其他事務的執(zhí)行
    • 持久性:已被提交的事務對數(shù)據庫的修改應該永久保存在數(shù)據庫中
  3. MySQL中的事務操作

    • 開啟事務環(huán)境

      start transaction
      

      begin
      
    • 提交事務

      commit
      
    • 回滾事務

      rollback
      

其他內容

大家應該能夠想到缩功,關于MySQL的知識肯定遠遠不止上面列出的這些,比如MySQL的性能優(yōu)化都办、管理和維護MySQL的相關工具嫡锌、MySQL數(shù)據的備份和恢復虑稼、監(jiān)控MySQL、部署高可用架構等問題我們在這里都沒有進行討論势木。當然蛛倦,這些內容也都是跟項目開發(fā)密切相關的,我們就留到后續(xù)的章節(jié)中再續(xù)點進行講解啦桌。

Python數(shù)據庫編程

我們用如下所示的數(shù)據庫來演示在Python中如何訪問MySQL數(shù)據庫溯壶。

drop database if exists hrs;
create database hrs default charset utf8;

use hrs;

drop table if exists tb_emp;
drop table if exists tb_dept;

create table tb_dept
(
dno   int not null comment '編號',
dname varchar(10) not null comment '名稱',
dloc  varchar(20) not null comment '所在地',
primary key (dno)
);

insert into tb_dept values 
    (10, '會計部', '北京'),
    (20, '研發(fā)部', '成都'),
    (30, '銷售部', '重慶'),
    (40, '運維部', '深圳');

create table tb_emp
(
eno   int not null comment '員工編號',
ename varchar(20) not null comment '員工姓名',
job   varchar(20) not null comment '員工職位',
mgr   int comment '主管編號',
sal   int not null comment '員工月薪',
comm  int comment '每月補貼',
dno   int comment '所在部門編號',
primary key (eno)
);

alter table tb_emp add constraint fk_emp_dno foreign key (dno) references tb_dept (dno);

insert into tb_emp values 
    (7800, '張三豐', '總裁', null, 9000, 1200, 20),
    (2056, '喬峰', '分析師', 7800, 5000, 1500, 20),
    (3088, '李莫愁', '設計師', 2056, 3500, 800, 20),
    (3211, '張無忌', '程序員', 2056, 3200, null, 20),
    (3233, '丘處機', '程序員', 2056, 3400, null, 20),
    (3251, '張翠山', '程序員', 2056, 4000, null, 20),
    (5566, '宋遠橋', '會計師', 7800, 4000, 1000, 10),
    (5234, '郭靖', '出納', 5566, 2000, null, 10),
    (3344, '黃蓉', '銷售主管', 7800, 3000, 800, 30),
    (1359, '胡一刀', '銷售員', 3344, 1800, 200, 30),
    (4466, '苗人鳳', '銷售員', 3344, 2500, null, 30),
    (3244, '歐陽鋒', '程序員', 3088, 3200, null, 20),
    (3577, '楊過', '會計', 5566, 2200, null, 10),
    (3588, '朱九真', '會計', 5566, 2500, null, 10);

在Python 3中,我們通常使用純Python的三方庫PyMySQL來訪問MySQL數(shù)據庫甫男,它應該是目前Python操作MySQL數(shù)據庫最好的選擇且改。

  1. 安裝PyMySQL。

    pip install pymysql
    
  2. 添加一個部門板驳。

    import pymysql
    
    
    def main():
        no = int(input('編號: '))
        name = input('名字: ')
        loc = input('所在地: ')
        # 1. 創(chuàng)建數(shù)據庫連接對象
        con = pymysql.connect(host='localhost', port=3306,
                              database='hrs', charset='utf8',
                              user='yourname', password='yourpass')
        try:
            # 2. 通過連接對象獲取游標
            with con.cursor() as cursor:
                # 3. 通過游標執(zhí)行SQL并獲得執(zhí)行結果
                result = cursor.execute(
                    'insert into tb_dept values (%s, %s, %s)',
                    (no, name, loc)
                )
            if result == 1:
                print('添加成功!')
            # 4. 操作成功提交事務
            con.commit()
        finally:
            # 5. 關閉連接釋放資源
            con.close()
    
    
    if __name__ == '__main__':
        main()
    
  3. 刪除一個部門又跛。

    import pymysql
    
    
    def main():
        no = int(input('編號: '))
        con = pymysql.connect(host='localhost', port=3306,
                              database='hrs', charset='utf8',
                              user='yourname', password='yourpass',
                              autocommit=True)
        try:
            with con.cursor() as cursor:
                result = cursor.execute(
                    'delete from tb_dept where dno=%s',
                    (no, )
                )
            if result == 1:
                print('刪除成功!')
        finally:
            con.close()
    
    
    if __name__ == '__main__':
        main()
    

    說明:如果不希望每次SQL操作之后手動提交或回滾事務,可以像上面的代碼那樣若治,在創(chuàng)建連接的時候多加一個名為autocommit的參數(shù)并將它的值設置為True慨蓝,表示每次執(zhí)行SQL之后自動提交。如果程序中不需要使用事務環(huán)境也不希望手動的提交或回滾就可以這么做端幼。

  4. 更新一個部門礼烈。

    import pymysql
    
    
    def main():
        no = int(input('編號: '))
        name = input('名字: ')
        loc = input('所在地: ')
        con = pymysql.connect(host='localhost', port=3306,
                              database='hrs', charset='utf8',
                              user='yourname', password='yourpass',
                              autocommit=True)
        try:
            with con.cursor() as cursor:
                result = cursor.execute(
                    'update tb_dept set dname=%s, dloc=%s where dno=%s',
                    (name, loc, no)
                )
            if result == 1:
                print('更新成功!')
        finally:
            con.close()
    
    
    if __name__ == '__main__':
        main()
    
  5. 查詢所有部門。

    import pymysql
    from pymysql.cursors import DictCursor
    
    
    def main():
        con = pymysql.connect(host='localhost', port=3306,
                              database='hrs', charset='utf8',
                              user='yourname', password='yourpass')
        try:
            with con.cursor(cursor=DictCursor) as cursor:
                cursor.execute('select dno as no, dname as name, dloc as loc from tb_dept')
                results = cursor.fetchall()
                print(results)
                print('編號\t名稱\t\t所在地')
                for dept in results:
                    print(dept['no'], end='\t')
                    print(dept['name'], end='\t')
                    print(dept['loc'])
        finally:
            con.close()
    
    if __name__ == '__main__':
        main()
    
  6. 分頁查詢員工信息婆跑。

    import pymysql
    from pymysql.cursors import DictCursor
    
    
    class Emp(object):
    
        def __init__(self, no, name, job, sal):
            self.no = no
            self.name = name
            self.job = job
            self.sal = sal
    
        def __str__(self):
            return f'\n編號:{self.no}\n姓名:{self.name}\n職位:{self.job}\n月薪:{self.sal}\n'
    
    
    def main():
        page = int(input('頁碼: '))
        size = int(input('大小: '))
        con = pymysql.connect(host='localhost', port=3306,
                              database='hrs', charset='utf8',
                              user='yourname', password='yourpass')
        try:
            with con.cursor() as cursor:
                cursor.execute(
                    'select eno as no, ename as name, job, sal from tb_emp limit %s,%s',
                    ((page - 1) * size, size)
                )
                for emp_tuple in cursor.fetchall():
                    emp = Emp(*emp_tuple)
                    print(emp)
        finally:
            con.close()
    
    
    if __name__ == '__main__':
        main()
    

NoSQL入門

NoSQL概述

如今此熬,大多數(shù)的計算機系統(tǒng)(包括服務器、PC洽蛀、移動設備等)都會產生龐大的數(shù)據量摹迷。其實疟赊,早在2012年的時候郊供,全世界每天產生的數(shù)據量就達到了2.5EB(艾字節(jié),1EB\approx10^{18}B)近哟。這些數(shù)據有很大一部分是由關系型數(shù)據庫來存儲和管理的驮审。 早在1970年,E.F.Codd發(fā)表了論述關系型數(shù)據庫的著名論文“A relational model of data for large shared data banks”吉执,這篇文章奠定了關系型數(shù)據庫的基礎并在接下來的數(shù)十年時間內產生了深遠的影響疯淫。實踐證明,關系型數(shù)據庫是實現(xiàn)數(shù)據持久化最為重要的方式戳玫,它也是大多數(shù)應用在選擇持久化方案時的首選技術熙掺。

NoSQL是一項全新的數(shù)據庫革命性運動,雖然它的歷史可以追溯到1998年咕宿,但是NoSQL真正深入人心并得到廣泛的應用是在進入大數(shù)據時候以后币绩,業(yè)界普遍認為NoSQL是更適合大數(shù)據存儲的技術方案蜡秽,這才使得NoSQL的發(fā)展達到了前所未有的高度。2012年《紐約時報》的一篇專欄中寫到缆镣,大數(shù)據時代已經降臨芽突,在商業(yè)、經濟及其他領域中董瞻,決策將不再基于經驗和直覺而是基于數(shù)據和分析而作出寞蚌。事實上,在天文學钠糊、氣象學挟秤、基因組學、生物學抄伍、社會學煞聪、互聯(lián)網搜索引擎、金融逝慧、醫(yī)療昔脯、社交網絡、電子商務等諸多領域笛臣,由于數(shù)據過于密集和龐大云稚,在數(shù)據的分析和處理上也遇到了前所未有的限制和阻礙,這一切都使得對大數(shù)據處理技術的研究被提升到了新的高度沈堡,也使得各種NoSQL的技術方案進入到了公眾的視野静陈。

NoSQL數(shù)據庫按照其存儲類型可以大致分為以下幾類:

類型 部分代表 特點
列族數(shù)據庫 HBase
Cassandra
Hypertable
顧名思義是按列存儲數(shù)據的。最大的特點是方便存儲結構化和半結構化數(shù)據诞丽,方便做數(shù)據壓縮鲸拥,對針對某一列或者某幾列的查詢有非常大的I/O優(yōu)勢,適合于批量數(shù)據處理和即時查詢僧免。
文檔數(shù)據庫 MongoDB
CouchDB
ElasticSearch
文檔數(shù)據庫一般用類JSON格式存儲數(shù)據刑赶,存儲的內容是文檔型的。這樣也就有機會對某些字段建立索引懂衩,實現(xiàn)關系數(shù)據庫的某些功能撞叨,但不提供對參照完整性和分布事務的支持。
KV數(shù)據庫 DynamoDB
Redis
LevelDB
可以通過key快速查詢到其value浊洞,有基于內存和基于磁盤兩種實現(xiàn)方案牵敷。
圖數(shù)據庫 Neo4J
FlockDB
JanusGraph
使用圖結構進行語義查詢的數(shù)據庫,它使用節(jié)點法希、邊和屬性來表示和存儲數(shù)據枷餐。圖數(shù)據庫從設計上,就可以簡單快速的檢索難以在關系系統(tǒng)中建模的復雜層次結構苫亦。
對象數(shù)據庫 db4o
Versant
通過類似面向對象語言的語法操作數(shù)據庫毛肋,通過對象的方式存取數(shù)據奕锌。

說明:想了解更多的NoSQL數(shù)據庫,可以訪問http://nosql-database.org/村生。

Redis概述

Redis是一種基于鍵值對的NoSQL數(shù)據庫惊暴,它提供了對多種數(shù)據類型(字符串、哈希趁桃、列表辽话、集合、有序集合卫病、位圖等)的支持油啤,能夠滿足很多應用場景的需求。Redis將數(shù)據放在內存中蟀苛,因此讀寫性能是非常驚人的益咬。與此同時,Redis也提供了持久化機制帜平,能夠將內存中的數(shù)據保存到硬盤上幽告,在發(fā)生意外狀況時數(shù)據也不會丟掉。此外裆甩,Redis還支持鍵過期冗锁、地理信息運算、發(fā)布訂閱嗤栓、事務冻河、管道、Lua腳本擴展等功能茉帅,總而言之叨叙,Redis的功能和性能都非常強大,如果項目中要實現(xiàn)高速緩存和消息隊列這樣的服務堪澎,直接交給Redis就可以了擂错。目前,國內外很多著名的企業(yè)和商業(yè)項目都使用了Redis全封,包括:Twitter马昙、Github、StackOverflow刹悴、新浪微博、百度攒暇、優(yōu)酷土豆土匀、美團、小米形用、唯品會等就轧。

Redis簡介

2008年证杭,一個名為Salvatore Sanfilippo的程序員為他開發(fā)的LLOOGG項目定制了專屬的數(shù)據庫(因為之前他無論怎樣優(yōu)化MySQL,系統(tǒng)性能已經無法再提升了)妒御,這項工作的成果就是Redis的初始版本解愤。后來他將Redis的代碼放到了全球最大的代碼托管平臺Github,從那以后乎莉,Redis引發(fā)了大量開發(fā)者的好評和關注送讲,繼而有數(shù)百人參與了Redis的開發(fā)和維護,這使得Redis的功能越來越強大和性能越來越好惋啃。

Redis是REmote DIctionary Server的縮寫哼鬓,它是一個用ANSI C編寫的高性能的key-value存儲系統(tǒng)蹋偏,與其他的key-value存儲系統(tǒng)相比扮匠,Redis有以下一些特點(也是優(yōu)點):

  • Redis的讀寫性能極高占贫,并且有豐富的特性(發(fā)布/訂閱往踢、事務木西、通知等)膊毁。
  • Redis支持數(shù)據的持久化(RDB和AOF兩種方式)耸棒,可以將內存中的數(shù)據保存在磁盤中即寒,重啟的時候可以再次加載進行使用惰帽。
  • Redis支持多種數(shù)據類型予跌,包括:string、hash善茎、list券册、set,zset垂涯、bitmap烁焙、hyperloglog等。
  • Redis支持主從復制(實現(xiàn)讀寫分析)以及哨兵模式(監(jiān)控master是否宕機并自動調整配置)耕赘。
  • Redis支持分布式集群骄蝇,可以很容易的通過水平擴展來提升系統(tǒng)的整體性能。
  • Redis基于TCP提供的可靠傳輸服務進行通信操骡,很多編程語言都提供了Redis客戶端支持九火。

Redis的應用場景

  1. 高速緩存 - 將不常變化但又經常被訪問的熱點數(shù)據放到Redis數(shù)據庫中,可以大大降低關系型數(shù)據庫的壓力册招,從而提升系統(tǒng)的響應性能岔激。
  2. 排行榜 - 很多網站都有排行榜功能,利用Redis中的列表和有序集合可以非常方便的構造各種排行榜系統(tǒng)是掰。
  3. 商品秒殺/投票點贊 - Redis提供了對計數(shù)操作的支持虑鼎,網站上常見的秒殺、點贊等功能都可以利用Redis的計數(shù)器通過+1或-1的操作來實現(xiàn),從而避免了使用關系型數(shù)據的update操作炫彩。
  4. 分布式鎖 - 利用Redis可以跨多臺服務器實現(xiàn)分布式鎖(類似于線程鎖匾七,但是能夠被多臺機器上的多個線程或進程共享)的功能,用于實現(xiàn)一個阻塞式操作江兢。
  5. 消息隊列 - 消息隊列和高速緩存一樣昨忆,是一個大型網站不可缺少的基礎服務,可以實現(xiàn)業(yè)務解耦和非實時業(yè)務削峰等特性杉允,這些我們都會在后面的項目中為大家展示邑贴。

Redis的安裝和配置

可以使用Linux系統(tǒng)的包管理工具(如yum)來安裝Redis,也可以通過在Redis的官方網站下載Redis的源代碼夺颤,解壓縮解歸檔之后通過make工具對源代碼進行構建并安裝痢缎,在更新這篇文檔時,Redis官方提供的最新穩(wěn)定版本是Redis 5.0.4世澜。

wget http://download.redis.io/releases/redis-5.0.4.tar.gz
gunzip redis-5.0.4.tar.gz
tar -xvf redis-5.0.4.tar
cd redis-5.0.4
make && make install

在redis源代碼目錄下有一個名為redis.conf的配置文件独旷,我們可以先查看一下該文件。

vim redis.conf

配置將Redis服務綁定到指定的IP地址和端口寥裂。
配置底層有多少個數(shù)據庫嵌洼。
配置Redis的持久化機制 - RDB。
配置Redis的持久化機制 - AOF封恰。
配置訪問Redis服務器的驗證口令麻养。
配置Redis的主從復制,通過主從復制可以實現(xiàn)讀寫分離诺舔。
配置慢查詢鳖昌。

上面這些內容就是Redis的基本配置,如果你對上面的內容感到困惑也沒有關系低飒,先把Redis用起來再回頭去推敲這些內容就行了许昨。如果想找一些參考書,《Redis開發(fā)與運維》是一本不錯的入門讀物褥赊,而《Redis實戰(zhàn)》是不錯的進階讀物糕档。

Redis的服務器和客戶端

接下來啟動Redis服務器,下面的方式將以默認的配置啟動Redis服務拌喉。

redis-server

如果希望修改Redis的配置(如端口速那、認證口令、持久化方式等)尿背,可以通過下面兩種方式端仰。

方式一:通過參數(shù)指定認證口令和AOF持久化方式。

redis-server --requirepass yourpass --appendonly yes

方式二:通過指定的配置文件來修改Redis的配置残家。

redis-server /root/redis-5.0.4/redis.conf

下面我們使用第一種方式來啟動Redis并將其置于后臺運行榆俺,將Redis產生的輸出重定向到名為redis.log的文件中。

redis-server --requirepass yourpass > redis.log &

可以通過ps或者netstat來檢查Redis服務器是否啟動成功坞淮。

ps -ef | grep redis-server
netstat -nap | grep redis-server

接下來茴晋,我們嘗試用Redis客戶端去連接服務器。

redis-cli
127.0.0.1:6379> auth yourpass
OK
127.0.0.1:6379> ping
PONG
127.0.0.1:6379>

Redis有著非常豐富的數(shù)據類型回窘,也有很多的命令來操作這些數(shù)據诺擅,具體的內容可以查看Redis命令參考,在這個網站上啡直,除了Redis的命令參考烁涌,還有Redis的詳細文檔,其中包括了通知酒觅、事務撮执、主從復制、持久化舷丹、哨兵抒钱、集群等內容。

[圖片上傳失敗...(image-77d91-1603933545777)]

說明:上面的插圖來自付磊和張益軍先生編著的《Redis開發(fā)與運維》一書颜凯。

127.0.0.1:6379> set username admin
OK
127.0.0.1:6379> get username
"admin"
127.0.0.1:6379> set password "123456" ex 300
OK
127.0.0.1:6379> get password
"123456"
127.0.0.1:6379> ttl username
(integer) -1
127.0.0.1:6379> ttl password
(integer) 286
127.0.0.1:6379> hset stu1 name hao
(integer) 0
127.0.0.1:6379> hset stu1 age 38
(integer) 1
127.0.0.1:6379> hset stu1 gender male
(integer) 1
127.0.0.1:6379> hgetall stu1
1) "name"
2) "hao"
3) "age"
4) "38"
5) "gender"
6) "male"
127.0.0.1:6379> hvals stu1
1) "hao"
2) "38"
3) "male"
127.0.0.1:6379> hmset stu2 name wang age 18 gender female tel 13566778899
OK
127.0.0.1:6379> hgetall stu2
1) "name"
2) "wang"
3) "age"
4) "18"
5) "gender"
6) "female"
7) "tel"
8) "13566778899"
127.0.0.1:6379> lpush nums 1 2 3 4 5
(integer) 5
127.0.0.1:6379> lrange nums 0 -1
1) "5"
2) "4"
3) "3"
4) "2"
5) "1"
127.0.0.1:6379> lpop nums
"5"
127.0.0.1:6379> lpop nums
"4"
127.0.0.1:6379> rpop nums
"1"
127.0.0.1:6379> rpop nums
"2"
127.0.0.1:6379> sadd fruits apple banana orange apple grape grape
(integer) 4
127.0.0.1:6379> scard fruits
(integer) 4
127.0.0.1:6379> smembers fruits
1) "grape"
2) "orange"
3) "banana"
4) "apple"
127.0.0.1:6379> sismember fruits apple
(integer) 1
127.0.0.1:6379> sismember fruits durian
(integer) 0
127.0.0.1:6379> sadd nums1 1 2 3 4 5
(integer) 5
127.0.0.1:6379> sadd nums2 2 4 6 8
(integer) 4
127.0.0.1:6379> sinter nums1 nums2
1) "2"
2) "4"
127.0.0.1:6379> sunion nums1 nums2
1) "1"
2) "2"
3) "3"
4) "4"
5) "5"
6) "6"
7) "8"
127.0.0.1:6379> sdiff nums1 nums2
1) "1"
2) "3"
3) "5"
127.0.0.1:6379> zadd topsinger 5234 zhangxy 1978 chenyx 2235 zhoujl 3520 xuezq
(integer) 4
127.0.0.1:6379> zrange topsinger 0 -1 withscores
1) "chenyx"
2) "1978"
3) "zhoujl"
4) "2235"
5) "xuezq"
6) "3520"
7) "zhangxy"
8) "5234"
127.0.0.1:6379> zrevrange topsinger 0 -1
1) "zhangxy"
2) "xuezq"
3) "zhoujl"
4) "chenyx"
127.0.0.1:6379> geoadd pois 116.39738549206541 39.90862689286386 tiananmen 116.27172936413572 39.99
135172904494 yiheyuan 117.27766503308104 40.65332064313784 gubeishuizhen
(integer) 3
127.0.0.1:6379> geodist pois tiananmen gubeishuizhen km
"111.5333"
127.0.0.1:6379> geodist pois tiananmen yiheyuan km
"14.1230"
127.0.0.1:6379> georadius pois 116.86499108288572 40.40149669363615 50 km withdist
1) 1) "gubeishuizhen"
   2) "44.7408"

在Python程序中使用Redis

可以使用pip安裝redis模塊谋币。redis模塊的核心是名為Redis的類,該類的對象代表一個Redis客戶端症概,通過該客戶端可以向Redis服務器發(fā)送命令并獲取執(zhí)行的結果蕾额。上面我們在Redis客戶端中使用的命令基本上就是Redis對象可以接收的消息,所以如果了解了Redis的命令就可以在Python中玩轉Redis彼城。

pip3 install redis
python3
>>> import redis
>>> client = redis.Redis(host='1.2.3.4', port=6379, password='yourpass')
>>> client.set('username', 'admin')
True
>>> client.hset('student', 'name', 'hao')
1
>>> client.hset('student', 'age', 38)
1
>>> client.keys('*')
[b'username', b'student']
>>> client.get('username')
b'admin'
>>> client.hgetall('student')
{b'name': b'hao', b'age': b'38'}

MongoDB概述

MongoDB簡介

MongoDB是2009年問世的一個面向文檔的數(shù)據庫管理系統(tǒng)诅蝶,由C++語言編寫,旨在為Web應用提供可擴展的高性能數(shù)據存儲解決方案募壕。雖然在劃分類別的時候后调炬,MongoDB被認為是NoSQL的產品,但是它更像一個介于關系數(shù)據庫和非關系數(shù)據庫之間的產品司抱,在非關系數(shù)據庫中它功能最豐富筐眷,最像關系數(shù)據庫。

MongoDB將數(shù)據存儲為一個文檔习柠,一個文檔由一系列的“鍵值對”組成匀谣,其文檔類似于JSON對象,但是MongoDB對JSON進行了二進制處理(能夠更快的定位key和value)资溃,因此其文檔的存儲格式稱為BSON武翎。關于JSON和BSON的差別大家可以看看MongoDB官方網站的文章《JSON and BSON》

目前溶锭,MongoDB已經提供了對Windows宝恶、MacOS、Linux、Solaris等多個平臺的支持垫毙,而且也提供了多種開發(fā)語言的驅動程序霹疫,Python當然是其中之一。

MongoDB的安裝和配置

可以從MongoDB的官方下載鏈接下載MongoDB综芥,官方為Windows系統(tǒng)提供了一個Installer程序丽蝎,而Linux和MacOS則提供了壓縮文件。下面簡單說一下Linux系統(tǒng)如何安裝和配置MongoDB膀藐。

wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-amazon-3.6.5.tgz
gunzip mongodb-linux-x86_64-amazon-3.6.5.tgz
mkdir mongodb-3.6.5
tar -xvf mongodb-linux-x86_64-amazon-3.6.5.tar --strip-components 1 -C mongodb-3.6.5/
export PATH=$PATH:~/mongodb-3.6.5/bin
mkdir -p /data/db
mongod --bind_ip 172.18.61.250

2018-06-03T18:03:28.232+0800 I CONTROL  [initandlisten] MongoDB starting : pid=1163 port=27017 dbpath=/data/db 64-bit host=iZwz97tbgo9lkabnat2lo8Z
2018-06-03T18:03:28.232+0800 I CONTROL  [initandlisten] db version v3.6.5
2018-06-03T18:03:28.232+0800 I CONTROL  [initandlisten] git version: a20ecd3e3a174162052ff99913bc2ca9a839d618
2018-06-03T18:03:28.232+0800 I CONTROL  [initandlisten] OpenSSL version: OpenSSL 1.0.0-fips29 Mar 2010
...
2018-06-03T18:03:28.945+0800 I NETWORK  [initandlisten] waiting for connections on port 27017

說明:上面的操作中屠阻,export命令是設置PATH環(huán)境變量,這樣可以在任意路徑下執(zhí)行mongod來啟動MongoDB服務器额各。MongoDB默認保存數(shù)據的路徑是/data/db目錄国觉,為此要提前創(chuàng)建該目錄。此外虾啦,在使用mongod啟動MongoDB服務器時麻诀,--bind_ip參數(shù)用來將服務綁定到指定的IP地址,也可以用--port參數(shù)來指定端口缸逃,默認端口為27017针饥。

MongoDB基本概念

我們通過與關系型數(shù)據庫進行對照的方式來說明MongoDB中的一些概念。

SQL MongoDB 解釋(SQL/MongoDB)
database database 數(shù)據庫/數(shù)據庫
table collection 二維表/集合
row document 記錄(行)/文檔
column field 字段(列)/域
index index 索引/索引
table joins --- 表連接/嵌套文檔
primary key primary key 主鍵/主鍵(_id字段)

通過Shell操作MongoDB

啟動服務器后可以使用交互式環(huán)境跟服務器通信需频,如下所示丁眼。

mongo --host 172.18.61.250

MongoDB shell version v3.6.5
connecting to: mongodb://172.18.61.250:27017/
  1. 查看、創(chuàng)建和刪除數(shù)據庫昭殉。

    > // 顯示所有數(shù)據庫
    > show dbs
    admin   0.000GB
    config  0.000GB
    local   0.000GB
    > // 創(chuàng)建并切換到school數(shù)據庫
    > use school
    switched to db school
    > // 刪除當前數(shù)據庫
    > db.dropDatabase()
    { "ok" : 1 }
    >
    
  2. 創(chuàng)建苞七、刪除和查看集合。

    > // 創(chuàng)建并切換到school數(shù)據庫
    > use school
    switched to db school
    > // 創(chuàng)建colleges集合
    > db.createCollection('colleges')
    { "ok" : 1 }
    > // 創(chuàng)建students集合
    > db.createCollection('students')
    { "ok" : 1 }
    > // 查看所有集合
    > show collections
    colleges
    students
    > // 刪除colleges集合
    > db.colleges.drop()
    true
    > 
    

    說明:在MongoDB中插入文檔時如果集合不存在會自動創(chuàng)建集合挪丢,所以也可以按照下面的方式通過創(chuàng)建文檔來創(chuàng)建集合蹂风。

  3. 文檔的CRUD操作。

    > // 向students集合插入文檔
    > db.students.insert({stuid: 1001, name: '駱昊', age: 38})
    WriteResult({ "nInserted" : 1 })
    > // 向students集合插入文檔
    > db.students.save({stuid: 1002, name: '王大錘', tel: '13012345678', gender: '男'})
    WriteResult({ "nInserted" : 1 })
    > // 查看所有文檔
    > db.students.find()
    { "_id" : ObjectId("5b13c72e006ad854460ee70b"), "stuid" : 1001, "name" : "駱昊", "age" : 38 }
    { "_id" : ObjectId("5b13c790006ad854460ee70c"), "stuid" : 1002, "name" : "王大錘", "tel" : "13012345678", "gender" : "男" }
    > // 更新stuid為1001的文檔
    > db.students.update({stuid: 1001}, {'$set': {tel: '13566778899', gender: '男'}})
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
    > // 插入或更新stuid為1003的文檔
    > db.students.update({stuid: 1003}, {'$set': {name: '白元芳', tel: '13022223333', gender: '男'}},  upsert=true)
    WriteResult({
            "nMatched" : 0,
            "nUpserted" : 1,
            "nModified" : 0,
            "_id" : ObjectId("5b13c92dd185894d7283efab")
    })
    > // 查詢所有文檔
    > db.students.find().pretty()
    {
            "_id" : ObjectId("5b13c72e006ad854460ee70b"),
            "stuid" : 1001,
            "name" : "駱昊",
            "age" : 38,
            "gender" : "男",
            "tel" : "13566778899"
    }
    {
            "_id" : ObjectId("5b13c790006ad854460ee70c"),
            "stuid" : 1002,
            "name" : "王大錘",
            "tel" : "13012345678",
            "gender" : "男"
    }
    {
            "_id" : ObjectId("5b13c92dd185894d7283efab"),
            "stuid" : 1003,
            "gender" : "男",
            "name" : "白元芳",
            "tel" : "13022223333"
    }
    > // 查詢stuid大于1001的文檔
    > db.students.find({stuid: {'$gt': 1001}}).pretty()
    {
            "_id" : ObjectId("5b13c790006ad854460ee70c"),
            "stuid" : 1002,
            "name" : "王大錘",
            "tel" : "13012345678",
            "gender" : "男"
    }
    {
            "_id" : ObjectId("5b13c92dd185894d7283efab"),
            "stuid" : 1003,
            "gender" : "男",
            "name" : "白元芳",
            "tel" : "13022223333"
    }
    > // 查詢stuid大于1001的文檔只顯示name和tel字段
    > db.students.find({stuid: {'$gt': 1001}}, {_id: 0, name: 1, tel: 1}).pretty()
    { "name" : "王大錘", "tel" : "13012345678" }
    { "name" : "白元芳", "tel" : "13022223333" }
    > // 查詢name為“駱昊”或者tel為“13022223333”的文檔
    > db.students.find({'$or': [{name: '駱昊'}, {tel: '13022223333'}]}, {_id: 0, name: 1, tel: 1}).pretty()
    { "name" : "駱昊", "tel" : "13566778899" }
    { "name" : "白元芳", "tel" : "13022223333" }
    > // 查詢學生文檔跳過第1條文檔只查1條文檔
    > db.students.find().skip(1).limit(1).pretty()
    {
            "_id" : ObjectId("5b13c790006ad854460ee70c"),
            "stuid" : 1002,
            "name" : "王大錘",
            "tel" : "13012345678",
            "gender" : "男"
    }
    > // 對查詢結果進行排序(1表示升序乾蓬,-1表示降序)
    > db.students.find({}, {_id: 0, stuid: 1, name: 1}).sort({stuid: -1})
    { "stuid" : 1003, "name" : "白元芳" }
    { "stuid" : 1002, "name" : "王大錘" }
    { "stuid" : 1001, "name" : "駱昊" }
    > // 在指定的一個或多個字段上創(chuàng)建索引
    > db.students.ensureIndex({name: 1})
    {
            "createdCollectionAutomatically" : false,
            "numIndexesBefore" : 1,
            "numIndexesAfter" : 2,
            "ok" : 1
    }
    > 
    

使用MongoDB可以非常方便的配置數(shù)據復制惠啄,通過冗余數(shù)據來實現(xiàn)數(shù)據的高可用以及災難恢復,也可以通過數(shù)據分片來應對數(shù)據量迅速增長的需求任内。關于MongoDB更多的操作可以查閱官方文檔 撵渡,同時推薦大家閱讀Kristina Chodorow寫的《MongoDB權威指南》

在Python程序中操作MongoDB

可以通過pip安裝pymongo來實現(xiàn)對MongoDB的操作死嗦。

pip3 install pymongo
python3
>>> from pymongo import MongoClient
>>> client = MongoClient('mongodb://127.0.0.1:27017') 
>>> db = client.school
>>> for student in db.students.find():
...     print('學號:', student['stuid'])
...     print('姓名:', student['name'])
...     print('電話:', student['tel'])
... 
學號: 1001.0
姓名: 駱昊
電話: 13566778899
學號: 1002.0
姓名: 王大錘
電話: 13012345678
學號: 1003.0
姓名: 白元芳
電話: 13022223333
>>> db.students.find().count()
3
>>> db.students.remove()
{'n': 3, 'ok': 1.0}
>>> db.students.find().count()
0
>>> coll = db.students
>>> from pymongo import ASCENDING
>>> coll.create_index([('name', ASCENDING)], unique=True)
'name_1'
>>> coll.insert_one({'stuid': int(1001), 'name': '駱昊', 'gender': True})
<pymongo.results.InsertOneResult object at 0x1050cc6c8>
>>> coll.insert_many([{'stuid': int(1002), 'name': '王大錘', 'gender': False}, {'stuid': int(1003), 'name': '白元芳', 'gender': True}])
<pymongo.results.InsertManyResult object at 0x1050cc8c8>
>>> for student in coll.find({'gender': True}):
...     print('學號:', student['stuid'])
...     print('姓名:', student['name'])
...     print('性別:', '男' if student['gender'] else '女')
... 
學號: 1001
姓名: 駱昊
性別: 男
學號: 1003
姓名: 白元芳
性別: 男
>>> 

關于PyMongo更多的知識可以通過它的官方文檔進行了解趋距,也可以使用MongoEngine這樣的庫來簡化Python程序對MongoDB的操作,除此之外越除,還有以異步I/O方式訪問MongoDB的三方庫motor都是不錯的選擇节腐。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末外盯,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子翼雀,更是在濱河造成了極大的恐慌饱苟,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件锅纺,死亡現(xiàn)場離奇詭異掷空,居然都是意外死亡肋殴,警方通過查閱死者的電腦和手機囤锉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來护锤,“玉大人官地,你說我怎么就攤上這事±优常” “怎么了驱入?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長氯析。 經常有香客問我亏较,道長,這世上最難降的妖魔是什么掩缓? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任雪情,我火速辦了婚禮,結果婚禮上你辣,老公的妹妹穿的比我還像新娘巡通。我一直安慰自己,他們只是感情好舍哄,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布宴凉。 她就那樣靜靜地躺著,像睡著了一般表悬。 火紅的嫁衣襯著肌膚如雪弥锄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天蟆沫,我揣著相機與錄音籽暇,去河邊找鬼。 笑死饥追,一個胖子當著我的面吹牛图仓,可吹牛的內容都是我干的。 我是一名探鬼主播但绕,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼救崔,長吁一口氣:“原來是場噩夢啊……” “哼惶看!你這毒婦竟也來了?” 一聲冷哼從身側響起六孵,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤纬黎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后劫窒,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體本今,經...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年主巍,在試婚紗的時候發(fā)現(xiàn)自己被綠了冠息。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡孕索,死狀恐怖逛艰,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情搞旭,我是刑警寧澤散怖,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站肄渗,受9級特大地震影響镇眷,放射性物質發(fā)生泄漏。R本人自食惡果不足惜翎嫡,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一欠动、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧钝的,春花似錦翁垂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至碗脊,卻和暖如春啼肩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背衙伶。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工祈坠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人矢劲。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓赦拘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親芬沉。 傳聞我的和親對象是個殘疾皇子躺同,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345