Bokeh服务端仪表盘:数据库更新时图表更新方法的正确性咨询
嘿,很高兴看到你已经搞定了那个TypeError问题,先给你个明确的肯定——你用source.data.update()来更新数据源的方法是完全正确的!
先解释下你之前的错误根源
之前触发TypeError: 'ColumnDataSource' object is not iterable,大概率是你尝试直接把ColumnDataSource对象当成可迭代结构来处理(比如误写了source.data = ColumnDataSource(...)或者试图遍历它)。而你现在通过提取新数据的.data字典来更新原数据源,完美避开了这个问题,这正是Bokeh官方推荐的数据源更新方式之一。
关于线程更新的思路:你的方向是对的!
Bokeh的Document(也就是你代码里的curdoc())是线程不安全的,所有涉及UI或数据源的修改都必须在Bokeh的主线程中执行。你用单独线程处理阻塞任务(比如模拟从数据库取数据的耗时操作),然后通过doc.add_next_tick_callback()触发主线程的更新,这个做法完全符合Bokeh服务器的最佳实践,非常靠谱!
代码的几个优化点
你的代码已经能正常工作,但可以做一些简化和优化,让它更高效简洁:
- 没必要每次创建新的ColumnDataSource:直接构造数据字典就行,不用额外实例化数据源对象,节省资源。
- 移除不必要的协程装饰器:你的
update函数里没有异步操作,@gen.coroutine完全可以去掉。 - 设置守护线程:让线程随Bokeh服务器关闭自动退出,避免残留进程。
优化后的代码示例:
from bokeh.models.sources import ColumnDataSource from bokeh.plotting import figure, curdoc import time from threading import Thread from random import randint from functools import partial # 初始化数据源 data = {'x': [1,2,3,4], 'y': [10, 20, 30, 40]} source = ColumnDataSource(data) # 创建图表 p = figure( plot_width=700, plot_height=700, title='Histogram of Arrival Delays by Carrier', x_axis_label='Delay (min)', y_axis_label='Proportion' ) p.hbar(y='x', height=0.2, right='y', source=source) doc = curdoc() doc.add_root(p) # 简化的更新函数 def update(x, y): # 直接构造新数据字典并更新 new_data = {'x': x, 'y': y} source.data.update(new_data) # 如果是完全替换数据(而不是增量更新),也可以直接用 source.data = new_data # 阻塞任务线程 def blocking_task(): while True: time.sleep(5) # 模拟从数据库获取新数据 x = [randint(0,10) for _ in range(4)] y = [randint(20,80) for _ in range(4)] # 安全触发主线程更新 doc.add_next_tick_callback(partial(update, x=x, y=y)) # 启动线程并设置为守护线程 thread = Thread(target=blocking_task) thread.daemon = True thread.start()
额外提示
如果你的数据库查询是异步的(比如用异步数据库驱动),也可以直接在Bokeh的主线程里用@gen.coroutine或者async def来处理,不用额外开线程,但如果是同步的阻塞查询,你的多线程方案就是最优解。
总的来说,你的核心实现思路完全正确,解决TypeError的方法也没问题,优化后的代码会更简洁高效,很适合用来搭建数据库数据展示的仪表盘!
内容的提问来源于stack exchange,提问作者Alessandro Gaballo

