麒麟v10 上部署 TiDB v5.1.2 生产环境优化实践
791
2023-05-03
Dataset基于SQLAlchemy的便利工具
数据集使得数据库中的数据读取和写入数据就像阅读和编写JSON文件一样简单。
Dataset对于操作JSON、CSV文件、NoSQL非常好用。
import dataset
连接MySQL数据库:
db = dataset.connect('mysql://username:password@10.10.10.10/ctf?charset=utf8')
用户名:username,密码:password,数据库地址(地址+端口):10.10.10.10,database名: ctf
连接SQLite数据库:
db = dataset.connect('sqlite:///ctf.db')
连接***数据库:
db = dataset.connect('postgresql://scott:tiger@localhost:5432/mydatabase')
一定要注意指定字符编码
table = db['city'] #(选择city表) user = table('name') # 找出表中'name'列属性所有数据 res = db.query('select name from table limit 10') # 如果不需要查看全部数据的话***用limit,因为全部数据的载入非常非常耗时间 for x in res: print x['name'] # 选name字段的数据 table.insert(dict(name='John Doe', age=37)) table.insert(dict(name='Jane Doe', age=34, gender='female')) john = table.find_one(name='John Doe')
在数据库中查找是否有同时满足多个条件的数据:table.find_one(属性1=属性值1, 属性2=属性值2, …)
注:find_one速度很慢
插入数据
dataset会根据输入自动创建表和字段名
table = db['user'] # 或者table = db.get_table('user') table.insert(dict(name='John Doe', age=46, country='China')) table.insert(dict(name='Jane Doe', age=37, country='France', gender='female')) # 主键id自动生成
更新数据
table.update(dict(name='John Doe', age=47), ['name']) # 第二个参数相当于sql update语句中的where,用来过滤出需要更新的记录
事务操作
事务操作可以简单的使用上下文管理器来实现,出现异常,将会回滚
从表获取数据
users = db['user'].all() for user in db['user']: # print(user['age']) # chinese_users = user.find(country='China') john = user.find_one(name='John Doe')
获取非重复数据
db['user'].distinct('country')
删除记录
table.delete(place='Berlin')
执行SQL语句
result = db.query('SELECT country, COUNT(*) c FROM user GROUP BY country') for row in result: print(row['country'], row['c'])
导出数据
result = db['users'].all() dataset.freeze(result, format='json', filename='users.json')
JSON
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,非常易于人阅读和编写。
import json
json.dumps 将 Python 对象编码成 JSON 字符串
json.loads 将已编码的 JSON 字符串解码为 Python 对象
MySQL数据库:
分类表-categories,包括类别web,reversing,crypto(加解密),mic等
题目表-tasks,包括题目id,题目名,flag,分值,文件&地址,题目等级,题目详细描述
flag表-flag,包括题目id,用户id,得分,时间戳
用户表-users,包括用户id,用户名,密码
题目分类表-cat_task,包括题目id,题目类别id
flag表中每条数据由于是有题目ID task_id和用户ID user_id来共同确认的,所以采用复合主键:primary key (task_id,user_id)
联合主键和复合主键的区别
python装饰器
Decorator通过返回包装对象实现间接调用,以此插入额外逻辑
wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了
from functools import wraps def logged(func): @wraps(func) def with_logging(*args,**kwargs): print func.__name__ + "was called" return func(*args,**kwargs) return with_logging @logged def f(x): """does some math""" return x + x * x print f.__name__ # prints 'f' print f.__doc__ # prints 'does some math'
web框架采用flask
from flask import Flask
引入Flask类,Flask类实现了一个WSGI(Web Server Gateway Interface)应用
app = Flask(__name__)
app是Flask的实例,它接收包或者模块的名字作为参数,但一般都是传递__name__
@app.route('/') def hello_world(): return 'Hello World!'
使用app.route装饰器会将URL和执行的视图函数的关系保存到app.url_map属性上。处理URL和视图函数的关系的程序就是路由,这里的视图函数就是hello_world
if __name__ == '__main__': app.run(host='0.0.0.0',port=9000)
使用这个判断可以保证当其他文件引用这个文件的时候(例如from hello import app)不会执行这个判断内的代码,也就是不会执行app.run函数。
执行app.run就可以启动服务了。默认Flask只监听虚拟机的本地127.0.0.1这个地址,端口为5000。而我们对虚拟机做的端口转发端口是9000,所以需要制定host和port参数,0.0.0.0表示监听所有地址,这样就可以在本机访问了。
flask中jsonify的作用
jsonify的作用实际上就是将我们传入的json形式数据序列化成为json字符串,作为响应的body,并且设置响应的Content-Type为application/json,构造出响应返回至客户端
效果等于json.dumps
jsonify的Content-Type字段值为application/json
json.dumps的Content-Type字段值为text/html
修改flask中静态文件夹
修改的flask默认的static文件夹只需要在创建Flask实例的时候,把static_folder和static_url_path参数设置为空字符串即可。
app = Flask(__name__, static_folder=”, static_url_path=”)
访问的时候用url_for函数,res文件夹和static文件夹同一级:
url_for(‘static’, filename=’res/favicon.ico’)
werkzeug
werkzeug是一个WSGI工具包,可以作为一个Web框架的底层库。它封装好了很多Web框架的东西,例如 Request,Response等等。Flask框架就是一Werkzeug 为基础开发的
generate_password_hash(password)
将用户输入的明文密码加密成密文进行存储
密码加盐哈希函数。用来将明文密码加密,返回加密后的密文,用来进行用户注册
函数定义:
werkzeug.security.generate_password_hash(password, method='pbkdf2:sha1', salt_length=8)
密文格式:method$salt$hash
password: 明文密码
method: 哈希的方式(需要是hashlib库支持的),格式为
pbpdf2:
method:哈希的方式,一般为SHA1,
iterations:(可选参数)迭代次数,默认为1000。
slat_length: 盐值的长度,默认为8
check_password_hash(hash,password)
验证经过generate_password_hash哈希的密码,将明文和密文进行比较,查看是否一致,用来验证用户登录
函数定义:
werkzeug.security.check_password_hash(pwhash, password)
pwhash: generate_password_hash生成的哈希字符串
password: 需要验证的明文密码
flask中的session
rom flask import session user = db['users'].find_one(username=username) session['user_id'] = user['id']
由于使用了session,所以需要设置一个secret_key用来做一些模块的hash
Flask Web Development 中的内容:
SECRET_KEY配置变量是通用密钥,可在Flask和多个第三方扩展中使用。如其名所示,加密的强度取决于变量值的机密度。不同的程序要使用不同的密钥,而且要保证其他人不知道你所用的字符串。
SECRET_KEY的作用主要是提供一个值做各种HASH, 是在其加密过程中作为算法的一个参数(salt或其他)。所以这个值的复杂度也就影响到了数据传输和存储时的复杂度。
flask 变量规则
要给URL添加变量部分,你可以把这些特殊的字段标记为
@route('/hello/
数据库查询
对dataset的数据查询,使用冒号来为变量传参。
select f.task_id from flags f where f.user_id = :user_id”’,user_id=session[‘user_id’])
模板渲染
使用render_template方法来渲染模板。将模板名和你想作为关键字的参数传入模板的变量
MySQL
IFNULL(expr1,expr2)
如果expr1不是NULL,IFNULL()返回expr1,否则它返回expr2。
IFNULL()返回一个数字或字符串值,取决于它被使用的上下文环境。
max函数是用来找出记录集中***值的记录
对于left join,不管on后面跟什么条件,左表的数据全部查出来,因此要想过滤需把条件放到where后面对于inner join,满足on后面的条件表的数据才能查出,可以起到过滤作用。也可以把条件放到where后面
在使用left jion时,on和where条件的区别如下:
on条件是在生成临时表时使用的条件,它不管on中的条件是否为真,都会返回左边表中的记录。where条件是在临时表生成好后,再对临时表进行过滤的条件。这时已经没有left join的含义(必须返回左边表的记录)了,条件不为真的就全部过滤掉。
order by的用法
使用order by,一般是用来,依照查询结果的某一列(或多列)属性,进行排序(升序:ASC;降序:DESC;默认为升序)。
当排序列含空值时:
ASC:排序列为空值的元组***显示。
DESC:排序列为空值的元组***显示。
可以把null值看做无穷大
select * from s order by sno desc, sage asc
group by的用法
group by按照查询结果集中的某一列(或多列),进行分组,值相等的为一组
1、细化集函数(count,sum,avg,max,min)的作用对象:
未对查询结果分组,集函数将作用于整个查询结果。
对查询结果分组后,集函数将分别作用于每个组。
SELECT cno,count(sno) from sc group by cno
2、GROUP BY子句的作用对象是查询的中间结果表
分组方法:按指定的一列或多列值分组,值相等的为一组。
使用GROUP BY子句后,SELECT子句的列名列表中只能出现分组属性(比如:sno)和集函数(比如:count())
select sno,count(cno) from sc group by sno
3、多个列属性进行分组
select cno,grade,count(cno) from sc group by cno,grade
4、使用HAVING短语筛选最终输出结果
只有满足HAVING短语指定条件的组才输出。
HAVING短语与WHERE子句的区别:作用对象不同。
1、WHERE子句作用于基表或视图,从中选择满足条件的元组。
2、HAVING短语作用于组,从中选择满足条件的组
select sno from sc group by sno having count(cno)>3
select sno,count(cno) from sc where grade>60 group by sno having count(cno)>3
MySQL的左连接、右连接、等值连接
1.左连接(left join )
select m.columnname……,n.* columnname….. from left_table m left join right_table n on m.columnname_join=n.columnname_join and n.columnname=xxx where m.columnname=xxx…..
ON是连接条件,用于把2表中等值的记录连接在一起,但是不影响记录集的数量。若是表left_table中的某记录,无法在表right_table找到对应的记录,则此记录依然显示在记录集中,只是表right_table需要在查询显示的列的值用NULL替代;
ON连接条件中表n.columnname=xxx用于控制right_table表是否有符合要求的列值还是用NULL替换的方式显示在查询列中,不影响记录集的数量;
WHERE字句控制记录是否符合查询要求,不符合则过滤掉
2.右连接(right join)
select m.columnname……,n.* columnname….. from left_table m right join right_table n on m. columnname_join=n. columnname_join and m. columnname=xxx where n.columnname=xxx…..
3.等值连接
select m.columnname……,n.* columnname….. from left_table m [inner] join right_table n on m. columnname_join=n. columnname_join where m.columnname=xxx….. and n.columnname=xxx….
或者
select m.columnname……,n.* columnname….. from left_table m , right_table n where m. columnname_join=n. columnname_join and m.columnname=xxx….. and n.columnname=xxx….
ON是连接条件,不再与左连接或右连接的功效一样,除了作为2表记录匹配的条件外,还会起到过滤记录的作用,若left_table中记录无法在right_table中找到对应的记录,则会被过滤掉;
WHERE字句,不管是涉及表left_table、表right_table上的限制条件,还是涉及2表连接的条件,都会对记录集起到过滤作用,把不符合要求的记录刷选掉;
jinja2获取循环索引
jinja2获取循环{% for i in n %}的索引使用loop.index
{% for i in names %}
flask 重定向和错误
可以用redirect()函数把用户重定向到其它地方。放弃请求并返回错误代码,用abort()函数。
from flask import abort, redirect, url_for @app.route('/') def index(): return redirect(url_for('login')) @app.route('/login') def login(): abort(401) this_is_never_executed()
默认情况下,错误代码会显示一个黑白的错误页面。如果你要定制错误页面,可以使用errorhandler()
装饰器:
from flask import render_template @app.errorhandler(404) def page_not_found(error): return render_template('page_not_found.html'), 404
注意 render_template()调用之后的 404 。这告诉Flask,该页的错误代码是404 ,即没有找到。默认为200,也就是一切正常。
flask CSRF防护机制
@app.before_request def csrf_protect(): if request.method == "POST": token = session.pop('_csrf_token', None) if not token or token != request.form.get('_csrf_token'): abort(403) def some_random_string(): return hashlib.sha256(os.urandom(16).hexdigest()) def generate_csrf_token(): if '_csrf_token' not in session: session['_csrf_token'] = some_random_string() return session['_csrf_token']
在flask的全局变量里面注冊 上面那个生成随机token的函数
app.jinja_env.globals[‘csrf_token’] = generate_csrf_token
在网页的模板是这么引入的