Найти - Пользователи
Полная версия: Многопоточная работа с mysql
Начало » Базы данных » Многопоточная работа с mysql
1
pandarado@gmail.com
Здравствуйте, нужна помощь вот в такой ситуации. Есть многопоточный бот, который собирает информацию с досок объявлений и сохраняет некоторые даные в БД. Функции: Получить все записи, Добавить запись, Обновить запись, Удалить запись. Запущен он на 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
PooH
Какие вы себе матерые грабли соорудили.

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

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

Ну и вишенкой на торте, используйте биндинг параметров в запросах везде. Не надо интерполяции строк, типа
 sql = "DELETE FROM `main` WHERE id = '%s'" % (ID)
Здесь это вроде не опасно, но это привычка. Ее стоит вырабатывать.

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


pandarado@gmail.com
db.store_result() - проблемма решилась этим
PooH
pandarado@gmail.com
Можно подробнее про пулл,
Да просто список соединений, каждый поток запрашивает соединение у него, а после работы не закрывает, а отдает обратно. Естественно доступ к пулу из потоков надо синхронизировать. Вообщем то там нюансов много, так что лучше поискать готовую библиотеку.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB