如何使数据库访问速度提升167倍

关键字:Redis NoSQL Flask RQ Flask_Cache cache
非关系型数据库 内存型数据库 RQ队列 缓存
之前上线了的一个应用 -- Framework7+Vue+Flask开发实战 - PT保种管理系统1 - 概述,由于经常要在海量数据中统计、查询,随着数据逐渐增多,每次数据库访问等待时间从几百ms,增长到10几秒!
所以,必须引入Cache机制。
目光转向目前 流行的Redis --> 内存型NoSQL数据库

结果比较:速度提升167倍!

time-cost

测试程序:

from timeit import timeit

timeit(stmt="[Ob.query.get(i) for i in range(10000)]", setup="from __main__ import Ob", number=10)
# PostgreSQL 25.15s

timeit(stmt="[r.get(i) for i in range(10000)]", setup="from __main__ import r", number=10)
# Redis 9.32s

timeit(stmt="[pipe.get(i) for i in range(10000)]; pipe.execute()", setup="from __main__ import r; pipe=r.pipeline(False)", number=100)
# Redis Pipe 0.15s

读取速度:62倍 167倍

timeit(stmt="[Cookie.query.filter_by(key='sys_status').update({'val':i}) for i in range(10000)]", setup="from app.models import Cookie", number=1)
# 20s

timeit(stmt="[r.set('tmp:%d'%i, i) for i in range(10000)]", setup="from __main__ import r;", number=10)
# 9.17s

timeit(stmt="[pipe.set('tmp:%d'%i, i) for i in range(10000)]; pipe.execute()", setup="from __main__ import r; pipe=r.pipeline(False)", number=10)
# 0.21s

写入速度:43倍 95倍

以上是本地测试数据。如果你使用SaaS网上托管主机,差距更加明显!因为它们的数据库一般不在本地,像Heroku,数据库在AWS,网络耗时又增加不少(慢2倍以上)。

如何在我们的框架中引入Redis:

之前我们的Flask后台框架:Vue 2.0 起步(4) 轻量级后端Flask用户认证 - 微信公众号RSS
引入RQ,参考:https://beenje.github.io/blog/posts/running-background-tasks-with-flask-and-rq/

# /app/main/views.py

import redis
from rq import push_connection, pop_connection, Queue
from rq.job import Job
from . import tasks, ob_api

def get_redis_connection():
    redis_connection = getattr(g, '_redis_connection', None)
    if redis_connection is None:
        redis_url = current_app.config['REDIS_URL']
        redis_connection = g._redis_connection = redis.from_url(redis_url)
    return redis_connection

@main.before_request
def push_rq_connection():
    push_connection(get_redis_connection())
    app_status = r.hget('status', 'app')
    if app_status and app_status.decode('ascii')=='idle':
      q = Queue()
      task_list = ['ob_sync', 'db2redis', 'ob_seeding_sync' ]
      if (r.hget('status', 'tasks:ob_sync') is None) or (q.fetch_job(r.hget('status', 'tasks:ob_sync').decode('ascii')) is None): # 同步OB 种子
        job = q.enqueue_call(func=tasks.ob_sync, args=('all',), timeout=3600, result_ttl=5*3600)  # 结果缓存6*3600
        print('start queue: tasks.ob_sync...', job.get_id())
        r.hset('status', 'app', 'ob_sync')
        r.hset('status', 'tasks:ob_sync', job.get_id())
      if (r.hget('status', 'tasks:db2redis') is None) or (q.fetch_job(r.hget('status', 'tasks:db2redis').decode('ascii')) is None): # 检查 db2redis有没有结果
        # job2
        pass
      if (r.hget('status', 'tasks:ob_seeding_sync') is None) or (q.fetch_job(r.hget('status', 'tasks:ob_seeding_sync').decode('ascii')) is None): # 串行
        # job3
        pass
    elif app_status:
      q = Queue()
      task_list = ['ob_sync', 'db2redis', 'ob_seeding_sync' ]
      for task in task_list:
        if r.hget('status', 'tasks:%s'%task):
          job_key = r.hget('status', 'tasks:%s'%task).decode('ascii')
          job = q.fetch_job(job_key)
          if job:
            print(task, job_key, job.status, job.result)
            if job.status in ['finished', 'failed'] : r.hset('status', 'app', 'idle')
            else: r.hset('status', 'app', task)
    else:
      r.hset('status', 'app', 'idle')

@main.teardown_request
def pop_rq_connection(exception=None):
    pop_connection()

Flask_Cache

另外,对于一些经常访问且变化不大的路由(views),可以引入Flask_Cache 高速缓存:

配置文件引入Redis server:

# config.py
class Config:
  REDISTOGO_URL = os.getenv('REDIS_URL', 'redis://localhost:6379')

app初始化时,引入flask_cache,flask_redis:

## /app/__init__.py
from flask_cache import Cache
from flask_redis import FlaskRedis
from urllib import parse

cache = Cache()
r = FlaskRedis()

def create_app(config_name):
    parse.uses_netloc.append("redis")
  redis_url = parse.urlparse(app.config['REDISTOGO_URL'])
  cache.init_app(app, config={
    'CACHE_TYPE': 'redis',
    'CACHE_KEY_PREFIX': 'fcache',
    'CACHE_REDIS_HOST': redis_url.hostname,
    'CACHE_REDIS_PORT': redis_url.port,
    'CACHE_REDIS_USERNAME': redis_url.username or '',
    'CACHE_REDIS_PASSWORD': redis_url.password or '',
    # 'CACHE_REDIS_URL': app.config['REDISTOGO_URL'],
    })

  r.init_app(app)

路由里就可以按需使用cache装饰器了:

# /app/main/views.py
import redis
from rq import push_connection, pop_connection, Queue
from rq.job import Job
from .. import db, admin, cache, r

@cache.memoize(timeout=20)
def query_db():
  time.sleep(3)
  r.set('query_db', time.ctime())
  return time.ctime() # you must return something, otherwise Cache will not work

@main.route("/cache")
def cache_view():
  app = current_app._get_current_object()
  with app.app_context():
    start = time.time()
    query_db()
    return "Results from DB in {:.2f}sec".format(time.time()-start)

@main.route('/api/ob_report', methods=['GET', 'POST'])
@login_required
@cache.cached(timeout=50)
def api_ob_report():
  app = current_app._get_current_object()
  rsp = ob_api.ob_report(app)
  return jsonify(code=rsp['code'], msg=rsp['msg'], sys_status=rsp['sys_status'])

注:Redis只在Linux平台可用,如果你用Windows:

Windows环境(Cygwin)下,使用PostgreSQL, Redis

2017数据库排名:


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

推荐阅读更多精彩内容