python數據庫進階

第1節 MySQL基礎

 

一,說明

1,認識MySQL與建立用戶
MySQL是最流行的關係型數據庫管理系統之一,由瑞典MySQL AB公司開發,目前屬於Oracle公司。MySQL是一種關聯數據管理系統,關聯數據庫將數據保存在不一樣的表中,而不是將全部數據放在一個大倉庫內,這樣就增長了速度並提升了靈活性。

- 關係型數據庫:採用關係模型來組織數據的數據庫
- 關係:一張二維表,每一個關係都有一個關係名,就是表名
- 模型:行和列(二維),具體指字段跟字段信息
2,關係型數據庫和非關係型數據庫的區別
  • 關係型數據庫經過外鍵關聯來創建表與表之間的關係,前端

  • 非關係型數據庫一般指數據以對象的形式存儲在數據庫中,而對象之間的關係經過每一個對象自身的屬性來決定java


      好比 有一個學生的數據:

            姓名:張三,性別:男,學號:12345,班級:二年級一班

    還有一個班級的數據:

          班級:二年級一班,班主任:李四

# 關係型數據庫中,咱們建立學生表和班級表來存這兩條數據,而且學生表中的班級存儲的是班級表中的主鍵。

# 非關係型數據庫中,咱們建立兩個對象,一個是學生對象,一個是班級對象,
3,常見數據庫
數據庫類型 特性 優勢 缺點
關係型數據庫SQLite、Oracle、mysql 一、關係型數據庫,是指採用了關係模型來組織數據的數據庫;二、關係型數據庫的最大特色就是事務的一致性;三、簡單來講,關係模型指的就是二維表格模型,而一個關係型數據庫就是由二維表及其之間的聯繫所組成的一個數據組織。 一、容易理解:二維表結構是很是貼近邏輯世界一個概念,關係模型相對網狀、層次等其餘模型來講更容易理解;二、使用方便:通用的SQL語言使得操做關係型數據庫很是方便;三、易於維護:豐富的完整性(實體完整性、參照完整性和用戶定義的完整性)大大減低了數據冗餘和數據不一致的機率;四、支持SQL,可用於複雜的查詢。 一、爲了維護一致性所付出的巨大代價就是其讀寫性能比較差;二、固定的表結構;三、高併發讀寫需求;四、海量數據的高效率讀寫;
非關係型數據庫MongoDb、redis、HBase 一、使用鍵值對存儲數據;二、分佈式;三、通常不支持ACID特性;四、非關係型數據庫嚴格上不是一種數據庫,應該是一種數據結構化存儲方法的集合。 一、無需通過sql層的解析,讀寫性能很高;二、基於鍵值對,數據沒有耦合性,容易擴展;三、存儲數據的格式:nosql的存儲格式是key,value形式、文檔形式、圖片形式等等,文檔形式、圖片形式等等,而關係型數據庫則只支持基礎類型。 一、不提供sql支持,學習和使用成本較高;二、無事務處理,附加功能bi和報表等支持也很差;

注1:數據庫事務必須具有ACID特性,ACID是Atomic原子性,Consistency一致性,Isolation隔離性,Durability持久性。python

注2:數據的持久存儲,尤爲是海量數據的持久存儲,仍是須要一種關係數據庫。mysql


 

二,登陸MySQL

不區分大小寫,可是系統默認大寫爲系統代碼,小寫爲程序員代碼程序員

1,經過root用戶進入數據庫

mysql -uroot -ppassword    # 登陸數據庫
exit     # 退出
\q       # 退出
2,建立用戶名及密碼

# 建立用戶三個步驟
create user 'lucky'@'%' identified by 'password'; # 建立用戶名(%表示遠程鏈接,可用此用戶名登錄)
grant all on *.* to 'lucky'@'%';    # 賦予全部權限
flush privileges;                   # 使權限生效

# 此時以root用戶登錄,登陸lucky用戶:
\q                                  # 退出root
mysql -ulucky -ppassword;           # 登陸lucky用戶
select user();                      # 查看當前用戶
select database();                  # 查看當前位於哪一個數據倉庫
3,建立數據庫

show databases;       # 查看當前有哪些數據庫
create database mydb; # 建立數據庫(數據庫已存在會報錯,若是沒有,則建立)
create database if not exists mydb;# 數據庫已存在不會報錯,若是沒有會建立

# 對於系統內部的,默認的數據倉庫,不須要也不能去修改
drop database mydb;   # 刪除數據庫
show databases;
create database if not exists mydb;
use mydb;             # 進入數據庫
select database();
4,建立數據表

create table student(        # student爲建立表的name
  id int,                 # id 爲字段編號,int爲其數據類型
  name varchar(20),       # name爲字段名,varchar爲類型,20爲長度
  );                       # 建表,分號結尾
show tables;                 # 查看當前數據倉庫內有哪些表格
5,查看數據表結構

desc student;               # 查看student數據表的結構
show create table student;  # 查看建立表時有哪些命令
(drop table student;        # 刪除表格)
6,經常使用方法

# 增

# 方法一
insert into student (id,name) value (1,'張三'); # 向數據表內增長內容into 能夠省略
insert into student value(2,'李四');    # ()中的字段名可寫可不寫,默認按順序添加
insert into student value(3)          # 也可只插入id
insert into student values(3,'王五'),(4,'趙六')

select * from student;   # 查詢當前數據表

# 方法二
insert student set id=5,name='錢七';

-------------------------------------------------------------

# 查

select * from student;                # 查看student
select * from student where id<=4;    # 查看student 而且id小於4的內容
select name from student where id<=4; # 查看student 內的name 段,而且id小於4的內容

-------------------------------------------------------------

# 改

update student set name='name1' where id=5;# 將id=5的那一行的name 改成'name1'
                                          # 若是不加where 限制條件,則表中的name全被更改
                                         
------------------------------------------------------------                                      
# 刪

delete from student where name='錢七';     # 刪除全部name=錢七的數據
7,mysql數據類型
數據類型 表明內容 事例
int 整型 id int
varchar(20) 指定長度,最多65535個字符。變長(超出會自動截掉) name varchar(20)(插入20個字符)
char(4) 指定長度,最多255個字符。定長 sex char(4)(無論插入幾個字符都會佔4個)
double(4,2) 雙精度浮點型,m總個數,d小數位 price double(4,2)
text 可變長度,最多65535個字符 detail text
datetime 日期時間類型YYYY-MM-DD HH:MM:SS dates datetime
enum('good','not good') 枚舉,在給出的value中選擇 ping enum('good','not good')
  • 流程redis


creat table tb2(
id INT,
name VARCHAR(20),
sex CHAR(4),
price DOUBLE(4,2),
detail TEXT,
datae DATATIME,
ping ENUM('好評','差評')
);
insert intotb2 value(1,'褲子','男',20.0,'這條褲子超級好!',now(),'好評');

 

3、小結

1,經常使用方法
功能 用法  
insert [into] student (id,name) value (1,'王一凡'); 增的第一個語法
  insert into tanzhou value(2,'name'); ()中的字段名可寫可不寫,默認按順序添加
  insert into student value(3) 也可只插入id
  insert into tanzhou set id=4,name='name1'; 增的第二個語法
select * from student; 查看student
  select * from student where id<4; 查看student 而且id小於4的內容
  select name from studen where id<4; 查看student 內的name 段,而且id小於4的內容
update student set name='name1' where id=5; 將id=5的那一行的name 改成'name1'
  update tanzhou set name='name1' 若是不加where 限制條件,則表中的name全被更改
delete from student where name='錢七'; 刪除全部name=錢七的數據
2,經常使用命令
功能 用法 註釋
登陸數據庫 mysql -uroot -ppassword  
退出 exit 或 \q  
建立用戶 create user 'lucky'@'%' identified by 'password'; %表示遠程鏈接,可用此用戶名登錄
賦予全部權限 grant all on *.* to 'lucky'@'%';  
使權限生效 flush privileges; flush寫入
查看當前用戶 select user();  
查看當前位於哪一個數據庫 select database();  
查看當前有哪些數據庫 show databases;  
建立數據庫 create database mydb;create database [if not exists] mydb; 建立以mydb爲名創建一個數據庫,[]爲可選參數
刪除數據庫 drop database mydb;  
進入數據庫 use mydb;  
建立數據表 create table [if not exists] student(<br />id int ,<br />name varchar(10)<br />); student爲建立表的name <br /> id 表示(元組)字段編號,數據爲int型 <br /> name爲字段名,varchar爲類型,20爲長度
查看當前數據倉庫內有哪些表 show tables;  
查看錶的結構 desc student;  
查看建立表時的命令 show creat table tanzhou;  
刪除表格 DROP TABLE tablename;  

第2節 表約束,表關係

 


 

1、表約束:

1.非空約束 ((not null))

例子:
create table tb1(
id int not null,     #非空約束字段,insert 的時候,必須添加字段,不能省略,空字符不等於null
name varchar(20) not null  
);

# 若是在建立表時沒有添加非空約束
# 後續手動添加非空約束(必須這個字段,沒有NULL值)
alter table tb1      # 修改表結構
modify id int not null;

# 取消非空約束
alter table tb1
modify id int;

 

2.惟一約束 (unique key)
  • 確保字段中的值的惟一sql


例子:
create table tb2(
id int unique key,  # 防止id相同
)

# 若是在建立表時沒有添加非空約束
# 後續手動添加惟一約束
alter table tb1      # 修改表結構
add unique key(name);

# 刪除惟一約束
alter table tb1
drop key name;

 

3.自增加 (auto_increment)
  • auto_increment:自動編號,通常與主鍵組合使用。一個表裏只有一個自增默認狀況下,起始值爲1,每次的增量爲1。mongodb


create table stu3(
id int primary key auto_increment, # 自增加,輸錯時在終端不會佔用數據,在其餘鏈接工具如pycharm中就會佔用一條數據
name varchar(10)
)auto_increment 100

insert into stu3 value('a');
insert into stu3(name) value('bc');
select * from stu3;

# 若是在建立表時沒有添加自動增加
# 後續手動添加自動增加
alter table stu3      # 修改表結構
modify id int auto_increment;

# 刪除自動增加
alter table stu3
modify id int;

 

4.默認約束 (default)
  • 初始值設置,插入記錄時,若是沒有明確爲字段賦值,則自動賦予默認值數據庫


create table stu4(
id int primary key auto_increment,
name varchar(20) not null,
age int not null default 18      # 默認約束
);

insert into stu4 value(1,'張三','18');
insert into stu4 value(2,'李四','');        #會報錯
insert into stu4(id,name) value(2,'玲玲');  #設置要輸入的字段值,age可爲空字符串
insert into stu4 value(6,'',19);  #空字符串 != null

# 若是在建立表時沒有添加默認約束
# 後續手動添加默認約束
alter table stu4      # 修改表結構
modify age int default 20;

# 刪除默認約束
alter table stu4
modify age int;

 

5.主鍵約束 (primary key)
  • 主鍵做用:能夠惟一標識一條數據,每張表裏只能有一個主鍵編程

  • 主鍵特性:非空且惟一,表沒有主鍵時,第一個出現的非空且惟一


create table stu2(
id int primary key,  # id是主鍵,主鍵不能爲空
name varchar(10)
);

desc stu2            # 此時能夠看到id的key變爲pri,且Null下變爲NO

# 若是在建立表時沒有添加主鍵約束
# 後續手動添加主鍵約束
alter table stu2      # 修改表結構
add primary key(id);

# 刪除主鍵約束
alter table stu2      # 修改表結構
drop primary key;

 

6.外鍵約束 (foreign key)
  • 保持數據的一致性、完整性,實現一對一或一對多關係。

  • 外鍵必須關聯到鍵上面去,通常狀況是,關聯到另外一張表的主鍵。

  • 由於一個表只在一類信息。用外鍵來作參照,保證數據的一致性,能夠減小數據冗餘。


# 表a
create table a(
a_id int primary key auto_increment,
a_name varchar(20) not null
);

# 表b
create table b(
b_id int primary ke,
b_name varchar(20)not null,
fy_id int not null,
constraint AB_id foreign key(fy_id) references a(a_id)  # 外鍵約束定義
);

# 說明:
1,經過(fy_id)關聯到(a_id)中,而後從新命名爲 AB_id(其中constraint 可不寫)
2,外鍵約束定義,B表中的fy_id,只能添加a_id中已有的數據。A表中的a_id 被參照的數據,不能被修改和刪除。

# 若是在建立表時沒有添加外鍵約束
# 後續手動添加外鍵約束
alter table b      # 修改表結構
constraint AB_id foreign key(fy_id) references a(a_id);

# 刪除外鍵約束
alter table stu2      # 修改表結構
drop foreign key AB_id;

 

2、表關係

  • 單位表,學生表,課程表,三張表,多對多

  • 一對一爲主鍵和主鍵關聯,一對可能是主鍵和外鍵關聯,加中間表就是多對多

1,一對一:
  • 用外鍵的方式,把兩個主鍵關聯

  • 舉例,學生表中有學號、姓名、學院,但學生還有寫好比電話,家庭地址等信息,這些信息不會放在學生表中,會新建一個學生的詳細信息表來儲存。

  • 這時的學生表和學生的詳細信息二者的關係就是一對一的關係,由於一個學生只有一條詳細信息,**用主鍵加主鍵的方式**來實現這種關係


# 創建學生表
create table student(
id int primary key,
name varchar(19) not null
);
insert into student value(1);

--------------------------------------------------------------------------------

#創建詳細學生表
create table student_details(
id_x int primary key,
sex varchar(20) not null,
age int,
address varchar(20) comment'家庭住址',
parents varchar(20),
home_num varchar(20),
foreign key(id_x) references student(id)
);

insert into student_details values(1,'趙六')  

# 說明:
1,此時student中的id1,對應student_details中的id1。。student中沒有插入的name,對應student_details中插入的趙六
2,學生表和學生詳情表經過student.id 和student_deatails.id_x關聯,造成一對一關係,只能插入主鍵相同的數據

 

2,一對多:
  • 一般狀況下,學校中一個學院能夠有不少學生,而一個學生只屬於某一個學院

  • 學院與學生之間的關係就是一對多的關係,經過**外鍵關聯**來實現這種關係


# 建立學院表
create table department(
id int primary key,
subject varchar(10) not null
);

insert into department values(001,'Python'),(002,'JAVA');


#建立學生表                                
create table vip(
id_v int primary key,          # 學生的id
name_v varchar(10) not null,   # 學生名字
id_w int not null              # 所屬學院id,只能添加已有的學院id
constraint SD_id foreign key(id_w) references department(id)  # 外鍵
);

insert into vip value(001,'王五',1);  # (001(id_v), 名字,1(department表的 ID))

 

3,多對多:
  • 舉例,學生要報名選修課,一個學生能夠報名多門課程,一門課程有不少學生報名,那麼學生表和課程表二者就造成了多對多關係

  • 對於多對多關係,須要建立中間表實現


#建立學生表
create table sst(
id int primary key auto_increment,
name varchar(10) not null
);


#建立選課表(中間表)
create table course(
s_id int primary key,         #記錄學生的id
course_id int,                #用來記錄課程id
primary key(s_id,course_id)   #聯合主鍵(此組合爲惟一的,防止一個id重複報一個課程)
foreign key(s_id) references student(id_v),  #關聯學生id 外鍵
foreign key(course_id) references(course_id) #關聯課程id 外鍵
name varchar(10) not null,
);

insert into cours values(001,'py'),(002,'java'),(003,'japan');
insert into ssh values(11,'a'),(22,'b'),(33,'c');
insert into course values(22,3),(11,2);    # 用中間表來多對多
select * from course;

 

4,做業

(1)創建選課系統中的5張表:(學院表,學生表,學生詳情表,課程表,選課表),並每張表插入4條數據。

(2)用文字描述:這5張表之間,對應關係。並說明如何實現這個對應關係。

 

第3節 MySql 基礎三

 

 

補充 alter 命令,修改表結構命令

 


create table tb1(
id int,
name char(4));
#修改表結構,增長字段 年齡
alter table tb1 add age int first; #增長年級字段到第一列;
alter table tb1 drop age ; #刪除年齡字段;
alter table tb1 add age int after id;#在id後面增長一個字段age,age在第個字段;

#修改已經存在字段的類型
alter table tb1 modify age char(4)  #把age字段修改爲char類型;

#修改列名(字段名)
alter table tb1 change age sex char(4); #將age字段修改成sex;

#修改表名
alter table tb1 rename tb2 #將表tb1修改成tb2

第4節 MySQL 單表查詢 子查詢 關聯查詢

 

1、MySQL單表查詢

mysql> show tables;
+----------------+
| Tables_in_mydb |
+----------------+
| choose         |
| course         |
| details       |
| student       |
| tanzhou       |
+----------------+
mysql> desc tanzhou;
+---------+-------------+------+-----+---------+----------------+
| Field   | Type       | Null | Key | Default | Extra         |
+---------+-------------+------+-----+---------+----------------+
| tz_id   | int(11)     | NO   | PRI | NULL   | auto_increment |
| tz_name | varchar(10) | NO   |     | NULL   |               |
+---------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
mysql> select * from tanzhou;
+-------+--------------+
| tz_id | tz_name     |
+-------+--------------+
|     1 | 軟件學院     |
|     2 | 語言學院     |
|     3 | 市場營銷     |
|     4 | 養殖學院     |
+-------+--------------+
4 rows in set (0.00 sec)
mysql> desc student;
+---------+-------------+------+-----+---------+----------------+
| Field   | Type       | Null | Key | Default | Extra         |
+---------+-------------+------+-----+---------+----------------+
| st_id   | int(11)     | NO   | PRI | NULL   | auto_increment |
| st_name | varchar(10) | NO   |     | NULL   |               |
| ts_id   | int(11)     | NO   | MUL | NULL   |               |
+---------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
mysql> select * from student;
+-------+-----------+-------+
| st_id | st_name   | ts_id |
+-------+-----------+-------+
|     1 | 三花     |     1 |
|     2 | 賈梅     |     1 |
|     3 | 餘玲玲   |     3 |
|     4 | 王濤     |     2 |
+-------+-----------+-------+
4 rows in set (0.00 sec)
mysql> desc course;
+--------+-------------+------+-----+---------+----------------+
| Field | Type       | Null | Key | Default | Extra         |
+--------+-------------+------+-----+---------+----------------+
| c_id   | int(11)     | NO   | PRI | NULL   | auto_increment |
| c_name | varchar(10) | YES |     | NULL   |               |
| tzc_id | int(11)     | NO   | MUL | NULL   |               |
+--------+-------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
mysql> desc choose;
+-------+---------+------+-----+---------+-------+
| Field | Type   | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| sc_id | int(11) | NO   | PRI | NULL   |       |
| cc_id | int(11) | NO   | PRI | NULL   |       |
+-------+---------+------+-----+---------+-------+
2 rows in set (0.01 sec)

一、查詢全部記錄
select * from student;#查詢student表中的全部內容;
二、查詢選中列記錄
select s_name from student;#查詢表中所在字段的內容;
三、查詢指定條件下的記錄
select s_name from student where s_id>4;#查詢學號s_id 大於4的學生的姓名;
四、查詢後爲列取別名
select s_name as 姓名 from student; #將s_name 字段查詢並命名爲姓名(as可加可不加)
select s_name 姓名 from student; #能夠省略 as 在作聯合查詢時要加上別名,方便區分不一樣的表
五、模糊查詢
select * from student where s_name like '三%';#查詢姓名中帶有 '三'的信息
select * from student where s_name like '%三';#查不出
select * from student where s_name like '三_';
select * from student where s_name like '三__'#兩條下劃線表示兩個字符

六、排序:order by (:ASC 升序(默認升序))
select * from student order by ts_id asc; #升序,默認
select * from student order by ts_id desc;#降序

七、限制顯示數據數量 LIMIT
select * from student limit 2;#顯示前兩條數據
select * from student limit 4,3;#從第4條數據開始,顯示3條數據

八、經常使用的聚合函數 max,min,sum,avg(平均數),round(avg(age))(四捨五入),count(統計) 一般和其它查詢配合使用
mysql> select * from details;
+----+------+------+
| id | age | sex |
+----+------+------+
|  1 |   28 | 男   |
|  2 |   18 | 女   |
|  3 |   28 | 女   |
|  4 |   18 | 男   |
+----+------+------+

select max(age) from details;#查詢age字段數值最大的數據

九、分組查詢 GROUP BY   通常和聚合函數一塊兒使用
  HAVEING 分組條件
  HAVEING 後面的字段必須是select 後出來過的字段
   
   #查詢每一個學院有多少學生
  select count(ts_id) from student group by ts_id; #(count了4次學院出現的次數(一個學院幾個學生))
  select count(ts_id) 學生人數 from student group by ts_id;
  select ts_id '學院',count(ts_id) 學生人數 from student group by ts_id;#每一個學院有多少學生
 
  #查詢只有一個學生的學院名稱
  select ts_id '學院',count(ts_id) 學生人數 from student group by ts_id having count(ts_id)=1;
 
2、MySQL 子查詢 嵌套查詢 出如今其它SQL語句內的 select 字句,select 中嵌套select

 


#子查詢(要求:嵌套在查詢內部,必須始終出如今圓括號內)
#求出學生的平均年齡
select avg(age) from details;
#查找出大於平均年齡的數據
select * from details where age>20;#此時20爲上一條查詢語句的結果
#將平均數的SQL語句做爲子查詢放入上一條語句中
select * from details where age>(select avg(age)) from details); #子查詢(把第一條查詢語句的結果當成條件)

#例:查詢譚州學院中語言學院和軟件學院兩個學院的學生信息
select tz_id from tanzhou where tz_name='軟件' or tz_name='語言';#查詢軟件學院和語言學院的ID
select * from student where ts_id in (select tz_id from tanzhou where tz_name='語言' or tz_name='軟件');#查詢譚州學院中語言學院和軟件學院兩個學院的全部學生
3、關聯查詢(內鏈接,外鏈接) 多表查詢

一、內鏈接:(經過外鍵把把兩張錶鏈接起來)

  • 無條件內鏈接:又名交叉鏈接/笛卡爾鏈接。第一張表的每一項會和另外一張表的每一項依此組合。

  • 有條件內鏈接:在無條件的內鏈接基礎上加一個OM子句。當鏈接的時候,篩選出那些有實際意義的記錄行來進行拼接。


#無條件內鏈接
沒有選擇的其餘學院信息也會顯示,有用數據少
數據冗餘大,笛卡爾鏈接(這兩種效果同樣)
select * from student,tanzhou;
select * from student inner join tanzhou;

#有條件內鏈接 on 條件
只會顯示有用信息
select * from student inner join tanzhou on ts_id=tz_id;#(學生id=課程id)

二、外鏈接:外鏈接必需要加條件

  • 左外鏈接:以左邊的表爲基準。兩張表作鏈接的時候,在鏈接條件不匹配的時候留下左表中的數據,而右表中的數據以NULL填充。

  • 右外鏈接:以右邊的表爲基準。兩張表作鏈接的時候,在鏈接條件不匹配的時候留下右表中的數據,而左表中的數據以NULL填充。


insert into tanzhou value(5,'設計');
select * from tanzhou left join student on ts_id=tz_id;#使用左鏈接把學生信息都取出來,該學生沒有學院信息的用NULL填充
select * from tanzhou right join student on ts_id=tz_id;#使用右外鏈接,把沒有學院的學生數據也顯示出來(設計沒有了)
select * from student right join tanzhou on ts_id=tz_id where s_id is null;#此時把沒有人的學院(設計)找出來了



#查詢學生的學號、姓名、課程、所屬學院
select * from student left join tanzhou on tz_id=ts_id#譚州id譚州student id
left join course on tz_id=tzc_id #譚州id和譚州課程id
left join choose on cc_id=c_id;#選課表和課程表

# 針對想要的列的信息查詢
select s_id,s_name,c_name,tz_name from student left join tanzhouon on tz_id=ts_id
left join course on tz_id=tzc_id
left join choose on cc_id=c_id;

#找出未選課的同窗
select s_id,s_name,c_name,tz_name from student left join tanzhouon on tz_id=ts_id
left join course on tz_id=tzc_id
left join choose on cc_id=c_id where c_name is null
#是NULL時用is
where c_name='java';
where c_name is null;
做業:

​ 一、查出學生神情表性別爲男,並同時年齡大於18的;

​ 二、根據上述的結果,查出學生表對應的姓名、年齡、家庭住址;

​ 三、查出學生的(姓名、年齡、性別、所屬學院)

 

 

 

 

 

 

redis 基本操做

1. 什麼是Redis


Redis是由意大利人Salvatore Sanfilippo(網名:antirez)開發的一款內存高速緩存數據庫。Redis全稱爲:Remote Dictionary Server,該軟件使用C語言編寫,Redis是一個key-value存儲系統,它支持豐富的數據類型,如:string、list、set、zset(sorted set)、hash。

Redis特色:
Redis之內存做爲數據存儲介質,因此讀寫數據的效率極高,遠遠超過數據庫。

Redis應用場景:
由於Redis交換數據快,因此在服務器中經常使用來存儲一些須要頻繁調取的數據,這樣能夠大大節省系統直接讀取磁盤來得到數據的I/O開銷,更重要的是能夠極大提高速度。
將這種熱點數據存到Redis(內存)中,要用的時候,直接從內存取,極大的提升了速度和節約了服務器的開銷。

 

進入redis

redis-cli   --raw



#不能進入,就安裝:
sudo apt-get update
sudo apt-get install redis-server

 

redis五種數據類型、及操做

  • string 字符串

  • list 列表

  • hash 哈希

  • set 集合

  • sorted sets 有序集合

string


#設置
set key value     例:   ( set name 'lucky' )
# 獲取
get key           例:   ( get name   )
# key是惟一的,不能用同一個key 否則就會覆蓋

查看過時時間


# -1 表示永久     -2 表示 不存在
ttl key 例:   ( ttl name )

設置過時時間


# 給已經存在 key,設置過時時間
expire key seconds   例: ( expire name 20 )

# 設置 key的同時,設置過時時間
set key value ex seconds   例: ( set age 18 ex 20 )

setex key seconds value   例: ( setex sex 20 '男' )

追加


# 給已有的value,再添加新的值
# append key value

append name love
#get name

 

設置 / 獲取 多個


#設置多個 string
mset key value key value..
例子
mset username 'lucky' password  '123'

#獲取多個
mget key key key ...

mget username password name

key 操做


# 查看全部的key
keys *

# 刪除
del key 例: (del name)

# 查看key是否存在 ,存在返回 1, 不存在返回 0
exists key

# 查看key類型
type key
運算

set num 1    # 自動識別,字符串裏面的 整數
# +1
incr key       例 (incr num)
# -1
decr key       例 (decr num)

# +整數
incrby key increment             例 (incrby num 50)

# -整數
decrby key increment             例 (decrby num 50)

list

  • 設置


    # lpush 左添加 (棧)
    lpush key value     #例 lpush my_list a b c d

    #rpush 右添加   (隊列)
    rpush key value     #例 rpush my_rlist a b c d
  • 查看


    # lrange key start stop
    例 查看全部
    lrange my_list 0 -1
  • 得到list的元素個數


    llen key        #例 llen my_list  
  • 查看位於lindex 位置上的元素


    lindex key index    #例 lindex my_list 3
  • 刪除

    • lpop 刪除左邊第一個


      lpop key        #例 lpop my_list
  • rpop 刪除右邊第一個


    rpop key        #例 rpop my_list
  • lrem 刪除指定


    rpush test_list a a b b c d e a d f m c
    #lrem key count value

    #count > 0 從左往右 刪除數量爲count的value
    例 : lrem test_list 2 a

    #count = 0 刪除全部的 value
    例 : lrem test_list 0 a

    #count < 0 從右往左 刪除數量爲count的value
    例 : lrem test_list -1 b

hash

  • 設置


    # hset key field value
    例 user { name:lucky}
    hset user name lucky
  • 獲取


    # hget key field

    hget user name
  • 刪除


    # hdel key field

    hdel user name
  • 設置多個


    #hmset key field value [field value]
    例 user{name:lucky , age:18, sex:male }
    hmset user name lucky age 18 sex male

  • 獲取多個


    # hmget key field field

    hmget user name age

  • 獲取所有field value


    # hgetall key

    hgetall user

  • 獲取全部的field


    # hkeys key

    hkeys user

  • 獲取全部的value


    # hvals key

    hvals user

  • 獲取field的個數


    # hlen key

    hlen user

set

  • 設置


    # sadd key value [value]   (惟一,無序)

    sadd my_set m n b v c x z b
  • 獲取


    # smembers key

    smembers my_set
  • 刪除

    • srem指定刪除


      # srem key member

      srem my_set c
    • spop隨機刪除


      # spop key

      spop my_set
  • 移動一個集合的值到另外一個集合


    # smove oldkey newkey member

    smove my_set my_set2 b
  • 判斷集合存在某個值


    # sismember key value

    sismember my_set2 b
  • 交集


    # sinter key1 key2 ..

    sinter my_set my_set2

    把 key1 key2的交集合併到newkey


    # sinterstore newkey key1 key2

    sinterstore new_set my_set my_set2
  • 並集


    # sunion key1 key2 ...

    sunion my_set my_set2

    把 key1 key2的並集合併到newkey


    # sunionstore newkey key1 key2

    sunionstore new_set2 my_set my_set2
  • 差集


    # sdiff key1 key2
    sdiff my_set my_set2

    把 key1 key2的差集合併到newkey


    # sdiffstore newkey key1 key2

    sdiffstore new_set3 my_set my_set2
  • 獲取集合個數


    # scard key

    scard my_set
  • 隨機返回一個


    # srandmember key

    srandmember my_set

zset

  • 設置


    # zadd key score member

    zadd my_zset 1 'one'
    zadd my_zset 2 'two'
    zadd my_zset 3 'three'
    zadd my_zset 4 'four'
    zadd my_zset 5 'five'
  • 獲取

    • zrange正序


      #zrange key start stop   (withscores)
      zrange my_zset 0 -1  
    • zrevrange倒序


      #zrevrange key start stop
      zrevrange my_zset 0 -1
  • 刪除


    #zrem key member
    zrem my_zset two
  • 索引

    • zrank正序


      # zrank key member

      zrank my_zset three
    • zrevrank反序


      # zrevrank key member

      zrevrank my_zset three
  • zcard 查看有序集合元素數


    # zcard key

    zcard my_zset
  • zrangebyscore 返回集合中 score 在給定區間的元素


    # zrange my_zset 0 -1 withscores

    zrangebyscore my_zset 2 3 withscores
    #返回了 score 在 2~3 區間的元素
  • zcount 返回集合中 score 在給定區間的數量


    # zcount key min max

    zcount my_zset 2 3
  • zscore : 查看score值


    # zscore key member

    zscore my_zset2 a

  • zremrangebyrank : 刪除集合中排名在給定區間的元素


    # zrange my_zset 0 -1 withscores
    zremrangebyrank my_zset 1 3

  • zremrangebyscore : 刪除集合中 score 在給定區間的元素


    # zrange my_zset 0 -1 withscores
    zremrangebyscore my_zset 1 2

做業

1.練習redis 5種數據類型、及操做 ( 不須要截圖上交,本身練習)



 

 

 

 

python操做mysql,redis

python操做mysql

pip install pymysql      


要操做數據庫,首先就要創建和數據庫的鏈接,配置pymysql.connect鏈接數據庫:


con = pymysql.connect(
   host = '主機',
   port = 端口,
   user = '用戶名',
   password = '密碼',
   db = '數據庫名',
   charset = 'utf8'
)

# 定義遊標
cursor = con.cursor()  
cursor.execute('show databases')        #執行sql
one = cursor.fetchone()              #取出一條數據
all = cursor.fetchall() #取出全部數據
print(one)
print(all)

# 插入
row = cursor.execute("insert into test(name,sex) values(%s,%s)", ('lucky','male'))

# 更新
row = cursor.execute("update test set name= '三花' where id = %s", (2,))

# 刪除
cursor.execute('delete from user where id=%s ',(1,) )


# 關閉鏈接
con.commit()    #提交事物
cursor.close()  #關閉遊標
con.close() # 關閉鏈接


## 聯合查詢

union = '''
select s.name, c.name,d.name from `student` s
left join `select` se on se.s_id = s.s_id
left join course c on se.c_id = c.id
left join department d on s.dept_id = d.id
ORDER BY s.name;
'''
# cursor.execute(union)
# find =cursor.fetchall()

python操做 redis

pip  install redis



# 建立鏈接
re = redis.Redis(host='127.0.0.1', port='55555')
## 測試
re.set('num',15)
print(re.get('num'))

## set 中文
re.set('name','張三')
print(re.get('name').decode('utf8') )


### 大部分的命令 和 redis 中操做同樣
不一樣:
re.ttl()       ### 不能看 負數 -1   -2
re.mset()         ## 用鍵值對
re.incr()        ## incr   能夠加參數,代替了 incrby
re.decr()        ## decr   能夠加參數,代替了 decrby
re.lrem()        ## num 放到後面
re.hmset()       # 多插入,用字典

 

 

 

 

json模塊和os模塊

 

數據交換JSON

JSON 的基本介紹

JSON全名是JavaScript Object Notation(即:JavaScript對象標記)它是JavaScript的子集。

前端和後端進行數據交互,其實就是JS和Python進行數據交互

json模塊 API

接口一:json.dumps(obj) # 將obj這個對象,變成JSON字符串

接口二:json.loads(s) # 將s這個JSON字符串,轉換成Python的數據類型

接口三:json.dump

接口四:json.load

JSON注意事項:

一、名稱必須用雙引號(即:」」)來包括

二、值能夠是雙引號包括的字符串、數字、true、false、null、JavaScript數組,或子對象。

Python JSON
字典 對象
列表或元組 數組
字符串 字符串
int或float 數字
True或False true或false
None null

 

 

 

文件與路徑操做OS模塊

os模塊 的目錄及文件操做

顯示當前路徑:os.getcwd()

展現當前目錄內容:os.listdir(path)

改變當前路徑: os.chdir(path)

建立目錄: os.mkdir

刪除目錄: os.rmdir

刪除文件: os.remove

重命名: os.rename

os.path 模塊的路徑操做

路徑拼接: os.path.join

所在目錄 / 父級目錄: os.path.dirname

基本短路徑: os.path.basename

絕對路徑: os.path.abspath

規範化路徑: os.path.normpath

資源大小: os.path.getsize

資源時間: os.path.getctime /

​ getatime /

​ getmtime

路徑是否存在: os.path.exists

是不是存在的目錄: os.path.isdir

是不是存在的文件: os.path.isfile

是不是絕對路徑: os.path.isabs

 

datetime和logging模塊

 

日期與時間

datetime 模塊中 主要類:

類名 功能說明
date 日期對象,經常使用的屬性有year, month, day
time 時間對象hour,minute,second,毫秒
datetime 日期時間對象,經常使用的屬性有hour, minute, second, microsecond
timedelta 時間間隔,即兩個時間點之間的長度

主要使用: datetime.datetiem( ) 、 datetime.timedelta()

日期: datetime.date( year, month, day )

時間: datetime.time( hour, minute, second, microsecond )

日期時間: datetime.datetime( year, month, day,hour, minute, second, microsecond )

時間間隔 : datetime.timedelta( days=0, seconds=0, microseconds=0 milliseconds=0,minutes=0, hours=0, weeks=0 )

datetime.datetime( ) 類中的經常使用方法:

  1. now ():返回當前日期時間的datetime對象:

  2. utcnow(…):返回當前日期時間的UTC datetime對象

3.strptime(…):解析成datetime對象。根據string, format 2個參數,返回一個對應的datetime對象例子: datetime.datetime.strptime(‘2018-3-22

4.Strftime(): 格式化時間例子:strtime= now.strftime("%Y-%m-%d %H:%M:%S")

5.時間戳轉日期時間: t1=datetime.datetime.fromtimestamp(ts)

6日期時間轉時間戳:t2=t1.timestamp()

獲取datetime對象 中數據:

now = datetime.datetime.now()

小時 分鐘 秒鐘

now.hour

now.minute

now.second

 

年月日

now.year

now.month

now.day

 

星期

now.weekday()

now.isoweekday()

ISO標準化日期 isocalendar(...) year 年份 week number 週期 weekday星期

a = now.isocalendar()

year = now.isoclendar()[0] 年份

weekNum= now.isoclendar()[1] 週期

weekdaY = now.isoclendar()[2] 星期

 

時間運算 : ( 返回timedelte() 對象)

timedelta類是用來計算二個datetime對象的差值的。 此類中包含以下屬性: 一、days:天數 二、microseconds:微秒數 三、seconds:秒數(>=0 而且 <1天)四、total_seconds : 總秒數

logging模塊

logging用法

  1. 初始化 logger = logging.getLogger(「name")

  2. 設置級別 logger.setLevel(logging.DEBUG),Logging中有NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL這幾種級別,日誌會記錄設置級別以上的日誌

  3. 定義Handler,經常使用的是StreamHandler和FileHandler, StreamHandler:將日誌在控制檯輸出 FileHandler: 將日誌記錄到文件裏面

  4. formatter,定義了log信息的 內容 和格式,例如:'%(asctime)s %(message)s', '%Y-%m-%d %H:%M:%S'

Logging 中幾種級別:NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL

Formatter 中已有格式 :

%(name)s Logger的名字

%(levelname)s 文本形式的日誌級別

%(message)s 用戶輸出的消息

%(asctime)s 字符串形式的當前時間。默認格式是 「2003-07-08 16:49:45,896」。逗號後面的是毫秒

%(levelno)s 數字形式的日誌級別

%(pathname)s 調用日誌輸出函數的模塊的完整路徑名,可能沒有

%(filename)s 調用日誌輸出函數的模塊的文件名

%(module)s  調用日誌輸出函數的模塊名

%(funcName)s 調用日誌輸出函數的函數名

%(lineno)d 調用日誌輸出函數的語句所在的代碼行

%(created)f 當前時間,用UNIX標準的表示時間的浮 點數表示

%(relativeCreated)d 輸出日誌信息時的,自Logger建立以 來的毫秒數

%(thread)d 線程ID。可能沒有

%(threadName)s 線程名。可能沒有

%(process)d 進程ID。可能沒有

 

 

mongobd

mongodb 組織數據的基本形式

進入與退出 數據庫

進入 pyvip@Vip:-$: mongo

退出 exit

庫、集合操做

庫級 管理語句

顯示全部庫: show dbs

切換數據庫: use 數據庫名稱

查看所在庫: db

刪除庫:db.dropDatabase()

集合 管理語句

查看當前數據庫的集合: show collections

集合建立: db.createCollection(name, options)

例子:db.createCollection('student')

{"ok": 1 }

刪除集合:db.集合名稱.drop()

數據級 增刪查改 操做

插入數據db.集合名稱.insert(document)

每一條數據,就是一個document,即 就是一條json

插入文檔時,若是不指定_id參數,MongoDB會爲文檔分配一個惟一的ObjectId

例: db.student.insert({name:‘lucky',age:18})

插入多條:

db.student.insert([

​ {name:'jianeng',sex:'male',age:18},

​ {name:'張三',sex:’female',age:30 },

​ {name:'李四',sex:’male',age:48 },

])

查詢數據 db.集合名稱.find()

查詢全部:db.student.find( )

更新數據db.集合名稱.update( <query>, (條件) <update>, {multi: <boolean>} )

全文檔更新: db.stu.update({name:’haha’},{xx:’yy’ } )

指定屬性更新,經過操做符$set db.student.update({name:'jianeng'},{$set:{name:'xxx',age:666 }})

更新多條: { multi: ture } 。 (前提只能指定字段更新) db.student.update( {sex:’feamle'},{$set:{sex:'女'}},{ multi:true} )

刪除數據db.集合名稱. remove( <query>, (條件) <justOne> )

刪除知足條件的,全部數據:db.student.remove({sex:'女'})

只是刪除 一條db.student.remove({sex:'女'},{justOne:true })

Python操做mongodb

鏈接器(驅動)

安裝python包:pip install pymongo

引入包pymongo:import pymongo

創建鏈接並建立客戶端: client= pymongo.MongoClient('127.0.0.1', 27017)

指定數據庫:db=client[ 數據庫名 ]

指定集合:stu=db [ 集合名]

 

主要方法:

insert_onei

nsert_many

update_one

update_many

delete_one

delete_many

find_one

find

 

 

傳輸模型和套接字

傳輸模型

基本模型

應用程序 應用程序

套接字 套接字

網絡設備 網絡設備

 

層次的劃分

供操做系統或應用進行網絡通訊的標準接口 應用層 應用層 Telnet FTP SMTP DNS HTTP

將不一樣的編碼方式轉換成網絡通訊中採用的標準表現形式 表現層 應用層 Telnet FTP SMTP DNS HTTP

不一樣的PC的不一樣進程之間創建或解除鏈接,插入同步點 會話層 應用層 Telnet FTP SMTP DNS HTTP

兩個主機之間的端對端的數據鏈接與傳輸 傳輸層 傳輸層 TCP UDP

選擇路由並正確的找着目標主機 網絡層 網絡層 IR ARP RARP ICMP

兩個相鄰節點之間準確的數據傳輸 數據鏈路層 數據鏈路層 網絡通訊硬件及接口

原始比特數據流在物理介質上傳輸 物理層 數據鏈路層 網絡通訊硬件及接口

 

傳輸層說明

說明一: 做爲Python開發,我們都是在應用層的HTTP協議之上進行開發的。

說明二: 網絡編程,主要是瞭解咱們Python能編寫的最低的層次, 即傳輸層的基本狀況

說明三: HTTP協議是基於TCP之上的 所以咱們須要瞭解TCP鏈接的基本過程

TCP鏈接

創建鏈接(三次握手)

傳輸數據

斷開鏈接(四次揮手)

IP地址與端口

IP地址

即:電腦說我是:192.168.0.1

端口號

即:電腦上的qq說個人端口是1234

套接字

建立套接字

套接字的基本概念

用戶認爲的傳輸:應用邏輯》》》》應用邏輯

實際的傳輸:套接字》》》套接字

建立套接字實例

import socket
sock = socket.socket()
sock
<socket.socket fd=288, # 文件描述符(惟一標識了一個socket)
			family=AddressFamily.AF_INET,  # AF_INET表示IPv4
			type=SocketKind.SOCK_STREAM,  # SOCK_STREAM表示TCP
			proto=0 >    # 一般都是0


三種套接字

服務端套接字

客戶端套接字

對等鏈接套接字

創建套接字鏈接

服務端監聽套接字的綁定與監聽

服務端套接字綁定

>>>import socket
>>>ADDRESS = ('127.0.0.1',8080) # 程序地址是由IP地址 與端口號 共同決定
>>>sock_server = socket.socket()
>>>sock_server.bind(ADDRESS)
>>>sock_server
<socket.socket fd=???, 
			family=AddressFamily.AF_INET, 
			type=SocketKind.SOCK_STREAM,  
			proto=0 
			laddr=('127.0.0.1',8080)>   
>>>socket_server.close()  #和文件同樣,socket用完了必定要關閉


>>>import socket
>>>sock_client = socket.socket()
>>>sock_client
<socket.socket fd=???, 
			family=AddressFamily.AF_INET, 
			type=SocketKind.SOCK_STREAM,  
			proto=0>  # 注意!此時這個客戶端套接字還並不擁有本身的ADDRESS 
>>>socket_client.connect(('127.0.0.1',8080)) # 鏈接到服務端套接字bind的ADDRESS元組
Traceback(most recent call last):
File"<stdin>",line 1, in<module>
ConnectionRefusedError:[WinError 10061] 因爲目標計算機積極拒絕,沒法鏈接     # 之因此服務端計算機會拒絕鏈接,是由於服務端套接字尚未開始監聽


服務端套接字的監聽

>>>sock_server.listen(5)
>>>socket_client.connect(('127.0.0.1',8080)
>>>sock_client
<socket.socket fd=???, 
			family=AddressFamily.AF_INET, 
			type=SocketKind.SOCK_STREAM,  
			proto=0
			laddr=('127.0.0.1',1951), # 能夠看到,鏈接後的客戶端套接字被隨機分配了一個ADDRESS
			raddr=('127.0.0.1',8080)> # 而且這裏也有對應的服務端套接字地址
>>>sock_server
<socket.socket fd=???, 
			family=AddressFamily.AF_INET, 
			type=SocketKind.SOCK_STREAM,  
			proto=0
			laddr=('127.0.0.1',8080)> # 沒有addr 


客戶端套接字的主動鏈接

 

服務端接受鏈接並生成對等的鏈接套接字

>>>res = sock_server.accept()   # 接受鏈接請求
>>>type(res)   # 返回一個元組
<class 'tuple'>
>>>coon. addr = res
>>>coon     # 元組第一項是一個對等的鏈接套接字
<socket.socket fd=320, 
			family=AddressFamily.AF_INET, 
			type=SocketKind.SOCK_STREAM,  
			proto=0
			laddr=('127.0.0.1',8080), 
			raddr=('127.0.0.1',1951)> # 遠端對等套接字地址
>>>addr   # 元組第二項是對等套接字地址元組
('127.0.0.1',1951)
>>>coon is sock_server
False  # 對等的鏈接套接字與服務端套接字是不一樣的套接字


accept阻塞說明

使用套接字傳輸數據

一、客戶端發送請求數據到服務端

二、服務端接受來自客戶端的請求數據

三、服務端向客戶端返回響應數據

四、客戶端接受來自服務端的響應

套接字傳輸

>>>sock_client.send(b'hello my server!') # 只能發送 bytes類型
16   # 返回發送出去的字節數

	>>>coon.recv(1024) # 指明一次性能接收到的最大的字節數量
	b'hello my server!'
>>>coon.send(b'i am here!')
10
        >>>sock_client.recv(1024)
        b'i am here!'


 

斷開套接字鏈接

客戶端主動斷開鏈接

>>>sock_client.close()
		>>>coon.recv(1024)
		b''
		>>>coon.close()


 

服務端判斷客戶端斷開鏈接

 

 

非阻塞套接字和IO多路複用

基本IO模型

普通套接字實現的服務端的缺陷:一次只能服務一個客戶端

普通套接字實現的服務端的瓶頸:一、在沒有新的套接字來以前,不能處理已經創建的套接字的請求。二、在沒有接受到客戶端請求數據以前,不能與其餘客戶端創建鏈接

import socket
server = socket.socket()
server.bind(('0.0.0.0',8080))
server.listen(5)
while True:
	connection,raddr = server.accept()  #阻塞
	while True:
		recv_data = coonection.recv(1024)  #阻塞
		if recv_data:
			print(recv_data)
			connection.send(recv_data)
		else:
			connection.close()
			break


 

非阻塞套接字

 

 

非阻塞IO模型

 

使用非阻塞套接字實現併發處理

總體思路:寧肯while True,也不要阻塞

connection_list = []
while True: # 第一層循環只負責生成對等鏈接套接字
	try:
		connection,remote_address = server.accept()
		connection.setblocking(False)
		print("Accept:{raddr}".format(raddr=remote_address))
		connection_list.append(connection) #保留已生成的對等鏈接套接字
	except BlockingIOError as error:
		pass
		
	for connection in connection_list: # 把全部已生成的對等鏈接套接字都處理一遍
		try:
			recv_data = connection.recv(1024)
			if recv_data:
				print("Received from {raddr}: {data}".format(raddr=connection.getpeername(),data=recv_data.decode('utf-8')))
				connection.send(b'Recved:%s' % recv_data)
			else:
				print('Disconnect from {raddr}'.format(raddr=connection.getpeername()))
				connection.close()
				connection_list.remove(connection)
		except BlockingIOError as error: #成功處理完一個對等鏈接套接字,就移出一個
			pass


 

 

 

 

多進程和多線程

第七節 線程和進程

線程:

線程是操做系統可以進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運做單位。一個線程指的是進程中一個單一順序的控制流,一個進程中能夠併發多個線程,每條線程執行不一樣的任務。

​ 進程:一個程序執行的實例,能夠稱爲一個進程,第一個進程有一個PID(進程標識符),進程具備不一樣的優先級,第一個進程至少有一個線程,進程裏面的第一個線程稱爲主線程,其它的線程經過主線程建立,稱爲子線程,子線程能夠繼續建立子線程,子線程之間沒有隸屬關係,各子線程之間是獨立的,每個程序的內存是獨立的。這樣的一個程序就叫作進程。好比,QQ是一個進程,畫圖程序是一個進程,裏面包含對各類資源的調用。

​ 線程:是操做系統最小的調度單位,是一串指令的集合。進程要操做CPU,必需要先建立一個線程,全部在同一個進程裏的線程,是共享同一塊內存空間的

 

進程和線程之間的區別:

​ 一、線程共享內存空間,進程是獨立的內存空間。

​ 二、線程能夠直接訪問數據段,多個子進程之間的數據是獨立的,子進程之間不能直接訪問。

​ 三、同一個進程的線程之間能夠直接交流(信息的傳遞,數據的共享),兩個進程想通訊,必須經過一箇中間代理來實現。

​ 四、建立新線程很簡單,建立新進程,須要對其父進程進行一次克隆。

​ 五、一個線程能夠控制和操做同一進程裏的其它線程,可是進程只能操做子進程,不能操做其它的的進程。

​ 六、對於主線程的修改,有可能會影響到其它線程的運行,但一個父進程的修改,不會影響其它子進程的運行

線程應用示例1:

import threading
import time


class MyThread(threading.Thread):
   def __init__(self, n, sleep_time):
       super().__init__()
       self.n = n
       self.sleep_time = sleep_time

   def run(self):
       print("Running Task ", self.n)
       time.sleep(self.sleep_time)
       print("task %s done." % self.n)


t_job = []
start_time = time.time()
for i in range(50):
   t = MyThread("t-%s" % i, 3)
   t.start()
   t_job.append(t)

for t in t_job:
   t.join()

print("main thread cost:%s " % (time.time() - start_time))
守護線程:

import threading
import time


class MyThread(threading.Thread):
   def __init__(self, n, sleep_time):
       super().__init__()
       self.n = n
       self.sleep_time = sleep_time

   def run(self):
       print("Running Task ", self.n)
       time.sleep(self.sleep_time)
       print("task %s done. %s " % (self.n,threading.current_thread()))


t_job = []
start_time = time.time()
for i in range(50):
   t = MyThread("t-%s" % i, 3)
   t.setDaemon(True) #把當前線程設置爲守護線程
   t.start()
   t_job.append(t)

time.sleep(5)
print("main thread cost:%s %s" % (time.time() - start_time,threading.current_thread()))

​ 每一個子線程都設置爲守護線程,主線程一結束,其它的子線程也跟着結束,無論子線程執行完畢沒有。

​ time.sleep(5),主線程休眠5秒,全部子線程均可以執行完畢,去掉此語句,主線程一結束,子線程所有結束

GIL 全局解釋器鎖

​ 在python中,無論CPU有多少核,在同一時間,只有一個線程在執行,一切並行都是假象

​ python的線程是調用操做系統的原生線程

​ Python代碼的執行由Python 虛擬機(也叫解釋器主循環,CPython版本)來控制,Python 在設計之初就考慮到要在解釋器的主循環中,同時只有一個線程在執行,即在任意時刻,只有一個線程在解釋器中運行。對Python 虛擬機的訪問由全局解釋器鎖(GIL)來控制,正是這個鎖能保證同一時刻只有一個線程在運行。

在多線程環境中,Python 虛擬機按如下方式執行:

​ 1. 設置GIL 2. 切換到一個線程去運行 3. 運行: a. 指定數量的字節碼指令,或者

b. 線程主動讓出控制(能夠調用time.sleep(0)) 4. 把線程設置爲睡眠狀態 5. 解鎖GIL 6. 再次重複以上全部步驟

在調用外部代碼(如C/C++擴展函數)的時候,GIL 將會被鎖定,直到這個函數結束爲止(因爲在這期間沒有Python 的字節碼被運行,因此不會作線程切換)。

全局解釋器鎖GIL設計理念與限制

​ GIL的設計簡化了CPython的實現,使得對象模型,包括關鍵的內建類型如字典,都是隱含能夠併發訪問的。鎖住全局解釋器使得比較容易的實現對多線程的支持,但也損失了多處理器主機的並行計算能力。

可是,不論標準的,仍是第三方的擴展模塊,都被設計成在進行密集計算任務是,釋放GIL。

還有,就是在作I/O操做時,GIL老是會被釋放。對全部面向I/O 的(會調用內建的操做系統C 代碼的)程序來講,GIL 會在這個I/O 調用以前被釋放,以容許其它的線程在這個線程等待I/O 的時候運行。若是是純計算的程序,沒有 I/O 操做,解釋器會每隔 100 次操做就釋放這把鎖,讓別的線程有機會執行(這個次數能夠經過 sys.setcheckinterval 來調整)若是某線程並未使用不少I/O 操做,它會在本身的時間片內一直佔用處理器(和GIL)。也就是說,I/O 密集型的Python 程序比計算密集型的程序更能充分利用多線程環境的好處。

Pypy能夠實現真正的多線程,Jpy也能夠實現真正的多線程

線程鎖(互斥鎖Mutex)

​ 線程在進程之下執行,一個進程下能夠運行多個線程,它們之間共享相同上下文。線程包括開始、執行順序和結束三部分。它有一個指針,用於記錄當前運行的上下文。當其它線程執行時,它能夠被搶佔(中斷)和臨時掛起(也稱睡眠) ——這種作法叫作 讓步(yielding)

​ 一個進程中的各個線程與主進程共享同一片數據空間,與獨立進程相比,線程之間信息共享和通訊更加容易。線程通常以併發執行,正是因爲這種併發和數據共享機制,使多任務間的協做成爲可能。固然,這種共享也並非沒有風險的,若是多個線程訪問同一數據空間,因爲訪問順序不一樣,可能致使結果不一致,這種狀況一般稱爲競態條件(race condition),不過大多數線程庫都有同步原語,以容許線程管理器的控制執行和訪問;另外一個要注意的問題是,線程沒法給予公平執行時間,CPU 時間分配會傾向那些阻塞更少的函數。

線程鎖(互斥鎖Mutex)

​ 一個進程以能夠啓動多個線程,多個線程共享父進程的內存空間,也就意味着每一個線程能夠訪問同一份數據,此時,若是2個線程要同時修改一份數據,會出現什麼情況?

遞歸鎖

import threading, time

num = 0
num2 = 0


def run1():
   print("grab the first part data")
   lock.acquire()
   global num
   num += 1
   lock.release()
   return num


def run2():
   print("grab the second part data")
   lock.acquire()
   global num2
   num2 += 1
   lock.release()


def run3():
   lock.acquire()
   res = run1()
   print("---------------between run1 and run2--------------")
   res2 = run2()
   lock.release()
   print(res,res2)


lock = threading.RLock()
for i in range(10):
   t = threading.Thread(target=run3)
   t.start()

while threading.active_count() != 1:
   print(threading.active_count())
else:
   print("---------------all threads done.------")
   print(num, num2)
Semaphore(信號量)

​ 互斥鎖同時只容許一個線程更改數據,而Semaphore 是同時容許必定數量的線程更改數據,好比廁全部3個坑,那最多隻容許3我的上廁所,後面的人只能等待裏面有人出來了才能進去。


import threading,time

def run(n):
   semaphore.acquire()
   time.sleep(1)
   print("run the thread: %s\n" %n)
   semaphore.release()

if __name__=='__main__':
   num=0
   semaphore=threading.BoundedSemaphore(5) #最多容許5個線程同時運行
   for i in range(22):
       t=threading.Thread(target=run,args=(i,))
       t.start()

while threading.active_count()!=1:
   pass
else:
   print("----all threads done------")
   print(num)
Events

​ 經過Event來實現兩個或多個線程間的交互,下面是一個紅綠燈的例子,即啓動一個線程作交通指揮燈,生成幾個線程作車輛,車輛行駛按紅燈停,綠燈行的規則

​ a server thread can set or rest it.

​ event.set()

​ event.clear()

​ If the flag is set,the wait method doesn't do anything. 標誌位設定了,表明綠燈,直接通行。

​ If the flag was cleared,wait will block until it becomes set again.標誌位被清空,表明紅燈,等待變綠

​ Any number of threads may wait for thd same event.


import threading, time

event = threading.Event()
event.set() #設置標誌位,表示如今是綠燈


def lighter():
   count = 0
   while True:
       if count > 5 and count < 10:  # 改爲紅燈
           event.clear()  # 把標誌位清空
           print("\033[41;1mred light is on ...\033[0m")
       elif count > 10:
           event.set()  # 變綠燈
           count = 0
       else:
           print("\033[42;1mgreen light is on ...\033[0m")
       time.sleep(1)
       count += 1


def car(name):
   while True:
       if event.is_set():  # 標誌符設置,表明綠燈亮,汽車通行
           print("[%s] running......" % name)
           time.sleep(1)
       else:
           print("[%s] sees red light,waithing....." % name)
           event.wait()
           print("[%s] green light is on ,start going......" % name)


light = threading.Thread(target=lighter, )
light.start()
car1 = threading.Thread(target=car, args=("Tesla",))
car1.start()

 

queue隊列(很是重要的知識點)

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

​ 一、生產者和消費者模型

​ 在併發編程中使用生產者和消費者模式通解解決大多數併發問題。該模式經過平衡生產線程和消費線程的工做能力來提升程序的總體處理數據的速度。

​ 二、爲何要使用生產者和消費者模式

​ 在線程世界裏,生產者就是產生數據的線程,消費者就是消費數據的純種。在多線程併發當中,若是生產者處理速度很快,而消費者處理速度很慢,那麼生產者就必須等待消費者處理完,才能繼續生產數據。一樣的道理,若是消費者的處理能力大於生產者,那麼消費者就必須等待生產者。爲了解決這個問題因而引入了生產者和消費者模式。

​ 三、什麼是生產者和消費者模式

​ 生產者消費者模式是經過一個容器來解決生產者和消費者的強耦合問題。生產者和消費彼此之間不直接通信,而經過阻塞隊列來進行通信,因此生產者生產完數據以後不用等待消費者來處理,直接扔給阻塞隊列;消費者不找生產者要數據,而是直接從阻塞隊列裏取,阻塞隊列就至關於一個緩衝區,平衡了生產者與消費者的處理能力。

​ 四、生產者和消費者模式示例代碼:


import threading,time,queue

q=queue.Queue(maxsize=10)
def Producer(name):
   count=1
   while True:
       q.put("骨頭 %s" %count)
       print("生產了骨頭",count)
       count+=1
       time.sleep(0.5)

def Consumer(name):
   #while q.qsize()>0:
   while True:
       print("[%s] 取到[%s],而且吃了它......"%(name,q.get()))
       time.sleep(1)

p=threading.Thread(target=Producer,args=("Alex",))
c=threading.Thread(target=Consumer,args=("Chenronghua",))
c1=threading.Thread(target=Consumer,args=("王森",))
p.start()
c.start()
c1.start()

 

threading 模塊

threading 是 Python 高級別的多線程模塊。

threading 模塊的函數
  • active_count() 當前活動的 Thread 對象個數

  • current_thread() 返回當前 Thread 對象

  • get_ident() 返回當前線程

  • enumerater() 返回當前活動 Thread 對象列表

  • main_thread() 返回主 Thread 對象

  • settrace(func) 爲全部線程設置一個 trace 函數

  • setprofile(func) 爲全部線程設置一個 profile 函數

  • stack_size([size]) 返回新建立線程棧大小;或爲後續建立的線程設定棧大小爲 size

  • TIMEOUT_MAX Lock.acquire(), RLock.acquire(), Condition.wait() 容許的最大值

threading 可用對象列表:

  • Thread 表示執行線程的對象

  • Lock 鎖原語對象

  • RLock 可重入鎖對象,使單一進程再次得到已持有的鎖(遞歸鎖)

  • Condition 條件變量對象,使得一個線程等待另外一個線程知足特定條件,好比改變狀態或某個值

  • Semaphore 爲線程間共享的有限資源提供一個」計數器」,若是沒有可用資源會被阻塞

  • Event 條件變量的通用版本,任意數量的線程等待某個時間的發生,在改事件發生後全部線程被激活

  • Timer 與 Thread 相識,不過它要在運行前等待一段時間

  • Barrier 建立一個」阻礙」,必須達到指定數量的線程後才能夠繼續

Thread 類

Thread 對象的屬性有:Thread.nameThread.identThread.daemon。詳見(The Python Standard Library)

Thread 對象方法:Thread.start()Thread.run()Thread.join(timeout=None)Thread.getNameThread.setNameThread.is_alive()Thread.isDaemon()Thread.setDaemon()。詳見(The Python Standard Library)

使用 Thread 類,能夠有不少種方法來建立線程,這裏使用常見的兩種:

  • 建立 Thread 實例,傳給它一個函數。

  • 派生 Thread 子類,並建立子類的實例。

     

 

 

 

 

 

 

進程和線程的補充

進程與線程的其他相關操做

等待結束

等待進程與線程結束

import time
import multiprocessing   # 導入進程模塊
print('mainprocess start:', time.asctime(time.localtime(time.time())))
def func():
print('subprocess start:', time.asctime(time.localtime(time.time())))
time.sleep(5)
print('subprocess end: ', time.asctime(time.localtime(time.time())))
p = multiprocessing.Process(target=func)
p.start()
time.sleep(5)
print('mainprocess end:', time.asctime(time.localtime(time.time())))

結果以下:
mainprocess start:Web Jan 17 15:24:15 2018
subprocess start:Web Jan 17 15:24:15 2018
subprocess end:Web Jan 17 15:24:20 2018
mainprocess end:Web Jan 17 15:24:20 2018

有子進程的狀況以下:

import time
import multiprocessing   # 導入進程模塊
print('mainprocess start:', time.asctime(time.localtime(time.time())))
def func():
print('subprocess start:', time.asctime(time.localtime(time.time())))
time.sleep(5)
print('subprocess end: ', time.asctime(time.localtime(time.time())))
p = multiprocessing.Process(target=func)
p.start()
p.join() # 等待子進程結束
time.sleep(5)
print('mainprocess end:', time.asctime(time.localtime(time.time())))

結果以下:
mainprocess start:Web Jan 17 15:31:33 2018
subprocess start:Web Jan 17 15:31:33 2018
subprocess end:Web Jan 17 15:31:38 2018
mainprocess end:Web Jan 17 15:31:43 2018

 

當前進程與當前線程

當前進程

import time
import multiprocessing as mp

print(mp.current_process()) # 在主進程中獲取當前進程對象

print('-----------')
def func():
print(mp.current_process()) # 在子進程中獲取當前進程對象
time.sleep(3)

# 以示區分,第一個進城
p1 = mp.Process(target=func)
print(p1)
p1.start()
p1.join()

print('-------')
# 以示區分,第二個進城
p2 = mp.Process(target=func)
print(p2)
p2.start()
p2.join()

當前線程

import time
import threading as th

print(th.current_thread()) # 在主進程中獲取當前進程對象

print('-----------')
def func():
print(mp.current_thread()) # 在子進程中獲取當前進程對象
time.sleep(3)

# 以示區分,第一個進城
t1 = mp.Thread(target=func)
print(t1)
t1.start()
t1.join()

print('-------')
# 以示區分,第二個進城
t2 = mp.Thread(target=func)
print(t2)
t2.start()
t2.join()

 

終止進程

import time
import multiprocessing as mp

def func():
print('sub-process start')
time.sleep(5)
print('sub-process end')

p = mp.Process(target=func)
print(p)
p.start()
time.sleep(2)
p.terminate() # func啓動兩秒後停止進程 也就是不會輸出「end」

結果以下:
sub-process start

注意!!!
線程並不能被中途停止,只能等待其運行結束

 

進程與線程的標識

進程id與線程ident

進程id

import time
import multiprocessing as mp

def func():
time.sleep(10) # 讓進程維持10秒,以便咱們觀察


p = mp.Process(target=func)
print('sub-process before start: ',p.pid)
p.start()
print('sub-process after end: ',p.pid)

結果以下:
sub-process before start: None
sub-process after start: 1736 # start以後才分配pid

注意!!!
若是用PyCharm執行會多出一個PyCharm的管理進程

線程ident

import time
import threading as th

def func():
time.sleep(10) # 讓線程維持10秒,以便咱們觀察


t = th.Thread(target=func)
print('sub-thread before start: ',t.ident)
t.start()
print('sub-thread after end: ',t.ident)

結果以下:
sub-thread before start: None
sub-thread after start: 140146122831616 # 線程啓動以後才分配

注意!!!
操做系統並不能看到線程的標識。由於線程是由Python解釋器來負責調度的。 操做系統僅須要調度進程就好了。

 

進程名與線程名

import multiprocessing as mp

p = mp.Process(name='p1') # 設置
print(p) #name會展現在repr表達式中
print(p.name)

print('-------------')

p.name = 'p2'   # 經過name屬性來設置進程名
print(p)

結果以下:
<Process(p1, initial)>
p1
---------------
<Process(p2, initial)>

 

進程與線程的生存狀態

import time
import multiprocessing as mp

def func():
print('sub-process start')
time.sleep(5)
print('sub-process end')

p = mp.Process(target=func)
print('before start: ', p.is_alive(), p)
p.start()
print('after start before end: ', p.is_alive(), p)
p.join()
print('after end: ', p.is_alive(), p)

結果以下:
before start:             False<Process(Process-1, initial)>
after start before end:   True<Process-1, start)> # 只有處在started狀態下的Process纔是alive的
sub-process start
sub-process end
after end:             False<Process(Process-1, stopped)>

注意:
線程的生存狀態與進程是相似的

 

守護模式

守護進程

import time
import multiprocessing as mp

def func():
print('sub-process started')
time.sleep(5)
print('sub-process end')

p = mp.Process(target=func,daemon=True) #設置成守護進程

p.start()
time.sleep(2)

結果以下:
sub-process started # 2s後 守護進程隨着主進程結束     主進程不會等待子進程結束

提示:
多線程中的守護線程與守護進程相似

 

 

以面向對象的形式使用進程與線程

一、繼承Process或Thread類

二、重寫init方法

三、重寫run方法

start()--》》》run(已經在新的進程了)--》》target #target是由默認的run運行

import multiprocessing as mp


class MyProcess(mp.Process):
def __init__(self,*args, **kwargs):
super().__init__()
self.args = args
self.kwargs = kwargs
def run(self):
print(mp.current_process())
print(self.args)
print(self.kwargs)


p = MyProcess(1,2,3,a=1,b=2,c=3)
p.start()


結果以下:
(1,2,3)
{'a':1,'b':2,'c':3}

 

 

 

 

併發通訊

queue隊列(很是重要的知識點)

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

​ 一、生產者和消費者模型

​ 在併發編程中使用生產者和消費者模式通解解決大多數併發問題。該模式經過平衡生產線程和消費線程的工做能力來提升程序的總體處理數據的速度。

​ 二、爲何要使用生產者和消費者模式

​ 在線程世界裏,生產者就是產生數據的線程,消費者就是消費數據的純種。在多線程併發當中,若是生產者處理速度很快,而消費者處理速度很慢,那麼生產者就必須等待消費者處理完,才能繼續生產數據。一樣的道理,若是消費者的處理能力大於生產者,那麼消費者就必須等待生產者。爲了解決這個問題因而引入了生產者和消費者模式。

​ 三、什麼是生產者和消費者模式

​ 生產者消費者模式是經過一個容器來解決生產者和消費者的強耦合問題。生產者和消費彼此之間不直接通信,而經過阻塞隊列來進行通信,因此生產者生產完數據以後不用等待消費者來處理,直接扔給阻塞隊列;消費者不找生產者要數據,而是直接從阻塞隊列裏取,阻塞隊列就至關於一個緩衝區,平衡了生產者與消費者的處理能力。

​ 四、生產者和消費者模式示例代碼:

import threading,time,queue

q=queue.Queue(maxsize=10)
def Producer(name):
   count=1
   while True:
       q.put("骨頭 %s" %count)
       print("生產了骨頭",count)
       count+=1
       time.sleep(0.5)

def Consumer(name):
   #while q.qsize()>0:
   while True:
       print("[%s] 取到[%s],而且吃了它......"%(name,q.get()))
       time.sleep(1)

p=threading.Thread(target=Producer,args=("Alex",))
c=threading.Thread(target=Consumer,args=("Chenronghua",))
c1=threading.Thread(target=Consumer,args=("王森",))
p.start()
c.start()
c1.start()

 

 

 

 

 

 

進程池和線程池

 

 

 

 

 

 

協程

協程

生成器與函數的區別

協程與生成器

從執行單元上來看,協程算併發 。

協程的意義

greenlet協程

什麼是greenlet

雖然CPython(標準Python)可以經過生成器來實現協程, 但使用起來還並非很方便。與此同時,Python的一個衍生版 Stackless Python 實現了原生的協程,它更利於使用。因而,你們開始將 Stackless 中關於協程的代碼 單獨拿出來作成了CPython的擴展包。這就是 greenlet 的由來,所以 greenlet 是底層實現了原生協程的 C擴展庫。

from greenlet import greenlet
import random
import time

def producer():
while True:
      item = random.randint(0,99)
      print('生產了 %s' %item)
      c.switch(item)   # 切換到消費者,並將item傳入消費者
      time.sleep(1)
       
def Consumer():
  while True:
      item = p.switch() # 切換到消費者,並等待消費者傳入item
      print('消費了 %s' %item)
   

c = greenlet(consumer) # 經一個普通函數變成協程
p = greenlet(producer)
c.switch()   # 讓消費者先進入暫停狀態(只有恢復時才能接收數據)

greenlet的價值

一、高性能的原生協程

二、予以更加明確的顯式切換

三、直接將函數包裝成協程,保持原有代碼風格

 

gevent協程

什麼是gevent

雖然,咱們有了 基於 epoll 的回調式編程模式,可是卻難以使用。即便咱們能夠經過配合 生成器協程 進行復雜的封裝,以簡化編程難度。可是仍然有一個大的問題: 封裝難度大,現有代碼幾乎徹底要重寫gevent,經過封裝了 libev(基於epoll) 和 greenlet 兩個庫。幫咱們作好封裝,容許咱們以相似於線程的方式使用協程。以致於咱們幾乎不用重寫原來的代碼就能充分利用 epoll 和 協程 威力。

gevent的價值(遇到阻塞就切換到另外一個協程繼續執行)

一、使用基於epoll的libev來避開阻塞

二、使用基於gevent的高效協程來切換執行

三、只有在遇到阻塞的時候切換,沒有輪需的開銷,也沒有線程的開銷

 

gevent 併發服務器

import gevent
# 將Python內置的socket直接換成封裝了IO多路複用的socket
from gevent import monkey; monkey.patch_socket()
import socket

server = socket.socket()
server.bind(('0.0.0.0',8080))
server.listen(1000)

def worker_coroutine(connection): # 工做協程的內容
while True:
recv_data = connection.recv(1000)
if recv_data:
print(recv_data)
connection.send(recv_data)
else:
connection.close()
break

while True:
connection,remote_address = server.accept()
# 生成一個協程,並將connection做爲參數傳入
gevent.spawn(worker_coroutine,connection)

 

gevent協程通訊

gevent通訊

一、協程之間是經過switch通訊,因爲gevent基於greenlet,因此也是。

二、由於 gevent 不須要咱們使用手動切換,而是遇到阻塞就切換,所以咱們不會去使用switch !

from gevent import monkey; monkey.patch_socket()
import gevent
from gevent.queue import Queue
import random

queue = Queue(3)

def producer(queue):
while True:
      item = random.randint(0,99)
      print('生產了 %s' %item)
      queue.put(item)
       
def Consumer(queue):
  while True:
      item = queue.get()
      print('消費了 %s' %item)
   
p = gevent.spawn(producer, queue) # 將函數封裝成協程,並開始調度
c = gevent.spawn(consumer, queue)

# gevent.sleep(1)
gevent.joinall([p, c]) # 阻塞(一阻塞就切換協程)等待
相關文章
相關標籤/搜索