【问题标题】:what is a mysql buffered cursor w.r.t python mysql connector什么是 mysql 缓冲游标 w.r.t python mysql 连接器
【发布时间】:2017-10-11 07:13:59
【问题描述】:

有人可以举个例子来理解吗?

执行查询后,MySQLCursorBuffered 游标从服务器获取整个结果集并缓冲行。 对于使用缓冲游标执行的查询,诸如 fetchone() 之类的行获取方法会从缓冲行集中返回行。对于非缓冲游标,在调用行获取方法之前不会从服务器获取行。在这种情况下,您必须确保在同一连接上执行任何其他语句之前获取结果集的所有行,否则将引发 InternalError (Unread result found) 异常。

谢谢

【问题讨论】:

    标签: mysql python-3.x mysql-connector mysql-connector-python


    【解决方案1】:

    我能想到这两种Cursors 不同的两种方式。

    第一种方法是,如果使用缓冲游标执行查询,则可以通过检查MySQLCursorBuffered.rowcount 来获取返回的行数。但是,无缓冲游标的rowcount 属性在调用execute 方法后立即返回-1。这基本上意味着尚未从服务器获取整个结果集。此外,无缓冲游标的rowcount 属性随着您从中获取行而增加,而缓冲游标的rowcount 属性在您从中获取行时保持不变。

    下面的 sn-p 代码试图说明以上几点:

    import mysql.connector
    
    
    conn = mysql.connector.connect(database='db',
                                   user='username',
                                   password='pass',
                                   host='localhost',
                                   port=3306)
    
    buffered_cursor = conn.cursor(buffered=True)
    unbuffered_cursor = conn.cursor(buffered=False)
    
    create_query = """
    drop table if exists people;
    create table if not exists people (
        personid int(10) unsigned auto_increment,
        firstname varchar(255),
        lastname varchar(255),
        primary key (personid)
    );
    insert into people (firstname, lastname)
    values ('Jon', 'Bon Jovi'),
    ('David', 'Bryan'),
    ('Tico', 'Torres'),
    ('Phil', 'Xenidis'),
    ('Hugh', 'McDonald')
    """
    
    # Create and populate a table
    results = buffered_cursor.execute(create_query, multi=True)
    conn.commit()
    
    buffered_cursor.execute("select * from people")
    print("Row count from a buffer cursor:", buffered_cursor.rowcount)
    unbuffered_cursor.execute("select * from people")
    print("Row count from an unbuffered cursor:", unbuffered_cursor.rowcount)
    
    print()
    print("Fetching rows from a buffered cursor: ")
    
    while True:
        try:
            row = next(buffered_cursor)
            print("Row:", row)
            print("Row count:", buffered_cursor.rowcount)
        except StopIteration:
            break
    
    print()
    print("Fetching rows from an unbuffered cursor: ")
    
    while True:
        try:
            row = next(unbuffered_cursor)
            print("Row:", row)
            print("Row count:", unbuffered_cursor.rowcount)
        except StopIteration:
            break
    

    上面的 sn-p 应该返回如下内容:

    Row count from a buffered reader:  5
    Row count from an unbuffered reader:  -1
    
    Fetching rows from a buffered cursor:
    Row: (1, 'Jon', 'Bon Jovi')
    Row count: 5
    Row: (2, 'David', 'Bryan')
    Row count: 5
    Row: (3, 'Tico', 'Torres')
    Row count: 5
    Row: (4, 'Phil', 'Xenidis')
    Row count: 5
    Row: (5, 'Hugh', 'McDonald')
    Row: 5
    
    Fetching rows from an unbuffered cursor:
    Row: (1, 'Jon', 'Bon Jovi')
    Row count: 1
    Row: (2, 'David', 'Bryan')
    Row count: 2
    Row: (3, 'Tico', 'Torres')
    Row count: 3
    Row: (4, 'Phil', 'Xenidis')
    Row count: 4
    Row: (5, 'Hugh', 'McDonald')
    Row count: 5
    

    如您所见,无缓冲游标的rowcount 属性从-1 开始,并随着我们循环遍历它生成的结果而增加。缓冲游标不是这种情况。

    第二种区分的方法是首先注意两者中的哪一个(在同一连接下)executes。如果您从执行未完全获取行的非缓冲游标开始,然后尝试使用缓冲游标执行查询,则会引发 InternalError 异常,并且您将被要求使用或丢弃由无缓冲游标。下图为:

    import mysql.connector
    
    
    conn = mysql.connector.connect(database='db',
                                   user='username',
                                   password='pass',
                                   host='localhost',
                                   port=3306)
    
    buffered_cursor = conn.cursor(buffered=True)
    unbuffered_cursor = conn.cursor(buffered=False)
    
    create_query = """
    drop table if exists people;
    create table if not exists people (
        personid int(10) unsigned auto_increment,
        firstname varchar(255),
        lastname varchar(255),
        primary key (personid)
    );
    insert into people (firstname, lastname)
    values ('Jon', 'Bon Jovi'),
    ('David', 'Bryan'),
    ('Tico', 'Torres'),
    ('Phil', 'Xenidis'),
    ('Hugh', 'McDonald')
    """
    
    # Create and populate a table
    results = buffered_cursor.execute(create_query, multi=True)
    conn.commit()
    
    unbuffered_cursor.execute("select * from people")
    unbuffered_cursor.fetchone()
    buffered_cursor.execute("select * from people")
    

    上面的 sn-p 将引发 InternalError 异常,并显示一条消息,指示有一些未读结果。它的基本意思是,在您可以使用同一连接下的任何游标执行另一个查询之前,需要完全消耗无缓冲游标返回的结果。如果你把unbuffered_cursor.fetchone()改成unbuffered_cursor.fetchall(),错误就会消失。

    还有其他不太明显的差异,例如内存消耗。缓冲游标可能会消耗更多内存,因为它们可能从服务器获取结果集并缓冲行。

    我希望这证明有用。

    【讨论】:

    • 您提到了这一点,但我觉得我对缓冲连接的最大担忧之一是内存消耗,因此我认为了解数据缓冲的程度可能会有所帮助。根据我的经验,缓冲区并不意味着所有数据都存储在内存中,但在这种情况下,听起来缓冲区可以包含所有数据 - 就像它是一个动态大小的缓冲区。
    • @avatarofhope2 为混乱道歉。我并不是要暗示所有记录都被提取和缓冲。虽然情况可能仍然如此(当结果集足够小时),但更安全/更清楚地说是批量获取和缓冲记录。
    猜你喜欢
    • 2015-03-08
    • 2013-10-19
    • 2014-08-04
    • 2019-02-23
    • 1970-01-01
    • 1970-01-01
    • 2020-08-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多