RE:NODE

სახელმძღვანელოები12 წუთის საკითხავი

FiveM-ის ბაზა და oxmysql: გამართვა და ნელი მოთხოვნები

როგორ იყენებს FiveM სერვერი მონაცემთა ბაზას: პანელის ბაზის სლოტი, oxmysql connection string, framework-ის სქემის იმპორტი, ნელი მოთხოვნები და backup-ები.

0 მკითხველი

ყველაფერი, რასაც FiveM roleplay სერვერი გადატვირთვებს შორის ახსოვს, მონაცემთა ბაზაში ცხოვრობს: პერსონაჟები, ფული, ავტომობილები, ქონება, პროფესიები, ბანები. framework მას პირდაპირ არ ესაუბრება. ის oxmysql-ის გავლით მიდის - resource-ით, რომელიც კავშირების pool-ს ინახავს და მოთხოვნის ფუნქციებს სერვერზე ყველა სხვა resource-ს აწვდის. სწორად გამართე connection string და მასზე აღარასოდეს იფიქრებ. არასწორად გამართე და სიმპტომი ბაზის შეცდომა არ იქნება - მოთამაშეები პერსონაჟის გარეშე გამოჩნდებიან, ფული relog-ზე განულდება, ან მთელი სერვერი ჩატვირთვის ეკრანზე გაიჭედება, სანამ ერთი resource მოთხოვნას ელოდება, რომელიც არასოდეს უპასუხებს.

რას ინახავს FiveM სერვერი სინამდვილეში#

framework-ის სქემა უფრო დიდია, ვიდრე ხალხი ელოდება. ESX Legacy და QBCore ორივე SQL dump-ს ატარებს, რომელიც იმპორტისას თხუთმეტიდან ორმოცამდე ცხრილს ქმნის, და ყოველი resource, რომელსაც შემდეგ დაამატებ, საკუთარს დაამატებს.

ისინი, რომლებიც მნიშვნელოვანია და რომლებსაც ხელით ნახავ:

  • პერსონაჟის ცხრილი. ESX მას users-ს უწოდებს და identifier-ზე ახარისხებს. QBCore და Qbox მას players-ს უწოდებენ და citizenid-ზე ახარისხებენ, მოთამაშის ლიცენზიის იდენტიფიკატორით ცალკე სვეტში. ერთი სტრიქონი პერსონაჟზე.
  • ავტომობილები. owned_vehicles ESX-ზე, player_vehicles QBCore-ზე, ორივე ნომრით გასაღებული და მფლობელთან დაკავშირებული.
  • პროფესიები და ხარისხები. ESX-ზე ეს ბაზის სტრიქონებია jobs და job_grades-ში. QBCore-სა და Qbox-ზე ისინი Lua-შია და არა SQL-ში, რაც ერთ-ერთი პრაქტიკული განსხვავებაა, აღწერილი FiveM framework-ების შედარებაში.
  • ბანები, ლოგები და რაც კი შენმა ტელეფონმა, ბანკმა და საცხოვრებლის resource-ებმა შექმნეს.

რაც ყველა მათგანზე უნდა გესმოდეს, ისაა, რომ საინტერესო სვეტები JSON ბლობებია. QBCore-ის players სტრიქონი money, charinfo, job, gang, metadata და inventory-ს JSON ტექსტად ინახავს. ESX იგივეს accounts და inventory-ით აკეთებს. ეს დიზაინი framework-ს მოქნილს ინარჩუნებს და ნიშნავს, რომ ამ სვეტების შიგნით ინდექსს ვერ გააკეთებ, მოთხოვნას "ყველა, ვისაც ბანკში ათ ათასზე მეტი აქვს" ეფექტიანად ვერ გაუშვებ, და ყოველ SELECT * მოთამაშეზე რამდენიმე კილობაიტს იღებ. ასევე ნიშნავს, რომ დაზიანებული ბლობი ცხრილს კი არა, ზუსტად ერთ პერსონაჟს ამტვრევს, რაც გარიგებაა, რომელიც მათ აირჩიეს.

პერსონაჟები უწყვეტად არ იწერება. ორივე framework ინახავს ტაიმერით და გათიშვისას - qb-core ინტერვალს Config.UpdateInterval-ად აჩვენებს, წუთებში, ხოლო ESX-ს მის კონფიგურაციაში ექვივალენტი აქვს. რაც პროცესს სუფთა გამორთვის გარეშე კლავს, ყველა ონლაინ მოთამაშისთვის ერთ ინტერვალამდე პროგრესს კარგავს. ეს ერთი საუკეთესო არგუმენტია იმისთვის, რომ FiveM სერვერი პანელის Restart ღილაკით გადატვირთო და არა მისი მოკვლით.

ბაზის სლოტი და რა სჭირდება მისგან oxmysql-ს#

oxmysql MySQL wire protocol-ს ლაპარაკობს, ამიტომ MySQL-ის ან MariaDB-თან თავსებადი სერვერი სჭირდება. შენგან ხუთი რამ უნდა: host, port, მომხმარებელი, პაროლი და ბაზის სახელი.

RE:NODE-ზე თამაშის გეგმა მოიცავს ერთ ბაზის სლოტს, რომელიც იქმნება პანელის Databases tab-იდან. პანელი host-ს, მომხმარებელსა და პაროლს თავად აგენერირებს; მონაცემთა ბაზის სერვერს არ აყენებ და არ ადმინისტრირებ, root ანგარიშიც არ არის მოსავლელი. სლოტის გვერდით არის Open in phpMyAdmin ღილაკი, რომელიც ერთჯერადი token-ით შედის, რომელიც სამოცი წამში იწურება - ამიტომ ცხრილების დათვალიერება ღილაკია და არა კიდევ ერთი პაროლი, რომელიც სადმე უნდა შეინახო. პანელი ძრავს, რომელიც სლოტის უკან დგას, არ ასახელებს და oxmysql-ს ამის ცოდნა არ სჭირდება: ის პროტოკოლს დაკავშირებისას ალაგებს. მნიშვნელოვანია, რომ credentials, რომელსაც პანელი გაძლევს, ისევე ჩაჯდეს server.cfg-ში.

ორი პატიოსანი ზღვარი. ერთი სლოტი ნიშნავს ერთ ბაზას - framework-ისთვის საკმარისია, რადგან ESX და QBCore ორივე ყველაფერს ერთში ინახავს, მაგრამ არასაკმარისია, თუ იმავე გეგმაზე Discord ბოტისთვისაც ცალკე ბაზა გინდოდა. და ჩვენი მონაცემთა ბაზის ჰოსტინგი PostgreSQL-ისა და MongoDB-ის ხაზია, რომელთაგან არცერთს oxmysql ვერ ესაუბრება, ამიტომ ეს პროდუქტი FiveM სქემისთვის განახლების გზა არ არის. პანელის ბაზის სლოტი არის.

Connection string#

oxmysql ერთ convar-ს კითხულობს. ჩააგდე ის server.cfg-ში ensure ხაზების ზემოთ, set-ით და არასოდეს sets-ით - server list-ზე გამოქვეყნებულ connection string-ს წუთებში აგროვებენ. სამს შორის განსხვავება FiveM server.cfg ახსნილშია.

server.cfg
set mysql_connection_string "mysql://s7_user:PaSsW0rd@db.example.net:3306/s7_fivem?charset=utf8mb4"ensure oxmysqlensure ox_libensure qb-core

ნაწილები თანმიმდევრობით: მომხმარებელი, პაროლი, host, port, ბაზის სახელი, პარამეტრები. სამი რამ ტყდება აქ ყველაფერზე მეტად.

სპეციალური სიმბოლოები პაროლში. URI-ს სტრუქტურული სიმბოლოები აქვს და @, :, /, ?, # და % პაროლში პარსინგს ისე ტეხს, რომ უაზრო შეცდომას იძლევა. ან percent-encode გაუკეთე (@ ხდება %40), ან გამოიყენე წერტილ-მძიმიანი ფორმა, რომელსაც ეს არ აინტერესებს:

config
set mysql_connection_string "server=db.example.net;port=3306;userid=s7_user;password=P@ss:word;database=s7_fivem"

Host არ არის `localhost`. მონაცემთა ბაზა ცალკე სერვისია, რომელსაც ქსელით უკავშირდები, ამიტომ 127.0.0.1 არაფერს უკავშირდება. დააკოპირე host ზუსტად ისე, როგორც პანელი ბეჭდავს.

ბაზის სახელს პრეფიქსი აქვს. პანელის მიერ გენერირებულ ბაზებს ჩვეულებრივ სერვერის პრეფიქსი აქვს, მაგალითად s7_. დატოვე. ER_BAD_DB_ERROR თითქმის ყოველთვის ვიღაცაა, ვინც მოაჭრა.

დაამატე ?charset=utf8mb4 და გულით იფიქრე. მის გარეშე პირველი მოთამაშე, რომლის პერსონაჟის სახელში აქცენტი, კირილიცა ან emoji ჩანს, სვეტში გატეხილ ბაიტებს წერს, და ამას კვირების შემდეგ გაიგებ. ორი სასარგებლო დამატება: ?connectionLimit=8 pool-ს ზღუდავს, და oxmysql იმავე query string-ში ჩვეულებრივ driver პარამეტრებს იღებს.

ჩართე დიაგნოსტიკა გამართვისას და გამორთე, როცა იმუშავებს:

config
set mysql_debug true                 # prints every query, very noisyset mysql_slow_query_warning 150     # milliseconds before a warning

mysql_debug მხოლოდ სატესტო სერვერს ეკუთვნის. mysql_slow_query_warning მუდმივად ჩართული უნდა იყოს; ეს ყველაზე იაფი მწარმოებლურობის ინსტრუმენტია, რაც გაქვს, და ის პასუხისმგებელ resource-ს ასახელებს.

MySQL.querypooled connectionsშენ, ხელითrowsResourceqb-garagesphpMyAdminერთჯერადი tokenბაზის სლოტიhost, user, passwordoxmysqlconnection pool
როგორ აღწევს resource ბაზას

Framework-ის სქემის იმპორტი#

ყოველი framework SQL dump-ს ატარებს. იმპორტი გააკეთე პირველ გაშვებამდე და არა მას შემდეგ, რაც სერვერმა ათი წუთი ER_NO_SUCH_TABLE-ის ბეჭდვაში გაატარა.

  1. იპოვე dump. ის ჩვეულებრივ ბირთვულ resource-შია - qb-core/qb-core.sql QBCore-ისთვის, ექვივალენტი ESX-ის რელიზში. ზოგი build მას რამდენიმე ფაილად ყოფს და თუ foreign key-ებია, თანმიმდევრობას მნიშვნელობა აქვს.
  2. გახსენი phpMyAdmin პანელიდან, აირჩიე შენი ბაზა მარცხენა სვეტში და გამოიყენე Import tab.
  3. იმპორტისას დააყენე სიმბოლოთა ნაკრები utf8mb4, connection string-ში მითითებულის შესაბამისად.
  4. შემდეგ შეამოწმე ცხრილების რაოდენობა. dump, რომელიც შუაში გაჩერდა, სერვერს ტოვებს, რომელიც ნახევრად მუშაობს, რაც უარესია, ვიდრე ის, რომელიც საერთოდ არ ეშვება.

თუ ფაილი ატვირთვის ლიმიტს აჭარბებს - framework dump-ები ხშირად აჭარბებს - შეკუმშე. phpMyAdmin იღებს .sql.gz და .sql.zip-ს და შემოსვლისას ხსნის, რაც dump-ს ჩვეულებრივ რვა-ცხრაჯერ ამცირებს. თუ იმპორტი შუაში დროს ამოწურავს, phpMyAdmin-ის Import გვერდზე არის ველი "Skip this number of queries": დათვალე, რა შესრულდა უკვე, და იქიდან განაგრძე. phpMyAdmin-ის იმპორტისა და ექსპორტის გზამკვლევი ორივეს დეტალურად გადის.

მოთხოვნები, რომლებიც არ გტკივა#

oxmysql resource-ში იტვირთება fxmanifest.lua-ში ერთი ხაზის დამატებით:

lua
server_scripts {    '@oxmysql/lib/MySQL.lua',    'server/main.lua',}

ეს გაძლევს MySQL.query, MySQL.single, MySQL.scalar, MySQL.insert, MySQL.update, MySQL.prepare და MySQL.transaction-ს, თითოეულს .await ვარიანტით coroutine-ში გამოსაყენებლად.

lua
-- one row, one valuelocal money = MySQL.scalar.await(    'SELECT money FROM players WHERE citizenid = ?', { citizenid })-- one row as a tablelocal row = MySQL.single.await(    'SELECT charinfo, job FROM players WHERE citizenid = ?', { citizenid })-- many statements, one round trip, all or nothingMySQL.transaction.await({    { 'UPDATE players SET money = ? WHERE citizenid = ?', { newMoney, citizenid } },    { 'INSERT INTO bank_log (citizenid, amount) VALUES (?, ?)', { citizenid, delta } },})

ყოველთვის გამოიყენე ? placeholder-ები და არა მნიშვნელობების სტრიქონში შეერთება. ეს უფრო სწრაფია, რადგან სერვერს გეგმის ხელახლა გამოყენება შეუძლია, და ერთადერთი დაცვაა იმისგან, რომ მოთამაშემ სახელში ბრჭყალი ჩააგდოს და შენი მოთხოვნა გადაგიწეროს.

ოთხი ჩვევა განასხვავებს ბაზას, რომელიც 64 მოთამაშეზე უქმია, იმისგან, რომელიც შეფერხებაა:

  • არასოდეს გაუშვა მოთხოვნა მოთამაშეებზე ციკლში. ერთი მოთხოვნა, რომელიც ოცდაათ სტრიქონს აბრუნებს, სჯობს ოცდაათს, რომლებიც თითო სტრიქონს აბრუნებს. თუ მოთამაშეებზე გადადიხარ და ციკლში .await-ს იძახებ, coroutine-საც ყოველ იტერაციაზე ბლოკავ.
  • არასოდეს გაუშვა მოთხოვნა tick-ზე. მნიშვნელობა Lua ცხრილში ჩააქეშე, უკან ცვლილებისას და შენახვისას ჩაწერე.
  • აირჩიე მხოლოდ საჭირო სვეტები. SELECT * players სტრიქონზე ყველა JSON ბლობს ქსელში ათრევს, ინვენტარები კი პატარა არ არის.
  • ჩაწერები ტრანზაქციაში დააჯგუფე. ათი განახლება ერთ ტრანზაქციაში ერთი round trip-ია და ერთი ლოკის ფანჯარა; ათი ცალკე განახლება ორივესგან ათია.

Connection pool-ები და ლიმიტები pool-ის მხარეს განიხილავს, რომელიც მაშინ გკბენს, როცა ოცი resource ერთდროულად წყვეტს მოხერხებულობას.

ნელი მოთხოვნის პოვნა#

mysql_slow_query_warning დაყენებული რომ იყოს, oxmysql ბეჭდავს ხაზს, რომელიც ასახელებს resource-ს, დახარჯულ დროს და ინსტრუქციას, როცა მოთხოვნა ზღვარს გადააჭარბებს. ეს ხაზი უმეტესად მთელი გამოძიებაა: resource, რომელსაც 2022-დან არავის შეუხედავს, SELECT-ს უშვებს LIKE '%name%'-ით ცხრილზე, რომელიც ოთხას ათას სტრიქონამდე გაიზარდა.

როცა გაფრთხილება საკმარისი არ არის, აიღე ინსტრუქცია phpMyAdmin-ში და წინ EXPLAIN მიაწერე. ის, რასაც ეძებ, სკანის ტიპია. სტრიქონი, რომელიც ამბობს, რომ მოთხოვნამ ერთი შედეგისთვის ცხრილის უმეტესობა შეამოწმა, გამოტოვებული ინდექსია და გამოსწორება ჩვეულებრივ ერთი ხაზია:

sql
ALTER TABLE player_vehicles ADD INDEX idx_citizenid (citizenid);ALTER TABLE owned_vehicles ADD INDEX idx_owner (owner);

დააინდექსე სვეტები, რომლებზეც ფილტრავ ან join-ს აკეთებ - owner, citizenid, identifier, plate. ყველაფერს ნუ დააინდექსებ: ყოველი ინდექსი კიდევ ერთი რამაა, რაც ყოველ ჩასმაზე უნდა ჩაიწეროს, და ცხრილი თორმეტი ინდექსით პერსონაჟის შენახვაზე უფრო ნელია, ვიდრე ის, რომელსაც სამი აქვს.

პრობლემის მეორე ნახევარი ცხრილებია, რომლებიც უსასრულოდ იზრდება. ლოგირების resource-ები, ტელეფონის შეტყობინებები, ტრანზაქციების ისტორიები და ადმინისტრატორის მოქმედების ლოგები სამუდამოდ გროვდება, სანამ ვინმე არ გაწმენდს. ყოველთვიური გაწმენდა მათ ნელ მოთხოვნად ქცევას უშლის:

sql
DELETE FROM <your log table> WHERE created_at < NOW() - INTERVAL 30 DAY;

გაუშვი ერთხელ ხელით, შეამოწმე სტრიქონების რაოდენობა და მერე გადაწყვიტე, ღირს თუ არა დაგეგმვა. მილიონი სტრიქონის ერთი ინსტრუქციით წაშლა ლოკებს დიდხანს აკავებს, ამიტომ დიდ ცხრილზე გააკეთე პარტიებად LIMIT 10000-ით და გაიმეორე.

Backup-ები და ის, რაც ადვილად გამოგრჩება#

შენი სერვერის backup ფაილებს იღებს: resources/, server.cfg, cache. მონაცემთა ბაზა ცალკე სერვისია ცალკე host-ზე, ამიტომ ის ამ არქივში არ არის. თუ რამის გაფუჭების შემდეგ სერვერის backup-ს აღადგენ, ყველა resource უკან გიბრუნდება და ყველა პერსონაჟი ზუსტად ისეთია, როგორიც ბაზის ახლანდელ მდგომარეობაშია, რაც შეიძლება ის მდგომარეობა იყოს, რომლისგანაც გაქცევა გინდოდა.

ამიტომ ბაზაც ექსპორტირე და ეს ჩათვალე ნამდვილ backup-ად:

  1. phpMyAdmin-ში აირჩიე ბაზა, შემდეგ Export, შემდეგ Custom.
  2. ფორმატი SQL, გამომავალი gzip-ით შეკუმშული და მონიშნე "Add DROP TABLE", რომ ფაილი სუფთად აღდგეს ბაზაზე, რომელსაც უკვე აქვს ცხრილები.
  3. ჩამოტვირთე და შეინახე იქ, რაც თამაშის სერვერი არ არის. ასლი resources/-ში backup არ არის - ის იმასთან ერთად კვდება, რასაც იცავდა.

გააკეთე ეს ყოველი framework-ის განახლების წინ, ნებისმიერი resource-ის .sql-ის გაშვებამდე და რუტინაზე, რომელსაც მართლა შეინარჩუნებ. შემდეგ აღადგინე ერთი, ერთხელ, სატესტო ბაზაში და შედი - აღდგენის ტესტირება, სანამ დაგჭირდება არსებობს, რადგან ექსპორტი, რომელიც არასოდეს შემოუტანიათ, ჰიპოთეზაა. მონაცემთა ბაზის backup-ები და აღდგენა ზოგად პროცედურას შეიცავს, ხოლო პანელის გზამკვლევები გვიჩვენებს, სად ცხოვრობს სლოტი.

დაკავშირების შეცდომები და რას ნიშნავს ისინი#

შეცდომამიზეზი
ECONNREFUSEDარასწორი host ან port, ან localhost სტრიქონში
ER_ACCESS_DENIED_ERRORარასწორი მომხმარებელი ან პაროლი, ან არაკოდირებული სიმბოლო
ER_BAD_DB_ERRORბაზის სახელი არასწორია, ჩვეულებრივ მოჭრილი პრეფიქსი
ER_NO_SUCH_TABLEსქემა არასოდეს შემოტანილა, ან სხვაგან შემოვიდა
ER_BAD_FIELD_ERRORresource-ს სჭირდება სვეტი, რომელიც შენს სქემის ვერსიას აკლია
ER_DATA_TOO_LONGJSON ბლობი თავის სვეტის ტიპს გადააჭარბა
ER_CON_COUNT_ERRORძალიან ბევრი კავშირი; შეამცირე connectionLimit

კიდევ ორი, რომლებიც შეცდომები არ არის, მაგრამ ჰგავს. Connection lost: the server closed the connection უქმ სერვერზე არის ბაზა, რომელიც ძველ კავშირს წყვეტს, და oxmysql მას ხელახლა ხსნის - თუ ეს საათში ერთხელ ხდება და არაფერი ტყდება, უგულებელყავი. და პირველი მოთხოვნა, რომელიც გადატვირთვის შემდეგ ორ წამს გრძელდება, pool-ის გახურებაა და არა ნელი ბაზა.

თუ საერთოდ არაფერი უკავშირდება, შეამოწმე ამ თანმიმდევრობით: convar set-ია და სწორად წერია, ensure oxmysql ყველაფერზე ადრე დგას, რაც მას იყენებს, host პანელის host-ია და არა localhost, და პაროლში არაკოდირებული სპეციალური სიმბოლოები არ არის. ეს თანმიმდევრობა თითქმის ყველა შემთხვევას წყვეტს.

FAQ#

სჭირდება თუ არა FiveM სერვერს მონაცემთა ბაზა?

მხოლოდ იმ შემთხვევაში, თუ რამის დამახსოვრება სჭირდება. freeroam ან racing სერვერი პერსისტენტობის გარეშე მონაცემთა ბაზის გარეშეც კარგად მუშაობს. ყველა roleplay framework-ს სჭირდება, რადგან პერსონაჟი, რომელიც გადატვირთვას ვერ უძლებს, პერსონაჟი არ არის.

შემიძლია PostgreSQL ან MongoDB გამოვიყენო?

oxmysql-ითაც და არც ერთი მთავარი FiveM framework-ითაც არა. ისინი MySQL პროტოკოლისა და მისი SQL დიალექტისთვისაა დაწერილი. ჩვენი PostgreSQL-ისა და MongoDB-ის ხაზები აპლიკაციებისთვისაა და არა ESX-ისა ან QBCore-ისთვის.

რამხელა ხდება FiveM-ის მონაცემთა ბაზა?

უფრო პატარა, ვიდრე ხალხს ეშინია. სერვერი რამდენიმე ათასი პერსონაჟით, მათი ავტომობილებითა და ერთი წლის ლოგებით ჩვეულებრივ რამდენიმე ასეულ მეგაბაიტშია. ლოგის ცხრილები იზრდება და არა პერსონაჟები - თუ შენი გიგაბაიტებშია, ერთი ლოგირების resource არის პასუხისმგებელი და მისი გაწმენდა უმეტესს უკან დაგიბრუნებს.

რატომ დაკარგა ყველამ ფული ავარიის შემდეგ?

იმიტომ, რომ პერსონაჟები ტაიმერით იწერება და არა ყოველ ცვლილებაზე. პროცესი, რომელიც სუფთა გამორთვის გარეშე კვდება, ყველა ონლაინ მოთამაშისთვის კარგავს ყველაფერს, რაც ბოლო შენახვის შემდეგ მოხდა. შეამოკლე framework-ის შენახვის ინტერვალი, თუ დანაკარგი ჩაწერის დატვირთვაზე მნიშვნელოვანია.

შეუძლია თუ არა ორ სერვერს ერთი ბაზის გაზიარება?

ტექნიკურად დიახ, და ასე უშვებენ სატესტო სერვერს ნამდვილ მონაცემებზე. ნუ გააკეთებ ორი ცოცხალი სერვერით ერთსა და იმავე framework-ზე: ორივე ერთსა და იმავე პერსონაჟის სტრიქონებს აქეშებს და წერს, ბოლოს ჩამწერი იმარჯვებს, რასაც მოთამაშეები ნივთების გაქრობად განიცდიან.

უნდა ვიცოდე SQL FiveM სერვერის გასაშვებად?

საკმარისად, რომ EXPLAIN წაიკითხო, ინდექსი დაამატო და dump ექსპორტირე. დანარჩენს framework-ის საკუთარი dump აკეთებს. ამ პოსტში ყველაფერი ოთხი-ხუთი ინსტრუქციაა, რომლებიც შეგიძლია დააკოპირო, და phpMyAdmin მათ უმეტესს შენ მაგივრად დაწერს.


კომენტარები

სრულიად ანონიმურად: ანგარიშის, ელფოსტის და cookie-ის გარეშე. ინახება მხოლოდ სახელი, ტექსტი და დრო - სხვა არაფერი. ბმულების რაოდენობა ლიმიტირებულია.

0/2000