python====操作数据库

一、 操作 MySQL

1. 基本介绍

Python3 操作 MySQL 数据库 可以使用的模块是 pymysqlMySQLdb

这个两个模块都是通过自己的 API 执行原生的 SQL 语句实现的。

MySQLdb 是最早出现的一个操作 MySQL 数据库的模块,核心由C语言编写,接口精炼,性能最棒,缺点是环境依赖较多,安装稍复杂,特别是 windows 中不好安装。更新较慢。

pymysql 为替代 MySQLdb 而生,纯 Python 实现,API 的接口与 MySQLdb 完全兼容,安装方便。

default charset utf8mb4 collate utf8mb4_general_ci;

2. 安装包 pymysql

pymsql是Python中操作MySQL的模块

shell> pip3 install pymysql

3. 基本操作

示例数据

info = {
    "base_info": {
        "manufacturer": "VMware, Inc.",
        "pod_name": "VMware7,1",
        "sn": "VMware-56 4d 2b 4b 91 1e 48 15-5b d2 73 9c ec 98 da 22",
        "host_name": "qfedu.com",
        "kernel": "3.10.0-957.el7.x86_64",
        "os": "CentOS Linux release 7.6.1810 (Core)"
    },
    "cpu": {
        "cpu_name": "Intel(R) Core(TM) i5-5350U CPU @ 1.80GHz",
        "cpu_pyc": 1,
        "cpu_cores_each": 1
    },
    "memory": [
        {
            "capacity": " 8192 MB",
            "slot": " DIMM_B2",
            "model": " DDR3",
            "speed": " 1333 MT/s",
            "manufacturer": " 00CE00B380CE",
            "sn": " 82B79F71"
        },
        {
            "capacity": " 8192 MB",
            "slot": " DIMM_B3",
            "model": " DDR3",
            "speed": " 1333 MT/s",
            "manufacturer": " 00CE00B380CE",
            "sn": " 32CDDE81"
        },
        {
            "capacity": " No Module Installed",
            "slot": " DIMM_B4",
            "model": " DDR3",
            "speed": " Unknown",
            "manufacturer": "",
            "sn": ""
        },
        {
            "capacity": " 8192 MB",
            "slot": " DIMM_B5",
            "model": " DDR3",
            "speed": " 1333 MT/s",
            "manufacturer": " 00CE04B380CE",
            "sn": " 85966B82"
        },
        {
            "capacity": " 8192 MB",
            "slot": " DIMM_B6",
            "model": " DDR3",
            "speed": " 1333 MT/s",
            "manufacturer": " 000000B380CE",
            "sn": " 00000000"
        }
    ]
}

3.1 创建表

import pymysql
  
# 创建连接
conn = pymysql.connect(host='172.16.153.10',
                       port=3306,
                       user='root',
                       passwd='123', 
                       db='shark_db',
                       charset='utf8mb4')
# 获取游标对象
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)

# 定义 sql 语句, 创建第一个表 服务器基础信息表 base_info
sql1 = """create table base_info
 (id int auto_increment primary key, 
  host_name varchar(64) not null, 
  kernel varchar(64),
  os varchar(64),
  manufacturer varchar(32),
  pod_name varchar(64),
  sn varchar(128),
  cpu_name varchar(64),
  cpu_pyc int not null,
  cpu_co
  res_each int not null)"""

# 创建第二个表 内存表  memory
sql2 = '''
create table memory(
    id int auto_increment primary key,
    capacity varchar(32),
    slot varchar(16),
    model varchar(4),
    speed varchar(32),
    manufacturer varchar(128),
    sn varchar(128),
    server_id int
) 
'''

# 执行 sql 语句
cursor.execute(sql1)
cursor.execute(sql2)

  
# 提交更改
conn.commit()

# 关闭游标对象
cursor.close()

# 关闭连接对象
conn.close()

3.2 插入数据

一次插入一条数据

# 一次插入一条数据, 并且使用 pymysql 定义的变量占位符
insert_data_sql = '''insert into base_info(
                        {},{},{},{},{},{},{},{},{}
                ) values(
                %s, %s, %s, %s, %s, %s, %s, %s, %s);'''

# 注意这里不是 Python 的字符串格式化,所以传值的时候不需要使用 %
cursor.execute(insert_data_sql, tuple(base_info.values()))

# 数据库中受影响的行数
print(cursor.rowcount)

conn.commit()
cursor.close()
conn.close()

一次插入多条数据

# 处理数据

mem_info =  info["memory"]
mem_li = []
server_id = 1
for mem in mem_info:
    mem["server_id"] = server_id
    v = tuple(mem.values())
    mem_li.append(v)

# 获取对应的 key
mem = mem_info[0]
m_keys = mem.keys()

# 一次插入一条数据, 并且使用 pymysql 定义的变量占位符 %s
sql = '''insert into memory({},{},{},{},{},{},{}
        ) values(%s, %s, %s, %s, %s, %s, %s);'''

sql = sql.format(*m_keys)
# print(sql)

# 语法:
# cursor.executemany(sql, [("v1","v2"),("v3", "v4")])

cursor.executemany(sql, mem_li)

conn.commit()
cursor.close()
conn.close()

3.3 查询数据

3.3.1 获取到的数据是元组类型
定义一个查询语句
query_sql = "select id, host_name, os from base_info;"

执行查询语句,并且返回得到结果的行数
row_nums = cursor.execute(query_sql, ('shark1'))

"""
获取到数据结果集具有迭代器的特性:
1. 可以通过索引取值,可以切片
2. 结果集中的数据每次取出一条就少一条
"""

获取结果集中的第一条数据, 注意不是整个表的第一条数据
one_data = cursor.fetchone()

获取结果集中接下来的第 2 条和 第 3 条 数据
many_data = cursor.fetchmany(2)

获取结果集中剩余的全部数据 
all_data = cursor.fetchall()

cursor.close()
conn.close()
print(row_nums)
print(one_data)
print(many_data)
print(all_data)

二、redis 的安装和基本使用

1. 简单介绍

redis是一个key-value存储系统,和Memcached类似。它支持存储的value类型相对更多,包括string(字符串)、list(链表、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。

这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。

与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

2. 软件包

yum install  epel-release   ## 保证安装 epel 源
yum install redis

3. 启动服务

[root@kube-master ~]# systemctl start redis

检查服务状态

image.png

检查端口

Redis 默认监听 6379 端口

image.png

4. 基本使用

4.1 连接到 redis-server

使用默认端口连接到本地 redis 服务
[root@kube-master ~]# redis-cli
127.0.0.1:6379>

退出输入客户端 exit

4.2 增删改查数据

4.2.1 增加

set

127.0.0.1:6379> help set
SET key value [EX seconds] [PX milliseconds] [NX|XX]

在 Redis 中设置值,默认,不存在则创建,存在则修改
参数:
ex,过期时间(秒)
px,过期时间(毫秒)
nx,假如设置为True,则只有 name 不存在时,当前 set 操作才执行
xx,假如设置为True,则只有 name 存在时,当前 set 操作才执行

Example

127.0.0.1:6379> set name shark EX 10
OK

4.2.2 获取

get

127.0.0.1:6379> get name
"shark"
4.2.3 查询过期时间

ttl

查看一个 key 的过期时间

127.0.0.1:6379> ttl name
(integer) 2   # 距离过期时间,剩余的秒数
  • -1 永不过期
  • -2 已经过期
4.2.4 指定过期时间

expire

设置一个 key 的过期时间(单位: 秒)

127.0.0.1:6379> set age 18
OK
127.0.0.1:6379> ttl age
(integer) -1
127.0.0.1:6379> EXPIRE age 20
(integer) 1
127.0.0.1:6379> ttl age
(integer) 18
127.0.0.1:6379> ttl age
(integer) 14
127.0.0.1:6379>
4.2.5 删除

del

[root@kube-master ~]# redis-cli set name shark
OK
[root@kube-master ~]# redis-cli get name
"shark"
[root@kube-master ~]# redis-cli del name
(integer) 1
[root@kube-master ~]# redis-cli get name
(nil)

4.3 配置文件

路径 /etc/redis.conf

# 设置监听地址
bind 127.0.0.1

# 设置监听端口
port 6379

# 设置密码为 foo
requirepass foo

4.4 其他连接方式(扩展自修)

image.png

三、python3 操作 redis

1. 安装包

pip3 install redis

2. 连接

In [1]: import redis

In [2]: rs = redis.StrictRedis(host='192.168.1.37',
                               port=6379,
                               db=0,
                               decode_responses=True
In [3]: rs.set("QF", "www.qfedu.com")
Out[3]: True

In [4]: rs.get("QF")
Out[4]: 'www.qfedu.com'
  • db=0 指定使用那个数据库, 0-15 共 16 个,默认是 0
  • decode_responses=True redis 返回值默认是二进制 bytes 类型,设置为 True 可直接返回字符串,默认使用 utf-8

3. 基本操作

3.1 set 在 Redis 中设置键值对值,默认,不存在则创建,存在则修改

"""
 参数:
         ex,过期时间(秒)
         px,过期时间(毫秒)
         nx,假如设置为True,则只有 name 不存在时,当前 set 操作才执行
         xx,假如设置为True,则只有 name 存在时,当前 set 操作才执行
"""
  • 示例
In [121]: rs.set("name", "shark",ex=50)
Out[121]: True

In [122]: rs.ttl("name")  # 查看过期时间
Out[122]: 41

In [123]: rs.get("name")
Out[123]: 'shark'

3.2 setex 设置键值对,并设置过期时间(单位秒)

setex(name,  time, value)

"""
 参数:
     time,过期时间(数字秒)
"""
  • 示例
In [26]: rs.setex('login', 30, 1)
Out[26]: True

In [27]: rs.ttl("login")
Out[27]: 27

In [28]: rs.get("login")
Out[28]: '1'

3.3 Hash 操作

Redis 中的 Hash 数据,很想 Python 中嵌套的字典 : {"n1": {"k1": "v1", "k2", "v2"}}

Hash 示意图
In [29]: user_info = {"name": "shark", "age": 18}

In [30]: rs.hmset("shark", user_info)
Out [30]: True
  
In [31]: rs.hget(name, "age") 
In [22]: 18 

4. 使用场景

比如我们想存储用户的登录信息
一般用户的登录信息有一下几点:

  • 用户名 name
  • 加密的密码 password
  • 会话 session_id
  • 会话过期时间 expiration_date

那现在就来模拟一下一个用户从登录到登出(过期)的情况

预备知识,我们模拟用户登录情况需要使用如下几个模块

  • hashlib
  • string
  • random

4.1 密码加密功能实现

第一步我们实现加密密码

hashlib 提供了常见的摘要算法,如MD5,SHA1等等。

什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。
举个例子, 假设有一段字符串 data = 'hello' , 使用摘要算法函数 f(data) 进行计算,会返回一个固定长度的16进制数字 hexdigest。
摘要算法的好处是:

  1. 无法反推, 因为摘要函数是一个单向函数,计算f(data)很容易,但通过hexdigest反推data却非常困难。
  2. 对原始数据做一个bit的修改,都会导致计算出的摘要完全不同。
In [40]: import hashlib

In [41]: hashlib.md5("hello".encode("utf-8"))
Out[41]: <md5 HASH object @ 0x10fc5deb8>

In [42]: hmd5 = hashlib.md5("hello".encode("utf-8"))  # 接收的是字节(二进制数据)

In [43]: hmd5.hexdigest()
Out[43]: '5d41402abc4b2a76b9719d911017c592'

In [44]: hmd5 = hashlib.md5("hello".encode("utf-8"))   # 多次计算,是等幂操作,加密的值是一样的

In [45]: hmd5.hexdigest()
Out[45]: '5d41402abc4b2a76b9719d911017c592'

4.2 生成会话 ID 随机数

string 可以产生有序的数字和英文大小写字母

In [55]: import string

In [56]: string.digits
Out[56]: '0123456789'

In [57]: string.ascii_letters
Out[57]: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'

In [58]:

random 可以把一个序列类型的数据打乱,这样我们就可以得到一个随机字符串,用于会话 ID

In [65]: import random, string

In [66]: base_str = f"{string.digits + string.ascii_letters}"

In [67]: base_str
Out[67]: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'

In [68]: random.sample(base_str, 6)   # 从传入的序列数据中产生 6 个随机数据
Out[68]: ['Q', '4', 'y', 'a', 'R', 'd']

In [69]: li = random.sample(base_str, 6)

In [70]: sessid = ''.join(li)

In [71]: sessid
Out[71]: 'B1umkn'

In [72]:

4.3 信息写入 Redis

当用户登录的时候我们需要添加多个数据,可以使用 hmset()方法

"""
hmset(name, mapping)
name 就是一个hash 的名字 , mapping 就是一个字典
在name对应的hash中批量设置键值对,没有的 key 就创建,已存在的 key 修改
"""

In [78]: import random, string, hashlib

In [79]: name="shark"

In [80]: base_str = f"{string.digits + string.ascii_letters}"

In [81]: sessid = ''.join(random.sample(base_str, 20))

In [82]: sessid
Out[82]: 'SFMNr3jzJZTRYLy64qfb'

In [83]: password = hashlib.md5("QFedu123!".encode('utf-8'))

In [84]: password = password.hexdigest()

In [85]: password
Out[85]: '19b169cff70f61b4e80663757025a17d'

In [86]: user_info = {"name": name, "password": password, "sessid": sessid}

In [87]: rs.hmset(name, user_info)
Out[87]: True

In [88]: rs.setex(f"{name}_sessid",86400,sessid))
Out[88]: True

每次用户再次跳转页面或者发送新的请求时,我们都应该查看他的会话是否超时。

首先,获取 redis 中 Hash 数据使用的是 hget()hmget方法

hget(name,key)

在name对应的hash中获取根据key获取value

In [155]: rs.hget(name, 'sessid')
Out[155]: 'SFMNr3jzJZTRYLy64qfb'

hmget(name, keys, *args)

在name对应的hash中获取多个key的值
参数:
name,reids对应的name
keys,要获取key集合,例如:['k1', 'k2', 'k3']
*args,要获取的key,例如:k1,k2,k3

In [156]: rs.hmget(name, ["name", "sessid", "login"])
Out[156]: ['shark', 'SFMNr3jzJZTRYLy64qfb', 'True']

In [157]: name, sessid, login = rs.hmget(name, ["name", "sessid", "login"])

In [158]: login
Out[158]: 'True'

模拟登录状态OK, 需要进一步检查 会话是否超时的情况

In [164]: rs.hmset(name, {"sessid": '', "login": "False"})
Out[164]: True

In [165]: rs.hget(name,"sessid")
Out[165]: ''

In [166]: rs.hget(name,"login")
Out[166]: 'False'
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352