day10-python併發編程之多線程協程及MySQL

 

第1章 python併發編程之多線程

1.1 死鎖現象與遞歸鎖

1.1.1 死鎖概念

進程也有死鎖與遞歸鎖,在進程那裏忘記說了,放到這裏一切說了額html

 

所謂死鎖: 是指兩個或兩個以上的進程或線程在執行過程當中,因爭奪資源而形成的一種互相等待的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程,以下就是死鎖python

1.1.2 博客實例

from threading import Thread,Lock

import time

mutexA=Lock()

mutexB=Lock()

 

class MyThread(Thread):

    def run(self):

        self.func1()

        self.func2()

    def func1(self):

        mutexA.acquire()

        print('\033[41m%s 拿到A鎖\033[0m' %self.name)

 

        mutexB.acquire()

        print('\033[42m%s 拿到B鎖\033[0m' %self.name)

        mutexB.release()

 

        mutexA.release()

 

    def func2(self):

        mutexB.acquire()

        print('\033[43m%s 拿到B鎖\033[0m' %self.name)

        time.sleep(2)

 

        mutexA.acquire()

        print('\033[44m%s 拿到A鎖\033[0m' %self.name)

        mutexA.release()

 

        mutexB.release()

 

if __name__ == '__main__':

    for i in range(10):

        t=MyThread()

        t.start()

 

'''

Thread-1 拿到A鎖

Thread-1 拿到B鎖

Thread-1 拿到B鎖

Thread-2 拿到A鎖

而後就卡住,死鎖了

'''

 
View Code

1.1.3 課堂實例

from threading import Thread,Lock,RLock

import time

 

# mutexA=Lock()

# mutexB=Lock()

 

mutexA=mutexB=RLock()

 

class MyThread(Thread):

    def run(self):

        self.f1()

        self.f2()

 

    def f1(self):

        mutexA.acquire()

        print('%s 拿到了A鎖' %self.name)

 

        mutexB.acquire()

        print('%s 拿到了B鎖' % self.name)

        mutexB.release() #1

 

        mutexA.release() #0

 

    def f2(self):

        mutexB.acquire()

        print('%s 拿到了B鎖' % self.name)

        time.sleep(0.1)

 

        mutexA.acquire()

        print('%s 拿到了A鎖' % self.name)

        mutexA.release()

 

        mutexB.release()

 

 

 

if __name__ == '__main__':

    for i in range(10):

        t=MyThread()

        t.start()
View Code

1.1.4 死鎖解決方法

解決方法,遞歸鎖,在Python中爲了支持在同一線程中屢次請求同一資源,python提供了可重入鎖RLock。mysql

 

這個RLock內部維護着一個Lock和一個counter變量,counter記錄了acquire的次數,從而使得資源能夠被屢次require。直到一個線程全部的acquire都被release,其餘的線程才能得到資源。上面的例子若是使用RLock代替Lock,則不會發生死鎖:linux

mutexA=mutexB=threading.RLock() #一個線程拿到鎖,counter加1,該線程內又碰到加鎖的狀況,則counter繼續加1,這期間全部其餘線程都只能等待,等待該線程釋放全部鎖,即counter遞減到0爲止

1.2 信號量Semaphore

同進程的同樣git

 

Semaphore管理一個內置的計數器,程序員

每當調用acquire()時內置計數器-1;github

調用release() 時內置計數器+1;redis

計數器不能小於0;當計數器爲0時,acquire()將阻塞線程直到其餘線程調用release()。sql

 

實例:(同時只有5個線程能夠得到semaphore,便可以限制最大鏈接數爲5):mongodb

 

1.2.1 博客實例

from threading import Thread,Semaphore

import threading

import time

# def func():

#     if sm.acquire():

#         print (threading.currentThread().getName() + ' get semaphore')

#         time.sleep(2)

#         sm.release()

def func():

    sm.acquire()

    print('%s get sm' %threading.current_thread().getName())

    time.sleep(3)

    sm.release()

if __name__ == '__main__':

    sm=Semaphore(5)

    for i in range(23):

        t=Thread(target=func)

        t.start()

 
View Code

1.2.2 課堂實例

from threading import Thread,Semaphore,current_thread

import time,random

 

sm=Semaphore(5)

 

def task():

    with sm:

        print('%s is laing' %current_thread().getName())

        time.sleep(random.randint(1,3))

 

if __name__ == '__main__':

    for i in range(20):

        t=Thread(target=task)

        t.start()
View Code

與進程池是徹底不一樣的概念,進程池Pool(4),最大隻能產生4個進程,並且從頭至尾都只是這四個進程,不會產生新的,而信號量是產生一堆線程/進程 

互斥鎖與信號量推薦博客:http://url.cn/5DMsS9r

1.3 Event事件

同進程的同樣

 

線程的一個關鍵特性是每一個線程都是獨立運行且狀態不可預測。若是程序中的其 他線程須要經過判斷某個線程的狀態來肯定本身下一步的操做,這時線程同步問題就會變得很是棘手。爲了解決這些問題,咱們須要使用threading庫中的Event對象。 對象包含一個可由線程設置的信號標誌,它容許線程等待某些事件的發生。在 初始狀況下,Event對象中的信號標誌被設置爲假。若是有線程等待一個Event對象, 而這個Event對象的標誌爲假,那麼這個線程將會被一直阻塞直至該標誌爲真。一個線程若是將一個Event對象的信號標誌設置爲真,它將喚醒全部等待這個Event對象的線程。若是一個線程等待一個已經被設置爲真的Event對象,那麼它將忽略這個事件, 繼續執行

1.3.1 event參數

event.isSet():返回event的狀態值;

event.wait():若是 event.isSet()==False將阻塞線程;

event.set(): 設置event的狀態值爲True,全部阻塞池的線程激活進入就緒狀態, 等待操做系統調度;

event.clear():恢復event的狀態值爲False。

1.3.2 博客實例

 

 

例如,有多個工做線程嘗試連接MySQL,咱們想要在連接前確保MySQL服務正常才讓那些工做線程去鏈接MySQL服務器,若是鏈接不成功,都會去嘗試從新鏈接。那麼咱們就能夠採用threading.Event機制來協調各個工做線程的鏈接操做

from threading import Thread,Event

import threading

import time,random

def conn_mysql():

    count=1

    while not event.is_set():

        if count > 3:

            raise TimeoutError('連接超時')

        print('<%s>第%s次嘗試連接' % (threading.current_thread().getName(), count))

        event.wait(0.5)

        count+=1

    print('<%s>連接成功' %threading.current_thread().getName())

 

 

def check_mysql():

    print('\033[45m[%s]正在檢查mysql\033[0m' % threading.current_thread().getName())

    time.sleep(random.randint(2,4))

    event.set()

if __name__ == '__main__':

    event=Event()

    conn1=Thread(target=conn_mysql)

    conn2=Thread(target=conn_mysql)

    check=Thread(target=check_mysql)

 

    conn1.start()

    conn2.start()

    check.start()
View Code

1.3.3 課堂實例

from threading import Thread,Event,current_thread

import time

 

event=Event()

 

def check():

    print('checking MySQL...')

    time.sleep(5)

    event.set()

 

def conn():

    count=1

    while not event.is_set():

        if count > 3:

            raise TimeoutError('超時')

        print('%s try to connect MySQL time %s' %(current_thread().getName(),count))

        event.wait(2)

        count+=1

 

    print('%s connected MySQL' %current_thread().getName())

 

if __name__ == '__main__':

    t1=Thread(target=check)

    t2=Thread(target=conn)

    t3=Thread(target=conn)

    t4=Thread(target=conn)

 

 

    t1.start()

    t2.start()

    t3.start()

    t4.start()
View Code

1.4 定時器

定時器,指定n秒後執行某操做

1.4.1 實例

from threading import Timer

 

 

def hello():

    print("hello, world")

 

t = Timer(1, hello)

t.start()  # after 1 seconds, "hello, world" will be printed
View Code

1.4.2 驗證碼定時器實例

from threading import Timer

import random,time

 

class Code:

    def __init__(self):

        self.make_cache()

 

    def make_cache(self,interval=5):

        self.cache=self.make_code()

        print(self.cache)

        self.t=Timer(interval,self.make_cache)

        self.t.start()

 

    def make_code(self,n=4):

        res=''

        for i in range(n):

            s1=str(random.randint(0,9))

            s2=chr(random.randint(65,90))

            res+=random.choice([s1,s2])

        return res

 

    def check(self):

        while True:

            inp=input('>>: ').strip()

            if inp.upper() ==  self.cache:

                print('驗證成功',end='\n')

                self.t.cancel()

                break

 

 

if __name__ == '__main__':

    obj=Code()

    obj.check()

 
驗證碼定時器

1.5 線程queue隊列

queue隊列 :使用import queue,用法與進程Queue同樣

queue is especially useful in threaded programming when information must be exchanged safely between multiple threads.

1.5.1 先進先出class queue.Queue(maxsize=0)

import queue

 

q=queue.Queue()

q.put('first')

q.put('second')

q.put('third')

 

print(q.get())

print(q.get())

print(q.get())

'''

結果(先進先出):

first

second

third

'''
View Code

1.5.2 堆棧(後進先出)class queue.LifoQueue(maxsize=0) #last in fisrt out

import queue

 

q=queue.LifoQueue()

q.put('first')

q.put('second')

q.put('third')

 

print(q.get())

print(q.get())

print(q.get())

'''

結果(後進先出):

third

second

first

'''
View Code

1.5.3 優先級隊列class queue.PriorityQueue(maxsize=0) #存儲數據時可設置優先級的隊列

import queue

 

q=queue.PriorityQueue()

#put進入一個元組,元組的第一個元素是優先級(一般是數字,也能夠是非數字之間的比較),數字越小優先級越高

q.put((20,'a'))

q.put((10,'b'))

q.put((30,'c'))

 

print(q.get())

print(q.get())

print(q.get())

'''

結果(數字越小優先級越高,優先級高的優先出隊):

(10, 'b')

(20, 'a')

(30, 'c')

'''
View Code 

1.5.4 課堂講解實例

import queue

 

# q=queue.Queue(3) #隊列:先進先出

#

# q.put(1)

# q.put(2)

# q.put(3)

# # q.put(4)

# # q.put_nowait(4)

# # q.put(4,block=False)

# q.put(4,block=True,timeout=3)

#

#

# # print(q.get())

# # print(q.get())

# # print(q.get())

 

# q=queue.LifoQueue(3) #堆棧:後進先出

# q.put(1)

# q.put(2)

# q.put(3)

#

# print(q.get())

# print(q.get())

# print(q.get())

 

q=queue.PriorityQueue(3) #優先級隊列

q.put((10,'a'))

q.put((-3,'b'))

q.put((100,'c'))

 

print(q.get())

print(q.get())

print(q.get())
View Code

1.6 Python標準模塊--concurrent.futures進程池與線程池

1.6.1 提交任務的兩種方式:

#同步調用:提交完任務後,就在原地等待,等待任務執行完畢,拿到任務的返回值,才能繼續下一行代碼,致使程序串行執行

#異步調用+回調機制:提交完任務後,不在原地等待,任務一旦執行完畢就會觸發回調函數的執行, 程序是併發執行

1.6.2 進程的執行狀態:

#阻塞

#非阻塞

1.6.3 concurrent.futures

#1 介紹

concurrent.futures模塊提供了高度封裝的異步調用接口

ThreadPoolExecutor:線程池,提供異步調用

ProcessPoolExecutor: 進程池,提供異步調用

Both implement the same interface, which is defined by the abstract Executor class.

 

#2 基本方法

#submit(fn, *args, **kwargs)

異步提交任務

 

#map(func, *iterables, timeout=None, chunksize=1)

取代for循環submit的操做

 

#shutdown(wait=True)

至關於進程池的pool.close()+pool.join()操做

wait=True,等待池內全部任務執行完畢回收完資源後才繼續

wait=False,當即返回,並不會等待池內的任務執行完畢

但無論wait參數爲什麼值,整個程序都會等到全部任務執行完畢

submit和map必須在shutdown以前

 

#result(timeout=None)

取得結果

 

#add_done_callback(fn)

回調函數
View Code

1.6.4 相關實例

################ProcessPoolExecutor#############

#介紹

The ProcessPoolExecutor class is an Executor subclass that uses a pool of processes to execute calls asynchronously. ProcessPoolExecutor uses the multiprocessing module, which allows it to side-step the Global Interpreter Lock but also means that only picklable objects can be executed and returned.

 

class concurrent.futures.ProcessPoolExecutor(max_workers=None, mp_context=None)

An Executor subclass that executes calls asynchronously using a pool of at most max_workers processes. If max_workers is None or not given, it will default to the number of processors on the machine. If max_workers is lower or equal to 0, then a ValueError will be raised.

 

 

#用法

from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor

 

import os,time,random

def task(n):

    print('%s is runing' %os.getpid())

    time.sleep(random.randint(1,3))

    return n**2

 

if __name__ == '__main__':

 

    executor=ProcessPoolExecutor(max_workers=3)

 

    futures=[]

    for i in range(11):

        future=executor.submit(task,i)

        futures.append(future)

    executor.shutdown(True)

    print('+++>')

    for future in futures:

        print(future.result())
ProcessPoolExecutor
#######################ThreadPoolExecutor######################

#介紹

ThreadPoolExecutor is an Executor subclass that uses a pool of threads to execute calls asynchronously.

class concurrent.futures.ThreadPoolExecutor(max_workers=None, thread_name_prefix='')

An Executor subclass that uses a pool of at most max_workers threads to execute calls asynchronously.

 

Changed in version 3.5: If max_workers is None or not given, it will default to the number of processors on the machine, multiplied by 5, assuming that ThreadPoolExecutor is often used to overlap I/O instead of CPU work and the number of workers should be higher than the number of workers for ProcessPoolExecutor.

 

New in version 3.6: The thread_name_prefix argument was added to allow users to control the threading.Thread names for worker threads created by the pool for easier debugging.

 

#用法

與ProcessPoolExecutor相同
ThreadPoolExecutor
###########################map的用法########################

from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor

 

import os,time,random

def task(n):

    print('%s is runing' %os.getpid())

    time.sleep(random.randint(1,3))

    return n**2

 

if __name__ == '__main__':

 

    executor=ThreadPoolExecutor(max_workers=3)

 

    # for i in range(11):

    #     future=executor.submit(task,i)

 

    executor.map(task,range(1,12)) #map取代了for+submit
map的用法
#######################回調函數####################

from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor

from multiprocessing import Pool

import requests

import json

import os

 

def get_page(url):

    print('<進程%s> get %s' %(os.getpid(),url))

    respone=requests.get(url)

    if respone.status_code == 200:

        return {'url':url,'text':respone.text}

 

def parse_page(res):

    res=res.result()

    print('<進程%s> parse %s' %(os.getpid(),res['url']))

    parse_res='url:<%s> size:[%s]\n' %(res['url'],len(res['text']))

    with open('db.txt','a') as f:

        f.write(parse_res)

 

 

if __name__ == '__main__':

    urls=[

        'https://www.baidu.com',

        'https://www.python.org',

        'https://www.openstack.org',

        'https://help.github.com/',

        'http://www.sina.com.cn/'

    ]

 

    # p=Pool(3)

    # for url in urls:

    #     p.apply_async(get_page,args=(url,),callback=pasrse_page)

    # p.close()

    # p.join()

 

    p=ProcessPoolExecutor(3)

    for url in urls:

        p.submit(get_page,url).add_done_callback(parse_page) #parse_page拿到的是一個future對象obj,須要用obj.result()拿到結果
回調函數

1.6.5 進程池ThreadPoolExecutor

# #同步調用示例:

# # from multiprocessing import Pool

# from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor

# import time,random,os

#

# def task(n):

#     print('%s is ruuning' %os.getpid())

#     time.sleep(random.randint(1,3))

#     return n**2

#

# def handle(res):

#     print('handle res %s' %res)

#

# if __name__ == '__main__':

#     #同步調用

#     pool=ProcessPoolExecutor(2)

#

#     for i in range(5):

#         res=pool.submit(task,i).result()

#         # print(res)

#         handle(res)

#

#     pool.shutdown(wait=True)

#     # pool.submit(task,33333)

#     print('主')

 

 

#異步調用示例:

# from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor

# import time,random,os

#

# def task(n):

#     print('%s is ruuning' %os.getpid())

#     time.sleep(random.randint(1,3))

#     # res=n**2

#     # handle(res)

#     return n**2

#

# def handle(res):

#     res=res.result()

#     print('handle res %s' %res)

#

# if __name__ == '__main__':

#     #異步調用

#     pool=ProcessPoolExecutor(2)

#

#     for i in range(5):

#         obj=pool.submit(task,i)

#         obj.add_done_callback(handle) #handle(obj)

#

#     pool.shutdown(wait=True)

#     print('主')
View Code

1.6.6 線程池ProcessPoolExecutor

#線程池

from concurrent.futures import ThreadPoolExecutor

from threading import current_thread

import requests

import time

 

def get(url):

    print('%s GET %s' %(current_thread().getName(),url))

    response=requests.get(url)

    time.sleep(2)

    if response.status_code == 200:

        return {'url':url,'content':response.text}

 

def parse(res):

    res=res.result()

    print('parse:[%s] res:[%s]' %(res['url'],len(res['content'])))

 

 

if __name__ == '__main__':

    pool=ThreadPoolExecutor(2)

 

    urls=[

        'https://www.baidu.com',

        'https://www.python.org',

        'https://www.openstack.org',

        'https://www.openstack.org',

        'https://www.openstack.org',

        'https://www.openstack.org',

        'https://www.openstack.org',

        'https://www.openstack.org',

        'https://www.openstack.org',

        'https://www.openstack.org',

        'https://www.openstack.org',

        'https://www.openstack.org',

    ]

    for url in urls:

        pool.submit(get,url).add_done_callback(parse)

 

    pool.shutdown(wait=True)
View Code

第2章 python併發編程之協程

2.1 引子

本節的主題是基於單線程來實現併發,即只用一個主線程(很明顯可利用的cpu只有一個)狀況下實現併發,爲此咱們須要先回顧下併發的本質:切換+保存狀態

    cpu正在運行一個任務,會在兩種狀況下切走去執行其餘的任務(切換由操做系統強制控制),一種狀況是該任務發生了阻塞,另一種狀況是該任務計算的時間過長或有一個優先級更高的程序替代了它

 

 

ps:在介紹進程理論時,說起進程的三種執行狀態,而線程纔是執行單位,因此也能夠將上圖理解爲線程的三種狀態

    一:其中第二種狀況並不能提高效率,只是爲了讓cpu可以雨露均沾,實現看起來全部任務都被「同時」執行的效果,若是多個任務都是純計算的,這種切換反而會下降效率。爲此咱們能夠基於yield來驗證。yield自己就是一種在單線程下能夠保存任務運行狀態的方法,咱們來簡單複習一下:

#1 yiled能夠保存狀態,yield的狀態保存與操做系統的保存線程狀態很像,可是yield是代碼級別控制的,更輕量級

#2 send能夠把一個函數的結果傳給另一個函數,以此實現單線程內程序之間的切換 

2.1.1 單純地切換反而會下降運行效率

##############單純地切換反而會下降運行效率######################

#串行執行

import time

def consumer(res):

    '''任務1:接收數據,處理數據'''

    pass

 

def producer():

    '''任務2:生產數據'''

    res=[]

    for i in range(10000000):

        res.append(i)

    return res

 

start=time.time()

#串行執行

res=producer()

consumer(res) #寫成consumer(producer())會下降執行效率

stop=time.time()

print(stop-start) #1.5536692142486572

 

 

 

#基於yield併發執行

import time

def consumer():

    '''任務1:接收數據,處理數據'''

    while True:

        x=yield

 

def producer():

    '''任務2:生產數據'''

    g=consumer()

    next(g)

    for i in range(10000000):

        g.send(i)

 

start=time.time()

#基於yield保存狀態,實現兩個任務直接來回切換,即併發的效果

#PS:若是每一個任務中都加上打印,那麼明顯地看到兩個任務的打印是你一次我一次,即併發執行的.

producer()

 

stop=time.time()

print(stop-start) #2.0272178649902344
單純地切換反而會下降運行效率

2.1.2 yield並不能實現遇到io切換

二:第一種狀況的切換。在任務一遇到io狀況下,切到任務二去執行,這樣就能夠利用任務一阻塞的時間完成任務二的計算,效率的提高就在於此。

################yield並不能實現遇到io切換####################

import time

def consumer():

    '''任務1:接收數據,處理數據'''

    while True:

        x=yield

 

def producer():

    '''任務2:生產數據'''

    g=consumer()

    next(g)

    for i in range(10000000):

        g.send(i)

        time.sleep(2)

 

start=time.time()

producer() #併發執行,可是任務producer遇到io就會阻塞住,並不會切到該線程內的其餘任務去執行

 

stop=time.time()

print(stop-start)
yield並不能實現遇到io切換

對於單線程下,咱們不可避免程序中出現io操做,但若是咱們能在本身的程序中(即用戶程序級別,而非操做系統級別)控制單線程下的多個任務能在一個任務遇到io阻塞時就切換到另一個任務去計算,這樣就保證了該線程可以最大限度地處於就緒態,即隨時均可以被cpu執行的狀態,至關於咱們在用戶程序級別將本身的io操做最大限度地隱藏起來,從而能夠迷惑操做系統,讓其看到:該線程好像是一直在計算,io比較少,從而更多的將cpu的執行權限分配給咱們的線程。

 

    協程的本質就是在單線程下,由用戶本身控制一個任務遇到io阻塞了就切換另一個任務去執行,以此來提高效率。爲了實現它,咱們須要找尋一種能夠同時知足如下條件的解決方案:

#1. 能夠控制多個任務之間的切換,切換以前將任務的狀態保存下來,以便從新運行時,能夠基於暫停的位置繼續執行。
#2. 做爲1的補充:能夠檢測io操做,在遇到io操做的狀況下才發生切換

2.1.3 課堂實例

# 單純地切換反而會下降運行效率

#串行執行

# import time

# def consumer(res):

#     '''任務1:接收數據,處理數據'''

#     pass

#

# def producer():

#     '''任務2:生產數據'''

#     res=[]

#     for i in range(10000000):

#         res.append(i)

#     return res

#

# start=time.time()

# #串行執行

# res=producer()

# consumer(res)

# stop=time.time()

# print(stop-start)

 

 

 

#基於yield併發執行

import time

def consumer():

    '''任務1:接收數據,處理數據'''

    while True:

        print('consumer')

        x=yield

        time.sleep(100)

 

def producer():

    '''任務2:生產數據'''

    g=consumer()

    next(g)

    for i in range(10000000):

        print('producer')

        g.send(i)

 

start=time.time()

#基於yield保存狀態,實現兩個任務直接來回切換,即併發的效果

#PS:若是每一個任務中都加上打印,那麼明顯地看到兩個任務的打印是你一次我一次,即併發執行的.

producer()

 

stop=time.time()

print(stop-start) #
View Code

2.2 協程介紹

2.2.1 協程概念

協程:是單線程下的併發,又稱微線程,纖程。英文名Coroutine。一句話說明什麼是線程:協程是一種用戶態的輕量級線程,即協程是由用戶程序本身控制調度的。

2.2.2 強調

須要強調的是:

#1. python的線程屬於內核級別的,即由操做系統控制調度(如單線程遇到io或執行時間過長就會被迫交出cpu執行權限,切換其餘線程運行)

#2. 單線程內開啓協程,一旦遇到io,就會從應用程序級別(而非操做系統)控制切換,以此來提高效率(!!!非io操做的切換與效率無關)

對比操做系統控制線程的切換,用戶在單線程內控制協程的切換

2.2.3 優缺點

優勢以下:

#1. 協程的切換開銷更小,屬於程序級別的切換,操做系統徹底感知不到,於是更加輕量級

#2. 單線程內就能夠實現併發的效果,最大限度地利用cpu

缺點以下:

#1. 協程的本質是單線程下,沒法利用多核,能夠是一個程序開啓多個進程,每一個進程內開啓多個線程,每一個線程內開啓協程

#2. 協程指的是單個線程,於是一旦協程出現阻塞,將會阻塞整個線程

2.2.4 總結

總結協程特色:

  1. 必須在只有一個單線程裏實現併發
  2. 修改共享數據不需加鎖
  3. 用戶程序裏本身保存多個控制流的上下文棧
  4. 附加:一個協程遇到IO操做自動切換到其它協程(如何實現檢測IO,yield、greenlet都沒法實現,就用到了gevent模塊(select機制))

 

2.3 Greenlet

2.3.1 博客實例

若是咱們在單個線程內有20個任務,要想實如今多個任務之間切換,使用yield生成器的方式過於麻煩(須要先獲得初始化一次的生成器,而後再調用send。。。很是麻煩),而使用greenlet模塊能夠很是簡單地實現這20個任務直接的切換

#安裝

pip3 install greenlet
from greenlet import greenlet

 

def eat(name):

    print('%s eat 1' %name)

    g2.switch('egon')

    print('%s eat 2' %name)

    g2.switch()

def play(name):

    print('%s play 1' %name)

    g1.switch()

    print('%s play 2' %name)

 

g1=greenlet(eat)

g2=greenlet(play)

 

g1.switch('egon')#能夠在第一次switch時傳入參數,之後都不須要
View Code

單純的切換(在沒有io的狀況下或者沒有重複開闢內存空間的操做),反而會下降程序的執行速度

#順序執行

import time

def f1():

    res=1

    for i in range(100000000):

        res+=i

 

def f2():

    res=1

    for i in range(100000000):

        res*=i

 

start=time.time()

f1()

f2()

stop=time.time()

print('run time is %s' %(stop-start)) #10.985628366470337

 

#切換

from greenlet import greenlet

import time

def f1():

    res=1

    for i in range(100000000):

        res+=i

        g2.switch()

 

def f2():

    res=1

    for i in range(100000000):

        res*=i

        g1.switch()

 

start=time.time()

g1=greenlet(f1)

g2=greenlet(f2)

g1.switch()

stop=time.time()

print('run time is %s' %(stop-start)) # 52.763017892837524
View Code

greenlet只是提供了一種比generator更加便捷的切換方式,當切到一個任務執行時若是遇到io,那就原地阻塞,仍然是沒有解決遇到IO自動切換來提高效率的問題。

 單線程裏的這20個任務的代碼一般會既有計算操做又有阻塞操做,咱們徹底能夠在執行任務1時遇到阻塞,就利用阻塞的時間去執行任務2。。。。如此,才能提升效率,這就用到了Gevent模塊。

2.3.2 課堂實例

#pip3 install greenlet

from greenlet import greenlet

import time

 

def eat(name):

    print('%s eat 1' %name)

    time.sleep(1000)

    g2.switch('egon')

    print('%s eat 2' %name)

    g2.switch()

 

 

def play(name):

    print('%s play 1' % name)

    g1.switch()

    print('%s play 2' % name)

 

g1=greenlet(eat)

g2=greenlet(play)

 

g1.switch('egon')
View Code

2.4 Gevent介紹

#安裝

pip3 install gevent

Gevent 是一個第三方庫,能夠輕鬆經過gevent實現併發同步或異步編程,在gevent中用到的主要模式是Greenlet, 它是以C擴展模塊形式接入Python的輕量級協程。 Greenlet所有運行在主程序操做系統進程的內部,但它們被協做式地調度。

2.4.1 用法

#用法

g1=gevent.spawn(func,1,,2,3,x=4,y=5)建立一個協程對象g1,spawn括號內第一個參數是函數名,如eat,後面能夠有多個參數,能夠是位置實參或關鍵字實參,都是傳給函數eat的

g2=gevent.spawn(func2)

g1.join() #等待g1結束

g2.join() #等待g2結束

#或者上述兩步合做一步:gevent.joinall([g1,g2])

g1.value#拿到func1的返回值

2.4.2 遇到IO阻塞時會自動切換任務

import gevent

def eat(name):

    print('%s eat 1' %name)

    gevent.sleep(2)

    print('%s eat 2' %name)

 

def play(name):

    print('%s play 1' %name)

    gevent.sleep(1)

    print('%s play 2' %name)

 

 

g1=gevent.spawn(eat,'egon')

g2=gevent.spawn(play,name='egon')

g1.join()

g2.join()

#或者gevent.joinall([g1,g2])

print('')
View Code

上例gevent.sleep(2)模擬的是gevent能夠識別的io阻塞,

而time.sleep(2)或其餘的阻塞,gevent是不能直接識別的須要用下面一行代碼,打補丁,就能夠識別了

from gevent import monkey;monkey.patch_all()必須放到被打補丁者的前面,如time,socket模塊以前

或者咱們乾脆記憶成:要用gevent,須要將from gevent import monkey;monkey.patch_all()放到文件的開頭。

from gevent import monkey;monkey.patch_all()

 

import gevent

import time

def eat():

    print('eat food 1')

    time.sleep(2)

    print('eat food 2')

 

def play():

    print('play 1')

    time.sleep(1)

    print('play 2')

 

g1=gevent.spawn(eat)

g2=gevent.spawn(play_phone)

gevent.joinall([g1,g2])

print('')
View Code

咱們能夠用threading.current_thread().getName()來查看每一個g1和g2,查看的結果爲DummyThread-n,即假線程。

2.4.3 課堂實例

from gevent import monkey;monkey.patch_all()

import gevent

import time

 

def eat(name):

    print('%s eat 1' %name)

    # gevent.sleep(3)

    time.sleep(3)

    print('%s eat 2' %name)

 

 

def play(name):

    print('%s play 1' % name)

    # gevent.sleep(2)

    time.sleep(3)

    print('%s play 2' % name)

 

g1=gevent.spawn(eat,'egon')

g2=gevent.spawn(play,'alex')

# gevent.sleep(1)

 

# g1.join()

# g2.join()

gevent.joinall([g1,g2])
View Code

2.5 Gevent之同步與異步

from gevent import spawn,joinall,monkey;monkey.patch_all()

 

import time

def task(pid):

    """

    Some non-deterministic task

    """

    time.sleep(0.5)

    print('Task %s done' % pid)

 

 

def synchronous():

    for i in range(10):

        task(i)

 

def asynchronous():

    g_l=[spawn(task,i) for i in range(10)]

    joinall(g_l)

 

if __name__ == '__main__':

    print('Synchronous:')

    synchronous()

 

    print('Asynchronous:')

    asynchronous()

#上面程序的重要部分是將task函數封裝到Greenlet內部線程的gevent.spawn。 初始化的greenlet列表存放在數組threads中,此數組被傳給gevent.joinall 函數,後者阻塞當前流程,並執行全部給定的greenlet。執行流程只會在 全部greenlet執行完後纔會繼續向下走。
View Code 

2.6 Gevent之應用舉例一

##########################協程應用:爬蟲############################

from gevent import monkey;monkey.patch_all()

import gevent

import requests

import time

 

def get_page(url):

    print('GET: %s' %url)

    response=requests.get(url)

    if response.status_code == 200:

        print('%d bytes received from %s' %(len(response.text),url))

 

 

start_time=time.time()

gevent.joinall([

    gevent.spawn(get_page,'https://www.python.org/'),

    gevent.spawn(get_page,'https://www.yahoo.com/'),

    gevent.spawn(get_page,'https://github.com/'),

])

stop_time=time.time()

print('run time is %s' %(stop_time-start_time))
協程應用:爬蟲

2.7 Gevent之應用舉例二

經過gevent實現單線程下的socket併發(from gevent import monkey;monkey.patch_all()必定要放到導入socket模塊以前,不然gevent沒法識別socket的阻塞)

2.7.1 服務端

####################服務端#####################

 

from gevent import monkey;monkey.patch_all()

from socket import *

import gevent

 

#若是不想用money.patch_all()打補丁,能夠用gevent自帶的socket

# from gevent import socket

# s=socket.socket()

 

def server(server_ip,port):

    s=socket(AF_INET,SOCK_STREAM)

    s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)

    s.bind((server_ip,port))

    s.listen(5)

    while True:

        conn,addr=s.accept()

        gevent.spawn(talk,conn,addr)

 

def talk(conn,addr):

    try:

        while True:

            res=conn.recv(1024)

            print('client %s:%s msg: %s' %(addr[0],addr[1],res))

            conn.send(res.upper())

    except Exception as e:

        print(e)

    finally:

        conn.close()

 

if __name__ == '__main__':

    server('127.0.0.1',8080)
服務端

2.7.2 客戶端

##################客戶端###################

#_*_coding:utf-8_*_

__author__ = 'Linhaifeng'

 

from socket import *

 

client=socket(AF_INET,SOCK_STREAM)

client.connect(('127.0.0.1',8080))

 

 

while True:

    msg=input('>>: ').strip()

    if not msg:continue

 

    client.send(msg.encode('utf-8'))

    msg=client.recv(1024)

    print(msg.decode('utf-8'))
客戶端

2.7.3 多線程併發多個客戶端

########################多線程併發多個客戶端##########################

from threading import Thread

from socket import *

import threading

 

def client(server_ip,port):

    c=socket(AF_INET,SOCK_STREAM) #套接字對象必定要加到函數內,即局部名稱空間內,放在函數外則被全部線程共享,則你們公用一個套接字對象,那麼客戶端端口永遠同樣了

    c.connect((server_ip,port))

 

    count=0

    while True:

        c.send(('%s say hello %s' %(threading.current_thread().getName(),count)).encode('utf-8'))

        msg=c.recv(1024)

        print(msg.decode('utf-8'))

        count+=1

if __name__ == '__main__':

    for i in range(500):

        t=Thread(target=client,args=('127.0.0.1',8080))

        t.start()
多線程併發多個客戶端

2.8 課堂講解練習

2.8.1 服務端

from gevent import monkey,spawn;monkey.patch_all()

from threading import current_thread

from socket import *

 

def comunicate(conn):

    print('子線程:%s' %current_thread().getName())

    while True:

        try:

            data=conn.recv(1024)

            if not data:break

            conn.send(data.upper())

        except ConnectionResetError:

            break

    conn.close()

 

def server(ip,port):

    print('主線程:%s' %current_thread().getName())

    server = socket(AF_INET, SOCK_STREAM)

    server.bind((ip,port))

    server.listen(5)

 

    while True:

        conn, addr = server.accept()

        print(addr)

        # comunicate(conn)

        # t=Thread(target=comunicate,args=(conn,))

        # t.start()

        spawn(comunicate,conn)

 

    server.close()

 

if __name__ == '__main__':

    g=spawn(server,'127.0.0.1', 8081)

    g.join()

 
View Code

2.8.2 客戶端

from socket import *

from threading import current_thread,Thread

 

def client():

    client=socket(AF_INET,SOCK_STREAM)

    client.connect(('127.0.0.1',8081))

 

    while True:

        client.send(('%s say hello' %current_thread().getName()).encode('utf-8'))

        data=client.recv(1024)

        print(data.decode('utf-8'))

 

    client.close()

 

if __name__ == '__main__':

    for i in range(500):

        t=Thread(target=client)

        t.start()

 
View Code

第3章 mysql一:初識數據庫

3.1 數據庫管理軟件的由來

基於咱們以前所學,數據要想永久保存,都是保存於文件中,毫無疑問,一個文件僅僅只能存在於某一臺機器上。

若是咱們暫且忽略直接基於文件來存取數據的效率問題,而且假設程序全部的組件都運行在一臺機器上,那麼用文件存取數據,並無問題。

很不幸,這些假設都是你本身意淫出來的,上述假設存在如下幾個問題。。。。。。

3.1.1 程序全部的組件就不可能運行在一臺機器上

#由於這臺機器一旦掛掉則意味着整個軟件的崩潰,而且程序的執行效率依賴於承載它的硬件,而一臺機器機器的性能總歸是有限的,受限於目前的硬件水平,
就一臺機器的性能垂直進行擴展是有極限的。
#因而咱們只能經過水平擴展來加強咱們系統的總體性能,這就須要咱們將程序的各個組件分佈於多臺機器去執行。

3.1.2 數據安全問題

#根據1的描述,咱們將程序的各個組件分佈到各臺機器,但需知各組件仍然是一個總體,言外之意,全部組件的數據仍是要共享的。但每臺機器上的組件都只能操做本機的文件,
這就致使了數據必然不一致。
#因而咱們想到了將數據與應用程序分離:把文件存放於一臺機器,而後將多臺機器經過網絡去訪問這臺機器上的文件(用socket實現),即共享這臺機器上的文件,
共享則意味着競爭,會發生數據不安全,須要加鎖處理。。。。

3.1.3 併發

根據2的描述,咱們必須寫一個socket服務端來管理這臺機器(數據庫服務器)上的文件,而後寫一個socket客戶端,完成以下功能:

#1.遠程鏈接(支持併發)

#2.打開文件

#3.讀寫(加鎖)

#4.關閉文件

3.1.4 總結:

#咱們在編寫任何程序以前,都須要事先寫好基於網絡操做一臺主機上文件的程序(socket服務端與客戶端程序),因而有人將此類程序寫成一個專門的處理軟件,
這就是mysql等數據庫管理軟件的由來,但mysql解決的不只僅是數據共享的問題,還有查詢效率,安全性等一系列問題,總之,把程序員從數據管理中解脫出來,
專一於本身的程序邏輯的編寫。

3.2 數據庫概述

3.2.1 什麼是數據(Data)

描述事物的符號記錄稱爲數據,描述事物的符號既能夠是數字,也能夠是文字、圖片,圖像、聲音、語言等,數據由多種表現形式,它們均可以通過數字化後存入計算機

在計算機中描述一個事物,就須要抽取這一事物的典型特徵,組成一條記錄,就至關於文件裏的一行內容,如:

egon,male,18,1999,山東,計算機系,2017,oldboy

單純的一條記錄並無任何意義,若是咱們按逗號做爲分隔,依次定義各個字段的意思,至關於定義表的標題

name,sex,age,birth,born_addr,major,entrance_time,school #字段

egon,male,18,1999,山東,計算機系,2017,oldboy #記錄

這樣咱們就能夠了解egon,性別爲男,年齡18歲,出生於1999年,出生地爲山東,2017年考入老男孩計算機系

 

3.2.2 什麼是數據庫(DataBase,簡稱DB)

數據庫即存放數據的倉庫,只不過這個倉庫是在計算機存儲設備上,並且數據是按必定的格式存放的

過去人們將數據存放在文件櫃裏,如今數據量龐大,已經再也不適用

數據庫是長期存放在計算機內、有組織、可共享的數據便可。

數據庫中的數據按必定的數據模型組織、描述和儲存,具備較小的冗餘度、較高的數據獨立性和易擴展性,並可爲各類 用戶共享

3.2.3 什麼是數據庫管理系統(DataBase Management System 簡稱DBMS)

在瞭解了Data與DB的概念後,如何科學地組織和存儲數據,如何高效獲取和維護數據成了關鍵

這就用到了一個系統軟件---數據庫管理系統

如MySQL、Oracle、SQLite、Access、MS SQL Server

mysql主要用於大型門戶,例如搜狗、新浪等,它主要的優點就是開放源代碼,由於開放源代碼這個數據庫是免費的,他如今是甲骨文公司的產品。

oracle主要用於銀行、鐵路、飛機場等。該數據庫功能強大,軟件費用高。也是甲骨文公司的產品。

sql server是微軟公司的產品,主要應用於大中型企業,如聯想、方正等。

 

3.2.4 數據庫服務器、數據管理系統、數據庫、表與記錄的關係(重點理解!!!)

記錄:1 劉海龍  324245234 22(多個字段的信息組成一條記錄,即文件中的一行內容)

表:student,scholl,class_list(即文件)

數據庫:oldboy_stu(即文件夾)

數據庫管理系統:如mysql(是一個軟件)

數據庫服務器:一臺計算機(對內存要求比較高)

總結:

    數據庫服務器-:運行數據庫管理軟件

    數據庫管理軟件:管理-數據庫

    數據庫:即文件夾,用來組織文件/表

    表:即文件,用來存放多行內容/多條記錄

 

 

 

3.3 mysql介紹

MySQL是一個關係型數據庫管理系統,由瑞典MySQL AB 公司開發,目前屬於 Oracle 旗下公司。MySQL 最流行的關係型數據庫管理系統,在 WEB 應用方面MySQL是最好的 RDBMS (Relational Database Management System,關係數據庫管理系統) 應用軟件之一。

3.3.1 mysql是什麼

#mysql就是一個基於socket編寫的C/S架構的軟件

#客戶端軟件

  mysql自帶:如mysql命令,mysqldump命令等

  python模塊:如pymysql

3.3.2 數據庫管理軟件分類

#分兩大類:

  關係型:如sqllite,db2,oracle,access,sql server,MySQL,注意:sql語句通用

  非關係型:mongodb,redis,memcache

 

#能夠簡單的理解爲:

    關係型數據庫須要有表結構

    非關係型數據庫是key-value存儲的,沒有表結構 

3.4 下載安裝

3.4.1 Linux版本

#二進制rpm包安裝

yum -y install mysql-server mysql
########################源碼安裝mysql######################

1.解壓tar包

cd /software

tar -xzvf mysql-5.6.21-linux-glibc2.5-x86_64.tar.gz

mv mysql-5.6.21-linux-glibc2.5-x86_64 mysql-5.6.21

 

2.添加用戶與組

groupadd mysql

useradd -r -g mysql mysql

chown -R mysql:mysql mysql-5.6.21

 

3.安裝數據庫

su mysql

cd mysql-5.6.21/scripts

./mysql_install_db --user=mysql --basedir=/software/mysql-5.6.21 --datadir=/software/mysql-5.6.21/data

 

4.配置文件

cd /software/mysql-5.6.21/support-files

cp my-default.cnf /etc/my.cnf

cp mysql.server /etc/init.d/mysql

vim /etc/init.d/mysql   #若mysql的安裝目錄是/usr/local/mysql,則可省略此步

修改文件中的兩個變動值

basedir=/software/mysql-5.6.21

datadir=/software/mysql-5.6.21/data

 

5.配置環境變量

vim /etc/profile

export MYSQL_HOME="/software/mysql-5.6.21"

export PATH="$PATH:$MYSQL_HOME/bin"

source /etc/profile

 

6.添加自啓動服務

chkconfig --add mysql

chkconfig mysql on

 

7.啓動mysql

service mysql start

 

8.登陸mysql及改密碼與配置遠程訪問

mysqladmin -u root password 'your_password'     #修改root用戶密碼

mysql -u root -p     #登陸mysql,須要輸入密碼

mysql>GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'your_password' WITH GRANT OPTION;     #容許root用戶遠程訪問

mysql>FLUSH PRIVILEGES;     #刷新權限
源碼安裝mysql
###########################源碼安裝mariadb####################

1. 解壓

tar zxvf  mariadb-5.5.31-linux-x86_64.tar.gz  

mv mariadb-5.5.31-linux-x86_64 /usr/local/mysql //必需這樣,不少腳本或可執行程序都會直接訪問這個目錄

 

2. 權限

groupadd mysql             //增長 mysql 屬組

useradd -g mysql mysql     //增長 mysql 用戶 並歸於mysql 屬組

chown mysql:mysql -Rf  /usr/local/mysql    // 設置 mysql 目錄的用戶及用戶組歸屬。

chmod +x -Rf /usr/local/mysql    //賜予可執行權限

 

3. 拷貝配置文件

cp /usr/local/mysql/support-files/my-medium.cnf /etc/my.cnf     //複製默認mysql配置 文件到/etc目錄

 

4. 初始化

/usr/local/mysql/scripts/mysql_install_db --user=mysql          //初始化數據庫

cp  /usr/local/mysql/support-files/mysql.server    /etc/init.d/mysql    //複製mysql服務程序 到系統目錄

chkconfig  mysql on     //添加mysql 至系統服務並設置爲開機啓動

service  mysql  start  //啓動mysql

 

5. 環境變量配置

vim /etc/profile   //編輯profile,將mysql的可執行路徑加入系統PATH

export PATH=/usr/local/mysql/bin:$PATH

source /etc/profile  //使PATH生效。

 

6. 帳號密碼

mysqladmin -u root password 'yourpassword' //設定root帳號及密碼

mysql -u root -p  //使用root用戶登陸mysql

use mysql  //切換至mysql數據庫。

select user,host,password from user; //查看系統權限

drop user ''@'localhost'; //刪除不安全的帳戶

drop user root@'::1';

drop user root@127.0.0.1;

select user,host,password from user; //再次查看系統權限,確保不安全的帳戶均被刪除。

flush privileges;  //刷新權限

 

7. 一些必要的初始配置

1)修改字符集爲UTF8

vi /etc/my.cnf

在[client]下面添加 default-character-set = utf8

在[mysqld]下面添加 character_set_server = utf8

2)增長錯誤日誌

vi /etc/my.cnf

在[mysqld]下面添加:

log-error = /usr/local/mysql/log/error.log

general-log-file = /usr/local/mysql/log/mysql.log

3) 設置爲不區分大小寫,linux下默認會區分大小寫。

vi /etc/my.cnf

在[mysqld]下面添加:

lower_case_table_name=1

 

修改完重啓:#service  mysql  restart
源碼安裝mariadb 

3.4.2 Window版本

#################################安裝########################

#一、下載:MySQL Community Server 5.7.16

http://dev.mysql.com/downloads/mysql/

 

#二、解壓

若是想要讓MySQL安裝在指定目錄,那麼就將解壓後的文件夾移動到指定目錄,如:C:\mysql-5.7.16-winx64

 

#三、添加環境變量

【右鍵計算機】--》【屬性】--》【高級系統設置】--》【高級】--》【環境變量】--》【在第二個內容框中找到 變量名爲Path 的一行,雙擊】 --> 【將MySQL的bin目錄路徑追加到變值值中,用 ; 分割】

 

#四、初始化

mysqld --initialize-insecure

 

#五、啓動MySQL服務

mysqld # 啓動MySQL服務

 

#六、啓動MySQL客戶端並鏈接MySQL服務

mysql -u root -p # 鏈接MySQL服務器
安裝
######################將MySQL服務製做成windows服務#######################

上一步解決了一些問題,但不夠完全,由於在執行【mysqd】啓動MySQL服務器時,當前終端會被hang住,那麼作一下設置便可解決此問題:

 

 

 

注意:--install前,必須用mysql啓動命令的絕對路徑

# 製做MySQL的Windows服務,在終端執行此命令:

"c:\mysql-5.7.16-winx64\bin\mysqld" --install

 

# 移除MySQL的Windows服務,在終端執行此命令:

"c:\mysql-5.7.16-winx64\bin\mysqld" --remove

 

 

 

註冊成服務以後,之後再啓動和關閉MySQL服務時,僅需執行以下命令:

# 啓動MySQL服務

net start mysql

 

# 關閉MySQL服務

net stop mysql

 
將MySQL服務製做成windows服務

3.5 mysql軟件基本管理

3.5.1 啓動查看

#################linux平臺下查看###################

[root@egon ~]# systemctl start mariadb #啓動

[root@egon ~]# systemctl enable mariadb #設置開機自啓動

Created symlink from /etc/systemd/system/multi-user.target.wants/mariadb.service to /usr/lib/systemd/system/mariadb.service.

[root@egon ~]# ps aux |grep mysqld |grep -v grep #查看進程,mysqld_safe爲啓動mysql的腳本文件,內部調用mysqld命令

mysql     3329  0.0  0.0 113252  1592 ?        Ss   16:19   0:00 /bin/sh /usr/bin/mysqld_safe --basedir=/usr

mysql     3488  0.0  2.3 839276 90380 ?        Sl   16:19   0:00 /usr/libexec/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --log-error=/var/log/mariadb/mariadb.log --pid-file=/var/run/mariadb/mariadb.pid --socket=/var/lib/mysql/mysql.sock

[root@egon ~]# netstat -an |grep 3306 #查看端口

tcp        0      0 0.0.0.0:3306            0.0.0.0:*               LISTEN 

[root@egon ~]# ll -d /var/lib/mysql #權限不對,啓動不成功,注意user和group

drwxr-xr-x 5 mysql mysql 4096 Jul 20 16:28 /var/lib/mysql
linux平臺下查看
#################You must reset your password using ALTER USER statement before executing this statement.################

 

安裝完mysql 以後,登錄之後,無論運行任何命令,老是提示這個

mac mysql error You must reset your password using ALTER USER statement before executing this statement.

解決方法:

step 1: SET PASSWORD = PASSWORD('your new password');

step 2: ALTER USER 'root'@'localhost' PASSWORD EXPIRE NEVER;

step 3: flush privileges;

 
You must reset your password using ALTER USER statement before executing this statement.

3.5.2 登陸,設置密碼

初始狀態下,管理員root,密碼爲空,默認只容許從本機登陸localhost

設置密碼

[root@egon ~]# mysqladmin -uroot password "123"        設置初始密碼 因爲原密碼爲空,所以-p能夠不用

[root@egon ~]# mysqladmin -uroot -p"123" password "456"        修改mysql密碼,由於已經有密碼了,因此必須輸入原密碼才能設置新密碼

 

命令格式:

[root@egon ~]# mysql -h172.31.0.2 -uroot -p456

[root@egon ~]# mysql -uroot -p

[root@egon ~]# mysql                    以root用戶登陸本機,密碼爲空
View Code

3.5.3 忘記密碼

linux平臺下,破解密碼的兩種方式

##################方法一:刪除受權庫mysql,從新初始化###################

[root@egon ~]# rm -rf /var/lib/mysql/mysql #全部受權信息所有丟失!!!

[root@egon ~]# systemctl restart mariadb

[root@egon ~]# mysql
#############方法二:啓動時,跳過受權庫#############

[root@egon ~]# vim /etc/my.cnf    #mysql主配置文件

[mysqld]

skip-grant-table

[root@egon ~]# systemctl restart mariadb

[root@egon ~]# mysql

MariaDB [(none)]> update mysql.user set password=password("123") where user="root" and host="localhost";

MariaDB [(none)]> flush privileges;

MariaDB [(none)]> \q

[root@egon ~]# #打開/etc/my.cnf去掉skip-grant-table,而後重啓

[root@egon ~]# systemctl restart mariadb

[root@egon ~]# mysql -u root -p123 #以新密碼登陸
方法二:啓動時,跳過受權庫

windows平臺下,5.7版本mysql,破解密碼的兩種方式:

####################方式一####################

#1 關閉mysql

#2 在cmd中執行:mysqld --skip-grant-tables

#3 在cmd中執行:mysql

#4 執行以下sql:

update mysql.user set authentication_string=password('') where user = 'root';

flush privileges;

 

#5 tskill mysqld #或taskkill -f /PID 7832

#6 從新啓動mysql
方式一
#######################方式二######################

#1. 關閉mysql,能夠用tskill mysqld將其殺死

#2. 在解壓目錄下,新建mysql配置文件my.ini

#3. my.ini內容,指定

[mysqld]

skip-grant-tables

 

#4.啓動mysqld

#5.在cmd裏直接輸入mysql登陸,而後操做

update mysql.user set authentication_string=password('') where user='root and host='localhost';

 

flush privileges;

 

#6.註釋my.ini中的skip-grant-tables,而後啓動myqsld,而後就能夠以新密碼登陸了
方式二 

3.5.4 課堂講解破解密碼(windows下)

C:\Users\Administrator> mysqladmin -uroot -p password "123"

 

#重置密碼

net stop MySQL

mysqld --skip-grant-tables

mysql -uroot -p

    update mysql.user set password=password("") where user='root' and host="localhost";

    flush privileges;

 

C:\Users\Administrator>tasklist |findstr mysql

mysqld.exe                    6316 Console                    1    454,544 K

 

C:\Users\Administrator>taskkill /F /PID 6316

成功: 已終止 PID 爲 6316 的進程。

 

C:\Users\Administrator>net start MySQL

MySQL 服務正在啓動 .

MySQL 服務已經啓動成功。
View Code

3.5.5 在windows下,爲mysql服務指定配置文件

強調:配置文件中的註釋能夠有中文,可是配置項中不能出現中文

#########################my.ini###########################

#在mysql的解壓目錄下,新建my.ini,而後配置

#1. 在執行mysqld命令時,下列配置會生效,即mysql服務啓動時生效

[mysqld]

;skip-grant-tables

port=3306

character_set_server=utf8

default-storage-engine=innodb

innodb_file_per_table=1

 

 

#解壓的目錄

basedir=E:\mysql-5.7.19-winx64

#data目錄

datadir=E:\my_data #在mysqld --initialize時,就會將初始數據存入此處指定的目錄,在初始化以後,啓動mysql時,就會去這個目錄裏找數據

 

 

 

#2. 針對客戶端命令的全局配置,當mysql客戶端命令執行時,下列配置生效

[client]

port=3306

default-character-set=utf8

user=root

password=123

 

#3. 只針對mysql這個客戶端的配置,2中的是全局配置,而此處的則是隻針對mysql這個命令的局部配置

[mysql]

;port=3306

;default-character-set=utf8

user=egon

password=4573

 

 

#!!!若是沒有[mysql],則用戶在執行mysql命令時的配置以[client]爲準
my.ini

3.5.6 統一字符編碼

#1. 修改配置文件

[mysqld]

default-character-set=utf8

[client]

default-character-set=utf8

[mysql]

default-character-set=utf8

 

#mysql5.5以上:修改方式有所改動

[mysqld]

character-set-server=utf8

collation-server=utf8_general_ci

[client]

default-character-set=utf8

[mysql]

default-character-set=utf8

 

#2. 重啓服務

#3. 查看修改結果:

\s

show variables like '%char%'
View Code

3.6 初識sql語句

有了mysql這個數據庫軟件,就能夠將程序員從對數據的管理中解脫出來,專一於對程序邏輯的編寫

mysql服務端軟件即mysqld幫咱們管理好文件夾以及文件,前提是做爲使用者的咱們,須要下載mysql的客戶端,或者其餘模塊來鏈接到mysqld,而後使用mysql軟件規定的語法格式去提交本身命令,實現對文件夾或文件的管理。該語法即sql(Structured Query Language 即結構化查詢語言)

SQL語言主要用於存取數據、查詢數據、更新數據和管理關係數據庫系統,SQL語言由IBM開發。SQL語言分爲3種類型:

#一、DDL語句    數據庫定義語言: 數據庫、表、視圖、索引、存儲過程,例如CREATE DROP ALTER

#二、DML語句    數據庫操縱語言: 插入數據INSERT、刪除數據DELETE、更新數據UPDATE、查詢數據SELECT

#三、DCL語句    數據庫控制語言: 例如控制用戶的訪問權限GRANT、REVOKE
#1. 操做文件夾

        增:create database db1 charset utf8;

        查:show databases;

        改:alter database db1 charset latin1;

        刪除: drop database db1;

 

 

#2. 操做文件

    先切換到文件夾下:use db1

        增:create table t1(id int,name char);

        查:show tables

        改:alter table t1 modify name char(3);

              alter table t1 change name name1 char(2);

        刪:drop table t1;

   

 

#3. 操做文件中的內容/記錄

        增:insert into t1 values(1,'egon1'),(2,'egon2'),(3,'egon3');

        查:select * from t1;

        改:update t1 set name='sb' where id=2;

        刪:delete from t1 where id=1;

 

        清空表:

            delete from t1; #若是有自增id,新增的數據,仍然是以刪除前的最後同樣做爲起始。

            truncate table t1;數據量大,刪除速度比上一條快,且直接從零開始,

 

            auto_increment 表示:自增

            primary key 表示:約束(不能重複且不能爲空);加速查找
View Code 

第4章 mysql二:庫操做

4.1 系統數據庫

information_schema: 虛擬庫,不佔用磁盤空間,存儲的是數據庫啓動後的一些參數,如用戶表信息、列信息、權限信息、字符信息等

performance_schema: MySQL 5.5開始新增一個數據庫:主要用於收集數據庫服務器性能參數,記錄處理查詢請求時發生的各類事件、鎖等現象

mysql: 受權庫,主要存儲系統用戶的權限信息

test:    MySQL數據庫系統自動建立的測試數據庫

 

4.2 建立數據庫

4.2.1 語法(help create database)

CREATE DATABASE 數據庫名 charset utf8;

4.2.2 數據庫命名規則:

能夠由字母、數字、下劃線、@、#、$

區分大小寫

惟一性

不能使用關鍵字如 create select

不能單獨使用數字

最長128位

4.3 數據庫相關操做

1 查看數據庫

show databases;

show create database db1;

select database();

 

2 選擇數據庫

USE 數據庫名

 

3 刪除數據庫

DROP DATABASE 數據庫名;

 

4 修改數據庫

alter database db1 charset utf8;
View Code

第5章 mysql三:表操做

5.1 存儲引擎介紹

mysql支持的存儲引擎

MariaDB [(none)]> show engines\G  #查看全部支持的存儲引擎

MariaDB [(none)]> show variables like 'storage_engine%'; #查看正在使用的存儲引擎

5.2 使用存儲引擎

5.2.1 方法1:建表時指定

MariaDB [db1]> create table innodb_t1(id int,name char)engine=innodb;

MariaDB [db1]> create table innodb_t2(id int)engine=innodb;

MariaDB [db1]> show create table innodb_t1;

MariaDB [db1]> show create table innodb_t2;

5.2.2 方法2:在配置文件中指定默認的存儲引擎

/etc/my.cnf

[mysqld]

default-storage-engine=INNODB

innodb_file_per_table=1

5.2.3 查看

[root@egon db1]# cd /var/lib/mysql/db1/

[root@egon db1]# ls

db.opt  innodb_t1.frm  innodb_t1.ibd  innodb_t2.frm  innodb_t2.ibd

5.2.4 練習

建立四個表,分別使用innodb,myisam,memory,blackhole存儲引擎,進行插入數據測試

MariaDB [db1]> create table t1(id int)engine=innodb;

MariaDB [db1]> create table t2(id int)engine=myisam;

MariaDB [db1]> create table t3(id int)engine=memory;

MariaDB [db1]> create table t4(id int)engine=blackhole;

MariaDB [db1]> quit

[root@egon db1]# ls /var/lib/mysql/db1/ #發現後兩種存儲引擎只有表結構,無數據

db.opt  t1.frm  t1.ibd  t2.MYD  t2.MYI  t2.frm  t3.frm  t4.frm

 

#memory,在重啓mysql或者重啓機器後,表內數據清空

#blackhole,往表內插入任何數據,都至關於丟入黑洞,表內永遠不存記錄
View Code

5.3 表介紹

表至關於文件,表中的一條記錄就至關於文件的一行內容,不一樣的是,表中的一條記錄有對應的標題,稱爲表的字段

 

 

id,name,qq,age稱爲字段,其他的,一行內容稱爲一條記錄

 

5.4 建立表

5.4.1 語法:

create table 表名(

字段名1 類型[(寬度) 約束條件],

字段名2 類型[(寬度) 約束條件],

字段名3 類型[(寬度) 約束條件]

);

5.4.2 注意:

1. 在同一張表中,字段名是不能相同

2. 寬度和約束條件可選

3. 字段名和類型是必須的

5.4.3 實例

MariaDB [(none)]> create database db1 charset utf8;

 

MariaDB [(none)]> use db1;

 

MariaDB [db1]> create table t1( 

    -> id int,

    -> name varchar(50),

    -> sex enum('male','female'),

    -> age int(3)

    -> );

 

MariaDB [db1]> show tables; #查看db1庫下全部表名

 

MariaDB [db1]> desc t1;

+-------+-----------------------+------+-----+---------+-------+

| Field | Type                  | Null | Key | Default | Extra |

+-------+-----------------------+------+-----+---------+-------+

| id    | int(11)               | YES  |     | NULL    |       |

| name  | varchar(50)           | YES  |     | NULL    |       |

| sex   | enum('male','female') | YES  |     | NULL    |       |

| age   | int(3)                | YES  |     | NULL    |       |

+-------+-----------------------+------+-----+---------+-------+

 

MariaDB [db1]> select id,name,sex,age from t1;

Empty set (0.00 sec)

 

MariaDB [db1]> select * from t1;

Empty set (0.00 sec)

 

MariaDB [db1]> select id,name from t1;

Empty set (0.00 sec)
View Code
############################往表中插入數據#####################

MariaDB [db1]> insert into t1 values

    -> (1,'egon',18,'male'),

    -> (2,'alex',81,'female')

    -> ;

MariaDB [db1]> select * from t1;

+------+------+------+--------+

| id   | name | age  | sex    |

+------+------+------+--------+

|    1 | egon |   18 | male   |

|    2 | alex |   81 | female |

+------+------+------+--------+

 

 

 

MariaDB [db1]> insert into t1(id) values

    -> (3),

    -> (4);

MariaDB [db1]> select * from t1;

+------+------+------+--------+

| id   | name | age  | sex    |

+------+------+------+--------+

|    1 | egon |   18 | male   |

|    2 | alex |   81 | female |

|    3 | NULL | NULL | NULL   |

|    4 | NULL | NULL | NULL   |

+------+------+------+--------+
往表中插入數據

注意注意注意:表中的最後一個字段不要加逗號

5.5 查看錶結構

MariaDB [db1]> describe t1; #查看錶結構,可簡寫爲desc 表名

+-------+-----------------------+------+-----+---------+-------+

| Field | Type                  | Null | Key | Default | Extra |

+-------+-----------------------+------+-----+---------+-------+

| id    | int(11)               | YES  |     | NULL    |       |

| name  | varchar(50)           | YES  |     | NULL    |       |

| sex   | enum('male','female') | YES  |     | NULL    |       |

| age   | int(3)                | YES  |     | NULL    |       |

+-------+-----------------------+------+-----+---------+-------+

 

 

MariaDB [db1]> show create table t1\G; #查看錶詳細結構,可加\G
View Code

5.6 修改表ALTER TABLE

5.6.1 語法

語法:

1. 修改表名

      ALTER TABLE 表名

                          RENAME 新表名;

 

2. 增長字段

      ALTER TABLE 表名

                          ADD 字段名  數據類型 [完整性約束條件…],

                          ADD 字段名  數據類型 [完整性約束條件…];

      ALTER TABLE 表名

                          ADD 字段名  數據類型 [完整性約束條件…]  FIRST;

      ALTER TABLE 表名

                          ADD 字段名  數據類型 [完整性約束條件…]  AFTER 字段名;

                           

3. 刪除字段

      ALTER TABLE 表名

                          DROP 字段名;

 

4. 修改字段

      ALTER TABLE 表名

                          MODIFY  字段名 數據類型 [完整性約束條件…];

      ALTER TABLE 表名

                          CHANGE 舊字段名 新字段名 舊數據類型 [完整性約束條件…];

      ALTER TABLE 表名

                          CHANGE 舊字段名 新字段名 新數據類型 [完整性約束條件…];
View Code

5.6.2 示例

示例:

1. 修改存儲引擎

mysql> alter table service

    -> engine=innodb;

 

2. 添加字段

mysql> alter table student10

    -> add name varchar(20) not null,

    -> add age int(3) not null default 22;

   

mysql> alter table student10

    -> add stu_num varchar(10) not null after name;                //添加name字段以後

 

mysql> alter table student10                       

    -> add sex enum('male','female') default 'male' first;          //添加到最前面

 

3. 刪除字段

mysql> alter table student10

    -> drop sex;

 

mysql> alter table service

    -> drop mac;

 

4. 修改字段類型modify

mysql> alter table student10

    -> modify age int(3);

mysql> alter table student10

    -> modify id int(11) not null primary key auto_increment;    //修改成主鍵

 

5. 增長約束(針對已有的主鍵增長auto_increment)

mysql> alter table student10 modify id int(11) not null primary key auto_increment;

ERROR 1068 (42000): Multiple primary key defined

 

mysql> alter table student10 modify id int(11) not null auto_increment;

Query OK, 0 rows affected (0.01 sec)

Records: 0  Duplicates: 0  Warnings: 0

 

6. 對已經存在的表增長複合主鍵

mysql> alter table service2

    -> add primary key(host_ip,port);       

 

7. 增長主鍵

mysql> alter table student1

    -> modify name varchar(10) not null primary key;

 

8. 增長主鍵和自動增加

mysql> alter table student1

    -> modify id int not null primary key auto_increment;

 

9. 刪除主鍵

a. 刪除自增約束

mysql> alter table student10 modify id int(11) not null;

 

b. 刪除主鍵

mysql> alter table student10                                

    -> drop primary key;

 
View Code

5.7 複製表

複製表結構+記錄 (key不會複製: 主鍵、外鍵和索引)

mysql> create table new_service select * from service;

 

只複製表結構

mysql> select * from service where 1=2;        //條件爲假,查不到任何記錄

Empty set (0.00 sec)

mysql> create table new1_service select * from service where 1=2; 

Query OK, 0 rows affected (0.00 sec)

Records: 0  Duplicates: 0  Warnings: 0

 

mysql> create table t4 like employees;
View Code

5.8 刪除表

DROP TABLE 表名;

5.9 數據類型

5.9.1 介紹

存儲引擎決定了表的類型,而表內存放的數據也要有不一樣的類型,每種數據類型都有本身的寬度,但寬度是可選的

詳細參考:

http://www.runoob.com/mysql/mysql-data-types.html

http://dev.mysql.com/doc/refman/5.7/en/data-type-overview.html

mysql經常使用數據類型概覽

#1. 數字:

    整型:tinyinit  int  bigint

    小數:

        float :在位數比較短的狀況下不精準

        double :在位數比較長的狀況下不精準

            0.000001230123123123

            存成:0.000001230000

 

        decimal:(若是用小數,則用推薦使用decimal)

            精準

            內部原理是以字符串形式去存

 

#2. 字符串:

    char(10):簡單粗暴,浪費空間,存取速度快

        root存成root000000

    varchar:精準,節省空間,存取速度慢

 

    sql優化:建立表時,定長的類型往前放,變長的日後放

                    好比性別           好比地址或描述信息

 

    >255個字符,超了就把文件路徑存放到數據庫中。

            好比圖片,視頻等找一個文件服務器,數據庫中只存路徑或url。

 

 

 

#3. 時間類型:

    最經常使用:datetime

 

 

#4. 枚舉類型與集合類型
View Code

5.9.2 數值類型之整數類型

整數類型:TINYINT SMALLINT MEDIUMINT INT BIGINT

做用:存儲年齡,等級,id,各類號碼等

========================================

        tinyint[(m)] [unsigned] [zerofill]

 

            小整數,數據類型用於保存一些範圍的整數數值範圍:

            有符號:

                -128 ~ 127

            無符號:

~ 255

 

            PS: MySQL中無布爾值,使用tinyint(1)構造。

 

 

 

========================================

        int[(m)][unsigned][zerofill]

 

            整數,數據類型用於保存一些範圍的整數數值範圍:

            有符號:

                    -2147483648 ~ 2147483647

            無符號:

~ 4294967295

 

 

 

========================================

        bigint[(m)][unsigned][zerofill]

            大整數,數據類型用於保存一些範圍的整數數值範圍:

            有符號:

                    -9223372036854775808 ~ 9223372036854775807

            無符號:

 ~  18446744073709551615

 
View Code
###########################驗證###################################

=========有符號和無符號tinyint==========

#tinyint默認爲有符號

MariaDB [db1]> create table t1(x tinyint); #默認爲有符號,即數字前有正負號

MariaDB [db1]> desc t1;

MariaDB [db1]> insert into t1 values

    -> (-129),

    -> (-128),

    -> (127),

    -> (128);

MariaDB [db1]> select * from t1;

+------+

| x    |

+------+

| -128 | #-129存成了-128

| -128 | #有符號,最小值爲-128

|  127 | #有符號,最大值127

|  127 | #128存成了127

+------+

 

 

 

#設置無符號tinyint

MariaDB [db1]> create table t2(x tinyint unsigned);

MariaDB [db1]> insert into t2 values

    -> (-1),

    -> (0),

    -> (255),

    -> (256);

MariaDB [db1]> select * from t2;

+------+

| x    |

+------+

|    0 | -1存成了0

|    0 | #無符號,最小值爲0

|  255 | #無符號,最大值爲255

|  255 | #256存成了255

+------+

 

 

 

============有符號和無符號int=============

#int默認爲有符號

MariaDB [db1]> create table t3(x int); #默認爲有符號整數

MariaDB [db1]> insert into t3 values

    -> (-2147483649),

    -> (-2147483648),

    -> (2147483647),

    -> (2147483648);

MariaDB [db1]> select * from t3;

+-------------+

| x           |

+-------------+

| -2147483648 | #-2147483649存成了-2147483648

| -2147483648 | #有符號,最小值爲-2147483648

|  2147483647 | #有符號,最大值爲2147483647

|  2147483647 | #2147483648存成了2147483647

+-------------+

 

 

 

#設置無符號int

MariaDB [db1]> create table t4(x int unsigned);

MariaDB [db1]> insert into t4 values

    -> (-1),

    -> (0),

    -> (4294967295),

    -> (4294967296);

MariaDB [db1]> select * from t4;

+------------+

| x          |

+------------+

|          0 | #-1存成了0

|          0 | #無符號,最小值爲0

| 4294967295 | #無符號,最大值爲4294967295

| 4294967295 | #4294967296存成了4294967295

+------------+

 

 

 

 

==============有符號和無符號bigint=============

MariaDB [db1]> create table t6(x bigint);

MariaDB [db1]> insert into t5 values 

    -> (-9223372036854775809),

    -> (-9223372036854775808),

    -> (9223372036854775807),

    -> (9223372036854775808);

 

MariaDB [db1]> select * from t5;

+----------------------+

| x                    |

+----------------------+

| -9223372036854775808 |

| -9223372036854775808 |

|  9223372036854775807 |

|  9223372036854775807 |

+----------------------+

 

 

 

MariaDB [db1]> create table t6(x bigint unsigned);

MariaDB [db1]> insert into t6 values 

    -> (-1),

    -> (0),

    -> (18446744073709551615),

    -> (18446744073709551616);

 

MariaDB [db1]> select * from t6;

+----------------------+

| x                    |

+----------------------+

|                    0 |

|                    0 |

| 18446744073709551615 |

| 18446744073709551615 |

+----------------------+

 

 

 

 

======用zerofill測試整數類型的顯示寬度=============

MariaDB [db1]> create table t7(x int(3) zerofill);

MariaDB [db1]> insert into t7 values

    -> (1),

    -> (11),

    -> (111),

    -> (1111);

MariaDB [db1]> select * from t7;

+------+

| x    |

+------+

|  001 |

|  011 |

|  111 |

| 1111 | #超過寬度限制仍然能夠存

+------+
驗證

注意:爲該類型指定寬度時,僅僅只是指定查詢結果的顯示寬度,與存儲範圍無關,存儲範圍以下

其實咱們徹底不必爲整數類型指定顯示寬度,使用默認的就能夠了

默認的顯示寬度,都是在最大值的基礎上加1

 

 

int的存儲寬度是4個Bytes,即32個bit,即2**32

無符號最大值爲:4294967296-1

有符號最大值:2147483648-1

有符號和無符號的最大數字須要的顯示寬度均爲10,而針對有符號的最小值則須要11位才能顯示徹底,因此int類型默認的顯示寬度爲11是很是合理的

最後:整形類型,其實沒有必要指定顯示寬度,使用默認的就ok

 

5.9.3 數值類型之浮點型

定點數類型  DEC等同於DECIMAL  

浮點類型:FLOAT DOUBLE

做用:存儲薪資、身高、體重、體質參數等

======================================

#FLOAT[(M,D)] [UNSIGNED] [ZEROFILL]

 

定義:

        單精度浮點數(非準確小數值),m是數字總個數,d是小數點後個數。m最大值爲255,d最大值爲30

 

有符號:

           -3.402823466E+38 to -1.175494351E-38,

           1.175494351E-38 to 3.402823466E+38

無符號:

           1.175494351E-38 to 3.402823466E+38

 

 

精確度:

           **** 隨着小數的增多,精度變得不許確 ****

 

 

======================================

#DOUBLE[(M,D)] [UNSIGNED] [ZEROFILL]

 

定義:

           雙精度浮點數(非準確小數值),m是數字總個數,d是小數點後個數。m最大值爲255,d最大值爲30

 

有符號:

           -1.7976931348623157E+308 to -2.2250738585072014E-308

           2.2250738585072014E-308 to 1.7976931348623157E+308

 

無符號:

           2.2250738585072014E-308 to 1.7976931348623157E+308

           

精確度:

           ****隨着小數的增多,精度比float要高,但也會變得不許確 ****

 

======================================

decimal[(m[,d])] [unsigned] [zerofill]

 

定義:

          準確的小數值,m是數字總個數(負號不算),d是小數點後個數。 m最大值爲65,d最大值爲30。

 

 

精確度:

           **** 隨着小數的增多,精度始終準確 ****

           對於精確數值計算時須要用此類型

           decaimal可以存儲精確值的緣由在於其內部按照字符串存儲。
View Code
#####################驗證########################

mysql> create table t1(x float(256,31));

ERROR 1425 (42000): Too big scale 31 specified for column 'x'. Maximum is 30.

mysql> create table t1(x float(256,30));

ERROR 1439 (42000): Display width out of range for column 'x' (max = 255)

mysql> create table t1(x float(255,30)); #建表成功

Query OK, 0 rows affected (0.02 sec)

 

mysql> create table t2(x double(255,30)); #建表成功

Query OK, 0 rows affected (0.02 sec)

 

mysql> create table t3(x decimal(66,31));

ERROR 1425 (42000): Too big scale 31 specified for column 'x'. Maximum is 30.

mysql> create table t3(x decimal(66,30));

ERROR 1426 (42000): Too-big precision 66 specified for 'x'. Maximum is 65.

mysql> create table t3(x decimal(65,30)); #建表成功

Query OK, 0 rows affected (0.02 sec)

 

mysql> show tables;

+---------------+

| Tables_in_db1 |

+---------------+

| t1            |

| t2            |

| t3            |

+---------------+

rows in set (0.00 sec)

 

 

 

mysql> insert into t1 values(1.1111111111111111111111111111111); #小數點後31個1

Query OK, 1 row affected (0.01 sec)

 

mysql> insert into t2 values(1.1111111111111111111111111111111);

Query OK, 1 row affected (0.00 sec)

 

mysql> insert into t3 values(1.1111111111111111111111111111111);

Query OK, 1 row affected, 1 warning (0.01 sec)

 

mysql> select * from t1; #隨着小數的增多,精度開始不許確

+----------------------------------+

| x                                |

+----------------------------------+

| 1.111111164093017600000000000000 |

+----------------------------------+

row in set (0.00 sec)

 

mysql> select * from t2; #精度比float要準確點,但隨着小數的增多,一樣變得不許確

+----------------------------------+

| x                                |

+----------------------------------+

| 1.111111111111111200000000000000 |

+----------------------------------+

row in set (0.00 sec)

 

mysql> select * from t3; #精度始終準確,d爲30,因而只留了30位小數

+----------------------------------+

| x                                |

+----------------------------------+

| 1.111111111111111111111111111111 |

+----------------------------------+

row in set (0.00 sec)
驗證

5.9.4 課堂講解實例

#一、整型(默認有符號)

 

create table t8(n tinyint);

 

insert into t8 values(-1);

insert into t8 values(128);

insert into t8 values(-129);

 

create table t9(n tinyint unsigned);

insert into t9 values(-1),(256);

 

#整型的寬度表明顯示寬度

create table t11(n int(3) unsigned zerofill);

create table t12(n int unsigned zerofill);

 

create table t13(

    id int

);

 

 

 

 

#二、浮點型

create table t13(x float(255,30));

create table t14(x double(255,30));

create table t15(x decimal(65,30));

 

 

insert into t13 values(1.111111111111111111111111111111);

insert into t14 values(1.111111111111111111111111111111);

insert into t15 values(1.111111111111111111111111111111);
View Code

5.10 日期類型

DATE TIME DATETIME TIMESTAMP YEAR

做用:存儲用戶註冊時間,文章發佈時間,員工入職時間,出生時間,過時時間等

5.10.1 示例

        YEAR

            YYYY(1901/2155)

        DATE

            YYYY-MM-DD(1000-01-01/9999-12-31)

        TIME

            HH:MM:SS('-838:59:59'/'838:59:59')

        DATETIME

            YYYY-MM-DD HH:MM:SS(1000-01-01 00:00:00/9999-12-31 23:59:59    Y)

        TIMESTAMP

            YYYYMMDD HHMMSS(1970-01-01 00:00:00/2037 年某時)            
################################驗證################################

============year===========

MariaDB [db1]> create table t10(born_year year); #不管year指定何種寬度,最後都默認是year(4)

MariaDB [db1]> insert into t10 values 

    -> (1900),

    -> (1901),

    -> (2155),

    -> (2156);

MariaDB [db1]> select * from t10;

+-----------+

| born_year |

+-----------+

|      0000 |

|      1901 |

|      2155 |

|      0000 |

+-----------+

 

 

============date,time,datetime===========

MariaDB [db1]> create table t11(d date,t time,dt datetime);

MariaDB [db1]> desc t11;

+-------+----------+------+-----+---------+-------+

| Field | Type     | Null | Key | Default | Extra |

+-------+----------+------+-----+---------+-------+

| d     | date     | YES  |     | NULL    |       |

| t     | time     | YES  |     | NULL    |       |

| dt    | datetime | YES  |     | NULL    |       |

+-------+----------+------+-----+---------+-------+

 

MariaDB [db1]> insert into t11 values(now(),now(),now());

MariaDB [db1]> select * from t11;

+------------+----------+---------------------+

| d          | t        | dt                  |

+------------+----------+---------------------+

| 2017-07-25 | 16:26:54 | 2017-07-25 16:26:54 |

+------------+----------+---------------------+

 

 

 

============timestamp===========

MariaDB [db1]> create table t12(time timestamp);

MariaDB [db1]> insert into t12 values();

MariaDB [db1]> insert into t12 values(null);

MariaDB [db1]> select * from t12;

+---------------------+

| time                |

+---------------------+

| 2017-07-25 16:29:17 |

| 2017-07-25 16:30:01 |

+---------------------+

 

 

 

============注意啦,注意啦,注意啦===========

1. 單獨插入時間時,須要以字符串的形式,按照對應的格式插入

2. 插入年份時,儘可能使用4位值

3. 插入兩位年份時,<=69,以20開頭,好比50,  結果2050     

                >=70,以19開頭,好比71,結果1971

MariaDB [db1]> create table t12(y year);

MariaDB [db1]> insert into t12 values 

    -> (50),

    -> (71);

MariaDB [db1]> select * from t12;

+------+

| y    |

+------+

| 2050 |

| 1971 |

+------+

 

 

 

============綜合練習===========

MariaDB [db1]> create table student(

    -> id int,

    -> name varchar(20),

    -> born_year year,

    -> birth date,

    -> class_time time,

    -> reg_time datetime);

 

MariaDB [db1]> insert into student values

    -> (1,'alex',"1995","1995-11-11","11:11:11","2017-11-11 11:11:11"),

    -> (2,'egon',"1997","1997-12-12","12:12:12","2017-12-12 12:12:12"),

    -> (3,'wsb',"1998","1998-01-01","13:13:13","2017-01-01 13:13:13");

 

MariaDB [db1]> select * from student;

+------+------+-----------+------------+------------+---------------------+

| id   | name | born_year | birth      | class_time | reg_time            |

+------+------+-----------+------------+------------+---------------------+

|    1 | alex |      1995 | 1995-11-11 | 11:11:11   | 2017-11-11 11:11:11 |

|    2 | egon |      1997 | 1997-12-12 | 12:12:12   | 2017-12-12 12:12:12 |

|    3 | wsb  |      1998 | 1998-01-01 | 13:13:13   | 2017-01-01 13:13:13 |

+------+------+-----------+------------+------------+---------------------+
驗證

5.10.2 datetime與timestamp的區別

####################datetime與timestamp的區別###################

在實際應用的不少場景中,MySQL的這兩種日期類型都可以知足咱們的須要,存儲精度都爲秒,但在某些狀況下,會展示出他們各自的優劣。下面就來總結一下兩種日期類型的區別。

 

1.DATETIME的日期範圍是1001——9999年,TIMESTAMP的時間範圍是1970——2038年。

 

2.DATETIME存儲時間與時區無關,TIMESTAMP存儲時間與時區有關,顯示的值也依賴於時區。在mysql服務器,操做系統以及客戶端鏈接都有時區的設置。

 

3.DATETIME使用8字節的存儲空間,TIMESTAMP的存儲空間爲4字節。所以,TIMESTAMP比DATETIME的空間利用率更高。

 

4.DATETIME的默認值爲null;TIMESTAMP的字段默認不爲空(not null),默認值爲當前時間(CURRENT_TIMESTAMP),若是不作特殊處理,而且update語句中沒有指定該列的更新值,則默認更新爲當前時間。
datetime與timestamp的區別

5.10.3 課堂講解示例

create table student(

    id int,

    name char(16),

    born_year year,

    birth_date date,

    class_time time,

    reg_time datetime

);

 

insert into student values

(1,'egon',now(),now(),now(),now())

;

 

insert into student values

(2,'alex','1999','1999-11-11','11:11:11',"1999-11-11 11:11:11")

;
View Code

5.11 字符串類型

5.11.1 定長char與變長varchar的介紹

#官網:https://dev.mysql.com/doc/refman/5.7/en/char.html

#注意:char和varchar括號內的參數指的都是字符的長度

 

#char類型:定長,簡單粗暴,浪費空間,存取速度快

    字符長度範圍:0-255(一箇中文是一個字符,是utf8編碼的3個字節)

    存儲:

        存儲char類型的值時,會往右填充空格來知足長度

        例如:指定長度爲10,存>10個字符則報錯,存<10個字符則用空格填充直到湊夠10個字符存儲

 

    檢索:

        在檢索或者說查詢時,查出的結果會自動刪除尾部的空格,除非咱們打開pad_char_to_full_length SQL模式(SET sql_mode = 'PAD_CHAR_TO_FULL_LENGTH';)

 

#varchar類型:變長,精準,節省空間,存取速度慢

    字符長度範圍:0-65535(若是大於21845會提示用其餘類型 。mysql行最大限制爲65535字節,字符編碼爲utf-8:https://dev.mysql.com/doc/refman/5.7/en/column-count-limit.html)

    存儲:

        varchar類型存儲數據的真實內容,不會用空格填充,若是'ab  ',尾部的空格也會被存起來

        強調:varchar類型會在真實數據前加1-2Bytes的前綴,該前綴用來表示真實數據的bytes字節數(1-2Bytes最大表示65535個數字,正好符合mysql對row的最大字節限制,即已經足夠使用)

        若是真實的數據<255bytes則須要1Bytes的前綴(1Bytes=8bit 2**8最大表示的數字爲255)

        若是真實的數據>255bytes則須要2Bytes的前綴(2Bytes=16bit 2**16最大表示的數字爲65535)

   

    檢索:

        尾部有空格會保存下來,在檢索或者說查詢時,也會正常顯示包含空格在內的內容
View Code

Value

CHAR(4)

Storage Required

VARCHAR(4)

Storage Required

''

'    '

4 bytes

''

1 byte

'ab'

'ab  '

4 bytes

'ab'

3 bytes

'abcd'

'abcd'

4 bytes

'abcd'

5 bytes

'abcdefgh'

'abcd'

4 bytes

'abcd'

5 bytes

 

測試前瞭解兩個函數

length:查看字節數

char_length:查看字符數

 

5.11.2 定長char與變長varchar的示例

1. char填充空格來知足固定長度,可是在查詢時卻會很不要臉地刪除尾部的空格(裝做本身好像沒有浪費過空間同樣),而後修改sql_mode讓其現出原形

mysql> create table t1(x char(5),y varchar(5));

Query OK, 0 rows affected (0.26 sec)

 

#char存5個字符,而varchar存4個字符

mysql> insert into t1 values('你瞅啥 ','你瞅啥 ');

Query OK, 1 row affected (0.05 sec)

 

mysql> SET sql_mode='';

Query OK, 0 rows affected, 1 warning (0.00 sec)

 

#在檢索時char很不要臉地將本身浪費的2個字符給刪掉了,裝的好像本身沒浪費過空間同樣,而varchar很老實,存了多少,就顯示多少

mysql> select x,char_length(x),y,char_length(y) from t1;

+-----------+----------------+------------+----------------+

| x         | char_length(x) | y          | char_length(y) |

+-----------+----------------+------------+----------------+

| 你瞅啥    |              3 | 你瞅啥     |              4 |

+-----------+----------------+------------+----------------+

row in set (0.00 sec)

 

#略施小計,讓char現出原形

mysql> SET sql_mode = 'PAD_CHAR_TO_FULL_LENGTH';

Query OK, 0 rows affected (0.00 sec)

 

#這下子char原形畢露了......

mysql> select x,char_length(x),y,char_length(y) from t1;

+-------------+----------------+------------+----------------+

| x           | char_length(x) | y          | char_length(y) |

+-------------+----------------+------------+----------------+

| 你瞅啥      |              5 | 你瞅啥     |              4 |

+-------------+----------------+------------+----------------+

row in set (0.00 sec)

 

 

#char類型:3箇中文字符+2個空格=11Bytes

#varchar類型:3箇中文字符+1個空格=10Bytes

mysql> select x,length(x),y,length(y) from t1;

+-------------+-----------+------------+-----------+

| x           | length(x) | y          | length(y) |

+-------------+-----------+------------+-----------+

| 你瞅啥      |        11 | 你瞅啥     |        10 |

+-------------+-----------+------------+-----------+

row in set (0.00 sec)
View Code
############################瞭解concat#########################

mysql> select concat('數據: ',x,'長度: ',char_length(x)),concat(y,char_length(y)

) from t1;

+------------------------------------------------+--------------------------+

| concat('數據: ',x,'長度: ',char_length(x))     | concat(y,char_length(y)) |

+------------------------------------------------+--------------------------+

| 數據: 你瞅啥  長度: 5                          | 你瞅啥 4                 |

+------------------------------------------------+--------------------------+

row in set (0.00 sec)

 
瞭解concat

2. 雖然 CHAR 和 VARCHAR 的存儲方式不太相同,可是對於兩個字符串的比較,都只比 較其值,忽略 CHAR 值存在的右填充,即便將 SQL _MODE 設置爲 PAD_CHAR_TO_FULL_ LENGTH 也同樣,,但這不適用於like

Values in CHAR and VARCHAR columns are sorted and compared according to the character set collation assigned to the column.

 

All MySQL collations are of type PAD SPACE. This means that all CHAR, VARCHAR, and TEXT values are compared without regard to any trailing spaces. 「Comparison」 in this context does not include the LIKE pattern-matching operator, for which trailing spaces are significant. For example:

 

mysql> CREATE TABLE names (myname CHAR(10));

Query OK, 0 rows affected (0.03 sec)

 

mysql> INSERT INTO names VALUES ('Monty');

Query OK, 1 row affected (0.00 sec)

 

mysql> SELECT myname = 'Monty', myname = 'Monty  ' FROM names;

+------------------+--------------------+

| myname = 'Monty' | myname = 'Monty  ' |

+------------------+--------------------+

|                1 |                  1 |

+------------------+--------------------+

row in set (0.00 sec)

 

mysql> SELECT myname LIKE 'Monty', myname LIKE 'Monty  ' FROM names;

+---------------------+-----------------------+

| myname LIKE 'Monty' | myname LIKE 'Monty  ' |

+---------------------+-----------------------+

|                   1 |                     0 |

+---------------------+-----------------------+

row in set (0.00 sec)

 
View Code

5.11.3 課堂講解示例

char:定長

varchar:變長

 

#寬度表明的是字符的個數

create table t16(name char(5));

create table t17(name varchar(5));

 

insert into t16 values('李傑 '); #'李傑   '

insert into t17 values('李傑 '); #'李傑 '

 

select char_length(name) from t16; #5

select char_length(name) from t17; #3

 

mysql> set sql_mode='PAD_CHAR_TO_FULL_LENGTH';

 

select * from t16 where name='李傑';

select * from t17 where name='李傑';

 

select * from t16 where name like '李傑';

 

name char(5)

egon |alex |wxx  |

 

 

name varchar(5)

1bytes+egon|1bytes+alex|1bytes+wxx|
View Code

5.11.4 總結

#經常使用字符串系列:char與varchar

注:雖然varchar使用起來較爲靈活,可是從整個系統的性能角度來講,char數據類型的處理速度更快,有時甚至能夠超出varchar處理速度的50%
所以,用戶在設計數據庫時應當綜合考慮各方面的因素,以求達到最佳的平衡
#其餘字符串系列(效率:char>varchar>text) TEXT系列 TINYTEXT TEXT MEDIUMTEXT LONGTEXT BLOB 系列 TINYBLOB BLOB MEDIUMBLOB LONGBLOB BINARY系列 BINARY VARBINARY text:text數據類型用於保存變長的大字符串,能夠組多到65535 (2**16 − 1)個字符。 mediumtext:A TEXT column with a maximum length of 16,777,215 (2**24 − 1) characters. longtext:A TEXT column with a maximum length of 4,294,967,295 or 4GB (2**32 − 1) characters.

5.12 枚舉類型與集合類型

字段的值只能在給定範圍中選擇,如單選框,多選框

enum 單選                                  只能在給定的範圍內選一個值,如性別 sex 男male/女female

set 多選 在給定的範圍內能夠選擇一個或一個以上的值(愛好1,愛好2,愛好3...)

5.12.1 示例

        枚舉類型(enum)

            An ENUM column can have a maximum of 65,535 distinct elements. (The practical limit is less than 3000.)

            示例:

                CREATE TABLE shirts (

                    name VARCHAR(40),

                    size ENUM('x-small', 'small', 'medium', 'large', 'x-large')

                );

                INSERT INTO shirts (name, size) VALUES ('dress shirt','large'), ('t-shirt','medium'),('polo shirt','small');

 

 

 

            集合類型(set)

            A SET column can have a maximum of 64 distinct members.

            示例:

                CREATE TABLE myset (col SET('a', 'b', 'c', 'd'));

                INSERT INTO myset (col) VALUES ('a,d'), ('d,a'), ('a,d,a'), ('a,d,d'), ('d,a,d');
View Code
#########################驗證####################

MariaDB [db1]> create table consumer(

    -> name varchar(50),

    -> sex enum('male','female'),

    -> level enum('vip1','vip2','vip3','vip4','vip5'), #在指定範圍內,多選一

    -> hobby set('play','music','read','study') #在指定範圍內,多選多

    -> );

 

MariaDB [db1]> insert into consumer values 

    -> ('egon','male','vip5','read,study'),

    -> ('alex','female','vip1','girl');

 

MariaDB [db1]> select * from consumer;

+------+--------+-------+------------+

| name | sex    | level | hobby      |

+------+--------+-------+------------+

| egon | male   | vip5  | read,study |

| alex | female | vip1  |            |

+------+--------+-------+------------+
驗證

5.12.2 課堂講解示例

create table employee(

    id int,

    name char(10),

    sex enum('male','female','other'),

    hobbies set('play','eat','music','read')

);

 

insert into employee values

(1,'egon','male','music,read');

 

insert into employee values

(2,'alex','xxxx','music,read');
View Code
相關文章
相關標籤/搜索