首页 > 其他分享 >peewee update和save性能分析

peewee update和save性能分析

时间:2023-08-29 22:23:45浏览次数:38  
标签:peewee self update field user ._ pk save

image

背景

python项目中使用了peewee这款orm框架,在对数据库更新时有两种语法,分别是saveupdate方法。有同事说从peewee的日志来看,update比save更快,于是做了一个简单的比较实验,看看真实情况如何。

基础环境:
python: 3.8.10
peewee: 3.16.2
数据库:sqlite

准备

插入1w条数据

import datetime
from peewee import AutoField, DateTimeField, Model, SqliteDatabase, TextField, IntegerField

class BaseModel(Model):
    """A base model that will use our Sqlite database."""
    id = AutoField()
    update_time = DateTimeField(default=datetime.datetime.now)

    class Meta:
        database = db
		
		
class User(BaseModel):
    name = TextField()
    age = IntegerField()

    class Meta:
        table_name = "user"
		
		
if __name__ == "__main__":
    User.truncate_table()
    db.connect()
    db.create_tables([User])
	
	data = []
    for i in range(10000):
        data.append({"name": f"person_P{i}", "age": i})
        print(i)

    User.insert_many(data).execute()

update 更新

if __name__ == "__main__":
    import logging
    import time

    logger = logging.getLogger('peewee')
    logger.propagate = False
    logger.addHandler(logging.StreamHandler())
    logger.setLevel(logging.DEBUG)
	
    start = time.time()
    for user in users:
        User.update(age=6012).where(User.id==user.id).execute()
        print(user.id, user.name, user.age)
	end = time.time()
    print(f"total: {end-start}")
	

peewee:日志

('UPDATE "user" SET "update_time" = ?, "name" = ?, "age" = ? WHERE ("user"."id" = ?)', [datetime.datetime(2023, 8, 29, 17, 30, 36, 719081), 'person_P9996', 12341, 10024])
('UPDATE "user" SET "update_time" = ?, "name" = ?, "age" = ? WHERE ("user"."id" = ?)', [datetime.datetime(2023, 8, 29, 17, 30, 36, 719088), 'person_P9997', 12341, 10025])
('UPDATE "user" SET "update_time" = ?, "name" = ?, "age" = ? WHERE ("user"."id" = ?)', [datetime.datetime(2023, 8, 29, 17, 30, 36, 719096), 'person_P9998', 12341, 10026])
('UPDATE "user" SET "update_time" = ?, "name" = ?, "age" = ? WHERE ("user"."id" = ?)', [datetime.datetime(2023, 8, 29, 17, 30, 36, 719103), 'person_P9999', 12341, 10027])

结果:67.96582674980164 s

save更新

if __name__ == "__main__":
    import logging
    import time

    logger = logging.getLogger('peewee')
    logger.propagate = False
    logger.addHandler(logging.StreamHandler())
    logger.setLevel(logging.DEBUG)
	
    start = time.time()
    for user in users:
       user.age = 12341
        user.save()
        print(user.id, user.name, user.age)
	end = time.time()
    print(f"total: {end-start}")

peewee日志:

('UPDATE "user" SET "age" = ? WHERE ("user"."id" = ?)', [2000, 10025])
10025 person_P9997 3000
('UPDATE "user" SET "age" = ? WHERE ("user"."id" = ?)', [2000, 10026])
10026 person_P9998 3000
('UPDATE "user" SET "age" = ? WHERE ("user"."id" = ?)', [2000, 10027])

结果:67.52418804168701 s

结果分析

从三个方面来分析:

  • 从打印的日志来看,save会更新记录全部字段,update只会更新指定的字段。
  • 从结果来看,1w行以内的更新操作两者没有性能的差别。
  • 从方法实现来看,update方法是底层方法,save方法调用了update方法或insert方法实现更新操作。

所以理论上来说updatesave 更底层,效率略高。实际使用中save写法较为方便,个人更喜欢save方法。

update 方法

    def __sql__(self, ctx):
        super(Update, self).__sql__(ctx)

        with ctx.scope_values(subquery=True):
            ctx.literal('UPDATE ')

            expressions = []
            for k, v in sorted(self._update.items(), key=ctx.column_sort_key):
                if not isinstance(v, Node):
                    if isinstance(k, Field):
                        v = k.to_value(v)
                    else:
                        v = Value(v, unpack=False)
                elif isinstance(v, Model) and isinstance(k, ForeignKeyField):
                    # NB: we want to ensure that when passed a model instance
                    # in the context of a foreign-key, we apply the fk-specific
                    # adaptation of the model.
                    v = k.to_value(v)

                if not isinstance(v, Value):
                    v = qualify_names(v)

                expressions.append(NodeList((k, SQL('='), v)))

            (ctx
             .sql(self.table)
             .literal(' SET ')
             .sql(CommaNodeList(expressions)))

            if self._from:
                with ctx.scope_source(parentheses=False):
                    ctx.literal(' FROM ').sql(CommaNodeList(self._from))

            if self._where:
                with ctx.scope_normal():
                    ctx.literal(' WHERE ').sql(self._where)
            self._apply_ordering(ctx)
            return self.apply_returning(ctx)

update方法可以看到是拼接出一个sql语句,update xx set xx=xx where

save 方法

    def save(self, force_insert=False, only=None):
        field_dict = self.__data__.copy()
        if self._meta.primary_key is not False:
            pk_field = self._meta.primary_key
            pk_value = self._pk
        else:
            pk_field = pk_value = None
        if only is not None:
            field_dict = self._prune_fields(field_dict, only)
        elif self._meta.only_save_dirty and not force_insert:
            field_dict = self._prune_fields(field_dict, self.dirty_fields)
            if not field_dict:
                self._dirty.clear()
                return False

        self._populate_unsaved_relations(field_dict)
        rows = 1

        if self._meta.auto_increment and pk_value is None:
            field_dict.pop(pk_field.name, None)

        if pk_value is not None and not force_insert:
            if self._meta.composite_key:
                for pk_part_name in pk_field.field_names:
                    field_dict.pop(pk_part_name, None)
            else:
                field_dict.pop(pk_field.name, None)
            if not field_dict:
                raise ValueError('no data to save!')
            rows = self.update(**field_dict).where(self._pk_expr()).execute()
        elif pk_field is not None:
            pk = self.insert(**field_dict).execute()
            if pk is not None and (self._meta.auto_increment or
                                   pk_value is None):
                self._pk = pk
                # Although we set the primary-key, do not mark it as dirty.
                self._dirty.discard(pk_field.name)
        else:
            self.insert(**field_dict).execute()

        self._dirty -= set(field_dict)  # Remove any fields we saved.
        return rows

save方法是调用update方法或insert方法间接实现更新。

标签:peewee,self,update,field,user,._,pk,save
From: https://www.cnblogs.com/goldsunshine/p/17665599.html

相关文章

  • Unable to save plugin settings: The plugin com.thief.idea failed to save setting
    不知道什么原因未解决 IDEA这个报错翻译过来就是:“保存设置失败”,至于是为什么失败,并没有在此处说明,但是IDEA把具体原因放到了他的日志文件中,所以只要我们找到了日志文件,那么就可以对症下药,解决问题!1.寻找日志文件我的日志文件地址 C:\Users\用户名\AppData\Local\JetBrai......
  • Windows 11 22H2 中文版、英文版 (x64、ARM64) 下载 (updated Aug 2023)
    Windows1122H2中文版、英文版(x64、ARM64)下载(updatedAug2023)Windows11,version22H2,2023年8月更新请访问原文链接:https://sysin.org/blog/windows-11/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.org全新推出Windows11全新Windows体验,让您与热......
  • Windows 10, version 22H2 (updated Aug 2023) 中文版、英文版下载
    Windows10,version22H2(updatedAug2023)中文版、英文版下载Windows1022H2企业版arm64x64请访问原文链接:https://sysin.org/blog/windows-10/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.orgWindows10更新历史记录Windows10,version22H2,alledit......
  • Windows Server 2022 中文版、英文版下载 (updated Aug 2023)
    WindowsServer2022中文版、英文版下载(updatedAug2023)WindowsServer2022正式版,2023年8月更新请访问原文链接:https://sysin.org/blog/windows-server-2022/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.org早期直观体验版本21H2,根据名称预计今年秋季......
  • Windows 10 on ARM, version 22H2 (updated Aug 2023) ARM64 AArch64 中文版、英文版
    Windows10onARM,version22H2(updatedAug2023)ARM64AArch64中文版、英文版下载基于ARM的Windows10请访问原文链接:https://sysin.org/blog/windows-10-arm/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.org基于ARM的Windows10起初,Windows10(与Win......
  • Unable to update index for central http://repo1.maven.org/maven2/
    Unable to update index for central http://repo1.maven.org/maven2/ 就是这句,myeclipse启动后控制台输出这句话:解决办法:1.在myeclipse3.4(我用的这个版本)里面Window => Preferences => Myeclipse Enterprise Workbench => Maven4Myeclipse => Maven=>禁用Downloa......
  • hibernate——Session接口中定义的saveOrUpdate()方法浅析
    Session接口中定义的saveOrUpdate方法,集合了save和update方法,根据对象的状态来选择是进行保存还是更新,那它是怎么判断当前对象的状态的呢?API中对这个方法是这样说的,它是根据一个unsaved-value来决定的。这个值是在映射文件中的<id>标签中的一个属性。<id>标签表示的是表的主键,若主......
  • CF1862F Magic Will Save the World
    思路假设总共耗时是\(s\)秒,那么最多可以消灭的总生命值是\(s\times(w+f)\)。所以我们可以先求出所有怪物的生命值之和\(sum\),那么,至少需要时间\(t=\lfloor\frac{sum}{w+f}\rfloor\)。然后我们可以算出用这些时间最多可以用水魔法消灭的生命值为\(w\timest\)。那么,我......
  • 解放生产力orm并发更新下应该这么处理求求你别再用UpdateById了
    合集-easy-query(7) 1.献给转java的c#和java程序员的数据库orm框架05-222.javaer你还在手写分表分库?来看看这个框架怎么做的干货满满05-263.你没见过的分库分表原理解析和解决方案(一)06-074.你没见过的分库分表原理解析和解决方案(二)06-305.我真的不想再用mybatis和其......
  • git中Updates were rejected because the tip of your current branch is behind解决
    出现错误原因是操作过程中出现失误,git上进行的修改没有同步到本地的git仓库解决方案:gitpush-uoriginmaster-f在远程仓库中进行的相关修改会被删除,是远程仓库回到你本地仓库为修改之前的版本。然后上传你本地仓库的修改。注意:如果是多人开发则其他人的开发结果将会被恢复到......