如何高效利用并发异步方式获取数据库数据 (并发异步get数据库)

在现代应用程序中,从数据库中获取数据始终是一个重要的任务。 许多应用程序具有数百个活跃的数据库连接,并且随着应用程序规模的增长,这些连接数量会不断增加。在这种情况下,从数据库中获取数据的效率成为了一个非常重要的问题。 这就要求我们使用并发和异步方式来获取数据库数据,以提高应用性能和效率。在本文中,将讨论如何使用并发异步方法来高效地获取数据库数据。

1.使用线程池

我们通常使用多线程的方式,使用线程池可以使程序获得更好的性能。使用线程池时,可以将数据库连接池作为线程池的线程进行调度。在这种方式下,线程池将会负责管理连接数量以及连接的状态。同时,线程池还可以根据当前的负载情况来自动调整线程数量,并且根据负载的大小来管理线程池。

2.使用异步/非阻塞I/O

使用异步/非阻塞I/O是降低网络负荷的重要手段,这可以用在获取数据库数据的场景下。当我们需要从数据库中获取大量数据时,使用异步/非阻塞I/O可以使数据获取和处理更加快速和高效。与传统的阻塞式I/O相比,它可以并发地获取和处理多个I/O操作。

3.使用数据库连接池

使用数据库连接池来高效地获取数据非常重要。使用连接池可以有效地降低数据库的I/O负荷。同时,数据库连接池可以帮助我们管理数据库连接,减少连接的打开和关闭所带来的资源消耗,以及减少网络流量和CPU的使用。

4.使用缓存

使用缓存可以减低数据库I/O负荷,从而减少网络负载和CPU使用。缓存可以将数据存储在内存中,让你可以通过访问缓存数据而不是从数据库中读取数据,然后再返回到客户端。这种方式可以大大减少从数据库中读取数据的次数,从而降低网络负荷。

5.优化SQL查询

优化SQL查询也是极其重要的。通过优化查询,我们可以大大提高数据库查询的效率,减少数据库I/O负荷和CPU使用。优化查询的方法有很多,例如,改善表结构、添加索引和使用分区等。

在使用并发异步方式获取数据库数据时,你需要注意以下几点:使用线程池、使用异步/非阻塞I/O、使用数据库连接池、使用缓存和优化SQL查询。合理运用这些技巧可以有效地提高应用程序的性能和效率。当然,另外一个非常重要的问题是, 如何设置开发计划,安排任务,制定技术规范?那就需要更综合的分析和研究。

相关问题拓展阅读:

python2.7怎么实现异步

您好,请问您是想知道python2.7怎么实现异步吗?

  改进之前

  之前,我的查询步骤很简单,就是:

  前端提交查询请求 –> 建立数据库连接 –> 新建游标 –> 执行命令 –> 接受结果 –> 关闭游标、连接

  这几大步骤的顺序执行。

  这里面当然问题很大:

  建立数据库连接实际上就是新建一个套接字。这是进程间通信的几种方法里,开销更大的了。

  在“执行命令”和“接受结果”两个步骤中,线程在阻塞在数据库内部的运行过程中,数据库连接和游标都处于闲置状态。

  这样一来,每一次查询都要顺序的新建数据库连接,都要阻塞在数据库返回结果的过程中。当前端提交大量查询请求时,查询效率肯定是很低的。

  之一次改进

  之前的模块里,问题更大的就是之一步——建立数据库连接套接字了。如果大碰能够一次性建立连接,之后查询能够反复服用这个连接就好了。

  所以,首先应该把数据库查询模块作为一个单独的守护进程去执行,而前端app作为主进程响应用户的点击操作。那么两条进程怎么传递消息呢?翻了几天Python文档,终于构思出来:用队列queue作为生产者(web前端)向消费者(数据库后端)传递任务的渠道。生产者,会与SQL命令一起,同时传递一个管道pipe的连接对象,作为任务完成后,回传结果的渠道。确保,任务的接收方与发送方保持一致。

  作为第二个问题的解决方法,可以使用线程池来并发获取任务队列中的task,然后执行命令并回传结果。

  第二次改进

  之一次改进的效果还是很明显的,不用任何测试手段。直接点击页面链接,可以很直观地感觉到反者判应速度有很明显的加快。

  但是对于第二个问题,使用线程池还是有些欠妥当。因为,CPython解释器存在GIL问题,所有线程实际上都在一个解释器进程里调度。线程稍微开多一点,解释器进程就会频繁的切换线程,而线程切换的开销也不小。线程多一点,甚至会出现“抖动”问题(也就是刚刚唤醒一个线程,就进入挂起状态,刚刚换到栈帧或内存的上下文,又被换回内存或者磁盘),效率大大降低。也就是说,线程池的并发量很有限。

  试过了多进程、多线程,只能在单个线程里做文章了。

  Python中的asyncio库

  Python里有大量的协程库可以实现单线程内的并发操作,比如Twisted、Gevent等等。Python官方在3.5版本里提供了asyncio库同样可以实现协程并发。asyncio库大大降低了Python中协程的实现难度,就像定义普通函数那样就可以了,只是要在def前面多加一个async关键词。async def函数中,需要阻塞在其他async def函数的位置前面可以加上await关键词。

  import asyncio

  async def wait():

  await asyncio.sleep(2)

  async def execute(task):

  process_task(task)

  await wait()

  continue_job()

  async def函数的执行稍微麻烦点。需要首先获取一个loop对象,然后由这个对象代为执行async def函数。

  loop = asyncio.get_event_loop()

  loop.run_until_complete(execute(task))

  loop.close()

  loop在执行execute(task)函数时,如果遇到await关键字,就会暂时挂起当前协程,转而去执行其他阻塞在await关键词的协程,从而实现协程并发。

  不过需要注意的是,run_until_complete()函数本身是一个阻塞函数。也就是说,当前线程会等候一个run_until_complete()函数执行完毕之后,才会继续执行下一部函数。所以下面这段代码并不能并发执行。

  for task in task_list:

  loop.run_until_complete(task)

  对与这个问题首仿改,asyncio库也有相应的解决方案:gather函数。

  loop = asyncio.get_event_loop()

  tasks =

  for task in task_list>

  loop.run_until_complete(asyncio.gather(*tasks))

  loop.close()

  当然了,async def函数的执行并不只有这两种解决方案,还有call_soon与run_forever的配合执行等等,更多内容还请参考官方文档。

  Python下的I/O多路复用

  协程,实际上,也存在上下文切换,只不过开销很轻微。而I/O多路复用则完全不存在这个问题。

  目前,Linux上比较火的I/O多路复用API要算epoll了。Tornado,就是通过调用C语言封装的epoll库,成功解决了C10K问题(当然还有Pypy的功劳)。

  在Linux里查文档,可以看到epoll只有三类函数,调用起来比较方便易懂。

  创建epoll对象,并返回其对应的文件描述符(file descriptor)。

  int epoll_create(int size);

  int epoll_create1(int flags);

  控制监听事件。之一个参数epfd就对应于前面命令创建的epoll对象的文件描述符;第二个参数表示该命令要执行的动作:监听事件的新增、修改或者删除;第三个参数,是要监听的文件对应的描述符;第四个,代表要监听的事件。

  int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

  等候。这是一个阻塞函数,调用者会等候内核通知所注册的事件被触发。

  int epoll_wait(int epfd, struct epoll_event *events,

  int maxevents, int timeout);

  int epoll_pwait(int epfd, struct epoll_event *events,

  int maxevents, int timeout,

  const sigset_t *sigmask);

  在Python的select库里:

  select.epoll()对应于之一类创建函数;

  epoll.register(),epoll.unregister(),epoll.modify()均是对控制函数epoll_ctl的封装;

  epoll.poll()则是对等候函数epoll_wait的封装。

  Python里epoll相关API的更大问题应该是在epoll.poll()。相比于其所封装的epoll_wait,用户无法手动指定要等候的事件,也就是后者的第二个参数struct epoll_event *events。没法实现精确控制。因此只能使用替代方案:select.select()函数。

  根据Python官方文档,select.select(rlist, wlist, xlist)是对Unix系统中select函数的直接调用,与C语言API的传参很接近。前三个参数都是列表,其中的元素都是要注册到内核的文件描述符。如果想用自定义类,就要确保实现了fileno()方法。

  其分别对应于:

  rlist: 等候直到可读

  wlist: 等候直到可写

  xlist: 等候直到异常。这个异常的定义,要查看系统文档。

  select.select(),类似于epoll.poll(),先注册文件和事件,然后保持等候内核通知,是阻塞函数。

  实际应用

  Psycopg2库支持对异步和协程,但和一般情况下的用法略有区别。普通数据库连接支持不同线程中的不同游标并发查询;而异步连接则不支持不同游标的同时查询。所以异步连接的不同游标之间必须使用I/O复用方法来协调调度。

  所以,我的大致实现思路是这样的:首先并发执行大量协程,从任务队列中提取任务,再向连接池请求连接,创建游标,然后执行命令,并返回结果。在获取游标和接受查询结果之前,均要阻塞等候内核通知连接可用。

  其中,连接池返回连接时,会根据引用连接的协程数量,返回负载最轻的连接。这也是自己定义AsyncConnectionPool类的目的。

  我的代码位于:bottle-blog/dbservice.py

  存在问题

  当然了,这个流程目前还一些问题。

  首先就是每次轮询拿到任务之后,都会走这么一个流程。

  获取连接 –> 新建游标 –> 执行任务 –> 关闭游标 –> 取消连接引用

  本来,更好的情况应该是:在轮询之前,就建好游标;在轮询时,直接等候内核通知,执行相应任务。这样可以减少轮询时的任务量。但是如果协程提前对应好连接,那就不能保证在获取任务时,保持各连接负载均衡了。

  所以这一块,还有工作要做。

  还有就是epoll没能用上,有些遗憾。

  以后打算写点C语言的内容,或者用Python/C API,或者用Ctypes包装共享库,来实现epoll的调用。

并发编程-同步、异步、阻塞、非阻塞

同步执行:一个进程在执行某个任务时,另外一个进程必须等待其执行完毕,才能继续执行

异步执行:一个进程在执行某个任务时,另外一个进程无需等待其执行完毕,就可以继续执行,当有消息返回时,系统会通知后者进行处理,这样可以提高执行效率

所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不会返回。按照这个定义,其实绝大多数函数都是同步调用。但是一般而言,我们在说同步、异步的时候,特指那些需要其他部件协作或者需要一定时此罩间完成的任务。

异步的概念和同步相对。当一个异步功能调用发出后,调用者森纳闹不能立刻得到结果。当该异步功能完成后,通过状态、通知或回调来通知调用者。如果异步功能用状态来通知,那么调用者就需要每隔一定时间检查一次,效率就很低(有些初学多线程编程的人,总喜欢用一个循环去检查某个变量的值,这其实是一 种很严重的错误)。如果是使用通知的方式,效率则很高,因为异步功能几乎不需要做额外的操作。至于回调函数,其实和通知没太多区别。

阻塞调用是指调用结果返回之前,当前线程会被挂起(如遇到io操作)。函数只有在得到结果之后才会将阻塞的线程激活。有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。

非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前也会立刻返回,同时该函数不会阻塞当前线程。

1. 同步与异步针对的是函数/任务的调用方式:同步就是当一个进程发起一个函数(任务)调用的时候,一直等到函数(任务)完成,而进程继续处于激活状态。而异步情况下是当一个进程发起一个函数(任务)调用的时候,不会等函数返回,而是茄迅继续往下执行当,函数返回的时候通过状态、通知、事件等方式通知进程任务完成。

2. 阻塞与非阻塞针对的是进程或线程:阻塞是当请求不能满足的时候就将进程挂起,而非阻塞则不会阻塞当前进程,同步和异步的时候当前的进程/线程一直是激活状态

在编程里面,什么是异步,并发,面向对象,过程 ,什么意思啊?

C语言是面向过程的编程,它的最重要特岩埋点是函数,通过主函数来调用一个个子函数。程序运行的顺序都是程序员决定好了的。它是我学的之一种程序语言。 C++是面向对象的粗明蚂编程,类是它的主要特点,程序执行过槐李程中,先由主函数进入,定义一些类,根据需…

关于并发异步get数据库的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。

来源地址:如何高效利用并发异步方式获取数据库数据 (并发异步get数据库)

转载声明:本站文章若无特别说明,皆为原创,转载请注明来源:www.88531.cn资享网,谢谢!^^

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享