Προγραμματισμός

Γιατί πρέπει να χρησιμοποιήσετε μια βάση δεδομένων γραφημάτων

Ο Jeff Carpenter είναι τεχνικός ευαγγελιστής στο DataStax.

Πρόσφατα υπήρξε μεγάλη δημοσιότητα σχετικά με τις βάσεις δεδομένων γραφημάτων. Ενώ οι βάσεις δεδομένων γραφημάτων όπως το DataStax Enterprise Graph (με βάση το Titan DB), το Neo4 και το IBM Graph υπάρχουν εδώ και αρκετά χρόνια, πρόσφατες ανακοινώσεις υπηρεσιών διαχείρισης cloud όπως το AWS Neptune και η προσθήκη της Microsoft για ικανότητα γραφικών στο Azure Cosmos DB δείχνουν ότι οι βάσεις δεδομένων γραφημάτων έχουν εισέλθει στο mainstream. Με όλο αυτό το ενδιαφέρον, πώς προσδιορίζετε εάν μια βάση δεδομένων γραφημάτων είναι κατάλληλη για την εφαρμογή σας;

Τι είναι η βάση δεδομένων γραφημάτων;

Προτού προχωρήσουμε περισσότερο, ας καθορίσουμε κάποια ορολογία. Τι είναι η βάση δεδομένων γραφημάτων; Σκεφτείτε το από την άποψη του μοντέλου δεδομένων. Ένα μοντέλο δεδομένων γραφήματος αποτελείται από κορυφές που αντιπροσωπεύουν τις οντότητες σε έναν τομέα και άκρες που αντιπροσωπεύουν τις σχέσεις μεταξύ αυτών των οντοτήτων. Επειδή και οι δύο κορυφές και τα άκρα μπορούν να έχουν επιπλέον ζεύγη ονομασίας-τιμής ιδιότητες, αυτό το μοντέλο δεδομένων είναι επίσημα γνωστό ως γράφημα ιδιοτήτων. Ορισμένες βάσεις δεδομένων γραφημάτων απαιτούν να ορίσετε ένα σχήμα για το γράφημα σας, δηλαδή καθορισμός ετικέτες ή ονόματα για τις κορυφές, τις άκρες και τις ιδιότητές σας πριν από τη συμπλήρωση οποιωνδήποτε δεδομένων - ενώ άλλες βάσεις δεδομένων σας επιτρέπουν να λειτουργείτε χωρίς ένα σταθερό σχήμα.

Όπως ίσως έχετε παρατηρήσει, δεν υπάρχουν νέες πληροφορίες στο μοντέλο δεδομένων γραφημάτων που δεν θα μπορούσαμε να καταγράψουμε σε ένα παραδοσιακό σχεσιακό μοντέλο δεδομένων. Σε τελική ανάλυση, είναι απλό να περιγράψουμε σχέσεις μεταξύ πινάκων χρησιμοποιώντας ξένα κλειδιά ή μπορούμε να περιγράψουμε ιδιότητες μιας σχέσης με έναν πίνακα σύνδεσης. Η βασική διαφορά μεταξύ αυτών των μοντέλων δεδομένων είναι ο τρόπος οργάνωσης και πρόσβασης στα δεδομένα. Η αναγνώριση των άκρων ως «πρώτης τάξεως πολίτης» παράλληλα με κορυφές στο μοντέλο δεδομένων γραφήματος επιτρέπει στον υποκείμενο μηχανισμό βάσης δεδομένων να επαναλαμβάνεται πολύ γρήγορα προς οποιαδήποτε κατεύθυνση μέσω δικτύων κορυφών και άκρων για την ικανοποίηση ερωτημάτων εφαρμογής, μια διαδικασία γνωστή ως εγκάρσια.

Η ευελιξία του μοντέλου δεδομένων γραφημάτων είναι ένας βασικός παράγοντας που οδηγεί στην πρόσφατη αύξηση της δημοτικότητας της βάσης δεδομένων γραφημάτων. Οι ίδιες απαιτήσεις για διαθεσιμότητα και μαζική κλίμακα που οδήγησαν στην ανάπτυξη και υιοθέτηση διαφόρων προσφορών NoSQL τα τελευταία 10 περίπου χρόνια συνεχίζουν να αποδίδουν καρπούς στην πρόσφατη τάση του γραφήματος.

Πώς να ξέρετε πότε χρειάζεστε μια βάση δεδομένων γραφήματος

Ωστόσο, όπως συμβαίνει με οποιαδήποτε δημοφιλή τεχνολογία, μπορεί να υπάρχει η τάση να εφαρμόζονται βάσεις δεδομένων γραφημάτων σε κάθε πρόβλημα. Είναι σημαντικό να βεβαιωθείτε ότι έχετε μια θήκη χρήσης που είναι κατάλληλη. Για παράδειγμα, τα γραφήματα εφαρμόζονται συχνά σε προβληματικούς τομείς όπως:

  • Κοινωνικά δίκτυα
  • Σύσταση και εξατομίκευση
  • Πελάτης 360, συμπεριλαμβανομένης της ανάλυσης οντοτήτων (συσχετίζοντας δεδομένα χρηστών από πολλές πηγές)
  • Ανίχνευση απάτης
  • Διαχείριση περιουσιακών στοιχείων

Είτε η θήκη χρήσης σας ταιριάζει σε έναν από αυτούς τους τομείς είτε όχι, υπάρχουν ορισμένοι άλλοι παράγοντες που πρέπει να λάβετε υπόψη που μπορούν να σας βοηθήσουν να προσδιορίσετε εάν μια βάση δεδομένων γραφημάτων είναι κατάλληλη για εσάς:

  • Πολλές σε πολλές σχέσεις. Στο βιβλίο του «Σχεδιασμός Εντατικών Εφαρμογών Δεδομένων» (O’Reilly), ο Martin Kleppmann προτείνει ότι οι συχνές σχέσεις πάρα-προς-πολλά στον προβληματικό τομέα σας είναι ένας καλός δείκτης για τη χρήση γραφημάτων, καθώς οι σχεσιακές βάσεις δεδομένων τείνουν να αγωνίζονται για την αποτελεσματική πλοήγηση αυτών των σχέσεων.
  • Υψηλή αξία σχέσεων. Ένα άλλο ευρετικό που έχω ακούσει συχνά: εάν οι σχέσεις μεταξύ των στοιχείων δεδομένων σας είναι εξίσου σημαντικές ή πιο σημαντικές από τα ίδια τα στοιχεία, θα πρέπει να εξετάσετε το ενδεχόμενο χρήσης γραφήματος.
  • Χαμηλή καθυστέρηση σε μεγάλη κλίμακα. Η προσθήκη άλλης βάσης δεδομένων στην εφαρμογή σας προσθέτει επίσης την πολυπλοκότητα στην εφαρμογή σας. Η ικανότητα των βάσεων δεδομένων γραφημάτων να πλοηγείται στις σχέσεις που αντιπροσωπεύονται σε μεγάλα σύνολα δεδομένων πιο γρήγορα από άλλους τύπους βάσεων δεδομένων είναι αυτό που δικαιολογεί αυτήν την πρόσθετη πολυπλοκότητα. Αυτό ισχύει ιδιαίτερα σε περιπτώσεις όπου ένα σύνθετο σχετικό ερώτημα συμμετοχής δεν εκτελείται πλέον και δεν υπάρχουν πρόσθετα οφέλη βελτιστοποίησης για το ερώτημα ή τη σχεσιακή δομή.

Ορισμός σχήματος γραφημάτων και ερωτημάτων με το Gremlin

Ας ρίξουμε μια ματιά στο πώς να ξεκινήσετε τη χρήση μιας βάσης δεδομένων γραφημάτων χρησιμοποιώντας ένα πραγματικό παράδειγμα, το σύστημα σύστασης που προσθέσαμε πρόσφατα στο KillrVideo. Το KillrVideo είναι μια εφαρμογή αναφοράς για κοινή χρήση και παρακολούθηση βίντεο που δημιουργήσαμε για να βοηθήσουμε τους προγραμματιστές να μάθουν πώς να χρησιμοποιούν το DataStax Enterprise, συμπεριλαμβανομένου του DataStax Enterprise Graph, μιας βάσης δεδομένων γραφημάτων που δημιουργήθηκε πάνω από εξαιρετικά επεκτάσιμες τεχνολογίες δεδομένων, συμπεριλαμβανομένων των Apache Cassandra και Apache Spark.

Η γλώσσα που χρησιμοποιείται για την περιγραφή και την αλληλεπίδραση με γραφήματα στο DataStax Enterprise Graph είναι η Gremlin, η οποία είναι μέρος του έργου Apache TinkerPop. Το Gremlin είναι γνωστό ως η γλώσσα μετάβασης στην περιγραφή των διαβάσεων γραφημάτων λόγω της ευελιξίας, της επεκτασιμότητας και της υποστήριξής του τόσο για δηλωτικά όσο και για επιτακτικά ερωτήματα. Το Gremlin βασίζεται στη γλώσσα Groovy και τα προγράμματα οδήγησης είναι διαθέσιμα σε πολλές γλώσσες. Το πιο σημαντικό, το Gremlin υποστηρίζεται από τις πιο δημοφιλείς βάσεις δεδομένων γραφημάτων, συμπεριλαμβανομένων των DataStax Enterprise Graph, Neo4j, AWS Neptune και Azure Cosmos DB.

Σχεδιάσαμε έναν αλγόριθμο προτάσεων για να προσδιορίσουμε τα δεδομένα που θα χρειαζόμασταν ως εισαγωγή. Η προσέγγιση ήταν να δημιουργηθούν προτάσεις για έναν δεδομένο χρήστη με βάση βίντεο που τους άρεσαν παρόμοιοι χρήστες. Στόχος μας ήταν να δημιουργήσουμε προτάσεις σε πραγματικό χρόνο καθώς οι χρήστες αλληλεπιδρούν με την εφαρμογή KillrVideo, δηλαδή ως αλληλεπίδραση OLTP.

Για να καθορίσουμε το σχήμα, εντοπίσαμε ένα υποσύνολο των δεδομένων που διαχειρίζεται το KillrVideo που χρειαζόμασταν για το γράφημα μας. Αυτό περιελάμβανε χρήστες, βίντεο, βαθμολογίες και ετικέτες, καθώς και ιδιότητες αυτών των στοιχείων που ενδέχεται να αναφέρουμε στον αλγόριθμο ή να παρουσιάσουμε στα αποτελέσματα προτάσεων. Στη συνέχεια δημιουργήσαμε ένα σχήμα γραφήματος στο Gremlin που μοιάζει με αυτό:

// δημιουργία ετικετών κορυφής

schema.vertexLabel ("user"). partitionKey ("userId").

ιδιότητες ("userId", "email", "add_date"). ifNotExists (). create ();

schema.vertexLabel ("video"). partitionKey ("videoId").

ιδιότητες ("videoId", "name", "description", "add_date",

preview_image_location "). ifNotExists (). δημιουργία ();

schema.vertexLabel ("tag"). partitionKey ("όνομα").

ιδιότητες ("name", "tagged_date"). ifNotExists (). create ();

// δημιουργήστε ετικέτες άκρης

schema.edgeLabel ("rated"). πολλαπλές (). ιδιότητες ("rating").

σύνδεση ("χρήστης", "βίντεο"). ifNotExists (). create ();

schema.edgeLabel ("uploaded"). single (). properties ("add_date").

σύνδεση ("χρήστης", "βίντεο"). ifNotExists (). create ();

schema.edgeLabel ("taggedWith"). single ().

σύνδεση ("βίντεο", "ετικέτα"). ifNotExists (). create ();

Επιλέξαμε να μοντελοποιήσουμε τους χρήστες, τα βίντεο και τις ετικέτες ως κορυφές και χρησιμοποιήσαμε τις άκρες για να προσδιορίσουμε ποιοι χρήστες ανέβασαν ποια βίντεο, αξιολογήσεις χρηστών για βίντεο και τις ετικέτες που σχετίζονται με κάθε βίντεο. Αναθέσαμε ιδιότητες σε κορυφές και ακμές που αναφέρονται σε ερωτήματα ή περιλαμβάνονται στα αποτελέσματα. Το σχήμα που προκύπτει μοιάζει με αυτό στο DataStax Studio, ένα εργαλείο προγραμματισμού στυλ φορητού υπολογιστή για την ανάπτυξη και εκτέλεση ερωτημάτων σε CQL και Gremlin.

Με βάση αυτό το σχήμα, ορίσαμε ερωτήματα που συμπληρώνουν δεδομένα στο γράφημα και ερωτήματα που ανακτούν δεδομένα από το γράφημα. Ας δούμε ένα ερώτημα γραφήματος που δημιουργεί προτάσεις. Ακολουθεί η βασική ροή: Για έναν δεδομένο χρήστη, προσδιορίστε παρόμοιους χρήστες που άρεσαν τα βίντεο που άρεσε ο συγκεκριμένος χρήστης, επιλέξτε βίντεο που άρεσαν επίσης οι παρόμοιοι χρήστες, εξαιρέστε βίντεο που έχει ήδη δει ο συγκεκριμένος χρήστης, παραγγείλετε αυτά τα βίντεο με δημοτικότητα και παρέχετε τα αποτελέσματα.

def numRatingsToSample = 1000

def localUserRatingsToSample = 10

def minPositiveRating = 4

def userID = ...

g.V (). έχει ("user", "userId", userID) .as ("^ currentUser")

// λάβετε όλα τα βίντεο που παρακολούθησε ο χρήστης και αποθηκεύστε τα

.map (out ("rated"). dedup (). fold ()). as ("^ παρακολουθήθηκε βίντεο")

// επιστρέψτε στον τρέχοντα χρήστη

.select ("^ currentUser")

// προσδιορίστε τα βίντεο που ο χρήστης αξιολόγησε πολύ

.outE («βαθμολογία»). έχει («βαθμολογία», gte (minPositiveRating)). inV ()

// ποιοι άλλοι χρήστες βαθμολόγησαν αυτά τα βίντεο;

.inE («βαθμολογία»). έχει («βαθμολογία», gte (minPositiveRating))

// περιορίστε τον αριθμό των αποτελεσμάτων, ώστε αυτό να λειτουργήσει ως ερώτημα OLTP

. δείγμα (numRatingsToSample)

// ταξινόμηση κατά βαθμολογία για να ευνοήσει τους χρήστες που αξιολόγησαν αυτά τα βίντεο με τον υψηλότερο

.by («βαθμολογία»). outV ()

// εξαλείψτε τον τρέχοντα χρήστη

.where (neq ("^ currentUser"))

Ας σταματήσουμε για μια στιγμή για να πάρουμε την ανάσα μας. Μέχρι στιγμής σε αυτήν τη διασταύρωση έχουμε εντοπίσει παρόμοιους χρήστες. Το δεύτερο μέρος της διέλευσης παίρνει αυτούς τους παρόμοιους χρήστες, τραβά έναν περιορισμένο αριθμό βίντεο που άρεσαν σε παρόμοιοι χρήστες, καταργεί τα βίντεο που έχει ήδη παρακολουθήσει ο χρήστης και δημιουργεί ένα σύνολο αποτελεσμάτων ταξινομημένο κατά δημοτικότητα.

  // επιλέξτε έναν περιορισμένο αριθμό βίντεο με υψηλή βαθμολογία από κάθε παρόμοιο χρήστη

.local (outE (‘rated’). has (‘rating’, gte (minPositiveRating)). limit (localUserRatingsToSample)). sack (assign) .by (‘rating’). inV ()

// εξαιρέστε βίντεο που έχει ήδη παρακολουθήσει ο χρήστης

.not (όπου (εντός ("^ παρακολουθήθηκαν βίντεο")))

// προσδιορίστε τα πιο δημοφιλή βίντεο με άθροισμα όλων των αξιολογήσεων

.group (). by (). by (σάκος (). άθροισμα ())

// τώρα που έχουμε έναν μεγάλο χάρτη [βίντεο: σκορ], παραγγείλτε το

.order (local) .by (τιμές, decr) .limit (local, 100). select (keys). unfold ()

// προτεινόμενα βίντεο εξόδου, συμπεριλαμβανομένου του χρήστη που ανέβασε κάθε βίντεο

.project («βίντεο», «χρήστης»)

.με()

.by (__. σε («μεταφορτωμένο»))

Ενώ αυτή η διασταύρωση φαίνεται περίπλοκη, λάβετε υπόψη ότι είναι ολόκληρη η επιχειρησιακή λογική ενός αλγορίθμου προτάσεων. Δεν θα διερευνήσουμε λεπτομερώς κάθε βήμα αυτής της διέλευσης εδώ, αλλά η αναφορά γλώσσας είναι ένας εξαιρετικός πόρος και διατίθενται μαθήματα κατάρτισης υψηλής ποιότητας.

Συνιστώ την ανάπτυξη διαδραστικών διαδραστικών διαδικασιών μέσω ενός αντιπροσωπευτικού συνόλου δεδομένων χρησιμοποιώντας ένα εργαλείο όπως το DataStax Studio ή την κονσόλα Gremlin από το Apache TinkerPop. Αυτό σας επιτρέπει να επαναλάβετε γρήγορα και να βελτιώσετε τις διασταυρώσεις σας. Το DataStax Studio είναι ένα περιβάλλον που βασίζεται στον Ιστό και παρέχει πολλούς τρόπους για να απεικονίσετε τα αποτελέσματα διασταύρωσης ως δίκτυα κόμβων και άκρων, όπως φαίνεται στην παρακάτω εικόνα. Άλλες υποστηριζόμενες προβολές περιλαμβάνουν πίνακες, γραφήματα και γραφήματα, καθώς και παρακολούθηση απόδοσης.

DataStax

Ενσωμάτωση βάσης δεδομένων γραφημάτων στην αρχιτεκτονική σας

Αφού σχεδιάσετε το σχήμα γραφήματος και τα ερωτήματά σας, ήρθε η ώρα να ενσωματώσετε το γράφημα στην εφαρμογή σας. Δείτε πώς ενσωματώσαμε το DataStax Enterprise Graph στο KillrVideo. Η αρχιτεκτονική πολλαπλών επιπέδων του KillrVideo αποτελείται από μια εφαρμογή ιστού που βρίσκεται πάνω από ένα σύνολο μικροϋπηρεσιών που διαχειρίζονται χρήστες, βίντεο (συμπεριλαμβανομένων ετικετών) και βαθμολογίες. Αυτές οι υπηρεσίες αξιοποιούν τη βάση δεδομένων DataStax Enterprise Graph (ενσωματωμένη στο Apache Cassandra) για αποθήκευση δεδομένων και πρόσβαση στα δεδομένα χρησιμοποιώντας CQL.

Εφαρμόσαμε τη μηχανή προτάσεων ως μέρος της υπηρεσίας προτεινόμενων βίντεο, όπως φαίνεται παρακάτω. Αυτή η υπηρεσία δημιουργεί μια λίστα με προτάσεις που δίνουν ένα αναγνωριστικό χρήστη. Για να υλοποιήσουμε τη μηχανή προτάσεων, μεταφράσαμε το Gremlin traversal που περιγράφεται παραπάνω σε κώδικα Java.

DataStax

Αυτή η αρχιτεκτονική υπογραμμίζει μια συχνή πρόκληση στις αρχιτεκτονικές μικροϋπηρεσιών - την ανάγκη αλληλεπίδρασης με δεδομένα που ανήκουν σε πολλές υπηρεσίες. Όπως φαίνεται παραπάνω, το γράφημα που χρησιμοποιείται για τη δημιουργία προτάσεων βασίζεται σε δεδομένα από τις υπηρεσίες διαχείρισης χρηστών, καταλόγου βίντεο και αξιολογήσεων.

Διατηρήσαμε την ιδιοκτησία δεδομένων των υπαρχουσών υπηρεσιών μας χρησιμοποιώντας ασύγχρονα μηνύματα. Οι υπηρεσίες διαχείρισης χρηστών, καταλόγου βίντεο και αξιολογήσεων δημοσιεύουν συμβάντα σχετικά με τις αλλαγές δεδομένων. Η υπηρεσία προτεινόμενων βίντεο εγγράφεται σε αυτά τα συμβάντα και πραγματοποιεί αντίστοιχες ενημερώσεις στο γράφημα. Οι αντισταθμίσεις που κάναμε εδώ είναι χαρακτηριστικές εφαρμογών που χρησιμοποιούν μια προσέγγιση πολλαπλών μοντέλων, ένα θέμα που εξερεύνησα στο προηγούμενο άρθρο μου.

Εφαρμογή του Gremlin traversals στην Java

Το DataStax Java Driver παρέχει ένα φιλικό, άπτατο API για την εφαρμογή Gremlin traversals με το DataStax Enterprise Graph. Το API το κατέστησε ασήμαντο για τη μετεγκατάσταση ερωτημάτων με βάση το Groovy που δημιουργήσαμε στο DataStax Studio σε κώδικα Java.

Στη συνέχεια, καταφέραμε να κάνουμε τον κώδικα Java ακόμη πιο αναγνώσιμο και διατηρήσιμο χρησιμοποιώντας μια λειτουργία Gremlin γνωστή ως DSL, συγκεκριμένες γλώσσες τομέα. Ένα DSL είναι μια επέκταση του Gremlin σε έναν συγκεκριμένο τομέα. Για το KillrVideo, δημιουργήσαμε ένα DSL για να επεκτείνουμε την εφαρμογή διέλευσης του Gremlin με όρους που σχετίζονται με τον τομέα βίντεο. ο KillrVideoTraversalDsl Η κλάση ορίζει λειτουργίες ερωτήματος όπως το uσερ (), που εντοπίζει την κορυφή στο γράφημα με ένα παρεχόμενο UUID, και προτείνωByUserRating (), η οποία δημιουργεί προτάσεις για έναν παρεχόμενο χρήστη με βάση παραμέτρους, όπως μια ελάχιστη βαθμολογία και έναν απαιτούμενο αριθμό προτάσεων.

Η χρήση ενός DSL απλοποίησε την εφαρμογή της Υπηρεσίας Προτεινόμενων Βίντεο σε κάτι σαν το παρακάτω δείγμα, το οποίο δημιουργεί ένα Γραφική παράσταση στη συνέχεια εκτελούμε χρησιμοποιώντας το πρόγραμμα οδήγησης Java DataStax:

GraphStatement gStatement = DseGraph.statementFromTraversal (killr.users (userIdString))

.recommendByUserRating (100, 4, 500, 10)

);

Η χρήση ενός DSL μας επέτρεψε να κρύψουμε κάποια από την πολυπλοκότητα των αλληλεπιδράσεων γραφήματος σε επαναχρησιμοποιήσιμες συναρτήσεις, οι οποίες μπορούν στη συνέχεια να συνδυαστούν όπως απαιτείται για να σχηματίσουν πιο περίπλοκες διαβάσεις. Αυτό θα μας επιτρέψει να εφαρμόσουμε πρόσθετες μηχανές προτάσεων που ξεκινούν από μια επιλεγμένη κορυφή χρήστη που παρέχεται από το χρήστης() μέθοδο και επιτρέψτε στην εφαρμογή να ανταλλάξει μεταξύ των διαφορετικών εφαρμογών.

Ένα παράδειγμα γραφήματος εργασίας

Μπορείτε να δείτε τα αποτελέσματα της ενσωμάτωσης του DataStax Enterprise Graph στο KillrVideo στην ενότητα "Συνιστάται για εσάς" της εφαρμογής ιστού που φαίνεται παρακάτω. Δοκιμάστε το μόνοι σας στη διεύθυνση //www.killrvideo.com δημιουργώντας έναν λογαριασμό και βαθμολογώντας μερικά βίντεο.

DataStax

Ελπίζω ότι αυτό το άρθρο θα σας δώσει μερικές εξαιρετικές ιδέες για το πώς μια βάση δεδομένων γραφημάτων μπορεί να έχει νόημα για την εφαρμογή σας και πώς να ξεκινήσετε με το Gremlin και το DataStax Enterprise Graph.

Ο Jeff Carpenter είναι τεχνικός ευαγγελιστής στο DataStax, όπου αξιοποιεί το ιστορικό του στην αρχιτεκτονική του συστήματος, στις μικροεπηρεσίες και στο Apache Cassandra για να βοηθήσει τους προγραμματιστές και τους μηχανικούς λειτουργιών να δημιουργήσουν κατανεμημένα συστήματα που είναι επεκτάσιμα, αξιόπιστα και ασφαλή. Ο Jeff είναι ο συγγραφέας του Cassandra: The Definitive Guide, 2η Έκδοση.

Το New Tech Forum παρέχει έναν χώρο για να εξερευνήσετε και να συζητήσετε την αναδυόμενη τεχνολογία σε πρωτοφανές βάθος και εύρος. Η επιλογή είναι υποκειμενική, με βάση την επιλογή των τεχνολογιών που πιστεύουμε ότι είναι σημαντικές και έχουν μεγάλο ενδιαφέρον για τους αναγνώστες. δεν αποδέχεται ασφάλεια μάρκετινγκ για δημοσίευση και διατηρεί το δικαίωμα να επεξεργαστεί όλο το περιεχόμενο. Στείλτε όλες τις ερωτήσεις στο[email protected].

$config[zx-auto] not found$config[zx-overlay] not found