进程也有死锁与递归锁,在进程那里忘记说了,放到这里一切说了额html
所谓死锁: 是指两个或两个以上的进程或线程在执行过程当中,因争夺资源而形成的一种互相等待的现象,若无外力做用,它们都将没法推动下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,以下就是死锁python
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锁 而后就卡住,死锁了 '''
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()
解决方法,递归锁,在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为止
同进程的同样git
Semaphore管理一个内置的计数器,程序员
每当调用acquire()时内置计数器-1;github
调用release() 时内置计数器+1;redis
计数器不能小于0;当计数器为0时,acquire()将阻塞线程直到其余线程调用release()。sql
实例:(同时只有5个线程能够得到semaphore,便可以限制最大链接数为5):mongodb
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()
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()
与进程池是彻底不一样的概念,进程池Pool(4),最大只能产生4个进程,并且从头至尾都只是这四个进程,不会产生新的,而信号量是产生一堆线程/进程
互斥锁与信号量推荐博客:http://url.cn/5DMsS9r
同进程的同样
线程的一个关键特性是每一个线程都是独立运行且状态不可预测。若是程序中的其 他线程须要经过判断某个线程的状态来肯定本身下一步的操做,这时线程同步问题就会变得很是棘手。为了解决这些问题,咱们须要使用threading库中的Event对象。 对象包含一个可由线程设置的信号标志,它容许线程等待某些事件的发生。在 初始状况下,Event对象中的信号标志被设置为假。若是有线程等待一个Event对象, 而这个Event对象的标志为假,那么这个线程将会被一直阻塞直至该标志为真。一个线程若是将一个Event对象的信号标志设置为真,它将唤醒全部等待这个Event对象的线程。若是一个线程等待一个已经被设置为真的Event对象,那么它将忽略这个事件, 继续执行
event.isSet():返回event的状态值; event.wait():若是 event.isSet()==False将阻塞线程; event.set(): 设置event的状态值为True,全部阻塞池的线程激活进入就绪状态, 等待操做系统调度; event.clear():恢复event的状态值为False。
例如,有多个工做线程尝试连接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()
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()
定时器,指定n秒后执行某操做
from threading import Timer def hello(): print("hello, world") t = Timer(1, hello) t.start() # after 1 seconds, "hello, world" will be printed
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()
queue队列 :使用import queue,用法与进程Queue同样
queue is especially useful in threaded programming when information must be exchanged safely between multiple threads.
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 '''
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 '''
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') '''
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())
#同步调用:提交完任务后,就在原地等待,等待任务执行完毕,拿到任务的返回值,才能继续下一行代码,致使程序串行执行
#异步调用+回调机制:提交完任务后,不在原地等待,任务一旦执行完毕就会触发回调函数的执行, 程序是并发执行
#阻塞
#非阻塞
#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) 回调函数
################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())
#######################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相同
###########################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
#######################回调函数#################### 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()拿到结果
# #同步调用示例: # # 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('主')
#线程池 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)
本节的主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只有一个)状况下实现并发,为此咱们须要先回顾下并发的本质:切换+保存状态
cpu正在运行一个任务,会在两种状况下切走去执行其余的任务(切换由操做系统强制控制),一种状况是该任务发生了阻塞,另一种状况是该任务计算的时间过长或有一个优先级更高的程序替代了它
ps:在介绍进程理论时,说起进程的三种执行状态,而线程才是执行单位,因此也能够将上图理解为线程的三种状态
一:其中第二种状况并不能提高效率,只是为了让cpu可以雨露均沾,实现看起来全部任务都被“同时”执行的效果,若是多个任务都是纯计算的,这种切换反而会下降效率。为此咱们能够基于yield来验证。yield自己就是一种在单线程下能够保存任务运行状态的方法,咱们来简单复习一下:
#1 yiled能够保存状态,yield的状态保存与操做系统的保存线程状态很像,可是yield是代码级别控制的,更轻量级 #2 send能够把一个函数的结果传给另一个函数,以此实现单线程内程序之间的切换
##############单纯地切换反而会下降运行效率###################### #串行执行 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
二:第一种状况的切换。在任务一遇到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)
对于单线程下,咱们不可避免程序中出现io操做,但若是咱们能在本身的程序中(即用户程序级别,而非操做系统级别)控制单线程下的多个任务能在一个任务遇到io阻塞时就切换到另一个任务去计算,这样就保证了该线程可以最大限度地处于就绪态,即随时均可以被cpu执行的状态,至关于咱们在用户程序级别将本身的io操做最大限度地隐藏起来,从而能够迷惑操做系统,让其看到:该线程好像是一直在计算,io比较少,从而更多的将cpu的执行权限分配给咱们的线程。
协程的本质就是在单线程下,由用户本身控制一个任务遇到io阻塞了就切换另一个任务去执行,以此来提高效率。为了实现它,咱们须要找寻一种能够同时知足如下条件的解决方案:
#1. 能够控制多个任务之间的切换,切换以前将任务的状态保存下来,以便从新运行时,能够基于暂停的位置继续执行。 #2. 做为1的补充:能够检测io操做,在遇到io操做的状况下才发生切换
# 单纯地切换反而会下降运行效率 #串行执行 # 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) #
协程:是单线程下的并发,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序本身控制调度的。
须要强调的是:
#1. python的线程属于内核级别的,即由操做系统控制调度(如单线程遇到io或执行时间过长就会被迫交出cpu执行权限,切换其余线程运行)
#2. 单线程内开启协程,一旦遇到io,就会从应用程序级别(而非操做系统)控制切换,以此来提高效率(!!!非io操做的切换与效率无关)
对比操做系统控制线程的切换,用户在单线程内控制协程的切换
优势以下:
#1. 协程的切换开销更小,属于程序级别的切换,操做系统彻底感知不到,于是更加轻量级
#2. 单线程内就能够实现并发的效果,最大限度地利用cpu
缺点以下:
#1. 协程的本质是单线程下,没法利用多核,能够是一个程序开启多个进程,每一个进程内开启多个线程,每一个线程内开启协程
#2. 协程指的是单个线程,于是一旦协程出现阻塞,将会阻塞整个线程
总结协程特色:
若是咱们在单个线程内有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时传入参数,之后都不须要
单纯的切换(在没有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
greenlet只是提供了一种比generator更加便捷的切换方式,当切到一个任务执行时若是遇到io,那就原地阻塞,仍然是没有解决遇到IO自动切换来提高效率的问题。
单线程里的这20个任务的代码一般会既有计算操做又有阻塞操做,咱们彻底能够在执行任务1时遇到阻塞,就利用阻塞的时间去执行任务2。。。。如此,才能提升效率,这就用到了Gevent模块。
#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')
#安装 pip3 install gevent
Gevent 是一个第三方库,能够轻松经过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet所有运行在主程序操做系统进程的内部,但它们被协做式地调度。
#用法 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的返回值
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('主')
上例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('主')
咱们能够用threading.current_thread().getName()来查看每一个g1和g2,查看的结果为DummyThread-n,即假线程。
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])
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执行完后才会继续向下走。
##########################协程应用:爬虫############################ 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))
经过gevent实现单线程下的socket并发(from gevent import monkey;monkey.patch_all()必定要放到导入socket模块以前,不然gevent没法识别socket的阻塞)
####################服务端##################### 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)
##################客户端################### #_*_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'))
########################多线程并发多个客户端########################## 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()
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()
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()
基于咱们以前所学,数据要想永久保存,都是保存于文件中,毫无疑问,一个文件仅仅只能存在于某一台机器上。
若是咱们暂且忽略直接基于文件来存取数据的效率问题,而且假设程序全部的组件都运行在一台机器上,那么用文件存取数据,并无问题。
很不幸,这些假设都是你本身意淫出来的,上述假设存在如下几个问题。。。。。。
#由于这台机器一旦挂掉则意味着整个软件的崩溃,而且程序的执行效率依赖于承载它的硬件,而一台机器机器的性能总归是有限的,受限于目前的硬件水平,
就一台机器的性能垂直进行扩展是有极限的。 #因而咱们只能经过水平扩展来加强咱们系统的总体性能,这就须要咱们将程序的各个组件分布于多台机器去执行。
#根据1的描述,咱们将程序的各个组件分布到各台机器,但需知各组件仍然是一个总体,言外之意,全部组件的数据仍是要共享的。但每台机器上的组件都只能操做本机的文件,
这就致使了数据必然不一致。 #因而咱们想到了将数据与应用程序分离:把文件存放于一台机器,而后将多台机器经过网络去访问这台机器上的文件(用socket实现),即共享这台机器上的文件,
共享则意味着竞争,会发生数据不安全,须要加锁处理。。。。
根据2的描述,咱们必须写一个socket服务端来管理这台机器(数据库服务器)上的文件,而后写一个socket客户端,完成以下功能:
#1.远程链接(支持并发) #2.打开文件 #3.读写(加锁) #4.关闭文件
#咱们在编写任何程序以前,都须要事先写好基于网络操做一台主机上文件的程序(socket服务端与客户端程序),因而有人将此类程序写成一个专门的处理软件,
这就是mysql等数据库管理软件的由来,但mysql解决的不只仅是数据共享的问题,还有查询效率,安全性等一系列问题,总之,把程序员从数据管理中解脱出来,
专一于本身的程序逻辑的编写。
描述事物的符号记录称为数据,描述事物的符号既能够是数字,也能够是文字、图片,图像、声音、语言等,数据由多种表现形式,它们均可以通过数字化后存入计算机
在计算机中描述一个事物,就须要抽取这一事物的典型特征,组成一条记录,就至关于文件里的一行内容,如:
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年考入老男孩计算机系
数据库即存放数据的仓库,只不过这个仓库是在计算机存储设备上,并且数据是按必定的格式存放的
过去人们将数据存放在文件柜里,如今数据量庞大,已经再也不适用
数据库是长期存放在计算机内、有组织、可共享的数据便可。
数据库中的数据按必定的数据模型组织、描述和储存,具备较小的冗余度、较高的数据独立性和易扩展性,并可为各类 用户共享
在了解了Data与DB的概念后,如何科学地组织和存储数据,如何高效获取和维护数据成了关键
这就用到了一个系统软件---数据库管理系统
如MySQL、Oracle、SQLite、Access、MS SQL Server
mysql主要用于大型门户,例如搜狗、新浪等,它主要的优点就是开放源代码,由于开放源代码这个数据库是免费的,他如今是甲骨文公司的产品。
oracle主要用于银行、铁路、飞机场等。该数据库功能强大,软件费用高。也是甲骨文公司的产品。
sql server是微软公司的产品,主要应用于大中型企业,如联想、方正等。
记录:1 刘海龙 324245234 22(多个字段的信息组成一条记录,即文件中的一行内容)
表:student,scholl,class_list(即文件)
数据库:oldboy_stu(即文件夹)
数据库管理系统:如mysql(是一个软件)
数据库服务器:一台计算机(对内存要求比较高)
总结:
数据库服务器-:运行数据库管理软件
数据库管理软件:管理-数据库
数据库:即文件夹,用来组织文件/表
表:即文件,用来存放多行内容/多条记录
MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下公司。MySQL 最流行的关系型数据库管理系统,在 WEB 应用方面MySQL是最好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。
#mysql就是一个基于socket编写的C/S架构的软件 #客户端软件 mysql自带:如mysql命令,mysqldump命令等 python模块:如pymysql
#分两大类: 关系型:如sqllite,db2,oracle,access,sql server,MySQL,注意:sql语句通用 非关系型:mongodb,redis,memcache #能够简单的理解为: 关系型数据库须要有表结构 非关系型数据库是key-value存储的,没有表结构
#二进制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; #刷新权限
###########################源码安装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
#################################安装######################## #一、下载: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
#################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
#################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;
初始状态下,管理员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用户登陆本机,密码为空
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,而后就能够以新密码登陆了
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 服务已经启动成功。
强调:配置文件中的注释能够有中文,可是配置项中不能出现中文
#########################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]为准
#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%'
有了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 表示:约束(不能重复且不能为空);加速查找
information_schema: 虚拟库,不占用磁盘空间,存储的是数据库启动后的一些参数,如用户表信息、列信息、权限信息、字符信息等
performance_schema: MySQL 5.5开始新增一个数据库:主要用于收集数据库服务器性能参数,记录处理查询请求时发生的各类事件、锁等现象
mysql: 受权库,主要存储系统用户的权限信息
test: MySQL数据库系统自动建立的测试数据库
CREATE DATABASE 数据库名 charset utf8;
能够由字母、数字、下划线、@、#、$
区分大小写
惟一性
不能使用关键字如 create select
不能单独使用数字
最长128位
1 查看数据库 show databases; show create database db1; select database(); 2 选择数据库 USE 数据库名 3 删除数据库 DROP DATABASE 数据库名; 4 修改数据库 alter database db1 charset utf8;
mysql支持的存储引擎 MariaDB [(none)]> show engines\G #查看全部支持的存储引擎 MariaDB [(none)]> show variables like 'storage_engine%'; #查看正在使用的存储引擎
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;
/etc/my.cnf [mysqld] default-storage-engine=INNODB innodb_file_per_table=1
[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
建立四个表,分别使用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,往表内插入任何数据,都至关于丢入黑洞,表内永远不存记录
表至关于文件,表中的一条记录就至关于文件的一行内容,不一样的是,表中的一条记录有对应的标题,称为表的字段
id,name,qq,age称为字段,其他的,一行内容称为一条记录
create table 表名(
字段名1 类型[(宽度) 约束条件],
字段名2 类型[(宽度) 约束条件],
字段名3 类型[(宽度) 约束条件]
);
1. 在同一张表中,字段名是不能相同 2. 宽度和约束条件可选 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)
############################往表中插入数据##################### 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 | +------+------+------+--------+
注意注意注意:表中的最后一个字段不要加逗号
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
语法: 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 旧字段名 新字段名 新数据类型 [完整性约束条件…];
示例: 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;
复制表结构+记录 (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;
DROP TABLE 表名;
存储引擎决定了表的类型,而表内存放的数据也要有不一样的类型,每种数据类型都有本身的宽度,但宽度是可选的
详细参考:
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. 枚举类型与集合类型
整数类型: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
###########################验证################################### =========有符号和无符号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
定点数类型 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可以存储精确值的缘由在于其内部按照字符串存储。
#####################验证######################## 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)
#一、整型(默认有符号) 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);
DATE TIME DATETIME TIMESTAMP YEAR
做用:存储用户注册时间,文章发布时间,员工入职时间,出生时间,过时时间等
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 | +------+------+-----------+------------+------------+---------------------+
####################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语句中没有指定该列的更新值,则默认更新为当前时间。
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") ;
#官网: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) 检索: 尾部有空格会保存下来,在检索或者说查询时,也会正常显示包含空格在内的内容
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:查看字符数
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)
############################了解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)
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)
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|
#经常使用字符串系列: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.
字段的值只能在给定范围中选择,如单选框,多选框
enum 单选 只能在给定的范围内选一个值,如性别 sex 男male/女female
set 多选 在给定的范围内能够选择一个或一个以上的值(爱好1,爱好2,爱好3...)
枚举类型(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');
#########################验证#################### 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 | | +------+--------+-------+------------+
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');