Уведомления

Группа в Telegram: @pythonsu

#1 Март 2, 2017 18:55:35

pandarado@gmail.com
Зарегистрирован: 2017-03-02
Сообщения: 3
Репутация: +  0  -
Профиль   Отправить e-mail  

Многопоточная работа с mysql

Здравствуйте, нужна помощь вот в такой ситуации. Есть многопоточный бот, который собирает информацию с досок объявлений и сохраняет некоторые даные в БД. Функции: Получить все записи, Добавить запись, Обновить запись, Удалить запись. Запущен он на Ubuntu 16 на локалхосте. Происходит следующее, бот работает какое то время, и создает скажем каждые 10 минут по новому потоку, записывает данные в базу данных.

 d_id=InsertMain(message.chat.id,text)
bot.send_message(message.chat.id,config.SuccesAppend)
c_id=message.chat.id
thread_ = threading.Thread(target=Select,args=[c_id,d_id])
thread_.setDaemon(True)
thread_.setName=str(c_id)
thread_.start()
Затем бот в бесконечном цикле собирает информацию и выводи ее пользователю по d_id.
 def Select(mId,d_id):
	while True:
		Wait=randint(0,1)
		WaitInput = randint(20,30)
		time.sleep(1)
		lock.acquire()
		try:
			result=conn.SelectMainAndID(d_id)
   		finally:
        		lock.release()
		if result:
			for value in result:
          .     else:
			#bot.send_message(mId,config.NoLink)
			logging.error('BD Error'+str(id_))
			continue
И со временем вылетает ошибка типа “Ошибка сегментирования сделан дамп памяти” грешу на эту строчку, тк часто возвращает пустоту result=conn.SelectMainAndID(d_id).
Вопрос почему при многопоточных запросах result=Null, при том что в базе есть что возвращать, а все остальные запросы типа INSERT,UPDATE,DELETE выполняются без проблем.
Код соединения и запроса:
 class Base:
	def SelectMain(self,mId):
		try:
			self.db = MySQLdb.connect(host="localhost", user="test", passwd="password", db="testdb", charset='utf8')
			self.cursor = self.db.cursor()
			sql ="SELECT * FROM main WHERE M_ID ='%s' FOR UPDATE "% (mId)
			self.cursor.execute(sql)
			data =  self.cursor.fetchall()
			return data
		except Exception:
			print ''.join(traceback.format_exception(*sys.exc_info()))
			return False
	def SelectMainAndID(self,mId):
		try:
			self.db = MySQLdb.connect(host="localhost", user="test", passwd="password", db="testdb", charset='utf8')
			self.cursor = self.db.cursor()
			sql ="SELECT * FROM main WHERE id ='%s' FOR UPDATE"% (mId)
			self.cursor.execute(sql)
			data =  self.cursor.fetchall()
			self.db.close()
			return data
		except:
			return False
	def DeleteMain(self,ID):
		try:
			self.db = MySQLdb.connect(host="localhost", user="test", passwd="password", db="testdb", charset='utf8')
			self.cursor = self.db.cursor()
			sql = "DELETE FROM `main` WHERE id = '%s'" % (ID)
			self.cursor.execute(sql)
			self.db.commit()
			self.db.close()
			return True
		except:
			return False
	def UpdateMain(self,flag,url,mId):
		try:
			self.db = MySQLdb.connect(host="localhost", user="test", passwd="password", db="testdb", charset='utf8')
			self.cursor = self.db.cursor()
			self.cursor.execute("""UPDATE main SET CheckUrl=%s,StartUrl=%s WHERE id=%s""",(flag,url,mId))
			self.db.commit()
			self.db.close()
			return True
		except:
			return False

Офлайн

#2 Март 3, 2017 04:33:51

PooH
От:
Зарегистрирован: 2006-12-05
Сообщения: 1948
Репутация: +  72  -
Профиль   Отправить e-mail  

Многопоточная работа с mysql

Какие вы себе матерые грабли соорудили.

Запросы в базу могут блокировать друг друга. Скорее всего на такую блокировку вы и нарываетесь.

Дальше, установка соединения для каждого запроса это очень дорого, обычно делают пул. Я бы сделал один поток работающий с базой данных - читает из базы и кладет в очередь, потоки берут данные из очереди, обрабатывают и кладут в другую, поток читает из нее и сбрасывает в базу. Соединение постоянное, нет взаимоблокировок, ну и плюсом можно читать/писать пачками строк.

Ну и вишенкой на торте, используйте биндинг параметров в запросах везде. Не надо интерполяции строк, типа

 sql = "DELETE FROM `main` WHERE id = '%s'" % (ID)
Здесь это вроде не опасно, но это привычка. Ее стоит вырабатывать.

ЗЫ: и обработка исключений у вас просто песня - молча жрете любые исключения, прикрутите хотя бы логирование.



Вот здесь один из первых отарков съел лаборанта. Это был такой умный отарк, что понимал даже теорию относительности. Он разговаривал с лаборантом, а потом бросился на него и загрыз…

Отредактировано PooH (Март 3, 2017 04:37:01)

Офлайн

#3 Март 3, 2017 16:41:21

pandarado@gmail.com
Зарегистрирован: 2017-03-02
Сообщения: 3
Репутация: +  0  -
Профиль   Отправить e-mail  

Многопоточная работа с mysql

PooH
Какие вы себе матерые грабли соорудили.
Да я первоначально делал очередь и все было нормально, но потом когда требования к боту усложнилось, пришлось отказаться, не буду вдаваться в подробности. Про биндинг спасибо, да действительно привычка, буду отучаться. Можно подробнее про пулл, тк вариант одного соединения не устраивает, а кроме как создать новое соединение ничего не пришло в голову, я даже на каждый созданый поток пытался создать соединение, но потом понял, что там вообще лес проблем. Сейчас при одном соединении на весь процесс следующие получаю при одновременных запросах:
OperationalError: (2006, ‘MySQL server has gone away’)
OperationalError: (2013, ‘Lost connection to MySQL server during query’) - и тут дело точно не в таймауте


Офлайн

#4 Март 3, 2017 18:13:56

pandarado@gmail.com
Зарегистрирован: 2017-03-02
Сообщения: 3
Репутация: +  0  -
Профиль   Отправить e-mail  

Многопоточная работа с mysql

db.store_result() - проблемма решилась этим

Офлайн

#5 Март 4, 2017 08:40:49

PooH
От:
Зарегистрирован: 2006-12-05
Сообщения: 1948
Репутация: +  72  -
Профиль   Отправить e-mail  

Многопоточная работа с mysql

pandarado@gmail.com
Можно подробнее про пулл,
Да просто список соединений, каждый поток запрашивает соединение у него, а после работы не закрывает, а отдает обратно. Естественно доступ к пулу из потоков надо синхронизировать. Вообщем то там нюансов много, так что лучше поискать готовую библиотеку.



Вот здесь один из первых отарков съел лаборанта. Это был такой умный отарк, что понимал даже теорию относительности. Он разговаривал с лаборантом, а потом бросился на него и загрыз…

Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version