prepare:
Prepare a table
set evn
DBUSER=root DBPASS=123 DBNAME=cyborg
TBNAME="atomic"
RDNAME="s0"
DB create
DBNAME=atomic mysql -u$DBUSER -p$DBPASS <<< "create DATABASE $DBNAME"
Delete DB
mysql -u$DBUSER -p$DBPASS <<< "drop database $DBNAME"
table create
mysql -u$DBUSER -p$DBPASS $DBNAME <<< "CREATE TABLE $TBNAME( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(100) NOT NULL, PRIMARY KEY (id))ENGINE=InnoDB DEFAULT CHARSET=utf8;
desc $TBNAME"
Delete table
mysql -u$DBUSER -p$DBPASS $DBNAME <<< "DROP TABLE $TBNAME"
insert table
mysql -u$DBUSER -p$DBPASS $DBNAME <<< "INSERT INTO $TBNAME (name) VALUES (\"s0\");"
update table
TBNAME="atomic" RDNAME="s0" mysql -u$DBUSER -p$DBPASS $DBNAME <<<"UPDATE $TBNAME SET name='$RDNAME' WHERE id=1"
數據庫鎖(DB lock)
sqlalchemy 使用with_lockmode鎖住DB鎖(不是sqlalchemy 實現的鎖)
test.py 如下:
from sqlalchemy import create_engine,Table,Column,Integer,String,MetaData,ForeignKey
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.orm import relationship, backref
from sqlalchemy.ext.declarative import declarative_base
import time
import sys
print(sys.argv)
mode = sys.argv[1] if len(sys.argv) > 1 else "get"
print("DB for %s" % mode)
Base = declarative_base()
Session = sessionmaker()
engine = create_engine("mysql://root:123@localhost/cyborg")
Session.configure(bind=engine)
session = Session()
class Atomic(Base):
__tablename__ = 'atomic'
id = Column(Integer, primary_key=True)
name = Column(String)
def __str__(self):
return 'Atomic[%d, %s]' % (self.id, self.name)
at_id = 1
filer_name = "s0"
exp_name = "s1"
wait = 10
print("start to get lock", time.strftime("%H:%M:%S"))
if mode == "get":
lock = session.query(Atomic).with_for_update().filter(
Atomic.id == at_id).first()
# remove with_for_update, "update" mode no need wait to get lock
print("get lock: ", time.strftime("%H:%M:%S"))
print(lock)
print("lock the record and wait for %s", wait)
time.sleep(wait)
session.commit()
else:
lock = session.query(Atomic).filter_by(name=filer_name).with_for_update().update(
{"name": exp_name}, synchronize_session="fetch")
print(lock)
print("get lock: ", time.strftime("%H:%M:%S"))
print("update the record and wait for %s", wait)
time.sleep(wait)
session.commit()
print(lock)
先執行update,再read
在terminal 1執行:
python2 test.py update
在terminal 2執行:
python2 test.py
很明顯讀取需要等待時間。
或者
mysql -u$DBUSER -p$DBPASS $DBNAME <<< "select * from $TBNAME"
不需要等待時間。
先執行read ’python2 test.py’,再update也需要等待。
New in version 0.9.0: Query.with_for_update() supersedes the Query.with_lockmode() method.
結論:
update的時候,即使不指定with_for_update, 也會自動獲取這個update鎖。
僅僅query的時候,如果不指定with_for_update, 那么立即執行,不會獲取這個鎖。
sqlalchemy session 執行 delete 時 synchronize_session 策略 (update 同樣適用)
False: 不同步 session,如果被刪除的 objects 已經在 session 中存在,在 session commit 或者 expire_all 之前,這些被刪除的對象都存在 session 中。
不同步可能會導致獲取被刪除 objects 時出錯。
fetch: 刪除之前從 db 中匹配被刪除的對象並保存在 session 中,然后再從 session 中刪除,這樣做是為了讓 session 的對象管理 identity_map 得知被刪除的對象究竟是哪些以便更新引用關系。
evaluate: 默認值。根據當前的 query criteria 掃描 session 中的 objects,如果不能正確執行則拋出錯誤,這句話也可以理解為,如果 session 中原本就沒有這些被刪除的 objects,掃描當然不會發生匹配,相當於匹配未正確執行。
from sqlalchemy import create_engine,Table,Column,Integer,String,MetaData,ForeignKey
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.orm import relationship, backref
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
Session = sessionmaker()
engine = create_engine("mysql://root:123@localhost/cyborg")
Session.configure(bind=engine)
session = Session()
class Atomic(Base):
__tablename__ = 'atomic'
id = Column(Integer, primary_key=True)
name = Column(String)
q = session.query(Atomic)
a = q.filter_by(name="s1")
print(type(a))
print(a)
at = a.one()
print(a.one())
import ipdb; ipdb.set_trace()
# "fetch" "evaluate"
a1 =a.update({"name": "s2"}, synchronize_session=False)
print(type(a1))
print(a1)
print(Atomic.name=="s1")
session.commit()
print(a.one())
REF:
SQL Atomic Operation on UPDATE and DELETE
SQLAlchemy ORM Examples (推薦,一個系列,一共11個部分)
Cyborg DB example:
mysql:
DBUSER=root DBPASS=y0devstk DBNAME=cyborg TBNAME="attach_handles" RDNAME=0 FIELD="in_use" QR_NAME="deployable_id" QR_VALUE=`mysql -u$DBUSER -p$DBPASS $DBNAME <<<"SELECT $QR_NAME FROM $TBNAME LIMIT 1" | tail -n 1` mysql -u$DBUSER -p$DBPASS $DBNAME <<<"UPDATE $TBNAME SET $FIELD=$RDNAME WHERE $QR_NAME=$QR_VALUE" mysql -u$DBUSER -p$DBPASS $DBNAME <<<"SELECT $FIELD FROM $TBNAME WHERE $QR_NAME=$QR_VALUE"
python
import cyborg.conf
import cyborg.db.sqlalchemy.models
from oslo_db import options
from cyborg import context
from cyborg import db as db_api
user="root"
psw="y0devstk"
# connection_debug=1,
# connection_trace=True,
# set_override
CONF = cyborg.conf.CONF
CONF(["--config-file=/etc/cyborg/cyborg.conf"])
options.set_defaults(CONF)
# options.set_defaults(CONF,
# connection="mysql+pymysql://%s:%s@127.0.0.1/cyborg?charset=utf8" % (user, psw))
print(CONF["database"].items())
# from oslo_context import context
ct = context.get_admin_context()
sqlalchemy_api = db_api.get_instance()
try:
r = sqlalchemy_api.attach_handle_list(ct)
r0 = r[0]
print(r0.in_use)
# r = sqlalchemy_api.attach_handle_allocate(ct, r0.attach_type, r0.deployable_id)
r1 = sqlalchemy_api.attach_handle_allocate(ct, r0.deployable_id)
print(r1.in_use)
except Exception as e:
print(e)
openstack oslo.config
